From 516608340616cf081cf96fedaffee4600613f352 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 14 Apr 2024 21:03:51 +0800 Subject: [PATCH 01/40] Fix. Msi, reg add SoftwareSASGeneration (#7708) Signed-off-by: fufesou --- res/msi/CustomActions/CustomActions.cpp | 52 ++++++++++++++++++++- res/msi/CustomActions/CustomActions.def | 1 + res/msi/Package/Components/RustDesk.wxs | 2 + res/msi/Package/Fragments/CustomActions.wxs | 1 + 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 7f7f24eeea..37a728a4ba 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -287,7 +287,7 @@ UINT __stdcall AddFirewallRules( LPWSTR pwzData = NULL; size_t szNameLen = 0; - hr = WcaInitialize(hInstall, "AddFirewallExceptions"); + hr = WcaInitialize(hInstall, "AddFirewallRules"); ExitOnFailure(hr, "Failed to initialize"); hr = WcaGetProperty(L"CustomActionData", &pwzData); @@ -541,3 +541,53 @@ UINT __stdcall SetPropertyFromConfig(__in MSIHANDLE hInstall) er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } + +UINT __stdcall AddRegSoftwareSASGeneration(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + LSTATUS result = 0; + HKEY hKey; + LPCWSTR subKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; + LPCWSTR valueName = L"SoftwareSASGeneration"; + DWORD valueType = REG_DWORD; + DWORD valueData = 1; + DWORD valueDataSize = sizeof(DWORD); + + HINSTANCE hi = 0; + + hr = WcaInitialize(hInstall, "AddRegSoftwareSASGeneration"); + ExitOnFailure(hr, "Failed to initialize"); + + hi = ShellExecuteW(NULL, L"open", L"reg", L" add HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System /f /v SoftwareSASGeneration /t REG_DWORD /d 1", NULL, SW_HIDE); + // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew + if ((int)hi <= 32) { + WcaLog(LOGMSG_STANDARD, "Failed to add registry name \"%ls\", %d, %d", valueName, (int)hi, GetLastError()); + } + else { + WcaLog(LOGMSG_STANDARD, "Registry name \"%ls\" is added", valueName); + } + + // Why RegSetValueExW always return 998? + // + result = RegCreateKeyExW(HKEY_LOCAL_MACHINE, subKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); + if (result != ERROR_SUCCESS) { + WcaLog(LOGMSG_STANDARD, "Failed to create or open registry key: %d", result); + goto LExit; + } + + result = RegSetValueExW(hKey, valueName, 0, valueType, reinterpret_cast(valueData), valueDataSize); + if (result != ERROR_SUCCESS) { + WcaLog(LOGMSG_STANDARD, "Failed to set registry value: %d", result); + RegCloseKey(hKey); + goto LExit; + } + + WcaLog(LOGMSG_STANDARD, "Registry value has been successfully set."); + RegCloseKey(hKey); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/res/msi/CustomActions/CustomActions.def b/res/msi/CustomActions/CustomActions.def index a089c583e0..a73ecda266 100644 --- a/res/msi/CustomActions/CustomActions.def +++ b/res/msi/CustomActions/CustomActions.def @@ -10,3 +10,4 @@ EXPORTS CreateStartService TryDeleteStartupShortcut SetPropertyFromConfig + AddRegSoftwareSASGeneration diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index a44d8bb73c..df9463fe87 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -58,6 +58,8 @@ + + diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index 0454a6731f..e064b188e1 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -15,5 +15,6 @@ + From a7a10f4eaaacdb6910c8fe4e374ac5d3a91c6be3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 14 Apr 2024 21:25:26 +0800 Subject: [PATCH 02/40] Refact. Win, file copy paste, default true (#7719) * Refact. Win, file copy paste, default true Signed-off-by: fufesou * Fix. File copy and paste menu, compatible with 1.2.3 Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/common/widgets/toolbar.dart | 11 +++++++++-- libs/hbb_common/src/config.rs | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index a8638a302c..e38e3fb752 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -441,10 +441,17 @@ Future> toolbarDisplayToggle( child: Text(translate('Mute')))); } // file copy and paste + // If the version is less than 1.2.4, file copy and paste is supported on Windows only. + final isSupportIfPeer_1_2_3 = versionCmp(pi.version, '1.2.4') < 0 && + isWindows && + pi.platform == kPeerPlatformWindows; + // If the version is 1.2.4 or later, file copy and paste is supported when kPlatformAdditionsHasFileClipboard is set. + final isSupportIfPeer_1_2_4 = versionCmp(pi.version, '1.2.4') >= 0 && + bind.mainHasFileClipboard() && + pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard); if (ffiModel.keyboard && perms['file'] != false && - bind.mainHasFileClipboard() && - pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard)) { + (isSupportIfPeer_1_2_3 || isSupportIfPeer_1_2_4)) { final enabled = !ffiModel.viewOnly; final option = 'enable-file-transfer'; final value = diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 1c58ae9b29..4acfb2cdf8 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1561,6 +1561,7 @@ impl UserDefaultConfig { } "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 0xFFF as f64), "custom-fps" => self.get_double_string(key, 30.0, 5.0, 120.0), + "enable_file_transfer" => self.get_string(key, "Y", vec![""]), _ => self .get_after(key) .map(|v| v.to_string()) From 7f3775a061b237897a5f2c00b92d2714e2a7fab4 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sun, 14 Apr 2024 16:52:27 +0000 Subject: [PATCH 03/40] update system2 (#7722) --- build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.py b/build.py index cbf9d00a2a..ebea1f34dc 100755 --- a/build.py +++ b/build.py @@ -33,9 +33,9 @@ def get_arch() -> str: def system2(cmd): - err = os.system(cmd) - if err != 0: - print(f"Error occurred when executing: {cmd}. Exiting.") + exit_code = os.system(cmd) + if exit_code != 0: + sys.stderr.write(f"Error occurred when executing: `{cmd}`. Exiting.\n") sys.exit(-1) From 0dba37f4f7704d6c59b3f5f752f7a37a9bae05a3 Mon Sep 17 00:00:00 2001 From: alewicki95 <160427461+alewicki95@users.noreply.github.com> Date: Mon, 15 Apr 2024 05:13:32 +0200 Subject: [PATCH 04/40] Update pl.rs (#7723) --- src/lang/pl.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 03e4be45ee..c32931661b 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -69,7 +69,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Retry", "Ponów"), ("OK", "OK"), ("Password Required", "Wymagane jest hasło"), - ("Please enter your password", "Wpisz proszę twoje hasło"), + ("Please enter your password", "Wpisz proszę Twoje hasło"), ("Remember password", "Zapamiętaj hasło"), ("Wrong Password", "Błędne hasło"), ("Do you want to enter again?", "Czy chcesz wprowadzić ponownie?"), @@ -105,7 +105,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Are you sure you want to delete this file?", "Czy na pewno chcesz usunąć ten plik?"), ("Are you sure you want to delete this empty directory?", "Czy na pewno chcesz usunąć ten pusty katalog?"), ("Are you sure you want to delete the file of this directory?", "Czy na pewno chcesz usunąć pliki z tego katalogu?"), - ("Do this for all conflicts", "wykonaj dla wszystkich konfliktów"), + ("Do this for all conflicts", "Wykonaj dla wszystkich konfliktów"), ("This is irreversible!", "To jest nieodwracalne!"), ("Deleting", "Usuwanie"), ("files", "pliki"), @@ -585,21 +585,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("2FA code must be 6 digits.", "Kod 2FA musi zawierać 6 cyfr."), ("Multiple Windows sessions found", "Znaleziono wiele sesji Windows"), ("Please select the session you want to connect to", "Wybierz sesję, do której chcesz się podłączyć"), - ("powered_by_me", ""), - ("outgoing_only_desk_tip", ""), - ("preset_password_warning", ""), - ("Security Alert", ""), - ("My address book", ""), - ("Personal", ""), - ("Owner", ""), - ("Set shared password", ""), - ("Exist in", ""), - ("Read-only", ""), - ("Read/Write", ""), - ("Full Control", ""), - ("share_warning_tip", ""), - ("Everyone", ""), - ("ab_web_console_tip", ""), - ("allow-only-conn-window-open-tip", ""), + ("powered_by_me", "Rowiązanie RustDesk"), + ("outgoing_only_desk_tip", "To jest spersonalizowana edycja. Możesz łączyć się z innymi urządzeniami, ale inne urządzenia nie mogą połączyć się z urządzeniem."), + ("preset_password_warning", "Ta spersonalizowana edycja jest wyposażona w wstępnie ustawione hasło. Każdy, kto zna to hasło, może uzyskać pełną kontrolę nad Twoim urządzeniem. Jeśli się tego nie spodziewałeś, natychmiast odinstaluj oprogramowanie."), + ("Security Alert", "Alert bezpieczeństwa"), + ("My address book", "Moja książka adresowa"), + ("Personal", "Osobiste"), + ("Owner", "Właściciel"), + ("Set shared password", "Ustaw hasło udostępniania"), + ("Exist in", "Istnieje w"), + ("Read-only", "Tylko do odczytu"), + ("Read/Write", "Odczyt/Zapis"), + ("Full Control", "Pełna kontrola"), + ("share_warning_tip", "Powyższe pola są udostępniane i widoczne dla innych."), + ("Everyone", "Wszyscy"), + ("ab_web_console_tip", "Więcej w konsoli web"), + ("allow-only-conn-window-open-tip", "Zezwalaj na połączenie tylko wtedy, gdy okno RustDesk jest otwarte"), ].iter().cloned().collect(); } From cdd92303b8a9612f8bb03a7cae62a5ec48df7685 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 15 Apr 2024 15:20:27 +0800 Subject: [PATCH 05/40] refactor android ffi --- .../com/carriez/flutter_hbb/MainService.kt | 20 ++----------------- flutter/android/app/src/main/kotlin/ffi.kt | 19 ++++++++++++++++++ src/flutter_ffi.rs | 8 ++++---- 3 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 flutter/android/app/src/main/kotlin/ffi.kt diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index edec8c42d2..1730e3527d 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -1,5 +1,7 @@ package com.carriez.flutter_hbb +import ffi.* + /** * Capture screen,get video and audio,send to rust. * Dispatch notifications @@ -64,10 +66,6 @@ const val AUDIO_CHANNEL_MASK = AudioFormat.CHANNEL_IN_STEREO class MainService : Service() { - init { - System.loadLibrary("rustdesk") - } - @Keep @RequiresApi(Build.VERSION_CODES.N) fun rustPointerInput(kind: String, mask: Int, x: Int, y: Int) { @@ -156,20 +154,6 @@ class MainService : Service() { private val powerManager: PowerManager by lazy { applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager } private val wakeLock: PowerManager.WakeLock by lazy { powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "rustdesk:wakelock")} - // jvm call rust - private external fun init(ctx: Context) - - /// When app start on boot, app_dir will not be passed from flutter - /// so pass a app_dir here to rust server - private external fun startServer(app_dir: String) - private external fun startService() - private external fun onVideoFrameUpdate(buf: ByteBuffer) - private external fun onAudioFrameUpdate(buf: ByteBuffer) - private external fun translateLocale(localeName: String, input: String): String - private external fun refreshScreen() - private external fun setFrameRawEnable(name: String, value: Boolean) - // private external fun sendVp9(data: ByteArray) - private fun translate(input: String): String { Log.d(logTag, "translate:$LOCAL_NAME") return translateLocale(LOCAL_NAME, input) diff --git a/flutter/android/app/src/main/kotlin/ffi.kt b/flutter/android/app/src/main/kotlin/ffi.kt new file mode 100644 index 0000000000..e2382c6475 --- /dev/null +++ b/flutter/android/app/src/main/kotlin/ffi.kt @@ -0,0 +1,19 @@ +// ffi.kt + +package ffi + +import android.content.Context +import java.nio.ByteBuffer + +init { + System.loadLibrary("rustdesk") +} + +external fun init(ctx: Context) +external fun startServer(app_dir: String) +external fun startService() +external fun onVideoFrameUpdate(buf: ByteBuffer) +external fun onAudioFrameUpdate(buf: ByteBuffer) +external fun translateLocale(localeName: String, input: String): String +external fun refreshScreen() +external fun setFrameRawEnable(name: String, value: Boolean) \ No newline at end of file diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 1c9e39c132..57919bd6eb 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2108,7 +2108,7 @@ pub mod server_side { use crate::start_server; #[no_mangle] - pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_startServer( + pub unsafe extern "system" fn Java_ffi_startServer( env: JNIEnv, _class: JClass, app_dir: JString, @@ -2122,7 +2122,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_startService( + pub unsafe extern "system" fn Java_ffi_startService( _env: JNIEnv, _class: JClass, ) { @@ -2132,7 +2132,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_translateLocale( + pub unsafe extern "system" fn Java_ffi_translateLocale( env: JNIEnv, _class: JClass, locale: JString, @@ -2151,7 +2151,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_refreshScreen( + pub unsafe extern "system" fn Java_ffi_refreshScreen( _env: JNIEnv, _class: JClass, ) { From 260d0cdc678b19fe4b1497784d559f3767c5c466 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 15 Apr 2024 15:37:33 +0800 Subject: [PATCH 06/40] fix me --- .../com/carriez/flutter_hbb/MainService.kt | 22 ++++++++--------- flutter/android/app/src/main/kotlin/ffi.kt | 24 ++++++++++--------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 1730e3527d..b3b00b6255 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -1,6 +1,6 @@ package com.carriez.flutter_hbb -import ffi.* +import ffi.RustDesk /** * Capture screen,get video and audio,send to rust. @@ -195,7 +195,7 @@ class MainService : Service() { override fun onCreate() { super.onCreate() Log.d(logTag,"MainService onCreate") - init(this) + RustDesk.init(this) HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply { start() serviceLooper = looper @@ -207,7 +207,7 @@ class MainService : Service() { // keep the config dir same with flutter val prefs = applicationContext.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE) val configPath = prefs.getString(KEY_APP_DIR_CONFIG_PATH, "") ?: "" - startServer(configPath) + RustDesk.startServer(configPath) createForegroundNotification() } @@ -262,7 +262,7 @@ class MainService : Service() { SCREEN_INFO.dpi = dpi if (isStart) { stopCapture() - refreshScreen() + RustDesk.refreshScreen() startCapture() } } @@ -290,7 +290,7 @@ class MainService : Service() { createForegroundNotification() if (intent.getBooleanExtra(EXT_INIT_FROM_BOOT, false)) { - startService() + RustDesk.startService() } Log.d(logTag, "service starting: ${startId}:${Thread.currentThread()}") val mediaProjectionManager = @@ -343,7 +343,7 @@ class MainService : Service() { val planes = image.planes val buffer = planes[0].buffer buffer.rewind() - onVideoFrameUpdate(buffer) + RustDesk.onVideoFrameUpdate(buffer) } } catch (ignored: java.lang.Exception) { } @@ -377,16 +377,16 @@ class MainService : Service() { } checkMediaPermission() _isStart = true - setFrameRawEnable("video",true) - setFrameRawEnable("audio",true) + RustDesk.setFrameRawEnable("video",true) + RustDesk.setFrameRawEnable("audio",true) return true } @Synchronized fun stopCapture() { Log.d(logTag, "Stop Capture") - setFrameRawEnable("video",false) - setFrameRawEnable("audio",false) + RustDesk.setFrameRawEnable("video",false) + RustDesk.setFrameRawEnable("audio",false) _isStart = false // release video virtualDisplay?.release() @@ -521,7 +521,7 @@ class MainService : Service() { thread { while (audioRecordStat) { audioReader!!.readSync(audioRecorder!!)?.let { - onAudioFrameUpdate(it) + RustDesk.onAudioFrameUpdate(it) } } Log.d(logTag, "Exit audio thread") diff --git a/flutter/android/app/src/main/kotlin/ffi.kt b/flutter/android/app/src/main/kotlin/ffi.kt index e2382c6475..62d203a27b 100644 --- a/flutter/android/app/src/main/kotlin/ffi.kt +++ b/flutter/android/app/src/main/kotlin/ffi.kt @@ -5,15 +5,17 @@ package ffi import android.content.Context import java.nio.ByteBuffer -init { - System.loadLibrary("rustdesk") -} +object RustDesk { + init { + System.loadLibrary("rustdesk") + } -external fun init(ctx: Context) -external fun startServer(app_dir: String) -external fun startService() -external fun onVideoFrameUpdate(buf: ByteBuffer) -external fun onAudioFrameUpdate(buf: ByteBuffer) -external fun translateLocale(localeName: String, input: String): String -external fun refreshScreen() -external fun setFrameRawEnable(name: String, value: Boolean) \ No newline at end of file + external fun init(ctx: Context) + external fun startServer(app_dir: String) + external fun startService() + external fun onVideoFrameUpdate(buf: ByteBuffer) + external fun onAudioFrameUpdate(buf: ByteBuffer) + external fun translateLocale(localeName: String, input: String): String + external fun refreshScreen() + external fun setFrameRawEnable(name: String, value: Boolean) +} \ No newline at end of file From 56d353cb64fd215baa1e92cd14d3e8c64fb03e24 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 15 Apr 2024 15:39:25 +0800 Subject: [PATCH 07/40] fix me --- src/flutter_ffi.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 57919bd6eb..bfc6042eea 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2108,7 +2108,7 @@ pub mod server_side { use crate::start_server; #[no_mangle] - pub unsafe extern "system" fn Java_ffi_startServer( + pub unsafe extern "system" fn Java_ffi_RustDesk_startServer( env: JNIEnv, _class: JClass, app_dir: JString, @@ -2122,7 +2122,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_startService( + pub unsafe extern "system" fn Java_ffi_RustDesk_startService( _env: JNIEnv, _class: JClass, ) { @@ -2132,7 +2132,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_translateLocale( + pub unsafe extern "system" fn Java_ffi_RustDesk_translateLocale( env: JNIEnv, _class: JClass, locale: JString, @@ -2151,7 +2151,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_refreshScreen( + pub unsafe extern "system" fn Java_ffi_RustDesk_refreshScreen( _env: JNIEnv, _class: JClass, ) { From 05f6fde4672e3c9e85acf78b9f2cc0650efc9d32 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 15 Apr 2024 15:49:37 +0800 Subject: [PATCH 08/40] ffi.RustDesk -> ffi.FFI --- .../com/carriez/flutter_hbb/MainService.kt | 22 +++++++++---------- flutter/android/app/src/main/kotlin/ffi.kt | 2 +- src/flutter_ffi.rs | 8 +++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index b3b00b6255..9fea4d4d35 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -1,6 +1,6 @@ package com.carriez.flutter_hbb -import ffi.RustDesk +import ffi.FFI /** * Capture screen,get video and audio,send to rust. @@ -195,7 +195,7 @@ class MainService : Service() { override fun onCreate() { super.onCreate() Log.d(logTag,"MainService onCreate") - RustDesk.init(this) + FFI.init(this) HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply { start() serviceLooper = looper @@ -207,7 +207,7 @@ class MainService : Service() { // keep the config dir same with flutter val prefs = applicationContext.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE) val configPath = prefs.getString(KEY_APP_DIR_CONFIG_PATH, "") ?: "" - RustDesk.startServer(configPath) + FFI.startServer(configPath) createForegroundNotification() } @@ -262,7 +262,7 @@ class MainService : Service() { SCREEN_INFO.dpi = dpi if (isStart) { stopCapture() - RustDesk.refreshScreen() + FFI.refreshScreen() startCapture() } } @@ -290,7 +290,7 @@ class MainService : Service() { createForegroundNotification() if (intent.getBooleanExtra(EXT_INIT_FROM_BOOT, false)) { - RustDesk.startService() + FFI.startService() } Log.d(logTag, "service starting: ${startId}:${Thread.currentThread()}") val mediaProjectionManager = @@ -343,7 +343,7 @@ class MainService : Service() { val planes = image.planes val buffer = planes[0].buffer buffer.rewind() - RustDesk.onVideoFrameUpdate(buffer) + FFI.onVideoFrameUpdate(buffer) } } catch (ignored: java.lang.Exception) { } @@ -377,16 +377,16 @@ class MainService : Service() { } checkMediaPermission() _isStart = true - RustDesk.setFrameRawEnable("video",true) - RustDesk.setFrameRawEnable("audio",true) + FFI.setFrameRawEnable("video",true) + FFI.setFrameRawEnable("audio",true) return true } @Synchronized fun stopCapture() { Log.d(logTag, "Stop Capture") - RustDesk.setFrameRawEnable("video",false) - RustDesk.setFrameRawEnable("audio",false) + FFI.setFrameRawEnable("video",false) + FFI.setFrameRawEnable("audio",false) _isStart = false // release video virtualDisplay?.release() @@ -521,7 +521,7 @@ class MainService : Service() { thread { while (audioRecordStat) { audioReader!!.readSync(audioRecorder!!)?.let { - RustDesk.onAudioFrameUpdate(it) + FFI.onAudioFrameUpdate(it) } } Log.d(logTag, "Exit audio thread") diff --git a/flutter/android/app/src/main/kotlin/ffi.kt b/flutter/android/app/src/main/kotlin/ffi.kt index 62d203a27b..d61bccfd60 100644 --- a/flutter/android/app/src/main/kotlin/ffi.kt +++ b/flutter/android/app/src/main/kotlin/ffi.kt @@ -5,7 +5,7 @@ package ffi import android.content.Context import java.nio.ByteBuffer -object RustDesk { +object FFI { init { System.loadLibrary("rustdesk") } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index bfc6042eea..e27b4140e7 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2108,7 +2108,7 @@ pub mod server_side { use crate::start_server; #[no_mangle] - pub unsafe extern "system" fn Java_ffi_RustDesk_startServer( + pub unsafe extern "system" fn Java_ffi_FFI_startServer( env: JNIEnv, _class: JClass, app_dir: JString, @@ -2122,7 +2122,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_RustDesk_startService( + pub unsafe extern "system" fn Java_ffi_FFI_startService( _env: JNIEnv, _class: JClass, ) { @@ -2132,7 +2132,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_RustDesk_translateLocale( + pub unsafe extern "system" fn Java_ffi_FFI_translateLocale( env: JNIEnv, _class: JClass, locale: JString, @@ -2151,7 +2151,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_RustDesk_refreshScreen( + pub unsafe extern "system" fn Java_ffi_FFI_refreshScreen( _env: JNIEnv, _class: JClass, ) { From 28340c80dd3baaebb85d818ea3edc024a474476c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 15 Apr 2024 15:58:07 +0800 Subject: [PATCH 09/40] fix me --- .../app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 9fea4d4d35..973c2f1088 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -156,7 +156,7 @@ class MainService : Service() { private fun translate(input: String): String { Log.d(logTag, "translate:$LOCAL_NAME") - return translateLocale(LOCAL_NAME, input) + return FFI.translateLocale(LOCAL_NAME, input) } companion object { From e9a6ca8ebc670e257536b29fe34ed9732f4aa709 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 15 Apr 2024 21:19:46 +0800 Subject: [PATCH 10/40] typo --- res/msi/preprocess.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index f051db5eeb..bcf829c7cb 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -67,7 +67,7 @@ def make_parser(): "-m", "--manufacturer", type=str, - default="RustDesk", + default="PURSLANE", help="The app manufacturer.", ) return parser @@ -166,7 +166,7 @@ def func(lines, index_start): ) -def replace_app_name_in_lans(app_name): +def replace_app_name_in_langs(app_name): langs_dir = Path(sys.argv[0]).parent.joinpath("Package/Language") for file_path in langs_dir.glob("*.wxs"): with open(file_path, "r") as f: @@ -444,4 +444,4 @@ def init_global_vars(args): if not gen_custom_dialog_bitmaps(): sys.exit(-1) - replace_app_name_in_lans(args.app_name) + replace_app_name_in_langs(args.app_name) From 1d4c129e9c286f4e2bb866b1629788b6aa2ecabe Mon Sep 17 00:00:00 2001 From: flusheDData <116861809+flusheDData@users.noreply.github.com> Date: Tue, 16 Apr 2024 05:10:31 +0200 Subject: [PATCH 11/40] Update es.rs (#7733) tip translation added --- src/lang/es.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/es.rs b/src/lang/es.rs index 2c0d160acd..e9ac3a3c5a 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Los campos mostrados arriba son compartidos y visibles por otros."), ("Everyone", "Todos"), ("ab_web_console_tip", "Más en consola web"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Permitir la conexión solo si la ventana RusDesk está abierta"), ].iter().cloned().collect(); } From a5d02998ad37a40ebec2d1c57c8c6b3cfd134515 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 16 Apr 2024 13:09:20 +0800 Subject: [PATCH 12/40] Fix. Msi, remove RustDesk words (#7732) * Fix. Msi, remove RustDesk words Signed-off-by: fufesou * Fix. Replace RustDesk in langs Signed-off-by: fufesou --------- Signed-off-by: fufesou --- res/msi/Package/Components/Regs.wxs | 2 +- res/msi/Package/Components/RustDesk.wxs | 22 +++++++++---------- .../Package/Fragments/AddRemoveProperties.wxs | 4 ++-- res/msi/Package/Package.wxs | 4 ++-- res/msi/preprocess.py | 3 ++- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/res/msi/Package/Components/Regs.wxs b/res/msi/Package/Components/Regs.wxs index 4b5efe95fe..23d4b6b8d5 100644 --- a/res/msi/Package/Components/Regs.wxs +++ b/res/msi/Package/Components/Regs.wxs @@ -41,7 +41,7 @@ - + diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index df9463fe87..ab1d404ecd 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -5,9 +5,9 @@ - - - + + + @@ -18,14 +18,14 @@ - - - + + + - + @@ -84,7 +84,7 @@ - + - + diff --git a/res/msi/Package/Fragments/AddRemoveProperties.wxs b/res/msi/Package/Fragments/AddRemoveProperties.wxs index f568b963de..f93852867a 100644 --- a/res/msi/Package/Fragments/AddRemoveProperties.wxs +++ b/res/msi/Package/Fragments/AddRemoveProperties.wxs @@ -25,8 +25,8 @@ - - + + diff --git a/res/msi/Package/Package.wxs b/res/msi/Package/Package.wxs index bc07ca5365..8361e2a121 100644 --- a/res/msi/Package/Package.wxs +++ b/res/msi/Package/Package.wxs @@ -22,7 +22,7 @@ - + @@ -45,7 +45,7 @@ - + diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index bcf829c7cb..9e10c3160b 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -13,6 +13,7 @@ g_version = "" g_build_date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M") +# Replace the following links with your own in the custom arp properties. # https://learn.microsoft.com/en-us/windows/win32/msi/property-reference g_arpsystemcomponent = { "Comments": { @@ -168,7 +169,7 @@ def func(lines, index_start): def replace_app_name_in_langs(app_name): langs_dir = Path(sys.argv[0]).parent.joinpath("Package/Language") - for file_path in langs_dir.glob("*.wxs"): + for file_path in langs_dir.glob("*.wxl"): with open(file_path, "r") as f: lines = f.readlines() for i, line in enumerate(lines): From 9b5e5aa47424a401e58c15303f056397d7c0975c Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 16 Apr 2024 14:12:39 +0800 Subject: [PATCH 13/40] fix: Msi custom app, different component guids (#7738) * fix: Msi custom app, different component guids Signed-off-by: fufesou * fix: msi, update readme Signed-off-by: fufesou --------- Signed-off-by: fufesou --- res/msi/README.md | 2 +- res/msi/preprocess.py | 48 ++++++++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/res/msi/README.md b/res/msi/README.md index 5ff2a10800..a4608467dd 100644 --- a/res/msi/README.md +++ b/res/msi/README.md @@ -1,6 +1,6 @@ # RustDesk msi project -Use Visual Studio 2022 to compile this project. +Use Visual Studio 2019 to compile this project. This project is mainly derived from . diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index 9e10c3160b..84e97b6249 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -39,7 +39,11 @@ def make_parser(): parser = argparse.ArgumentParser(description="Msi preprocess script.") parser.add_argument( - "-d", "--dist-dir", type=str, default="../../rustdesk", help="The dist direcotry to install." + "-d", + "--dist-dir", + type=str, + default="../../rustdesk", + help="The dist direcotry to install.", ) parser.add_argument( "-arp", @@ -103,11 +107,6 @@ def insert_components_between_tags(lines, index_start, app_name, dist_dir): if file_path.name.lower() == f"{app_name}.exe".lower(): continue - relative_file_path = file_path.relative_to(path) - guid = uuid.uuid5( - uuid.NAMESPACE_OID, app_name + "/" + str(relative_file_path) - ) - subdir = str(file_path.parent.relative_to(path)) dir_attr = "" if subdir != ".": @@ -117,7 +116,7 @@ def insert_components_between_tags(lines, index_start, app_name, dist_dir): # because it will cause error # "Error WIX0130 The primary key 'xxxx' is duplicated in table 'Directory'" to_insert_lines = f""" -{indent} +{indent} {indent}{g_indent_unit} {indent} """ @@ -251,7 +250,9 @@ def func(lines, index_start): ) for _, v in g_arpsystemcomponent.items(): if "msi" in v and "v" in v: - lines_new.append(f'{indent}\n') + lines_new.append( + f'{indent}\n' + ) for i, line in enumerate(lines_new): lines.insert(index_start + i + 1, line) @@ -318,7 +319,9 @@ def func(lines, index_start): lines_new.append( f'{indent}\n' ) - lines_new.append(f'{indent}\n') + lines_new.append( + f'{indent}\n' + ) lines_new.append( f'{indent}\n' ) @@ -341,9 +344,11 @@ def func(lines, index_start): f'{indent}\n' ) for k, v in g_arpsystemcomponent.items(): - if 'v' in v: - t = v['t'] if 't' in v is None else 'string' - lines_new.append(f'{indent}\n') + if "v" in v: + t = v["t"] if "t" in v is None else "string" + lines_new.append( + f'{indent}\n' + ) for i, line in enumerate(lines_new): lines.insert(index_start + i + 1, line) @@ -420,6 +425,22 @@ def init_global_vars(args): return True +def replace_component_guids_in_wxs(): + langs_dir = Path(sys.argv[0]).parent.joinpath("Package") + for file_path in langs_dir.glob("**/*.wxs"): + with open(file_path, "r") as f: + lines = f.readlines() + + # + for i, line in enumerate(lines): + match = re.search(r'Component.+Guid="([^"]+)"', line) + if match: + lines[i] = re.sub(r'Guid="[^"]+"', f'Guid="{uuid.uuid4()}"', line) + + with open(file_path, "w") as f: + f.writelines(lines) + + if __name__ == "__main__": parser = make_parser() args = parser.parse_args() @@ -433,6 +454,9 @@ def init_global_vars(args): if not gen_pre_vars(args, dist_dir): sys.exit(-1) + if app_name != "RustDesk": + replace_component_guids_in_wxs() + if not gen_upgrade_info(): sys.exit(-1) From 44bce59777de2e4b2465d11a6bc42c5740d0040c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 18:22:36 +0800 Subject: [PATCH 14/40] more java_ffi_FFI --- libs/scrap/src/android/ffi.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index e4a8877de2..d4e8b5bcf3 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -94,7 +94,7 @@ pub fn get_audio_raw<'a>() -> Option<&'a [u8]> { } #[no_mangle] -pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_onVideoFrameUpdate( +pub extern "system" fn java_ffi_FFI_onVideoFrameUpdate( env: JNIEnv, _class: JClass, buffer: JObject, @@ -108,7 +108,7 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_onVideoFrameUpd } #[no_mangle] -pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_onAudioFrameUpdate( +pub extern "system" fn java_ffi_FFI_onAudioFrameUpdate( env: JNIEnv, _class: JClass, buffer: JObject, @@ -122,7 +122,7 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_onAudioFrameUpd } #[no_mangle] -pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_setFrameRawEnable( +pub extern "system" fn java_ffi_FFI_setFrameRawEnable( env: JNIEnv, _class: JClass, name: JString, @@ -141,7 +141,7 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_setFrameRawEnab } #[no_mangle] -pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init( +pub extern "system" fn java_ffi_FFI_init( env: JNIEnv, _class: JClass, ctx: JObject, From a6c1d2d486b46de571f422e9e771532504e0c1de Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 18:42:35 +0800 Subject: [PATCH 15/40] customClientConfig in dart --- flutter/lib/models/native_model.dart | 4 +++- src/flutter_ffi.rs | 22 ++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index fe8fba732e..d8e5aa1ee8 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -198,7 +198,9 @@ class PlatformFFI { await _ffiBind.mainDeviceId(id: id); await _ffiBind.mainDeviceName(name: name); await _ffiBind.mainSetHomeDir(home: _homeDir); - await _ffiBind.mainInit(appDir: _dir); + final customClientConfig = ''; + await _ffiBind.mainInit( + appDir: _dir, customClientConfig: customClientConfig); } catch (e) { debugPrintStack(label: 'initialize failed: $e'); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index e27b4140e7..dec4bc370f 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -39,11 +39,15 @@ lazy_static::lazy_static! { static ref TEXTURE_RENDER_KEY: Arc = Arc::new(AtomicI32::new(0)); } -fn initialize(app_dir: &str) { +fn initialize(app_dir: &str, custom_client_config: &str) { flutter::async_tasks::start_flutter_async_runner(); *config::APP_DIR.write().unwrap() = app_dir.to_owned(); // core_main's load_custom_client does not work for flutter since it is only applied to its load_library in main.c - crate::load_custom_client(); + if custom_client_config.is_empty() { + crate::load_custom_client(); + } else { + crate::read_custom_client(custom_client_config); + } #[cfg(target_os = "android")] { // flexi_logger can't work when android_logger initialized. @@ -1275,8 +1279,8 @@ pub fn cm_get_clients_length() -> usize { crate::ui_cm_interface::get_clients_length() } -pub fn main_init(app_dir: String) { - initialize(&app_dir); +pub fn main_init(app_dir: String, custom_client_config: String) { + initialize(&app_dir, &custom_client_config); } pub fn main_device_id(id: String) { @@ -2122,10 +2126,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_FFI_startService( - _env: JNIEnv, - _class: JClass, - ) { + pub unsafe extern "system" fn Java_ffi_FFI_startService(_env: JNIEnv, _class: JClass) { log::debug!("startService from jvm"); config::Config::set_option("stop-service".into(), "".into()); crate::rendezvous_mediator::RendezvousMediator::restart(); @@ -2151,10 +2152,7 @@ pub mod server_side { } #[no_mangle] - pub unsafe extern "system" fn Java_ffi_FFI_refreshScreen( - _env: JNIEnv, - _class: JClass, - ) { + pub unsafe extern "system" fn Java_ffi_FFI_refreshScreen(_env: JNIEnv, _class: JClass) { crate::server::video_service::refresh() } } From c656c3c087e5155b87d04f2a68054328a8295d61 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 18:46:46 +0800 Subject: [PATCH 16/40] typo --- libs/scrap/src/android/ffi.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index d4e8b5bcf3..3c1ca87dac 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -94,7 +94,7 @@ pub fn get_audio_raw<'a>() -> Option<&'a [u8]> { } #[no_mangle] -pub extern "system" fn java_ffi_FFI_onVideoFrameUpdate( +pub extern "system" fn Java_ffi_FFI_onVideoFrameUpdate( env: JNIEnv, _class: JClass, buffer: JObject, @@ -108,7 +108,7 @@ pub extern "system" fn java_ffi_FFI_onVideoFrameUpdate( } #[no_mangle] -pub extern "system" fn java_ffi_FFI_onAudioFrameUpdate( +pub extern "system" fn Java_ffi_FFI_onAudioFrameUpdate( env: JNIEnv, _class: JClass, buffer: JObject, @@ -122,7 +122,7 @@ pub extern "system" fn java_ffi_FFI_onAudioFrameUpdate( } #[no_mangle] -pub extern "system" fn java_ffi_FFI_setFrameRawEnable( +pub extern "system" fn Java_ffi_FFI_setFrameRawEnable( env: JNIEnv, _class: JClass, name: JString, @@ -141,7 +141,7 @@ pub extern "system" fn java_ffi_FFI_setFrameRawEnable( } #[no_mangle] -pub extern "system" fn java_ffi_FFI_init( +pub extern "system" fn Java_ffi_FFI_init( env: JNIEnv, _class: JClass, ctx: JObject, From b9792fc17d78b43c4b6786118c6f9489c8f3f4cc Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 20:18:17 +0800 Subject: [PATCH 17/40] bridge rust version --- .github/workflows/bridge.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index c50d550257..d73dc7bfd6 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -8,6 +8,7 @@ on: env: FLUTTER_VERSION: "3.16.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" + RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 jobs: generate_bridge: @@ -49,9 +50,9 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 with: - toolchain: stable + toolchain: ${{ env.RUST_VERSION }} targets: ${{ matrix.job.target }} - components: '' + components: "rustfmt" - uses: Swatinem/rust-cache@v2 with: From 01ec539065b8fc14580b5e404e9e7f10cadb1134 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 21:46:54 +0800 Subject: [PATCH 18/40] load android custom client for jvm startServer --- .../src/main/kotlin/com/carriez/flutter_hbb/MainService.kt | 2 +- flutter/lib/models/native_model.dart | 4 +--- src/flutter_ffi.rs | 4 ++++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 973c2f1088..12fd66b161 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -207,7 +207,7 @@ class MainService : Service() { // keep the config dir same with flutter val prefs = applicationContext.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE) val configPath = prefs.getString(KEY_APP_DIR_CONFIG_PATH, "") ?: "" - FFI.startServer(configPath) + FFI.startServer(configPath, "") createForegroundNotification() } diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index d8e5aa1ee8..2886a23005 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -198,9 +198,7 @@ class PlatformFFI { await _ffiBind.mainDeviceId(id: id); await _ffiBind.mainDeviceName(name: name); await _ffiBind.mainSetHomeDir(home: _homeDir); - final customClientConfig = ''; - await _ffiBind.mainInit( - appDir: _dir, customClientConfig: customClientConfig); + await _ffiBind.mainInit(appDir: _dir, customClientConfig: ''); } catch (e) { debugPrintStack(label: 'initialize failed: $e'); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index dec4bc370f..72e435d3ff 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2116,12 +2116,16 @@ pub mod server_side { env: JNIEnv, _class: JClass, app_dir: JString, + custom_client_config: JString, ) { log::debug!("startServer from jvm"); let mut env = env; if let Ok(app_dir) = env.get_string(&app_dir) { *config::APP_DIR.write().unwrap() = app_dir.into(); } + if let Ok(custom_client_config) = env.get_string(&custom_client_config) { + crate::read_custom_client(custom_client_config); + } std::thread::spawn(move || start_server(true)); } From bc0ab88e74e15a6e623ba35ab73d7dbe2832d614 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 22:17:05 +0800 Subject: [PATCH 19/40] do not load empty custom_client_config --- src/flutter_ffi.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 72e435d3ff..9cfd71e3fd 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2124,7 +2124,9 @@ pub mod server_side { *config::APP_DIR.write().unwrap() = app_dir.into(); } if let Ok(custom_client_config) = env.get_string(&custom_client_config) { - crate::read_custom_client(custom_client_config); + if !custom_client_config.is_empty() { + crate::read_custom_client(custom_client_config); + } } std::thread::spawn(move || start_server(true)); } From 414455a8fb0de6fbb1590448527842f97a0246ec Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 22:25:10 +0800 Subject: [PATCH 20/40] fix ci --- src/flutter_ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 9cfd71e3fd..36b25708ae 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2125,7 +2125,7 @@ pub mod server_side { } if let Ok(custom_client_config) = env.get_string(&custom_client_config) { if !custom_client_config.is_empty() { - crate::read_custom_client(custom_client_config); + crate::read_custom_client(custom_client_config.into()); } } std::thread::spawn(move || start_server(true)); From 736503df1bfdb4325ca674e602ef08fbaa78ae0b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 22:38:47 +0800 Subject: [PATCH 21/40] fix ci --- src/flutter_ffi.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 36b25708ae..d29fc032df 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2125,7 +2125,8 @@ pub mod server_side { } if let Ok(custom_client_config) = env.get_string(&custom_client_config) { if !custom_client_config.is_empty() { - crate::read_custom_client(custom_client_config.into()); + let custom_client_config: String = custom_client_config.into(); + crate::read_custom_client(&custom_client_config); } } std::thread::spawn(move || start_server(true)); From 7bb4e22a77ef97c3150858adc5ac1fe2715278f8 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 16 Apr 2024 22:53:01 +0800 Subject: [PATCH 22/40] fix ci --- flutter/android/app/src/main/kotlin/ffi.kt | 2 +- flutter/lib/mobile/pages/connection_page.dart | 2 +- flutter/lib/mobile/pages/home_page.dart | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/ffi.kt b/flutter/android/app/src/main/kotlin/ffi.kt index d61bccfd60..ba29021b34 100644 --- a/flutter/android/app/src/main/kotlin/ffi.kt +++ b/flutter/android/app/src/main/kotlin/ffi.kt @@ -11,7 +11,7 @@ object FFI { } external fun init(ctx: Context) - external fun startServer(app_dir: String) + external fun startServer(app_dir: String, custom_client_config: String) external fun startService() external fun onVideoFrameUpdate(buf: ByteBuffer) external fun onAudioFrameUpdate(buf: ByteBuffer) diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index d2a3c0347e..51612c9e76 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -84,7 +84,7 @@ class _ConnectionPageState extends State { slivers: [ SliverList( delegate: SliverChildListDelegate([ - _buildUpdateUI(), + if (!bind.isCustomClient()) _buildUpdateUI(), _buildRemoteIDTextField(), ])), SliverFillRemaining( diff --git a/flutter/lib/mobile/pages/home_page.dart b/flutter/lib/mobile/pages/home_page.dart index f19541c8c7..e75f9998ae 100644 --- a/flutter/lib/mobile/pages/home_page.dart +++ b/flutter/lib/mobile/pages/home_page.dart @@ -4,6 +4,7 @@ import 'package:flutter_hbb/mobile/pages/settings_page.dart'; import 'package:get/get.dart'; import '../../common.dart'; import '../../common/widgets/chat_page.dart'; +import '../../models/platform_model.dart'; import 'connection_page.dart'; abstract class PageShape extends Widget { @@ -43,7 +44,7 @@ class HomePageState extends State { void initPages() { _pages.clear(); - _pages.add(ConnectionPage()); + if (!bind.isIncomingOnly()) _pages.add(ConnectionPage()); if (isAndroid) { _pages.addAll([ChatPage(type: ChatPageType.mobileMain), ServerPage()]); } @@ -141,7 +142,7 @@ class HomePageState extends State { ], ); } - return Text("RustDesk"); + return Text(bind.mainGetAppNameSync()); } } @@ -154,7 +155,7 @@ class WebHomePage extends StatelessWidget { // backgroundColor: MyTheme.grayBg, appBar: AppBar( centerTitle: true, - title: Text("RustDesk${isWeb ? " (Beta) " : ""}"), + title: Text(bind.mainGetAppNameSync()), actions: connectionPage.appBarActions, ), body: connectionPage, From 990c05fc3dea7276b9a4f31fa3b35eb999d3334f Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Wed, 17 Apr 2024 05:20:31 +0300 Subject: [PATCH 23/40] Update lv.rs (#7745) --- src/lang/lv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/lv.rs b/src/lang/lv.rs index aa31861048..9fbc7d10ad 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Iepriekš minētie lauki ir koplietoti un redzami citiem."), ("Everyone", "Visi"), ("ab_web_console_tip", "Vairāk par tīmekļa konsoli"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Atļaut savienojumu tikai tad, ja ir atvērts RustDesk logs"), ].iter().cloned().collect(); } From bdf8bbe26f8a4255997a257953b09c12e6083df0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 17 Apr 2024 12:48:27 +0800 Subject: [PATCH 24/40] custom android --- flutter/lib/common.dart | 21 +++++ .../lib/desktop/pages/desktop_home_page.dart | 17 +--- flutter/lib/mobile/pages/home_page.dart | 2 +- flutter/lib/mobile/pages/settings_page.dart | 78 ++++++++++++------- 4 files changed, 71 insertions(+), 47 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index fc5b537e1c..a878995c96 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3106,6 +3106,27 @@ Color? disabledTextColor(BuildContext context, bool enabled) { : Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6); } +Widget loadPowered(BuildContext context) { + return MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + launchUrl(Uri.parse('https://rustdesk.com')); + }, + child: Opacity( + opacity: 0.5, + child: Text( + translate("powered_by_me"), + overflow: TextOverflow.clip, + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(fontSize: 9, decoration: TextDecoration.underline), + )), + ), + ).marginOnly(top: 6); +} + // max 300 x 60 Widget loadLogo() { return FutureBuilder( diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 7395f4708a..c2902806fb 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -115,22 +115,7 @@ class _DesktopHomePageState extends State if (bind.isCustomClient()) Align( alignment: Alignment.center, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: () { - launchUrl(Uri.parse('https://rustdesk.com')); - }, - child: Opacity( - opacity: 0.5, - child: Text( - translate("powered_by_me"), - overflow: TextOverflow.clip, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontSize: 9, decoration: TextDecoration.underline), - )), - ), - ).marginOnly(top: 6), + child: loadPowered(context), ), Align( alignment: Alignment.center, diff --git a/flutter/lib/mobile/pages/home_page.dart b/flutter/lib/mobile/pages/home_page.dart index e75f9998ae..0f3e7b20aa 100644 --- a/flutter/lib/mobile/pages/home_page.dart +++ b/flutter/lib/mobile/pages/home_page.dart @@ -45,7 +45,7 @@ class HomePageState extends State { void initPages() { _pages.clear(); if (!bind.isIncomingOnly()) _pages.add(ConnectionPage()); - if (isAndroid) { + if (isAndroid && !bind.isOutgoingOnly()) { _pages.addAll([ChatPage(type: ChatPageType.mobileMain), ServerPage()]); } _pages.add(SettingsPage()); diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index f6b2175332..e79310650f 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -27,7 +27,7 @@ class SettingsPage extends StatefulWidget implements PageShape { final icon = Icon(Icons.settings); @override - final appBarActions = [ScanButton()]; + final appBarActions = bind.isDisableSettings() ? [] : [ScanButton()]; @override State createState() => _SettingsState(); @@ -218,6 +218,7 @@ class _SettingsState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { Provider.of(context); + final outgoingOnly = bind.isOutgoingOnly(); final List enhancementsTiles = []; final List shareScreenTiles = [ SettingsTile.switchTile( @@ -448,33 +449,36 @@ class _SettingsState extends State with WidgetsBindingObserver { gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, toValue); })); - return SettingsList( + final disabledSettings = bind.isDisableSettings(); + final settings = SettingsList( sections: [ - SettingsSection( - title: Text(translate('Account')), - tiles: [ - SettingsTile( - title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty - ? translate('Login') - : '${translate('Logout')} (${gFFI.userModel.userName.value})')), - leading: Icon(Icons.person), - onPressed: (context) { - if (gFFI.userModel.userName.value.isEmpty) { - loginDialog(); - } else { - logOutConfirmDialog(); - } - }, - ), - ], - ), + if (!bind.isDisableAccount()) + SettingsSection( + title: Text(translate('Account')), + tiles: [ + SettingsTile( + title: Obx(() => Text(gFFI.userModel.userName.value.isEmpty + ? translate('Login') + : '${translate('Logout')} (${gFFI.userModel.userName.value})')), + leading: Icon(Icons.person), + onPressed: (context) { + if (gFFI.userModel.userName.value.isEmpty) { + loginDialog(); + } else { + logOutConfirmDialog(); + } + }, + ), + ], + ), SettingsSection(title: Text(translate("Settings")), tiles: [ - SettingsTile( - title: Text(translate('ID/Relay Server')), - leading: Icon(Icons.cloud), - onPressed: (context) { - showServerSettings(gFFI.dialogManager); - }), + if (!disabledSettings) + SettingsTile( + title: Text(translate('ID/Relay Server')), + leading: Icon(Icons.cloud), + onPressed: (context) { + showServerSettings(gFFI.dialogManager); + }), SettingsTile( title: Text(translate('Language')), leading: Icon(Icons.translate), @@ -494,7 +498,7 @@ class _SettingsState extends State with WidgetsBindingObserver { }, ) ]), - if (isAndroid) + if (isAndroid && !outgoingOnly) SettingsSection( title: Text(translate("Recording")), tiles: [ @@ -523,13 +527,13 @@ class _SettingsState extends State with WidgetsBindingObserver { ), ], ), - if (isAndroid) + if (isAndroid && !disabledSettings && !outgoingOnly) SettingsSection( title: Text(translate("Share Screen")), tiles: shareScreenTiles, ), - defaultDisplaySection(), - if (isAndroid) + if (!bind.isIncomingOnly()) defaultDisplaySection(), + if (isAndroid && !disabledSettings && !outgoingOnly) SettingsSection( title: Text(translate("Enhancements")), tiles: enhancementsTiles, @@ -578,6 +582,20 @@ class _SettingsState extends State with WidgetsBindingObserver { ), ], ); + return Column( + children: [ + if (bind.isCustomClient()) + Align( + alignment: Alignment.center, + child: loadPowered(context), + ), + Align( + alignment: Alignment.center, + child: loadLogo(), + ), + settings + ], + ); } Future canStartOnBoot() async { From 4e8cbe3db1392234705e294efa88339c1204ddd0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 17 Apr 2024 22:01:09 +0800 Subject: [PATCH 25/40] buildPresetPasswordWarning for android --- flutter/lib/common.dart | 38 +++++++++++++++++++ .../lib/desktop/pages/desktop_home_page.dart | 38 ------------------- flutter/lib/mobile/pages/server_page.dart | 1 + 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index a878995c96..3106aaaafb 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3176,3 +3176,41 @@ bool isInHomePage() { final controller = Get.find(); return controller.state.value.selected == 0; } + +Widget buildPresetPasswordWarning() { + return FutureBuilder( + future: bind.isPresetPassword(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return CircularProgressIndicator(); // Show a loading spinner while waiting for the Future to complete + } else if (snapshot.hasError) { + return Text( + 'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error + } else if (snapshot.hasData && snapshot.data == true) { + return Container( + color: Colors.yellow, + child: Column( + children: [ + Align( + child: Text( + translate("Security Alert"), + style: TextStyle( + color: Colors.red, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + )).paddingOnly(bottom: 8), + Text( + translate("preset_password_warning"), + style: TextStyle(color: Colors.red), + ) + ], + ).paddingAll(8), + ); // Show a warning message if the Future completed with true + } else { + return SizedBox + .shrink(); // Show nothing if the Future completed with false or null + } + }, + ); +} diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index c2902806fb..dd87e09393 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -69,44 +69,6 @@ class _DesktopHomePageState extends State ); } - Widget buildPresetPasswordWarning() { - return FutureBuilder( - future: bind.isPresetPassword(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return CircularProgressIndicator(); // Show a loading spinner while waiting for the Future to complete - } else if (snapshot.hasError) { - return Text( - 'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error - } else if (snapshot.hasData && snapshot.data == true) { - return Container( - color: Colors.yellow, - child: Column( - children: [ - Align( - child: Text( - translate("Security Alert"), - style: TextStyle( - color: Colors.red, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - )).paddingOnly(bottom: 8), - Text( - translate("preset_password_warning"), - style: TextStyle(color: Colors.red), - ) - ], - ).paddingAll(8), - ); // Show a warning message if the Future completed with true - } else { - return SizedBox - .shrink(); // Show nothing if the Future completed with false or null - } - }, - ); - } - Widget buildLeftPane(BuildContext context) { final isIncomingOnly = bind.isIncomingOnly(); final isOutgoingOnly = bind.isOutgoingOnly(); diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index e097b04a67..120381ebe8 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -158,6 +158,7 @@ class _ServerPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ + buildPresetPasswordWarning(), gFFI.serverModel.isStart ? ServerInfo() : ServiceNotRunningNotification(), From 4252b5e27376365742db4e7447f4067d10d373d8 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 18 Apr 2024 13:12:45 +0800 Subject: [PATCH 26/40] enable ffmpeg native h26x software decoders for all platforms (#7750) * enable ffmpeg native h26x software decoders for all platforms * h26x software decoders depend on hwcodec feature, so all platforms enable it, software h26x decoders are always available like vpx, no available check and no option * ffmpeg: - build: mac arm64 build ffmpeg with my m1, others build with ci - version: win/linux use ffmpeg release/5.1, becaues higher version require higher nvidia driver, other platforms use release/7.0 * test: - ios not test. - android: sometimes the screen will appear blurry, but it will recover after a while. - arm64 linux: test a example of hwcodec repo Signed-off-by: 21pages * check hwcodec only when enabled and immediately when clicked enabled Signed-off-by: 21pages --------- Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 12 +++--- Cargo.lock | 4 +- .../desktop/pages/desktop_setting_page.dart | 11 ++++- flutter/lib/web/bridge.dart | 4 ++ flutter/ndk_arm.sh | 2 +- flutter/ndk_arm64.sh | 2 +- libs/scrap/Cargo.toml | 1 - libs/scrap/src/common/codec.rs | 15 ++++--- libs/scrap/src/common/hwcodec.rs | 40 ++++++++++++++----- src/flutter_ffi.rs | 4 ++ src/ipc.rs | 15 +++++++ src/server.rs | 3 +- src/server/video_service.rs | 3 -- src/ui.rs | 5 +++ src/ui/index.tis | 6 ++- src/ui_interface.rs | 18 +++++++-- 16 files changed, 106 insertions(+), 39 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 3e15301dcc..5dea0cb93d 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -331,8 +331,7 @@ jobs: - name: Build rustdesk run: | - # --hwcodec not supported on macos yet - ./build.py --flutter + ./build.py --flutter --hwcodec - name: create unsigned dmg if: env.UPLOAD_ARTIFACT == 'true' @@ -486,8 +485,7 @@ jobs: - name: Build rustdesk run: | - # --hwcodec not supported on macos yet - ./build.py --flutter ${{ matrix.job.extra-build-args }} + ./build.py --flutter --hwcodec ${{ matrix.job.extra-build-args }} - name: create unsigned dmg if: env.UPLOAD_ARTIFACT == 'true' @@ -636,7 +634,7 @@ jobs: - name: Build rustdesk lib run: | rustup target add ${{ matrix.job.target }} - cargo build --features flutter --release --target aarch64-apple-ios --lib + cargo build --features flutter,hwcodec --release --target aarch64-apple-ios --lib - name: Build rustdesk shell: bash @@ -1257,7 +1255,7 @@ jobs: export DEFAULT_FEAT=linux_headless fi export CARGO_INCREMENTAL=0 - cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release + cargo build --lib --features flutter,flutter_texture_render,hwcodec,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release - name: Upload Artifacts uses: actions/upload-artifact@master @@ -1432,7 +1430,7 @@ jobs: if ${{ matrix.job.enable-headless }}; then export DEFAULT_FEAT=linux_headless fi - cargo build --features inline,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release --bins + cargo build --features inline,hwcodec,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release --bins # package mkdir -p ./Release mv ./target/release/rustdesk ./Release/rustdesk diff --git a/Cargo.lock b/Cargo.lock index 95024e0b54..ba4e311dc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3024,8 +3024,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.3.0" -source = "git+https://github.com/21pages/hwcodec#6ce1cbab2ff270a81784303192e8906ef597ee02" +version = "0.3.2" +source = "git+https://github.com/21pages/hwcodec#1b754302d884d6d385a8f775acc514248006e3bb" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 017825a25a..7eedb1d5b9 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -404,7 +404,16 @@ class _GeneralState extends State<_General> { return Offstage( offstage: !(hwcodec || vram), child: _Card(title: 'Hardware Codec', children: [ - _OptionCheckBox(context, 'Enable hardware codec', 'enable-hwcodec') + _OptionCheckBox( + context, + 'Enable hardware codec', + 'enable-hwcodec', + update: () { + if (mainGetBoolOptionSync('enable-hwcodec')) { + bind.mainCheckHwcodec(); + } + }, + ) ]), ); } diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index eb6db31200..9528933934 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -1573,5 +1573,9 @@ class RustdeskImpl { throw UnimplementedError(); } + Future mainCheckHwcodec({dynamic hint}) { + throw UnimplementedError(); + } + void dispose() {} } diff --git a/flutter/ndk_arm.sh b/flutter/ndk_arm.sh index 7c2415d2d9..7b62953418 100755 --- a/flutter/ndk_arm.sh +++ b/flutter/ndk_arm.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -cargo ndk --platform 21 --target armv7-linux-androideabi build --release --features flutter +cargo ndk --platform 21 --target armv7-linux-androideabi build --release --features flutter,hwcodec diff --git a/flutter/ndk_arm64.sh b/flutter/ndk_arm64.sh index 99420ae8ce..e7c43582b7 100755 --- a/flutter/ndk_arm64.sh +++ b/flutter/ndk_arm64.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -cargo ndk --platform 21 --target aarch64-linux-android build --release --features flutter +cargo ndk --platform 21 --target aarch64-linux-android build --release --features flutter,hwcodec diff --git a/libs/scrap/Cargo.toml b/libs/scrap/Cargo.toml index ffb66aabb6..64805e0e50 100644 --- a/libs/scrap/Cargo.toml +++ b/libs/scrap/Cargo.toml @@ -61,6 +61,5 @@ gstreamer-video = { version = "0.16", optional = true } [dependencies.hwcodec] git = "https://github.com/21pages/hwcodec" optional = true -features = ["ffmpeg"] diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index cac1b307da..fd283386b5 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -399,7 +399,7 @@ impl Decoder { ..Default::default() }; #[cfg(feature = "hwcodec")] - if enable_hwcodec_option() { + { let best = HwRamDecoder::best(); decoding.ability_h264 |= if best.h264.is_some() { 1 } else { 0 }; decoding.ability_h265 |= if best.h265.is_some() { 1 } else { 0 }; @@ -492,7 +492,7 @@ impl Decoder { valid = h264_vram.is_some(); } #[cfg(feature = "hwcodec")] - if !valid && enable_hwcodec_option() { + if !valid { match HwRamDecoder::new(format) { Ok(v) => h264_ram = Some(v), Err(e) => log::error!("create H264 ram decoder failed: {}", e), @@ -518,7 +518,7 @@ impl Decoder { valid = h265_vram.is_some(); } #[cfg(feature = "hwcodec")] - if !valid && enable_hwcodec_option() { + if !valid { match HwRamDecoder::new(format) { Ok(v) => h265_ram = Some(v), Err(e) => log::error!("create H265 ram decoder failed: {}", e), @@ -792,10 +792,13 @@ impl Decoder { #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] pub fn enable_hwcodec_option() -> bool { - if let Some(v) = Config2::get().options.get("enable-hwcodec") { - return v != "N"; + if cfg!(windows) || cfg!(target_os = "linux") || cfg!(feature = "mediacodec") { + if let Some(v) = Config2::get().options.get("enable-hwcodec") { + return v != "N"; + } + return true; // default is true } - return true; // default is true + false } #[cfg(feature = "vram")] pub fn enable_vram_option() -> bool { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index c72e69822f..92f58cde2b 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -1,9 +1,10 @@ use crate::{ - codec::{base_bitrate, codec_thread_num, EncoderApi, EncoderCfg, Quality as Q}, + codec::{ + base_bitrate, codec_thread_num, enable_hwcodec_option, EncoderApi, EncoderCfg, Quality as Q, + }, hw, CodecFormat, EncodeInput, ImageFormat, ImageRgb, Pixfmt, HW_STRIDE_ALIGN, }; use hbb_common::{ - allow_err, anyhow::{anyhow, bail, Context}, bytes::Bytes, config::HwCodecConfig, @@ -223,10 +224,18 @@ pub struct HwRamDecoder { impl HwRamDecoder { pub fn best() -> CodecInfos { - get_config().map(|c| c.d).unwrap_or(CodecInfos { - h264: None, - h265: None, - }) + let mut info = CodecInfo::soft(); + if enable_hwcodec_option() { + if let Ok(hw) = get_config().map(|c| c.d) { + if let Some(h264) = hw.h264 { + info.h264 = Some(h264); + } + if let Some(h265) = hw.h265 { + info.h265 = Some(h265); + } + } + } + info } pub fn new(format: CodecFormat) -> ResultType { @@ -359,8 +368,8 @@ pub fn check_available_hwcodec() { let vram = crate::vram::check_available_vram(); #[cfg(not(feature = "vram"))] let vram = "".to_owned(); - let encoders = CodecInfo::score(Encoder::available_encoders(ctx, Some(vram.clone()))); - let decoders = CodecInfo::score(Decoder::available_decoders(Some(vram.clone()))); + let encoders = CodecInfo::prioritized(Encoder::available_encoders(ctx, Some(vram.clone()))); + let decoders = CodecInfo::prioritized(Decoder::available_decoders(Some(vram.clone()))); let ram = Available { e: encoders, d: decoders, @@ -370,7 +379,12 @@ pub fn check_available_hwcodec() { } } -pub fn hwcodec_new_check_process() { +#[cfg(any(target_os = "windows", target_os = "linux"))] +pub fn start_check_process(force: bool) { + if !force && !enable_hwcodec_option() { + return; + } + use hbb_common::allow_err; use std::sync::Once; let f = || { // Clear to avoid checking process errors @@ -410,7 +424,11 @@ pub fn hwcodec_new_check_process() { }; }; static ONCE: Once = Once::new(); - ONCE.call_once(|| { + if force && ONCE.is_completed() { std::thread::spawn(f); - }); + } else { + ONCE.call_once(|| { + std::thread::spawn(f); + }); + } } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index d29fc032df..86235dc2fc 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2100,6 +2100,10 @@ pub fn main_get_hard_option(key: String) -> SyncReturn { SyncReturn(get_hard_option(key)) } +pub fn main_check_hwcodec() { + check_hwcodec() +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/ipc.rs b/src/ipc.rs index 29954d61fc..3a2b88aed5 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -232,6 +232,7 @@ pub enum Data { #[cfg(windows)] ControlledSessionCount(usize), CmErr(String), + CheckHwcodec, } #[tokio::main(flavor = "current_thread")] @@ -502,6 +503,14 @@ async fn handle(data: Data, stream: &mut Connection) { .await ); } + Data::CheckHwcodec => + { + #[cfg(feature = "hwcodec")] + #[cfg(any(target_os = "windows", target_os = "linux"))] + if crate::platform::is_root() { + scrap::hwcodec::start_check_process(true); + } + } _ => {} } } @@ -926,6 +935,12 @@ pub async fn connect_to_user_session(usid: Option) -> ResultType<()> { Ok(()) } +#[tokio::main(flavor = "current_thread")] +pub async fn notify_server_to_check_hwcodec() -> ResultType<()> { + connect(1_000, "").await?.send(&&Data::CheckHwcodec).await?; + Ok(()) +} + #[cfg(test)] mod test { use super::*; diff --git a/src/server.rs b/src/server.rs index 9daf242624..9345936e0b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -449,7 +449,8 @@ pub async fn start_server(is_server: bool) { log::info!("XAUTHORITY={:?}", std::env::var("XAUTHORITY")); } #[cfg(feature = "hwcodec")] - scrap::hwcodec::hwcodec_new_check_process(); + #[cfg(any(target_os = "windows", target_os = "linux"))] + scrap::hwcodec::start_check_process(false); #[cfg(windows)] hbb_common::platform::windows::start_cpu_performance_monitor(); diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 65411d566a..19d711bdbe 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -715,9 +715,6 @@ fn handle_hw_encoder( #[cfg(feature = "hwcodec")] match _name { CodecName::H264VRAM | CodecName::H265VRAM => { - if !scrap::codec::enable_hwcodec_option() { - return Err(()); - } let is_h265 = _name == CodecName::H265VRAM; let best = HwRamEncoder::best(); if let Some(h264) = best.h264 { diff --git a/src/ui.rs b/src/ui.rs index d1019204ae..10aefe5ff4 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -621,6 +621,10 @@ impl UI { ); format!("data:image/png;base64,{s}") } + + pub fn check_hwcodec(&self) { + check_hwcodec() + } } impl sciter::EventHandler for UI { @@ -711,6 +715,7 @@ impl sciter::EventHandler for UI { fn generate2fa(); fn generate_2fa_img_src(String); fn verify2fa(String); + fn check_hwcodec(); } } diff --git a/src/ui/index.tis b/src/ui/index.tis index e15c5a3537..f202d0fadb 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -240,7 +240,11 @@ class Enhancements: Reactor.Component { event click $(menu#enhancements-menu>li) (_, me) { var v = me.id; if (v.indexOf("enable-") == 0) { - handler.set_option(v, handler.get_option(v) != 'N' ? 'N' : ''); + var set_value = handler.get_option(v) != 'N' ? 'N' : ''; + handler.set_option(v, set_value); + if (v == "enable-hwcodec" && set_value == '') { + handler.check_hwcodec(); + } } else if (v.indexOf("allow-") == 0) { handler.set_option(v, handler.get_option(v) == 'Y' ? '' : 'Y'); } else if (v == 'screen-recording') { diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 1e79e3299f..d8a9996c05 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -829,10 +829,9 @@ pub fn get_api_server() -> String { #[inline] pub fn has_hwcodec() -> bool { - #[cfg(not(any(feature = "hwcodec", feature = "mediacodec")))] - return false; - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - return true; + // Has real hardware codec using gpu + (cfg!(feature = "hwcodec") && (cfg!(windows) || cfg!(target_os = "linux"))) + || cfg!(feature = "mediacodec") } #[inline] @@ -1315,3 +1314,14 @@ pub fn verify2fa(code: String) -> bool { } res } + +pub fn check_hwcodec() { + #[cfg(feature = "hwcodec")] + #[cfg(any(target_os = "windows", target_os = "linux"))] + { + scrap::hwcodec::start_check_process(true); + if crate::platform::is_installed() { + ipc::notify_server_to_check_hwcodec().ok(); + } + } +} From 9b2ec62be910c395edf08c0279bca6abfa50269f Mon Sep 17 00:00:00 2001 From: writegr <167099595+writegr@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:39:38 +0800 Subject: [PATCH 27/40] chore: fix some typos in comments (#7752) Signed-off-by: writegr --- src/flutter.rs | 2 +- src/platform/linux_desktop_manager.rs | 2 +- src/platform/windows.rs | 2 +- src/server/input_service.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index f733bb70c1..f867559526 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1106,7 +1106,7 @@ pub fn session_start_( ) -> ResultType<()> { // is_connected is used to indicate whether to start a peer connection. For two cases: // 1. "Move tab to new window" - // 2. multi ui session within the same peer connnection. + // 2. multi ui session within the same peer connection. let mut is_connected = false; let mut is_found = false; for s in sessions::get_sessions() { diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 8e8cce95ce..21b123799f 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -277,7 +277,7 @@ impl DesktopManager { } } - // The logic mainly fron https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334. + // The logic mainly from https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334. fn get_avail_display() -> ResultType { let display_range = 0..51; for i in display_range.clone() { diff --git a/src/platform/windows.rs b/src/platform/windows.rs index baa932c405..c285702190 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1874,7 +1874,7 @@ pub fn current_resolution(name: &str) -> ResultType { dm.dmSize = std::mem::size_of::() as _; if EnumDisplaySettingsW(device_name.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) == 0 { bail!( - "failed to get currrent resolution, error {}", + "failed to get current resolution, error {}", io::Error::last_os_error() ); } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 7d1392bbf3..81ca155d7a 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -187,7 +187,7 @@ impl LockModesHandler { fn new(key_event: &KeyEvent) -> Self { let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); // Do not use the following code to detect `local_caps_enabled`. - // Because the state of get_key_state will not affect simuation of `VIRTUAL_INPUT_STATE` in this file. + // Because the state of get_key_state will not affect simulation of `VIRTUAL_INPUT_STATE` in this file. // // let local_caps_enabled = VirtualInput::get_key_state( // CGEventSourceStateID::CombinedSessionState, From 24ac7c162607e79a1d5a990714b1736e24bb5c66 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 18 Apr 2024 15:23:12 +0800 Subject: [PATCH 28/40] remove google-services.json --- flutter/android/app/build.gradle | 1 - flutter/android/app/google-services.json | 40 ------------------------ 2 files changed, 41 deletions(-) delete mode 100644 flutter/android/app/google-services.json diff --git a/flutter/android/app/build.gradle b/flutter/android/app/build.gradle index 9e32e163ed..ca405647be 100644 --- a/flutter/android/app/build.gradle +++ b/flutter/android/app/build.gradle @@ -108,4 +108,3 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib") { version { strictly("$kotlin_version") } } } -apply plugin: 'com.google.gms.google-services' diff --git a/flutter/android/app/google-services.json b/flutter/android/app/google-services.json deleted file mode 100644 index 3945e432a2..0000000000 --- a/flutter/android/app/google-services.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "project_info": { - "project_number": "768133699366", - "firebase_url": "https://rustdesk.firebaseio.com", - "project_id": "rustdesk", - "storage_bucket": "rustdesk.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:768133699366:android:5fc9015370e344457993e7", - "android_client_info": { - "package_name": "com.carriez.flutter_hbb" - } - }, - "oauth_client": [ - { - "client_id": "768133699366-s9gdfsijefsd5g1nura4kmfne42lencn.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyAPOsKcXjrAR-7Z148sYr_gdB_JQZkamTM" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "768133699366-s9gdfsijefsd5g1nura4kmfne42lencn.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file From 4d3fb77786095b442bdae4aa4b8ffc3856f95943 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 18 Apr 2024 16:19:29 +0800 Subject: [PATCH 29/40] remove hwcodec for sciter armv7 (#7753) Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 2 +- Cargo.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 5dea0cb93d..bb8c81a0f0 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1430,7 +1430,7 @@ jobs: if ${{ matrix.job.enable-headless }}; then export DEFAULT_FEAT=linux_headless fi - cargo build --features inline,hwcodec,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release --bins + cargo build --features inline,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release --bins # package mkdir -p ./Release mv ./target/release/rustdesk ./Release/rustdesk diff --git a/Cargo.lock b/Cargo.lock index ba4e311dc9..dd018c6b1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3024,8 +3024,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.3.2" -source = "git+https://github.com/21pages/hwcodec#1b754302d884d6d385a8f775acc514248006e3bb" +version = "0.3.3" +source = "git+https://github.com/21pages/hwcodec#eeebf980d4eb41daaf05090b097d5a59d688d3d8" dependencies = [ "bindgen 0.59.2", "cc", From edb5529df3b505941d8fcb27d818b21b652bcaaf Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 18 Apr 2024 17:32:15 +0800 Subject: [PATCH 30/40] flutter_icons does not generate mipmap-ldpi, so I drop it also --- .../app/src/main/res/mipmap-ldpi/ic_launcher.png | Bin 1524 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 flutter/android/app/src/main/res/mipmap-ldpi/ic_launcher.png diff --git a/flutter/android/app/src/main/res/mipmap-ldpi/ic_launcher.png b/flutter/android/app/src/main/res/mipmap-ldpi/ic_launcher.png deleted file mode 100644 index 60e95748c9b3e0e851ffaa9e3d1a449d136d03dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1524 zcmVPn&+TJW^9a&)z1;0~XEqff_Jx4D5Ktg= z@vU38z(?Dhy^KpYE%-GPZpC2o(VW)!?-9Wa!w_9AR}ZHMaUhW%)D#SpYFa36FtF35 zV9-qwEiUl&x;uyUT^{H~nzS28ZC0iXSK>)v&N7UxzTI9>O#Nnh}P2Y?&XKheTxg4QQtA z1vJkWb)2kIAsHrHa}iXJ9J=R71$NX*P;I-=29#)%Gf=VLMEX_*!X%NPL_u${N_GU|;U9QJN8%_M!k8d~N}k++9Iwd= zH3AU3CYREe94hp!;oV1;KtAP%9!_&?5%SB8XueH{FkFE!N`f#}!dnX^{;+7ou6=D! zjm;7&(1Og9+&%V`xkU?UYYAwGN>DNqM$7noz3L3$ppTAuLzckJlR3IeF*uN8%2JyI zT1`L$HG)!*I6}b}YvTYq;zNSXJk!p3xg9t}$ib<7vW4aw4G8@SYLE(HsEW5|snE^n zVYE=nwtC1s31}SwQKbI7=x0qhRv`A`LHkT3_R|SUV^GP%Rpfi3Dr&a%uX=SPk$Hw4 zy_$Mo6$aiAp<9u6S!$EpKHI`eqfH2XbQY=mhif=lX2OgDDA#Y`+XEtRGpzT$w5<+1 zDiwZ>bA%j?(PJTFxdow@f#*gW=)K2)q=mP%X|l1&$mIGAbU!8Gk)?jvdtchI0E$Fg zJBbxYGbl<~7M>bvqTe19RrMBIZ%{29EisW(YqD$Xt>$Wtfp2yO@yLSPd!GjzOBMSW z$#Iw2kFcFMkX`mjR0b?`*=gXOVH%Q$sd#OMijP*Q_+X8Km*+`HBuBr!_dP(TgtjN_ zXQSAhI`ChG+GVSWr$+vB_a%%_$fu;RqmLs^5;+t*CHz50mz!QA!U4^}7%8`oKq{r@ zp@Al=DWQ+ScOUa9GEe5{yv=Z^_D3Xko-*xcZ08zsPMMRo->{Ir+{BBcLzciI#m$pJ zQSW07Ien{)E%_RUV54o$f#ORUe+Vz!B9DdbSr!KEHPM31Go2hgZLLPVPh$-^bV?}l zne1BoGwyK8xNQH>u;7y{xa*miyT{_>5mQ!EQ}Z)jR2dDB4a|And5uX?6n9Zk(Vvbf zyK&>jyA-A6bYZooX)+fDZ|0b?)~#FLd*Q-`X8HN~9SREz$A?7! zfRB9Mr0BP3#jelE$?0_J)T#G|4 Date: Thu, 18 Apr 2024 22:38:32 +0800 Subject: [PATCH 31/40] Improve android threading, https://github.com/rustdesk/rustdesk/issues/4118#issuecomment-1515666629 todo: should we add some condition to assure imageReader not be closed while callback is running? --- .../com/carriez/flutter_hbb/MainService.kt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 12fd66b161..75304be64a 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -338,8 +338,11 @@ class MainService : Service() { ).apply { setOnImageAvailableListener({ imageReader: ImageReader -> try { + if (!isStart) { + return@setOnImageAvailableListener + } imageReader.acquireLatestImage().use { image -> - if (image == null) return@setOnImageAvailableListener + if (image == null || !isStart) return@setOnImageAvailableListener val planes = image.planes val buffer = planes[0].buffer buffer.rewind() @@ -390,8 +393,11 @@ class MainService : Service() { _isStart = false // release video virtualDisplay?.release() - surface?.release() imageReader?.close() + imageReader = null + // suface needs to be release after imageReader.close to imageReader access released surface + // https://github.com/rustdesk/rustdesk/issues/4118#issuecomment-1515666629 + surface?.release() videoEncoder?.let { it.signalEndOfInputStream() it.stop() @@ -402,9 +408,6 @@ class MainService : Service() { // release audio audioRecordStat = false - audioRecorder?.release() - audioRecorder = null - minBufferSize = 0 } fun destroy() { @@ -412,8 +415,6 @@ class MainService : Service() { _isReady = false stopCapture() - imageReader?.close() - imageReader = null mediaProjection = null checkMediaPermission() @@ -524,6 +525,10 @@ class MainService : Service() { FFI.onAudioFrameUpdate(it) } } + // let's release here rather than onDestroy to avoid threading issue + audioRecorder?.release() + audioRecorder = null + minBufferSize = 0 Log.d(logTag, "Exit audio thread") } } catch (e: Exception) { From a3c0911529ccc1e5cbd71707c6c8e74899802d1d Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 19 Apr 2024 00:47:13 +0800 Subject: [PATCH 32/40] fix: msi, explicit wide char api (#7764) Signed-off-by: fufesou --- res/msi/CustomActions/CustomActions.cpp | 6 +++--- res/msi/CustomActions/ServiceUtils.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 37a728a4ba..1543e8f655 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -59,7 +59,7 @@ UINT __stdcall RemoveInstallFolder( fileOp.pFrom = installFolder; fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; - nResult = SHFileOperation(&fileOp); + nResult = SHFileOperationW(&fileOp); if (nResult == 0) { WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", installFolder); @@ -179,7 +179,7 @@ bool TerminateProcessesByNameW(LPCWSTR processName, LPCWSTR excludeParam) CloseHandle(process); } } - } while (Process32Next(snapshot, &processEntry)); + } while (Process32NextW(snapshot, &processEntry)); } CloseHandle(snapshot); } @@ -497,7 +497,7 @@ UINT __stdcall TryDeleteStartupShortcut(__in MSIHANDLE hInstall) hr = StringCchPrintfW(pwszTemp, 1024, L"%ls%ls.lnk", szStartupDir, szShortcut); ExitOnFailure(hr, "Failed to compose a resource identifier string"); - if (DeleteFile(pwszTemp)) { + if (DeleteFileW(pwszTemp)) { WcaLog(LOGMSG_STANDARD, "Failed to delete startup shortcut of : \"%ls\"", pwszTemp); } else { diff --git a/res/msi/CustomActions/ServiceUtils.cpp b/res/msi/CustomActions/ServiceUtils.cpp index 5bf8c9fe52..234f70371d 100644 --- a/res/msi/CustomActions/ServiceUtils.cpp +++ b/res/msi/CustomActions/ServiceUtils.cpp @@ -12,7 +12,7 @@ bool MyCreateServiceW(LPCWSTR serviceName, LPCWSTR displayName, LPCWSTR binaryPa SC_HANDLE schService; // Get a handle to the SCM database. - schSCManager = OpenSCManager( + schSCManager = OpenSCManagerW( NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights @@ -24,7 +24,7 @@ bool MyCreateServiceW(LPCWSTR serviceName, LPCWSTR displayName, LPCWSTR binaryPa } // Create the service - schService = CreateService( + schService = CreateServiceW( schSCManager, // SCM database serviceName, // name of service displayName, // service name to display @@ -100,7 +100,7 @@ bool MyStartServiceW(LPCWSTR serviceName) return false; } - bool success = StartService(hService, 0, NULL); + bool success = StartServiceW(hService, 0, NULL); if (!success) { WcaLog(LOGMSG_STANDARD, "Failed to start service: %ls", serviceName); } From e83c28bf54909213c20df55e56b2be2b29d3e34c Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 19 Apr 2024 11:31:52 +0800 Subject: [PATCH 33/40] refact: win, virtual display (#7767) * refact: win, virtual display Signed-off-by: fufesou * Update flutter-build.yml --------- Signed-off-by: fufesou Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> --- .github/workflows/flutter-build.yml | 13 +- Cargo.toml | 2 +- build.py | 9 + flutter/lib/consts.dart | 15 +- .../lib/desktop/widgets/remote_toolbar.dart | 103 ++- flutter/lib/models/model.dart | 50 +- res/msi/CustomActions/CustomActions.cpp | 65 ++ res/msi/CustomActions/CustomActions.def | 1 + res/msi/Package/Components/RustDesk.wxs | 7 +- res/msi/Package/Fragments/CustomActions.wxs | 1 + src/core_main.rs | 6 +- src/lang/ar.rs | 1 + src/lang/bg.rs | 1 + src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/en.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/et.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/he.rs | 1 + src/lang/hr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/lv.rs | 1 + src/lang/nb.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + src/platform/windows.cc | 44 + src/platform/windows.rs | 84 +- src/privacy_mode.rs | 54 +- src/privacy_mode/win_topmost_window.rs | 4 + src/privacy_mode/win_virtual_display.rs | 41 +- src/server/connection.rs | 23 +- src/server/display_service.rs | 22 +- src/virtual_display_manager.rs | 789 +++++++++++++----- 61 files changed, 1099 insertions(+), 276 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index bb8c81a0f0..bb5399221a 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -113,7 +113,18 @@ jobs: shell: bash - name: Build rustdesk - run: python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack + run: | + Invoke-WebRequest -Uri https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip -OutFile usbmmidd_v2.zip + $SHA256_SUM = '629b51e9944762bae73948171c65d09a79595cf4c771a82ebc003fbba5b24f51' + if ((Get-FileHash -Path .\usbmmidd_v2.zip -Algorithm SHA256).Hash -ne $SHA256_SUM) { + Write-Error "SHA256 sum mismatch, falling back to the non-virtual-display version" + python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack + } else { + Write-Host "SHA256 sum matched, using the virtual-display version" + Expand-Archive usbmmidd_v2.zip -DestinationPath . + python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack --virtual-display + mv -Force .\usbmmidd_v2 ./flutter/build/windows/x64/runner/Release/ + } - name: find Runner.res # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res diff --git a/Cargo.toml b/Cargo.toml index 0f59d49cc3..10a0ecf8d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,7 @@ system_shutdown = "4.0" qrcode-generator = "4.1" [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi", "shellapi"] } +winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi", "shellapi", "devguid", "setupapi", "cguid", "cfgmgr32"] } winreg = "0.11" windows-service = "0.6" virtual_display = { path = "libs/virtual_display", optional = true } diff --git a/build.py b/build.py index ebea1f34dc..1779d7d46a 100755 --- a/build.py +++ b/build.py @@ -153,6 +153,12 @@ def make_parser(): action='store_true', help='Skip packing, only flutter version + Windows supported' ) + parser.add_argument( + '--virtual-display', + action='store_true', + default=False, + help='Build rustdesk libs with the virtual display feature enabled' + ) parser.add_argument( "--package", type=str @@ -293,6 +299,9 @@ def get_features(args): features.append('appimage') if args.unix_file_copy_paste: features.append('unix-file-copy-paste') + if windows: + if args.virtual_display: + features.append('virtual_display_driver') print("features:", features) return features diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 0e4a39d0a0..090ca62a41 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -19,7 +19,9 @@ const kKeyTranslateMode = 'translate'; const String kPlatformAdditionsIsWayland = "is_wayland"; const String kPlatformAdditionsHeadless = "headless"; const String kPlatformAdditionsIsInstalled = "is_installed"; -const String kPlatformAdditionsVirtualDisplays = "virtual_displays"; +const String kPlatformAdditionsIddImpl = "idd_impl"; +const String kPlatformAdditionsRustDeskVirtualDisplays = "rustdesk_virtual_displays"; +const String kPlatformAdditionsAmyuniVirtualDisplays = "amyuni_virtual_displays"; const String kPlatformAdditionsHasFileClipboard = "has_file_clipboard"; const String kPlatformAdditionsSupportedPrivacyModeImpl = "supported_privacy_mode_impl"; @@ -121,12 +123,11 @@ double kNewWindowOffset = isWindows ? 30.0 : 50.0; -EdgeInsets get kDragToResizeAreaPadding => - !kUseCompatibleUiMode && isLinux - ? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value - ? EdgeInsets.zero - : EdgeInsets.all(5.0) - : EdgeInsets.zero; +EdgeInsets get kDragToResizeAreaPadding => !kUseCompatibleUiMode && isLinux + ? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value + ? EdgeInsets.zero + : EdgeInsets.all(5.0) + : EdgeInsets.zero; // https://en.wikipedia.org/wiki/Non-breaking_space const int $nbsp = 0x00A0; diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 70a5b13a83..fd048337c2 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1058,11 +1058,16 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, screenAdjustor: _screenAdjustor, ), - // We may add this feature if it is needed and we have an EV certificate. - // _VirtualDisplayMenu( - // id: widget.id, - // ffi: widget.ffi, - // ), + if (pi.isRustDeskIdd) + _RustDeskVirtualDisplayMenu( + id: widget.id, + ffi: widget.ffi, + ), + if (pi.isAmyuniIdd) + _AmyuniVirtualDisplayMenu( + id: widget.id, + ffi: widget.ffi, + ), Divider(), toggles(), ]; @@ -1540,21 +1545,23 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } } -class _VirtualDisplayMenu extends StatefulWidget { +class _RustDeskVirtualDisplayMenu extends StatefulWidget { final String id; final FFI ffi; - _VirtualDisplayMenu({ + _RustDeskVirtualDisplayMenu({ Key? key, required this.id, required this.ffi, }) : super(key: key); @override - State<_VirtualDisplayMenu> createState() => _VirtualDisplayMenuState(); + State<_RustDeskVirtualDisplayMenu> createState() => + _RustDeskVirtualDisplayMenuState(); } -class _VirtualDisplayMenuState extends State<_VirtualDisplayMenu> { +class _RustDeskVirtualDisplayMenuState + extends State<_RustDeskVirtualDisplayMenu> { @override void initState() { super.initState(); @@ -1569,7 +1576,7 @@ class _VirtualDisplayMenuState extends State<_VirtualDisplayMenu> { return Offstage(); } - final virtualDisplays = widget.ffi.ffiModel.pi.virtualDisplays; + final virtualDisplays = widget.ffi.ffiModel.pi.RustDeskVirtualDisplays; final privacyModeState = PrivacyModeState.find(widget.id); final children = []; @@ -1611,6 +1618,82 @@ class _VirtualDisplayMenuState extends State<_VirtualDisplayMenu> { } } +class _AmyuniVirtualDisplayMenu extends StatefulWidget { + final String id; + final FFI ffi; + + _AmyuniVirtualDisplayMenu({ + Key? key, + required this.id, + required this.ffi, + }) : super(key: key); + + @override + State<_AmyuniVirtualDisplayMenu> createState() => + _AmiyuniVirtualDisplayMenuState(); +} + +class _AmiyuniVirtualDisplayMenuState extends State<_AmyuniVirtualDisplayMenu> { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + if (widget.ffi.ffiModel.pi.platform != kPeerPlatformWindows) { + return Offstage(); + } + if (!widget.ffi.ffiModel.pi.isInstalled) { + return Offstage(); + } + + final count = widget.ffi.ffiModel.pi.amyuniVirtualDisplayCount; + final privacyModeState = PrivacyModeState.find(widget.id); + + final children = [ + Obx(() => Row( + children: [ + TextButton( + onPressed: privacyModeState.isNotEmpty || count == 0 + ? null + : () => bind.sessionToggleVirtualDisplay( + sessionId: widget.ffi.sessionId, index: 0, on: false), + child: Icon(Icons.remove), + ), + Text(count.toString()), + TextButton( + onPressed: privacyModeState.isNotEmpty || count == 4 + ? null + : () => bind.sessionToggleVirtualDisplay( + sessionId: widget.ffi.sessionId, index: 0, on: true), + child: Icon(Icons.add), + ), + ], + )), + Divider(), + Obx(() => MenuButton( + onPressed: privacyModeState.isNotEmpty || count == 0 + ? null + : () { + bind.sessionToggleVirtualDisplay( + sessionId: widget.ffi.sessionId, + index: kAllVirtualDisplay, + on: false); + }, + ffi: widget.ffi, + child: Text(translate('Plug out all')), + )), + ]; + + return _SubmenuButton( + ffi: widget.ffi, + menuChildren: children, + child: Text(translate("Virtual display")), + ); + } +} + class _KeyboardMenu extends StatelessWidget { final String id; final FFI ffi; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 0a58aa0235..046767be6f 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -561,8 +561,12 @@ class FfiModel with ChangeNotifier { showRelayHintDialog(sessionId, type, title, text, dialogManager, peerId); } else if (text == 'Connected, waiting for image...') { showConnectedWaitingForImage(dialogManager, sessionId, type, title, text); + } else if (title == 'Privacy mode') { + final hasRetry = evt['hasRetry'] == 'true'; + showPrivacyFailedDialog( + sessionId, type, title, text, link, hasRetry, dialogManager); } else { - var hasRetry = evt['hasRetry'] == 'true'; + final hasRetry = evt['hasRetry'] == 'true'; showMsgBox(sessionId, type, title, text, link, hasRetry, dialogManager); } } @@ -657,6 +661,27 @@ class FfiModel with ChangeNotifier { bind.sessionOnWaitingForImageDialogShow(sessionId: sessionId); } + void showPrivacyFailedDialog( + SessionID sessionId, + String type, + String title, + String text, + String link, + bool hasRetry, + OverlayDialogManager dialogManager) { + if (text == 'no_need_privacy_mode_no_physical_displays_tip' || + text == 'Enter privacy mode') { + // There are display changes on the remote side, + // which will cause some messages to refresh the canvas and dismiss dialogs. + // So we add a delay here to ensure the dialog is displayed. + Future.delayed(Duration(milliseconds: 3000), () { + showMsgBox(sessionId, type, title, text, link, hasRetry, dialogManager); + }); + } else { + showMsgBox(sessionId, type, title, text, link, hasRetry, dialogManager); + } + } + _updateSessionWidthHeight(SessionID sessionId) { if (_rect == null) return; if (_rect!.width <= 0 || _rect!.height <= 0) { @@ -986,15 +1011,21 @@ class FfiModel with ChangeNotifier { } if (updateData.isEmpty) { - _pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays); + _pi.platformAdditions.remove(kPlatformAdditionsRustDeskVirtualDisplays); + _pi.platformAdditions.remove(kPlatformAdditionsAmyuniVirtualDisplays); } else { try { final updateJson = json.decode(updateData) as Map; for (final key in updateJson.keys) { _pi.platformAdditions[key] = updateJson[key]; } - if (!updateJson.containsKey(kPlatformAdditionsVirtualDisplays)) { - _pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays); + if (!updateJson + .containsKey(kPlatformAdditionsRustDeskVirtualDisplays)) { + _pi.platformAdditions + .remove(kPlatformAdditionsRustDeskVirtualDisplays); + } + if (!updateJson.containsKey(kPlatformAdditionsAmyuniVirtualDisplays)) { + _pi.platformAdditions.remove(kPlatformAdditionsAmyuniVirtualDisplays); } } catch (e) { debugPrint('Failed to decode platformAdditions $e'); @@ -2490,14 +2521,21 @@ class PeerInfo with ChangeNotifier { bool get isInstalled => platform != kPeerPlatformWindows || platformAdditions[kPlatformAdditionsIsInstalled] == true; - List get virtualDisplays => List.from( - platformAdditions[kPlatformAdditionsVirtualDisplays] ?? []); + List get RustDeskVirtualDisplays => List.from( + platformAdditions[kPlatformAdditionsRustDeskVirtualDisplays] ?? []); + int get amyuniVirtualDisplayCount => + platformAdditions[kPlatformAdditionsAmyuniVirtualDisplays] ?? 0; bool get isSupportMultiDisplay => (isDesktop || isWebDesktop) && isSupportMultiUiSession; bool get cursorEmbedded => tryGetDisplay()?.cursorEmbedded ?? false; + bool get isRustDeskIdd => + platformAdditions[kPlatformAdditionsIddImpl] == 'rustdesk_idd'; + bool get isAmyuniIdd => + platformAdditions[kPlatformAdditionsIddImpl] == 'amyuni_idd'; + Display? tryGetDisplay() { if (displays.isEmpty) { return null; diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 1543e8f655..3643ea9cea 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -591,3 +591,68 @@ UINT __stdcall AddRegSoftwareSASGeneration(__in MSIHANDLE hInstall) er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } + +UINT __stdcall RemoveAmyuniIdd( + __in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int nResult = 0; + LPWSTR installFolder = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzData = NULL; + + WCHAR workDir[1024] = L""; + DWORD fileAttributes = 0; + HINSTANCE hi = 0; + + USHORT processMachine = 0; + USHORT nativeMachine = 0; + BOOL isWow64Res = FALSE; + LPCWSTR exe = NULL; + + hr = WcaInitialize(hInstall, "RemoveAmyuniIdd"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &installFolder); + ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); + + hr = StringCchPrintfW(workDir, 1024, L"%lsusbmmidd_v2", installFolder); + ExitOnFailure(hr, "Failed to compose a resource identifier string"); + fileAttributes = GetFileAttributesW(workDir); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) { + WcaLog(LOGMSG_STANDARD, "Amyuni idd dir \"%ls\" is out found, %d", workDir, fileAttributes); + goto LExit; + } + + isWow64Res = IsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine); + if (isWow64Res == TRUE) { + if (nativeMachine == IMAGE_FILE_MACHINE_AMD64) { + exe = L"deviceinstaller64.exe"; + } else { + exe = L"deviceinstaller.exe"; + } + WcaLog(LOGMSG_STANDARD, "Remove amyuni idd %ls in %ls", exe, workDir); + hi = ShellExecuteW(NULL, L"open", exe, L"remove usbmmidd", workDir, SW_HIDE); + // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew + if ((int)hi <= 32) { + WcaLog(LOGMSG_STANDARD, "Failed to remove amyuni idd : %d, last error: %d", (int)hi, GetLastError()); + } + else { + WcaLog(LOGMSG_STANDARD, "Amyuni idd is removed"); + } + } else { + WcaLog(LOGMSG_STANDARD, "Failed to call IsWow64Process2(): %d", GetLastError()); + } + +LExit: + ReleaseStr(installFolder); + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/res/msi/CustomActions/CustomActions.def b/res/msi/CustomActions/CustomActions.def index a73ecda266..557bfaf187 100644 --- a/res/msi/CustomActions/CustomActions.def +++ b/res/msi/CustomActions/CustomActions.def @@ -11,3 +11,4 @@ EXPORTS TryDeleteStartupShortcut SetPropertyFromConfig AddRegSoftwareSASGeneration + RemoveAmyuniIdd diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index ab1d404ecd..31dbbc66db 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -29,6 +29,7 @@ + @@ -68,10 +69,12 @@ - + - + + + diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index e064b188e1..b443eff52c 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -16,5 +16,6 @@ + diff --git a/src/core_main.rs b/src/core_main.rs index cd27ec0ca7..25e2912959 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -215,7 +215,9 @@ pub fn core_main() -> Option> { } else if args[0] == "--install-idd" { #[cfg(all(windows, feature = "virtual_display_driver"))] if crate::virtual_display_manager::is_virtual_display_supported() { - hbb_common::allow_err!(crate::virtual_display_manager::install_update_driver()); + hbb_common::allow_err!( + crate::virtual_display_manager::rustdesk_idd::install_update_driver() + ); } return None; } else if args[0] == "--portable-service" { @@ -254,7 +256,7 @@ pub fn core_main() -> Option> { } else if args[0] == "--server" { log::info!("start --server with user {}", crate::username()); #[cfg(all(windows, feature = "virtual_display_driver"))] - crate::privacy_mode::restore_reg_connectivity(); + crate::privacy_mode::restore_reg_connectivity(true); #[cfg(any(target_os = "linux", target_os = "windows"))] { crate::start_server(true); diff --git a/src/lang/ar.rs b/src/lang/ar.rs index be4f6bc5b7..bdfd83cc1c 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 4ed1cc0c33..3a3a40ee91 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 42d714da7f..610282b1a6 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index b350420b8e..0309cf5628 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "所有人"), ("ab_web_console_tip", "打开 Web 控制台以执行更多操作"), ("allow-only-conn-window-open-tip", "仅当 RustDesk 窗口打开时允许连接"), + ("no_need_privacy_mode_no_physical_displays_tip", "没有物理显示器,没必要使用隐私模式。"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index b6bb80b59c..4c3a4b3cce 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Každý"), ("ab_web_console_tip", "Více na webové konzoli"), ("allow-only-conn-window-open-tip", "Povolit připojení pouze v případě, že je otevřené okno RustDesk"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 8ec86dace9..c20f151286 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index e8d63e7902..1c88bdde5c 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Jeder"), ("ab_web_console_tip", "Mehr über Webkonsole"), ("allow-only-conn-window-open-tip", "Verbindung nur zulassen, wenn das RustDesk-Fenster geöffnet ist"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 369ec466d8..7d4966120b 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 5782a85979..ac6cd2cd38 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -219,5 +219,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "The fields above are shared and visible to others."), ("ab_web_console_tip", "More on web console"), ("allow-only-conn-window-open-tip", "Only allow connection if RustDesk window is open"), + ("no_need_privacy_mode_no_physical_displays_tip", "No physical displays, no need to use the privacy mode."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index fc17f0af93..e1bab260d5 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index e9ac3a3c5a..1e7c40b269 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Todos"), ("ab_web_console_tip", "Más en consola web"), ("allow-only-conn-window-open-tip", "Permitir la conexión solo si la ventana RusDesk está abierta"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 5449dc8634..2e49ddee7a 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 4cf7b889fe..6eb59377de 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "هر کس"), ("ab_web_console_tip", "اطلاعات بیشتر در کنسول وب"), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 7f6d75b085..ff644806ea 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 3cec23385d..2da9339a2c 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 91913676b3..def491c525 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Svatko"), ("ab_web_console_tip", "Više na web konzoli"), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 1a63bdcf8c..bdae7d9bb4 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index b562937857..9a0fc988f4 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 493519fbfd..670853e4a1 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Everyone"), ("ab_web_console_tip", "Altre info sulla console web"), ("allow-only-conn-window-open-tip", "Consenti la connessione solo se la finestra RustDesk è aperta"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 184461198a..400ae13292 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 116cd5d02c..e0fc91a060 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index eb599cd985..7bffe6a2e2 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 6c7b84804f..06d3ce2bde 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 9fbc7d10ad..cc0cf98ff0 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Visi"), ("ab_web_console_tip", "Vairāk par tīmekļa konsoli"), ("allow-only-conn-window-open-tip", "Atļaut savienojumu tikai tad, ja ir atvērts RustDesk logs"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 5a0a83f447..d4251a5549 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index a4c0749125..705b34f165 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Iedereen"), ("ab_web_console_tip", "Meer over de webconsole"), ("allow-only-conn-window-open-tip", "Alleen verbindingen toestaan als het RustDesk-venster geopend is"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index c32931661b..06adebc09c 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Wszyscy"), ("ab_web_console_tip", "Więcej w konsoli web"), ("allow-only-conn-window-open-tip", "Zezwalaj na połączenie tylko wtedy, gdy okno RustDesk jest otwarte"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index c1f40cbae7..76fa2c2144 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 01795ebf76..2958878a5f 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Todos"), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 28aa015c98..c7780c371b 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 00c4ad0de4..0cc7acdb0e 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Все"), ("ab_web_console_tip", "Больше в веб-консоли"), ("allow-only-conn-window-open-tip", "Разрешать подключение только при открытом окне RustDesk"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 289a75410b..7492b59f2a 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Každý"), ("ab_web_console_tip", "Viac na webovej konzole"), ("allow-only-conn-window-open-tip", "Povoliť pripojenie iba vtedy, ak je otvorené okno aplikácie RustDesk"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index cbc15f35ee..cdc37594de 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index f6369c9dab..6a7ee5d634 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index b38b2713dd..e033834151 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index e40ddc8245..b78677fe60 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 2c608cf1cf..02aa924501 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 20abcd6394..ce262d19a2 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index cacaa15865..27fca3807f 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index fbd36cb12f..4e86fe760f 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "所有人"), ("ab_web_console_tip", "打開 Web 控制台以進行更多操作"), ("allow-only-conn-window-open-tip", "只在 RustDesk 視窗開啟時允許連接"), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 0f4fc652ef..63a8800f54 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Всі"), ("ab_web_console_tip", "Детальніше про веб-консоль"), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index bd671bd8f1..96cbbee7ec 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", ""), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", ""), ].iter().cloned().collect(); } diff --git a/src/platform/windows.cc b/src/platform/windows.cc index 381f4dc63d..0d328ac13a 100644 --- a/src/platform/windows.cc +++ b/src/platform/windows.cc @@ -669,4 +669,48 @@ extern "C" AllocConsole(); freopen("CONOUT$", "w", stdout); } + + bool is_service_running_w(LPCWSTR serviceName) + { + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_QUERY_STATUS); + if (hService == NULL) { + CloseServiceHandle(hSCManager); + return false; + } + + SERVICE_STATUS_PROCESS serviceStatus; + DWORD bytesNeeded; + if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, reinterpret_cast(&serviceStatus), sizeof(serviceStatus), &bytesNeeded)) { + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return false; + } + + bool isRunning = (serviceStatus.dwCurrentState == SERVICE_RUNNING); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return isRunning; + } } // end of extern "C" + +extern "C" +{ + int get_native_machine() + { + USHORT processMachine = 0; + USHORT nativeMachine = 0; + BOOL res = IsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine); + if (res == TRUE) { + return (int)nativeMachine; + } else { + return -1; + } + } +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index c285702190..a6dfd444fd 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -64,8 +64,6 @@ use windows_service::{ use winreg::enums::*; use winreg::RegKey; -pub const DRIVER_CERT_FILE: &str = "RustDeskIddDriver.cer"; - pub fn get_cursor_pos() -> Option<(i32, i32)> { unsafe { #[allow(invalid_value)] @@ -462,6 +460,8 @@ extern "C" { fn is_win_down() -> BOOL; fn is_local_system() -> BOOL; fn alloc_console_and_redirect(); + fn get_native_machine() -> i32; + fn is_service_running_w(svc_name: *const u16) -> bool; } extern "system" { @@ -1296,12 +1296,14 @@ fn get_uninstall(kill_self: bool) -> String { {before_uninstall} {uninstall_cert_cmd} reg delete {subkey} /f + {uninstall_amyuni_idd} if exist \"{path}\" rd /s /q \"{path}\" if exist \"{start_menu}\" rd /s /q \"{start_menu}\" if exist \"%PUBLIC%\\Desktop\\{app_name}.lnk\" del /f /q \"%PUBLIC%\\Desktop\\{app_name}.lnk\" if exist \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" ", before_uninstall=get_before_uninstall(kill_self), + uninstall_amyuni_idd=get_uninstall_amyuni_idd(&path), app_name = crate::get_app_name(), ) } @@ -2367,3 +2369,81 @@ impl Drop for WallPaperRemover { allow_err!(Self::set_wallpaper(Some(self.old_path.clone()))); } } + +// See winnt.h for more information. +#[derive(Clone, Copy)] +pub enum MachineArch { + Unknown = 0, + I386 = 0x014c, + ARM = 0x01c0, + AMD64 = 0x8664, + ARM64 = 0xAA64, +} + +pub fn get_machine_arch() -> Result { + let native_machine = unsafe { get_native_machine() }; + if native_machine != -1 { + let native_machine = native_machine as u16; + let check_types = [ + MachineArch::I386, + MachineArch::AMD64, + MachineArch::ARM, + MachineArch::ARM64, + ]; + for check_type in check_types.iter() { + if *check_type as u16 == native_machine { + return Ok(*check_type); + } + } + Ok(MachineArch::Unknown) + } else { + Err(io::Error::last_os_error()) + } +} + +pub fn get_amyuni_exe_name() -> Option { + match get_machine_arch() { + Ok(arch) => { + let exe = match arch { + MachineArch::I386 => "deviceinstaller.exe", + MachineArch::AMD64 => "deviceinstaller64.exe", + _ => { + log::error!("Unsupported machine architecture"); + return None; + } + }; + Some(exe.to_string()) + } + Err(e) => { + log::warn!("Failed to get machine architecture: {}", e); + None + } + } +} + +fn get_uninstall_amyuni_idd(path: &str) -> String { + let Some(exe) = get_amyuni_exe_name() else { + return "".to_string(); + }; + let work_dir = PathBuf::from(path).join("usbmmidd_v2"); + if work_dir.join(&exe).exists() { + format!( + "pushd {} && .\\{exe} remove usbmmidd && popd", + work_dir.to_string_lossy() + ) + } else { + "".to_string() + } +} + +#[inline] +pub fn is_self_service_running() -> bool { + is_service_running(&crate::get_app_name()) +} + +pub fn is_service_running(service_name: &str) -> bool { + unsafe { + let service_name = wide_string(service_name); + is_service_running_w(service_name.as_ptr() as _) + } +} diff --git a/src/privacy_mode.rs b/src/privacy_mode.rs index d0781d9936..f6eafcf417 100644 --- a/src/privacy_mode.rs +++ b/src/privacy_mode.rs @@ -6,9 +6,12 @@ use crate::{ display_service, ipc::{connect, Data}, }; -#[cfg(windows)] -use hbb_common::tokio; -use hbb_common::{anyhow::anyhow, bail, lazy_static, ResultType}; +use hbb_common::{ + anyhow::anyhow, + bail, lazy_static, + tokio::{self, sync::oneshot}, + ResultType, +}; use serde_derive::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -30,10 +33,10 @@ mod win_virtual_display; pub use win_virtual_display::restore_reg_connectivity; pub const INVALID_PRIVACY_MODE_CONN_ID: i32 = 0; -pub const OCCUPIED: &'static str = "Privacy occupied by another one"; +pub const OCCUPIED: &'static str = "Privacy occupied by another one."; pub const TURN_OFF_OTHER_ID: &'static str = - "Failed to turn off privacy mode that belongs to someone else"; -pub const NO_DISPLAYS: &'static str = "No displays"; + "Failed to turn off privacy mode that belongs to someone else."; +pub const NO_PHYSICAL_DISPLAYS: &'static str = "no_need_privacy_mode_no_physical_displays_tip"; #[cfg(windows)] pub const PRIVACY_MODE_IMPL_WIN_MAG: &str = win_mag::PRIVACY_MODE_IMPL; @@ -53,6 +56,8 @@ pub enum PrivacyModeState { } pub trait PrivacyMode: Sync + Send { + fn is_async_privacy_mode(&self) -> bool; + fn init(&self) -> ResultType<()>; fn clear(&mut self); fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType; @@ -203,7 +208,40 @@ fn get_supported_impl(impl_key: &str) -> String { } #[inline] -pub fn turn_on_privacy(impl_key: &str, conn_id: i32) -> Option> { +pub async fn turn_on_privacy(impl_key: &str, conn_id: i32) -> Option> { + if is_async_privacy_mode() { + turn_on_privacy_async(impl_key.to_string(), conn_id).await + } else { + turn_on_privacy_sync(impl_key, conn_id) + } +} + +#[inline] +fn is_async_privacy_mode() -> bool { + PRIVACY_MODE + .lock() + .unwrap() + .as_ref() + .map_or(false, |m| m.is_async_privacy_mode()) +} + +#[inline] +async fn turn_on_privacy_async(impl_key: String, conn_id: i32) -> Option> { + let (tx, rx) = oneshot::channel(); + std::thread::spawn(move || { + let res = turn_on_privacy_sync(&impl_key, conn_id); + let _ = tx.send(res); + }); + match hbb_common::timeout(5000, rx).await { + Ok(res) => match res { + Ok(res) => res, + Err(e) => Some(Err(anyhow!(e.to_string()))), + }, + Err(e) => Some(Err(anyhow!(e.to_string()))), + } +} + +fn turn_on_privacy_sync(impl_key: &str, conn_id: i32) -> Option> { // Check if privacy mode is already on or occupied by another one let mut privacy_mode_lock = PRIVACY_MODE.lock().unwrap(); @@ -300,7 +338,7 @@ pub fn get_supported_privacy_mode_impl() -> Vec<(&'static str, &'static str)> { } #[cfg(feature = "virtual_display_driver")] - if is_installed() { + if is_installed() && crate::platform::windows::is_self_service_running() { vec_impls.push(( PRIVACY_MODE_IMPL_WIN_VIRTUAL_DISPLAY, "privacy_mode_impl_virtual_display_tip", diff --git a/src/privacy_mode/win_topmost_window.rs b/src/privacy_mode/win_topmost_window.rs index 7fd27b60bd..a7f80a02d7 100644 --- a/src/privacy_mode/win_topmost_window.rs +++ b/src/privacy_mode/win_topmost_window.rs @@ -72,6 +72,10 @@ pub struct PrivacyModeImpl { } impl PrivacyMode for PrivacyModeImpl { + fn is_async_privacy_mode(&self) -> bool { + false + } + fn init(&self) -> ResultType<()> { Ok(()) } diff --git a/src/privacy_mode/win_virtual_display.rs b/src/privacy_mode/win_virtual_display.rs index 287bfd51e4..04a9d776cd 100644 --- a/src/privacy_mode/win_virtual_display.rs +++ b/src/privacy_mode/win_virtual_display.rs @@ -1,9 +1,11 @@ -use super::{PrivacyMode, PrivacyModeState, INVALID_PRIVACY_MODE_CONN_ID, NO_DISPLAYS}; +use super::{PrivacyMode, PrivacyModeState, INVALID_PRIVACY_MODE_CONN_ID, NO_PHYSICAL_DISPLAYS}; use crate::virtual_display_manager; use hbb_common::{allow_err, bail, config::Config, log, ResultType}; use std::{ io::Error, ops::{Deref, DerefMut}, + thread, + time::Duration, }; use virtual_display::MonitorMode; use winapi::{ @@ -27,7 +29,6 @@ use winapi::{ pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_virtual_display"; -const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0"; const CONFIG_KEY_REG_RECOVERY: &str = "reg_recovery"; struct Display { @@ -137,8 +138,9 @@ impl PrivacyModeImpl { primary, }; - if let Ok(s) = std::string::String::from_utf16(&dd.DeviceString) { - if &s[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING { + let ds = virtual_display_manager::get_cur_device_string(); + if let Ok(s) = String::from_utf16(&dd.DeviceString) { + if s.len() >= ds.len() && &s[..ds.len()] == ds { self.virtual_displays.push(display); continue; } @@ -155,7 +157,7 @@ impl PrivacyModeImpl { } fn restore_plug_out_monitor(&mut self) { - let _ = virtual_display_manager::plug_out_peer_request(&self.virtual_displays_added); + let _ = virtual_display_manager::plug_out_monitor_indices(&self.virtual_displays_added); self.virtual_displays_added.clear(); } @@ -304,8 +306,18 @@ impl PrivacyModeImpl { if self.virtual_displays.is_empty() { let displays = virtual_display_manager::plug_in_peer_request(vec![Self::default_display_modes()])?; - self.virtual_displays_added.extend(displays); + if virtual_display_manager::is_amyuni_idd() { + thread::sleep(Duration::from_secs(3)); + } self.set_displays(); + + // No physical displays, no need to use the privacy mode. + if self.displays.is_empty() { + virtual_display_manager::plug_out_monitor_indices(&displays)?; + bail!(NO_PHYSICAL_DISPLAYS); + } + + self.virtual_displays_added.extend(displays); } Ok(()) @@ -348,6 +360,10 @@ impl PrivacyModeImpl { } impl PrivacyMode for PrivacyModeImpl { + fn is_async_privacy_mode(&self) -> bool { + virtual_display_manager::is_amyuni_idd() + } + fn init(&self) -> ResultType<()> { Ok(()) } @@ -367,8 +383,8 @@ impl PrivacyMode for PrivacyModeImpl { } self.set_displays(); if self.displays.is_empty() { - log::debug!("No displays"); - bail!(NO_DISPLAYS); + log::debug!("{}", NO_PHYSICAL_DISPLAYS); + bail!(NO_PHYSICAL_DISPLAYS); } let mut guard = TurnOnGuard { @@ -379,7 +395,7 @@ impl PrivacyMode for PrivacyModeImpl { guard.ensure_virtual_display()?; if guard.virtual_displays.is_empty() { log::debug!("No virtual displays"); - bail!("No virtual displays"); + bail!("No virtual displays."); } let reg_connectivity_1 = reg_display_settings::read_reg_connectivity()?; @@ -416,7 +432,7 @@ impl PrivacyMode for PrivacyModeImpl { self.check_off_conn_id(conn_id)?; super::win_input::unhook()?; self.restore(); - restore_reg_connectivity(); + restore_reg_connectivity(false); if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID { if let Some(state) = state { @@ -457,11 +473,14 @@ fn reset_config_reg_connectivity() { Config::set_option(CONFIG_KEY_REG_RECOVERY.to_owned(), "".to_owned()); } -pub fn restore_reg_connectivity() { +pub fn restore_reg_connectivity(plug_out_monitors: bool) { let config_recovery_value = Config::get_option(CONFIG_KEY_REG_RECOVERY); if config_recovery_value.is_empty() { return; } + if plug_out_monitors { + let _ = virtual_display_manager::plug_out_monitor(-1); + } if let Ok(reg_recovery) = serde_json::from_str::(&config_recovery_value) { diff --git a/src/server/connection.rs b/src/server/connection.rs index 1a30730afb..8526a998d2 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1133,10 +1133,7 @@ impl Connection { ); #[cfg(feature = "virtual_display_driver")] if crate::platform::is_installed() { - let virtual_displays = virtual_display_manager::get_virtual_displays(); - if !virtual_displays.is_empty() { - platform_additions.insert("virtual_displays".into(), json!(&virtual_displays)); - } + platform_additions.extend(virtual_display_manager::get_platform_additions()); } platform_additions.insert( "supported_privacy_mode_impl".into(), @@ -2595,8 +2592,7 @@ impl Connection { self.send(make_msg("idd_not_support_under_win10_2004_tip".to_string())) .await; } else { - if let Err(e) = - virtual_display_manager::plug_in_index_modes(t.display as _, Vec::new()) + if let Err(e) = virtual_display_manager::plug_in_monitor(t.display as _, Vec::new()) { log::error!("Failed to plug in virtual display: {}", e); self.send(make_msg(format!( @@ -2607,13 +2603,8 @@ impl Connection { } } } else { - let indices = if t.display == -1 { - virtual_display_manager::get_virtual_displays() - } else { - vec![t.display as _] - }; - if let Err(e) = virtual_display_manager::plug_out_peer_request(&indices) { - log::error!("Failed to plug out virtual display {:?}: {}", &indices, e); + if let Err(e) = virtual_display_manager::plug_out_monitor(t.display) { + log::error!("Failed to plug out virtual display {}: {}", t.display, e); self.send(make_msg(format!( "Failed to plug out virtual displays: {}", e @@ -2639,7 +2630,7 @@ impl Connection { let name = display.name(); #[cfg(all(windows, feature = "virtual_display_driver"))] if let Some(_ok) = - virtual_display_manager::change_resolution_if_is_virtual_display( + virtual_display_manager::rustdesk_idd::change_resolution_if_is_virtual_display( &name, r.width as _, r.height as _, @@ -2858,7 +2849,6 @@ impl Connection { } else { let is_pre_privacy_on = privacy_mode::is_in_privacy_mode(); let pre_impl_key = privacy_mode::get_cur_impl_key(); - let turn_on_res = privacy_mode::turn_on_privacy(&impl_key, self.inner.id); if is_pre_privacy_on { if let Some(pre_impl_key) = pre_impl_key { @@ -2872,6 +2862,7 @@ impl Connection { } } + let turn_on_res = privacy_mode::turn_on_privacy(&impl_key, self.inner.id).await; match turn_on_res { Some(Ok(res)) => { if res { @@ -2906,7 +2897,7 @@ impl Connection { } Some(Err(e)) => { log::error!("Failed to turn on privacy mode. {}", e); - if !privacy_mode::is_in_privacy_mode() { + if privacy_mode::is_in_privacy_mode() { let _ = Self::turn_off_privacy_to_msg( privacy_mode::INVALID_PRIVACY_MODE_CONN_ID, ); diff --git a/src/server/display_service.rs b/src/server/display_service.rs index 838937057a..0c8263cbdf 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -160,15 +160,8 @@ fn displays_to_msg(displays: Vec) -> Message { #[cfg(all(windows, feature = "virtual_display_driver"))] if crate::platform::is_installed() { - let virtual_displays = crate::virtual_display_manager::get_virtual_displays(); - if !virtual_displays.is_empty() { - let mut platform_additions = serde_json::Map::new(); - platform_additions.insert( - "virtual_displays".into(), - serde_json::json!(&virtual_displays), - ); - pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); - } + let m = crate::virtual_display_manager::get_platform_additions(); + pi.platform_additions = serde_json::to_string(&m).unwrap_or_default(); } // current_display should not be used in server. @@ -227,10 +220,11 @@ pub(super) fn get_original_resolution( h: usize, ) -> MessageField { #[cfg(all(windows, feature = "virtual_display_driver"))] - let is_virtual_display = crate::virtual_display_manager::is_virtual_display(&display_name); + let is_rustdesk_virtual_display = + crate::virtual_display_manager::rustdesk_idd::is_virtual_display(&display_name); #[cfg(not(all(windows, feature = "virtual_display_driver")))] - let is_virtual_display = false; - Some(if is_virtual_display { + let is_rustdesk_virtual_display = false; + Some(if is_rustdesk_virtual_display { Resolution { width: 0, height: 0, @@ -382,8 +376,10 @@ pub fn try_get_displays() -> ResultType> { #[cfg(all(windows, feature = "virtual_display_driver"))] pub fn try_get_displays() -> ResultType> { let mut displays = Display::all()?; + let no_displays_v = no_displays(&displays); + virtual_display_manager::set_can_plug_out_all(!no_displays_v); if crate::platform::is_installed() - && no_displays(&displays) + && no_displays_v && virtual_display_manager::is_virtual_display_supported() { log::debug!("no displays, create virtual display"); diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index da8780c7f0..3188fc33ea 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -1,57 +1,47 @@ -#[cfg(target_os = "windows")] -use hbb_common::platform::windows::is_windows_version_or_greater; -use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; -use std::{ - collections::{HashMap, HashSet}, - sync::{Arc, Mutex}, -}; - -// virtual display index range: 0 - 2 are reserved for headless and other special uses. -const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0; -const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 1; -const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 5; - -lazy_static::lazy_static! { - static ref VIRTUAL_DISPLAY_MANAGER: Arc> = - Arc::new(Mutex::new(VirtualDisplayManager::default())); -} +use hbb_common::{bail, platform::windows::is_windows_version_or_greater, ResultType}; +use std::sync::atomic; + +// This string is defined here. +// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 +pub const RUSTDESK_IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0"; +pub const AMYUNI_IDD_DEVICE_STRING: &'static str = "USB Mobile Monitor Virtual Display\0"; + +const IDD_IMPL: &str = IDD_IMPL_AMYUNI; +const IDD_IMPL_RUSTDESK: &str = "rustdesk_idd"; +const IDD_IMPL_AMYUNI: &str = "amyuni_idd"; -#[derive(Default)] -struct VirtualDisplayManager { - headless_index_name: Option<(u32, String)>, - peer_index_name: HashMap, - is_driver_installed: bool, +const IS_CAN_PLUG_OUT_ALL_NOT_SET: i8 = 0; +const IS_CAN_PLUG_OUT_ALL_YES: i8 = 1; +const IS_CAN_PLUG_OUT_ALL_NO: i8 = 2; +static IS_CAN_PLUG_OUT_ALL: atomic::AtomicI8 = atomic::AtomicI8::new(IS_CAN_PLUG_OUT_ALL_NOT_SET); + +pub fn is_can_plug_out_all() -> bool { + IS_CAN_PLUG_OUT_ALL.load(atomic::Ordering::Relaxed) != IS_CAN_PLUG_OUT_ALL_NO } -impl VirtualDisplayManager { - fn prepare_driver(&mut self) -> ResultType<()> { - if !self.is_driver_installed { - self.install_update_driver()?; - } - Ok(()) +// No need to consider concurrency here. +pub fn set_can_plug_out_all(v: bool) { + if IS_CAN_PLUG_OUT_ALL.load(atomic::Ordering::Relaxed) == IS_CAN_PLUG_OUT_ALL_NOT_SET { + IS_CAN_PLUG_OUT_ALL.store( + if v { + IS_CAN_PLUG_OUT_ALL_YES + } else { + IS_CAN_PLUG_OUT_ALL_NO + }, + atomic::Ordering::Relaxed, + ); } +} - fn install_update_driver(&mut self) -> ResultType<()> { - if let Err(e) = virtual_display::create_device() { - if !e.to_string().contains("Device is already created") { - bail!("Create device failed {}", e); - } - } - // Reboot is not required for this case. - let mut _reboot_required = false; - virtual_display::install_update_driver(&mut _reboot_required)?; - self.is_driver_installed = true; - Ok(()) - } +pub fn is_amyuni_idd() -> bool { + IDD_IMPL == IDD_IMPL_AMYUNI +} - fn plug_in_monitor(index: u32, modes: &[virtual_display::MonitorMode]) -> ResultType<()> { - if let Err(e) = virtual_display::plug_in_monitor(index) { - bail!("Plug in monitor failed {}", e); - } - if let Err(e) = virtual_display::update_monitor_modes(index, &modes) { - log::error!("Update monitor modes failed {}", e); - } - Ok(()) +pub fn get_cur_device_string() -> &'static str { + match IDD_IMPL { + IDD_IMPL_RUSTDESK => RUSTDESK_IDD_DEVICE_STRING, + IDD_IMPL_AMYUNI => AMYUNI_IDD_DEVICE_STRING, + _ => "", } } @@ -66,209 +56,514 @@ pub fn is_virtual_display_supported() -> bool { } } -pub fn install_update_driver() -> ResultType<()> { - VIRTUAL_DISPLAY_MANAGER - .lock() - .unwrap() - .install_update_driver() -} - pub fn plug_in_headless() -> ResultType<()> { - let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - manager.prepare_driver()?; - let modes = [virtual_display::MonitorMode { - width: 1920, - height: 1080, - sync: 60, - }]; - let device_names = windows::get_device_names(); - VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?; - let device_name = get_new_device_name(&device_names); - manager.headless_index_name = Some((VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, device_name)); - Ok(()) + match IDD_IMPL { + IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_headless(), + IDD_IMPL_AMYUNI => amyuni_idd::plug_in_headless(), + _ => bail!("Unsupported virtual display implementation."), + } } -pub fn plug_out_headless() -> bool { - let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - if let Some((index, _)) = manager.headless_index_name.take() { - if let Err(e) = virtual_display::plug_out_monitor(index) { - log::error!("Plug out monitor failed {}", e); +pub fn get_platform_additions() -> serde_json::Map { + let mut map = serde_json::Map::new(); + if !crate::platform::windows::is_self_service_running() { + return map; + } + map.insert("idd_impl".into(), serde_json::json!(IDD_IMPL)); + match IDD_IMPL { + IDD_IMPL_RUSTDESK => { + let virtual_displays = rustdesk_idd::get_virtual_displays(); + if !virtual_displays.is_empty() { + map.insert( + "rustdesk_virtual_displays".into(), + serde_json::json!(virtual_displays), + ); + } } - true - } else { - false + IDD_IMPL_AMYUNI => { + let c = amyuni_idd::get_monitor_count(); + if c > 0 { + map.insert("amyuni_virtual_displays".into(), serde_json::json!(c)); + } + } + _ => {} } + map } -fn get_new_device_name(device_names: &HashSet) -> String { - for _ in 0..3 { - let device_names_af = windows::get_device_names(); - let diff_names: Vec<_> = device_names_af.difference(&device_names).collect(); - if diff_names.len() == 1 { - return diff_names[0].clone(); - } else if diff_names.len() > 1 { - log::error!( - "Failed to get diff device names after plugin virtual display, more than one diff names: {:?}", - &diff_names - ); - return "".to_string(); +#[inline] +pub fn plug_in_monitor(idx: u32, modes: Vec) -> ResultType<()> { + match IDD_IMPL { + IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_index_modes(idx, modes), + IDD_IMPL_AMYUNI => amyuni_idd::plug_in_monitor(), + _ => bail!("Unsupported virtual display implementation."), + } +} + +pub fn plug_out_monitor(index: i32) -> ResultType<()> { + match IDD_IMPL { + IDD_IMPL_RUSTDESK => { + let indices = if index == -1 { + rustdesk_idd::get_virtual_displays() + } else { + vec![index as _] + }; + rustdesk_idd::plug_out_peer_request(&indices) } - // Sleep is needed here to wait for the virtual display to be ready. - std::thread::sleep(std::time::Duration::from_millis(50)); + IDD_IMPL_AMYUNI => amyuni_idd::plug_out_monitor(index), + _ => bail!("Unsupported virtual display implementation."), } - log::error!("Failed to get diff device names after plugin virtual display",); - "".to_string() } -pub fn get_virtual_displays() -> Vec { - VIRTUAL_DISPLAY_MANAGER - .lock() - .unwrap() - .peer_index_name - .keys() - .cloned() - .collect() +pub fn plug_in_peer_request(modes: Vec>) -> ResultType> { + match IDD_IMPL { + IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_peer_request(modes), + IDD_IMPL_AMYUNI => { + amyuni_idd::plug_in_monitor()?; + Ok(vec![0]) + } + _ => bail!("Unsupported virtual display implementation."), + } } -pub fn plug_in_index_modes( - idx: u32, - mut modes: Vec, -) -> ResultType<()> { - let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - manager.prepare_driver()?; - if !manager.peer_index_name.contains_key(&idx) { - let device_names = windows::get_device_names(); - if modes.is_empty() { - modes.push(virtual_display::MonitorMode { - width: 1920, - height: 1080, - sync: 60, - }); - } - match VirtualDisplayManager::plug_in_monitor(idx, modes.as_slice()) { - Ok(_) => { - let device_name = get_new_device_name(&device_names); - manager.peer_index_name.insert(idx, device_name); - } - Err(e) => { - log::error!("Plug in monitor failed {}", e); +pub fn plug_out_monitor_indices(indices: &[u32]) -> ResultType<()> { + match IDD_IMPL { + IDD_IMPL_RUSTDESK => rustdesk_idd::plug_out_peer_request(indices), + IDD_IMPL_AMYUNI => { + for _idx in indices.iter() { + amyuni_idd::plug_out_monitor(0)?; } + Ok(()) } + _ => bail!("Unsupported virtual display implementation."), } - Ok(()) } pub fn reset_all() -> ResultType<()> { - if is_virtual_display_supported() { - return Ok(()); + match IDD_IMPL { + IDD_IMPL_RUSTDESK => rustdesk_idd::reset_all(), + IDD_IMPL_AMYUNI => crate::privacy_mode::turn_off_privacy(0, None).unwrap_or(Ok(())), + _ => bail!("Unsupported virtual display implementation."), } +} + +pub mod rustdesk_idd { + use super::windows; + use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; + use std::{ + collections::{HashMap, HashSet}, + sync::{Arc, Mutex}, + }; + + // virtual display index range: 0 - 2 are reserved for headless and other special uses. + const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0; + const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 1; + const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 5; - if let Err(e) = plug_out_peer_request(&get_virtual_displays()) { - log::error!("Failed to plug out virtual displays: {}", e); + lazy_static::lazy_static! { + static ref VIRTUAL_DISPLAY_MANAGER: Arc> = + Arc::new(Mutex::new(VirtualDisplayManager::default())); } - let _ = plug_out_headless(); - Ok(()) -} -pub fn plug_in_peer_request(modes: Vec>) -> ResultType> { - let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - manager.prepare_driver()?; - - let mut indices: Vec = Vec::new(); - for m in modes.iter() { - for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT { - if !manager.peer_index_name.contains_key(&idx) { - let device_names = windows::get_device_names(); - match VirtualDisplayManager::plug_in_monitor(idx, m) { - Ok(_) => { - let device_name = get_new_device_name(&device_names); - manager.peer_index_name.insert(idx, device_name); - indices.push(idx); - } - Err(e) => { - log::error!("Plug in monitor failed {}", e); + #[derive(Default)] + struct VirtualDisplayManager { + headless_index_name: Option<(u32, String)>, + peer_index_name: HashMap, + is_driver_installed: bool, + } + + impl VirtualDisplayManager { + fn prepare_driver(&mut self) -> ResultType<()> { + if !self.is_driver_installed { + self.install_update_driver()?; + } + Ok(()) + } + + fn install_update_driver(&mut self) -> ResultType<()> { + if let Err(e) = virtual_display::create_device() { + if !e.to_string().contains("Device is already created") { + bail!("Create device failed {}", e); + } + } + // Reboot is not required for this case. + let mut _reboot_required = false; + virtual_display::install_update_driver(&mut _reboot_required)?; + self.is_driver_installed = true; + Ok(()) + } + + fn plug_in_monitor(index: u32, modes: &[virtual_display::MonitorMode]) -> ResultType<()> { + if let Err(e) = virtual_display::plug_in_monitor(index) { + bail!("Plug in monitor failed {}", e); + } + if let Err(e) = virtual_display::update_monitor_modes(index, &modes) { + log::error!("Update monitor modes failed {}", e); + } + Ok(()) + } + } + + pub fn install_update_driver() -> ResultType<()> { + VIRTUAL_DISPLAY_MANAGER + .lock() + .unwrap() + .install_update_driver() + } + + #[inline] + fn get_device_names() -> Vec { + windows::get_device_names(Some(super::RUSTDESK_IDD_DEVICE_STRING)) + } + + pub fn plug_in_headless() -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + manager.prepare_driver()?; + let modes = [virtual_display::MonitorMode { + width: 1920, + height: 1080, + sync: 60, + }]; + let device_names = get_device_names().into_iter().collect(); + VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?; + let device_name = get_new_device_name(&device_names); + manager.headless_index_name = Some((VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, device_name)); + Ok(()) + } + + pub fn plug_out_headless() -> bool { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + if let Some((index, _)) = manager.headless_index_name.take() { + if let Err(e) = virtual_display::plug_out_monitor(index) { + log::error!("Plug out monitor failed {}", e); + } + true + } else { + false + } + } + + fn get_new_device_name(device_names: &HashSet) -> String { + for _ in 0..3 { + let device_names_af: HashSet = get_device_names().into_iter().collect(); + let diff_names: Vec<_> = device_names_af.difference(&device_names).collect(); + if diff_names.len() == 1 { + return diff_names[0].clone(); + } else if diff_names.len() > 1 { + log::error!( + "Failed to get diff device names after plugin virtual display, more than one diff names: {:?}", + &diff_names + ); + return "".to_string(); + } + // Sleep is needed here to wait for the virtual display to be ready. + std::thread::sleep(std::time::Duration::from_millis(50)); + } + log::error!("Failed to get diff device names after plugin virtual display",); + "".to_string() + } + + pub fn get_virtual_displays() -> Vec { + VIRTUAL_DISPLAY_MANAGER + .lock() + .unwrap() + .peer_index_name + .keys() + .cloned() + .collect() + } + + pub fn plug_in_index_modes( + idx: u32, + mut modes: Vec, + ) -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + manager.prepare_driver()?; + if !manager.peer_index_name.contains_key(&idx) { + let device_names = get_device_names().into_iter().collect(); + if modes.is_empty() { + modes.push(virtual_display::MonitorMode { + width: 1920, + height: 1080, + sync: 60, + }); + } + match VirtualDisplayManager::plug_in_monitor(idx, modes.as_slice()) { + Ok(_) => { + let device_name = get_new_device_name(&device_names); + manager.peer_index_name.insert(idx, device_name); + } + Err(e) => { + log::error!("Plug in monitor failed {}", e); + } + } + } + Ok(()) + } + + pub fn reset_all() -> ResultType<()> { + if super::is_virtual_display_supported() { + return Ok(()); + } + + if let Err(e) = plug_out_peer_request(&get_virtual_displays()) { + log::error!("Failed to plug out virtual displays: {}", e); + } + let _ = plug_out_headless(); + Ok(()) + } + + pub fn plug_in_peer_request( + modes: Vec>, + ) -> ResultType> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + manager.prepare_driver()?; + + let mut indices: Vec = Vec::new(); + for m in modes.iter() { + for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT { + if !manager.peer_index_name.contains_key(&idx) { + let device_names = get_device_names().into_iter().collect(); + match VirtualDisplayManager::plug_in_monitor(idx, m) { + Ok(_) => { + let device_name = get_new_device_name(&device_names); + manager.peer_index_name.insert(idx, device_name); + indices.push(idx); + } + Err(e) => { + log::error!("Plug in monitor failed {}", e); + } } + break; } - break; } } + + Ok(indices) } - Ok(indices) -} + pub fn plug_out_peer_request(indices: &[u32]) -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + for idx in indices.iter() { + if manager.peer_index_name.contains_key(idx) { + allow_err!(virtual_display::plug_out_monitor(*idx)); + manager.peer_index_name.remove(idx); + } + } + Ok(()) + } -pub fn plug_out_peer_request(indices: &[u32]) -> ResultType<()> { - let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - for idx in indices.iter() { - if manager.peer_index_name.contains_key(idx) { - allow_err!(virtual_display::plug_out_monitor(*idx)); - manager.peer_index_name.remove(idx); + pub fn is_virtual_display(name: &str) -> bool { + let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + if let Some((_, device_name)) = &lock.headless_index_name { + if windows::is_device_name(device_name, name) { + return true; + } + } + for (_, v) in lock.peer_index_name.iter() { + if windows::is_device_name(v, name) { + return true; + } } + false } - Ok(()) -} -pub fn is_virtual_display(name: &str) -> bool { - let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - if let Some((_, device_name)) = &lock.headless_index_name { - if windows::is_device_name(device_name, name) { - return true; + fn change_resolution(index: u32, w: u32, h: u32) -> bool { + let modes = [virtual_display::MonitorMode { + width: w, + height: h, + sync: 60, + }]; + match virtual_display::update_monitor_modes(index, &modes) { + Ok(_) => true, + Err(e) => { + log::error!("Update monitor {} modes {:?} failed: {}", index, &modes, e); + false + } } } - for (_, v) in lock.peer_index_name.iter() { - if windows::is_device_name(v, name) { - return true; + + pub fn change_resolution_if_is_virtual_display(name: &str, w: u32, h: u32) -> Option { + let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + if let Some((index, device_name)) = &lock.headless_index_name { + if windows::is_device_name(device_name, name) { + return Some(change_resolution(*index, w, h)); + } } + + for (k, v) in lock.peer_index_name.iter() { + if windows::is_device_name(v, name) { + return Some(change_resolution(*k, w, h)); + } + } + None } - false } -fn change_resolution(index: u32, w: u32, h: u32) -> bool { - let modes = [virtual_display::MonitorMode { - width: w, - height: h, - sync: 60, - }]; - match virtual_display::update_monitor_modes(index, &modes) { - Ok(_) => true, - Err(e) => { - log::error!("Update monitor {} modes {:?} failed: {}", index, &modes, e); - false +pub mod amyuni_idd { + use super::windows; + use crate::platform::windows::get_amyuni_exe_name; + use hbb_common::{bail, lazy_static, log, ResultType}; + use std::{ + ptr::null_mut, + sync::{Arc, Mutex}, + }; + use winapi::um::shellapi::ShellExecuteA; + + lazy_static::lazy_static! { + static ref LOCK: Arc> = Default::default(); + } + + fn run_deviceinstaller(args: &str) -> ResultType<()> { + let Some(exe_name) = get_amyuni_exe_name() else { + bail!("Cannot get amyuni exe name.") + }; + + let cur_exe = std::env::current_exe()?; + let Some(cur_dir) = cur_exe.parent() else { + bail!("Cannot get parent of current exe file."); + }; + + let work_dir = cur_dir.join("usbmmidd_v2"); + if !work_dir.exists() { + bail!("usbmmidd_v2 does not exist.",); + } + let Some(work_dir) = work_dir.to_str() else { + bail!("Cannot convert work_dir to string."); + }; + let mut work_dir2 = work_dir.as_bytes().to_vec(); + work_dir2.push(0); + + unsafe { + const SW_HIDE: i32 = 0; + let mut args = args.bytes().collect::>(); + args.push(0); + let mut exe_name = exe_name.bytes().collect::>(); + exe_name.push(0); + let hi = ShellExecuteA( + null_mut(), + "open\0".as_ptr() as _, + exe_name.as_ptr() as _, + args.as_ptr() as _, + work_dir2.as_ptr() as _, + SW_HIDE, + ) as i32; + if hi <= 32 { + log::error!("Failed to run deviceinstaller: {}", hi); + bail!("Failed to run deviceinstaller.") + } + Ok(()) } } -} -pub fn change_resolution_if_is_virtual_display(name: &str, w: u32, h: u32) -> Option { - let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - if let Some((index, device_name)) = &lock.headless_index_name { - if windows::is_device_name(device_name, name) { - return Some(change_resolution(*index, w, h)); + fn check_install_driver() -> ResultType<()> { + let _l = LOCK.lock().unwrap(); + let drivers = windows::get_display_drivers(); + if drivers + .iter() + .any(|(s, c)| s == super::AMYUNI_IDD_DEVICE_STRING && *c == 0) + { + return Ok(()); + } + + run_deviceinstaller("install usbmmidd.inf usbmmidd") + } + + pub fn plug_in_headless() -> ResultType<()> { + if get_monitor_count() > 0 { + return Ok(()); + } + + if let Err(e) = check_install_driver() { + log::error!("Failed to install driver: {}", e); + bail!("Failed to install driver."); + } + + run_deviceinstaller("enableidd 1") + } + + pub fn plug_in_monitor() -> ResultType<()> { + if let Err(e) = check_install_driver() { + log::error!("Failed to install driver: {}", e); + bail!("Failed to install driver."); + } + + if get_monitor_count() == 4 { + bail!("There are already 4 monitors plugged in."); } + + run_deviceinstaller("enableidd 1") } - for (k, v) in lock.peer_index_name.iter() { - if windows::is_device_name(v, name) { - return Some(change_resolution(*k, w, h)); + pub fn plug_out_monitor(index: i32) -> ResultType<()> { + let all_count = windows::get_device_names(None).len(); + let amyuni_count = get_monitor_count(); + let mut to_plug_out_count = match all_count { + 0 => return Ok(()), + 1 => { + if amyuni_count == 0 { + bail!("No virtual displays to plug out.") + } else { + if super::is_can_plug_out_all() { + 1 + } else { + bail!("This only virtual display cannot be pulled out.") + } + } + } + _ => { + if all_count == amyuni_count { + if super::is_can_plug_out_all() { + all_count + } else { + all_count - 1 + } + } else { + amyuni_count + } + } + }; + if to_plug_out_count != 0 && index != -1 { + to_plug_out_count = 1; + } + for _i in 0..to_plug_out_count { + let _ = run_deviceinstaller(&format!("enableidd 0")); } + Ok(()) + } + + #[inline] + pub fn get_monitor_count() -> usize { + windows::get_device_names(Some(super::AMYUNI_IDD_DEVICE_STRING)).len() } - None } mod windows { - use std::{collections::HashSet, ptr::null_mut}; + use std::ptr::null_mut; use winapi::{ - shared::minwindef::{DWORD, FALSE}, + shared::{ + devguid::GUID_DEVCLASS_DISPLAY, + minwindef::{DWORD, FALSE}, + ntdef::ULONG, + }, um::{ + cfgmgr32::{CM_Get_DevNode_Status, CR_SUCCESS}, + cguid::GUID_NULL, + setupapi::{ + SetupDiEnumDeviceInfo, SetupDiGetClassDevsW, SetupDiGetDeviceRegistryPropertyW, + SP_DEVINFO_DATA, + }, wingdi::{ DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_MIRRORING_DRIVER, }, + winnt::HANDLE, winuser::{EnumDisplayDevicesW, EnumDisplaySettingsExW, ENUM_CURRENT_SETTINGS}, }, }; - // This string is defined here. - // https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 - const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0"; + const DIGCF_PRESENT: DWORD = 0x00000002; + const SPDRP_DEVICEDESC: DWORD = 0x00000000; + const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE; #[inline] pub(super) fn is_device_name(device_name: &str, name: &str) -> bool { @@ -281,8 +576,8 @@ mod windows { } } - pub(super) fn get_device_names() -> HashSet { - let mut device_names = HashSet::new(); + pub(super) fn get_device_names(device_string: Option<&str>) -> Vec { + let mut device_names = Vec::new(); let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() }; dd.cb = std::mem::size_of::() as DWORD; let mut i_dev_num = 0; @@ -317,15 +612,115 @@ mod windows { continue; } - if let (Ok(device_name), Ok(device_string)) = ( + if let (Ok(device_name), Ok(ds)) = ( String::from_utf16(&dd.DeviceName), String::from_utf16(&dd.DeviceString), ) { - if &device_string[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING { - device_names.insert(device_name); + if let Some(s) = device_string { + if ds.len() >= s.len() && &ds[..s.len()] == s { + device_names.push(device_name); + } + } else { + device_names.push(device_name); } } } device_names } + + pub(super) fn get_display_drivers() -> Vec<(String, u32)> { + let mut display_drivers: Vec<(String, u32)> = Vec::new(); + + let device_info_set = unsafe { + SetupDiGetClassDevsW( + &GUID_DEVCLASS_DISPLAY, + null_mut(), + null_mut(), + DIGCF_PRESENT, + ) + }; + + if device_info_set == INVALID_HANDLE_VALUE { + println!( + "Failed to get device information set. Error: {}", + std::io::Error::last_os_error() + ); + return display_drivers; + } + + let mut device_info_data = SP_DEVINFO_DATA { + cbSize: std::mem::size_of::() as u32, + ClassGuid: GUID_NULL, + DevInst: 0, + Reserved: 0, + }; + + let mut device_index = 0; + loop { + let result = unsafe { + SetupDiEnumDeviceInfo(device_info_set, device_index, &mut device_info_data) + }; + if result == 0 { + break; + } + + let mut data_type: DWORD = 0; + let mut required_size: DWORD = 0; + + // Get the required buffer size for the driver description + let mut buffer; + unsafe { + SetupDiGetDeviceRegistryPropertyW( + device_info_set, + &mut device_info_data, + SPDRP_DEVICEDESC, + &mut data_type, + null_mut(), + 0, + &mut required_size, + ); + + buffer = vec![0; required_size as usize / 2]; + SetupDiGetDeviceRegistryPropertyW( + device_info_set, + &mut device_info_data, + SPDRP_DEVICEDESC, + &mut data_type, + buffer.as_mut_ptr() as *mut u8, + required_size, + null_mut(), + ); + } + + let Ok(driver_description) = String::from_utf16(&buffer) else { + println!("Failed to convert driver description to string"); + device_index += 1; + continue; + }; + + let mut status: ULONG = 0; + let mut problem_number: ULONG = 0; + // Get the device status and problem number + let config_ret = unsafe { + CM_Get_DevNode_Status( + &mut status, + &mut problem_number, + device_info_data.DevInst, + 0, + ) + }; + if config_ret != CR_SUCCESS { + println!( + "Failed to get device status. Error: {}", + std::io::Error::last_os_error() + ); + device_index += 1; + continue; + } + display_drivers.push((driver_description, problem_number)); + device_index += 1; + } + + display_drivers + } } From 5d5547ffef3014bbd1af53937f5a2bbb55eb3386 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 19 Apr 2024 12:44:52 +0800 Subject: [PATCH 34/40] put andriod custom client logo to settings section list (#7768) Signed-off-by: 21pages --- flutter/lib/mobile/pages/home_page.dart | 4 ++- flutter/lib/mobile/pages/settings_page.dart | 30 +++++++++++---------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/flutter/lib/mobile/pages/home_page.dart b/flutter/lib/mobile/pages/home_page.dart index 0f3e7b20aa..078e2b2f7b 100644 --- a/flutter/lib/mobile/pages/home_page.dart +++ b/flutter/lib/mobile/pages/home_page.dart @@ -26,8 +26,9 @@ class HomePageState extends State { var _selectedIndex = 0; int get selectedIndex => _selectedIndex; final List _pages = []; + int _chatPageTabIndex = -1; bool get isChatPageCurrentTab => isAndroid - ? _selectedIndex == 1 + ? _selectedIndex == _chatPageTabIndex : false; // change this when ios have chat page void refreshPages() { @@ -46,6 +47,7 @@ class HomePageState extends State { _pages.clear(); if (!bind.isIncomingOnly()) _pages.add(ConnectionPage()); if (isAndroid && !bind.isOutgoingOnly()) { + _chatPageTabIndex = _pages.length; _pages.addAll([ChatPage(type: ChatPageType.mobileMain), ServerPage()]); } _pages.add(SettingsPage()); diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index e79310650f..9477cbe6a2 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -219,6 +219,20 @@ class _SettingsState extends State with WidgetsBindingObserver { Widget build(BuildContext context) { Provider.of(context); final outgoingOnly = bind.isOutgoingOnly(); + final customClientSection = CustomSettingsSection( + child: Column( + children: [ + if (bind.isCustomClient()) + Align( + alignment: Alignment.center, + child: loadPowered(context), + ), + Align( + alignment: Alignment.center, + child: loadLogo(), + ) + ], + )); final List enhancementsTiles = []; final List shareScreenTiles = [ SettingsTile.switchTile( @@ -452,6 +466,7 @@ class _SettingsState extends State with WidgetsBindingObserver { final disabledSettings = bind.isDisableSettings(); final settings = SettingsList( sections: [ + customClientSection, if (!bind.isDisableAccount()) SettingsSection( title: Text(translate('Account')), @@ -582,20 +597,7 @@ class _SettingsState extends State with WidgetsBindingObserver { ), ], ); - return Column( - children: [ - if (bind.isCustomClient()) - Align( - alignment: Alignment.center, - child: loadPowered(context), - ), - Align( - alignment: Alignment.center, - child: loadLogo(), - ), - settings - ], - ); + return settings; } Future canStartOnBoot() async { From 1ffc10e44ffdc9c8a396ec840b856bd27940b171 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 19 Apr 2024 13:24:44 +0800 Subject: [PATCH 35/40] flutter 19.6 --- .github/workflows/flutter-build.yml | 2 +- flutter/lib/models/native_model.dart | 5 ++++- flutter/pubspec.lock | 16 ++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index bb5399221a..d45aed18a9 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -14,7 +14,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.19.5" + FLUTTER_VERSION: "3.19.6" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 2886a23005..3cfd336344 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -198,7 +198,10 @@ class PlatformFFI { await _ffiBind.mainDeviceId(id: id); await _ffiBind.mainDeviceName(name: name); await _ffiBind.mainSetHomeDir(home: _homeDir); - await _ffiBind.mainInit(appDir: _dir, customClientConfig: ''); + await _ffiBind.mainInit( + appDir: _dir, + customClientConfig: + 'YS26hUmC8iv2IaM0Uqb010/QDTLg0v3Vow637HkkoeXeb90mZHgVtpTrTRzUNgLAMcVIBik3tiTqZ3EUn/y2D3siZGlzYWJsZS1hYiI6ICJZIiwgImRpc2FibGUtYWNjb3VudCI6ICJZIiwgImRpc2FibGUtaW5zdGFsbGF0aW9uIjogIiIsICJkaXNhYmxlLXNldHRpbmdzIjogIlkiLCAiZGlzYWJsZS10Y3AtbGlzdGVuIjogIlkiLCAiYXBwLW5hbWUiOiAiTXlEZXNrIiwgImNvbm4tdHlwZSI6ICJpbmNvbWluZyIsICJwYWNrYWdlLW5hbWUiOiAiY29tLm15Y29tcHkubXlkZXNrIiwgImRlZmF1bHQtc2V0dGluZ3MiOiB7ImFwaS1zZXJ2ZXIiOiAiaHR0cDovL2xvY2FsaG9zdDoyMTExNCIsICJjdXN0b20tcmVuZGV6dm91cy1zZXJ2ZXIiOiAibG9jYWxob3N0IiwgImtleSI6ICJIdGFYQmtKTkh5bjBlMVUxaldYemU3QTdDTjdRVEdXcXhrT3RwYVZwbzdRPSJ9fQ=='); } catch (e) { debugPrintStack(label: 'initialize failed: $e'); } diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 11c7d2cb3f..2cd1c61693 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -849,18 +849,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -921,10 +921,10 @@ packages: dependency: "direct main" description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -1526,10 +1526,10 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.4.2" web_socket_channel: dependency: transitive description: From 40067f5aa2ebe99433abcb9187b30078364e9431 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 19 Apr 2024 13:49:45 +0800 Subject: [PATCH 36/40] no countdown in scam warning of android for custom build --- flutter/lib/mobile/pages/server_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 120381ebe8..a7bf12148b 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -227,7 +227,7 @@ class ScamWarningDialog extends StatefulWidget { } class ScamWarningDialogState extends State { - int _countdown = 12; + int _countdown = bind.isCustomClient() ? 0 : 12; bool show_warning = false; late Timer _timer; late ServerModel _serverModel; From 25eb8dc9b0885997a87527dee66a62eec0bf470b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 19 Apr 2024 14:35:50 +0800 Subject: [PATCH 37/40] typo --- flutter/lib/models/native_model.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 3cfd336344..3f81ba422f 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -199,9 +199,9 @@ class PlatformFFI { await _ffiBind.mainDeviceName(name: name); await _ffiBind.mainSetHomeDir(home: _homeDir); await _ffiBind.mainInit( - appDir: _dir, - customClientConfig: - 'YS26hUmC8iv2IaM0Uqb010/QDTLg0v3Vow637HkkoeXeb90mZHgVtpTrTRzUNgLAMcVIBik3tiTqZ3EUn/y2D3siZGlzYWJsZS1hYiI6ICJZIiwgImRpc2FibGUtYWNjb3VudCI6ICJZIiwgImRpc2FibGUtaW5zdGFsbGF0aW9uIjogIiIsICJkaXNhYmxlLXNldHRpbmdzIjogIlkiLCAiZGlzYWJsZS10Y3AtbGlzdGVuIjogIlkiLCAiYXBwLW5hbWUiOiAiTXlEZXNrIiwgImNvbm4tdHlwZSI6ICJpbmNvbWluZyIsICJwYWNrYWdlLW5hbWUiOiAiY29tLm15Y29tcHkubXlkZXNrIiwgImRlZmF1bHQtc2V0dGluZ3MiOiB7ImFwaS1zZXJ2ZXIiOiAiaHR0cDovL2xvY2FsaG9zdDoyMTExNCIsICJjdXN0b20tcmVuZGV6dm91cy1zZXJ2ZXIiOiAibG9jYWxob3N0IiwgImtleSI6ICJIdGFYQmtKTkh5bjBlMVUxaldYemU3QTdDTjdRVEdXcXhrT3RwYVZwbzdRPSJ9fQ=='); + appDir: _dir, + customClientConfig: '', + ); } catch (e) { debugPrintStack(label: 'initialize failed: $e'); } From 819eea9456cc73c3f1d1afd3e49a67c9c92e9b96 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 19 Apr 2024 17:57:04 +0800 Subject: [PATCH 38/40] refact: msi (#7774) * refact: msi Signed-off-by: fufesou * Remove unused coment Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 2 +- res/msi/preprocess.py | 44 +++++++++++------------------ src/core_main.rs | 11 ++++++-- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index d45aed18a9..c865db73a8 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -180,7 +180,7 @@ jobs: if: env.UPLOAD_ARTIFACT == 'true' run: | pushd ./res/msi - python preprocess.py -arp -d ../../rustdesk + python preprocess.py --arp -d ../../rustdesk nuget restore msi.sln msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10 mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-beta.msi diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index 84e97b6249..95f4205bdb 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -6,6 +6,7 @@ import uuid import argparse import datetime +import subprocess import re from pathlib import Path @@ -46,14 +47,12 @@ def make_parser(): help="The dist direcotry to install.", ) parser.add_argument( - "-arp", "--arp", action="store_true", help="Is ARPSYSTEMCOMPONENT", default=False, ) parser.add_argument( - "-custom-arp", "--custom-arp", type=str, default="{}", @@ -63,7 +62,7 @@ def make_parser(): "-c", "--custom", action="store_true", help="Is custom client", default=False ) parser.add_argument( - "-an", "--app-name", type=str, default="RustDesk", help="The app name." + "--app-name", type=str, default="RustDesk", help="The app name." ) parser.add_argument( "-v", "--version", type=str, default="", help="The app version." @@ -149,7 +148,7 @@ def func(lines, index_start): f'{indent}\n', f'{indent}\n', f'{indent}\n', - f'{indent}\n', + f'{indent}\n', f'{indent}\n', f'{indent}\n', "\n", @@ -390,37 +389,26 @@ def gen_content_between_tags(filename, tag_start, tag_end, func): return True -def init_global_vars(args): - var_file = "../../src/version.rs" - if not Path(var_file).exists(): - print(f"Error: {var_file} not found") - return False - - with open(var_file, "r") as f: - content = f.readlines() +def init_global_vars(dist_dir, app_name, args): + dist_app = dist_dir.joinpath(app_name + ".exe") + def read_process_output(args): + process = subprocess.Popen(f'{dist_app} {args}', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) + output, _ = process.communicate() + return output.decode('utf-8').strip() global g_version global g_build_date g_version = args.version.replace("-", ".") if g_version == "": - # pub const VERSION: &str = "1.2.4"; - version_pattern = re.compile(r'.*VERSION: &str = "(.*)";.*') - for line in content: - match = version_pattern.match(line) - if match: - g_version = match.group(1) - break + g_version = read_process_output('--version') if g_version == "": - print(f"Error: version not found in {var_file}") + print(f"Error: version not found in {dist_app}") return False - # pub const BUILD_DATE: &str = "2024-04-08 23:11"; - build_date_pattern = re.compile(r'BUILD_DATE: &str = "(.*)";') - for line in content: - match = build_date_pattern.match(line) - if match: - g_build_date = match.group(1) - break + g_build_date = read_process_output('--build-date') + if g_build_date == "": + print(f"Error: build date not found in {dist_app}") + return False return True @@ -448,7 +436,7 @@ def replace_component_guids_in_wxs(): app_name = args.app_name dist_dir = Path(sys.argv[0]).parent.joinpath(args.dist_dir).resolve() - if not init_global_vars(args): + if not init_global_vars(dist_dir, app_name, args): sys.exit(-1) if not gen_pre_vars(args, dist_dir): diff --git a/src/core_main.rs b/src/core_main.rs index 25e2912959..1f5de5e306 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -114,9 +114,14 @@ pub fn core_main() -> Option> { if args.contains(&"--noinstall".to_string()) { args.clear(); } - if args.len() > 0 && args[0] == "--version" { - println!("{}", crate::VERSION); - return None; + if args.len() > 0 { + if args[0] == "--version" { + println!("{}", crate::VERSION); + return None; + } else if args[0] == "--build-date" { + println!("{}", crate::BUILD_DATE); + return None; + } } #[cfg(windows)] { From 0e6fa37ae416f241ab86a559051bfa5b02238e3c Mon Sep 17 00:00:00 2001 From: solokot Date: Fri, 19 Apr 2024 12:58:06 +0300 Subject: [PATCH 39/40] Update ru.rs (#7771) --- src/lang/ru.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 0cc7acdb0e..3e28744c52 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -601,6 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Everyone", "Все"), ("ab_web_console_tip", "Больше в веб-консоли"), ("allow-only-conn-window-open-tip", "Разрешать подключение только при открытом окне RustDesk"), - ("no_need_privacy_mode_no_physical_displays_tip", ""), + ("no_need_privacy_mode_no_physical_displays_tip", "Физические дисплеи отсутствуют, нет необходимости использовать режим конфиденциальности."), ].iter().cloned().collect(); } From c75778943ff42420740a7388b03cdd990b19a811 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 19 Apr 2024 18:16:07 +0800 Subject: [PATCH 40/40] refact: msi, version and build date, check (#7775) Signed-off-by: fufesou --- res/msi/preprocess.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index 95f4205bdb..1d85efe214 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -180,7 +180,8 @@ def gen_upgrade_info(): def func(lines, index_start): indent = g_indent_unit * 3 - major, _, _ = g_version.split(".") + vs = g_version.split(".") + major = vs[0] upgrade_id = uuid.uuid4() to_insert_lines = [ f'{indent}\n', @@ -325,7 +326,8 @@ def func(lines, index_start): f'{indent}\n' ) - major, minor, build = g_version.split(".") + vs = g_version.split(".") + major, minor, build = vs[0], vs[1], vs[2] lines_new.append( f'{indent}\n' ) @@ -391,23 +393,31 @@ def gen_content_between_tags(filename, tag_start, tag_end, func): def init_global_vars(dist_dir, app_name, args): dist_app = dist_dir.joinpath(app_name + ".exe") + def read_process_output(args): - process = subprocess.Popen(f'{dist_app} {args}', stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) + process = subprocess.Popen( + f"{dist_app} {args}", + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True, + ) output, _ = process.communicate() - return output.decode('utf-8').strip() + return output.decode("utf-8").strip() global g_version global g_build_date g_version = args.version.replace("-", ".") if g_version == "": - g_version = read_process_output('--version') - if g_version == "": - print(f"Error: version not found in {dist_app}") + g_version = read_process_output("--version") + version_pattern = re.compile(r"\d+\.\d+\.\d+.*") + if not version_pattern.match(g_version): + print(f"Error: version {g_version} not found in {dist_app}") return False - g_build_date = read_process_output('--build-date') - if g_build_date == "": - print(f"Error: build date not found in {dist_app}") + g_build_date = read_process_output("--build-date") + build_date_pattern = re.compile(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}") + if not build_date_pattern.match(g_build_date): + print(f"Error: build date {g_build_date} not found in {dist_app}") return False return True