From 33eaf2e05c9262f9026b2540f3b252b4195cf44a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 3 Apr 2024 14:25:09 +0800 Subject: [PATCH 001/112] bump to flutter 3.19.5 for wrong cpu instruction used in flutter 3.16 -> 3.19.4, which cause not running on some CPU --- .github/workflows/flutter-build.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 0cf2dd461..ed90d1219 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.16.9" + FLUTTER_VERSION: "3.19.5" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" @@ -83,14 +83,6 @@ jobs: flutter-version: ${{ env.FLUTTER_VERSION }} cache: true - - name: Replace engine with rustdesk custom flutter engine - run: | - flutter doctor -v - flutter precache --windows - Invoke-WebRequest -Uri https://github.com/fufesou/flutter-engine/releases/download/bugfix-subwindow-crash-3.16.9-apply-pull-47787/windows-x64-release.zip -OutFile windows-x64-flutter-release.zip - Expand-Archive windows-x64-flutter-release.zip -DestinationPath . - mv -Force windows-x64-release/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/ - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 with: From ed257e39d0a7468232f1fc598f34829f6d0a4683 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 3 Apr 2024 14:27:35 +0800 Subject: [PATCH 002/112] Feat. msi (#7590) Signed-off-by: fufesou --- res/msi/.gitignore | 7 + res/msi/CustomActions/CustomAction.config | 17 --- res/msi/CustomActions/CustomAction.cs | 56 ------- res/msi/CustomActions/CustomActions.cpp | 63 ++++++++ res/msi/CustomActions/CustomActions.csproj | 15 -- res/msi/CustomActions/CustomActions.def | 5 + res/msi/CustomActions/CustomActions.vcxproj | 80 ++++++++++ res/msi/CustomActions/dllmain.cpp | 26 ++++ res/msi/CustomActions/framework.h | 10 ++ res/msi/CustomActions/packages.config | 5 + res/msi/CustomActions/pch.cpp | 5 + res/msi/CustomActions/pch.h | 13 ++ res/msi/Package/Components/RustDesk.wxs | 4 +- .../Package/Fragments/AddRemoveProperties.wxs | 8 +- res/msi/Package/Fragments/CustomActions.wxs | 20 +-- .../Package/Fragments/ShortcutProperties.wxs | 16 +- res/msi/Package/Fragments/Upgrades.wxs | 5 +- res/msi/Package/Includes.wxi | 3 - res/msi/Package/Package.wixproj | 2 +- res/msi/README.md | 7 + res/msi/msi.sln | 25 ++-- res/msi/preprocess.py | 138 +++++++++++------- 22 files changed, 349 insertions(+), 181 deletions(-) delete mode 100644 res/msi/CustomActions/CustomAction.config delete mode 100644 res/msi/CustomActions/CustomAction.cs create mode 100644 res/msi/CustomActions/CustomActions.cpp delete mode 100644 res/msi/CustomActions/CustomActions.csproj create mode 100644 res/msi/CustomActions/CustomActions.def create mode 100644 res/msi/CustomActions/CustomActions.vcxproj create mode 100644 res/msi/CustomActions/dllmain.cpp create mode 100644 res/msi/CustomActions/framework.h create mode 100644 res/msi/CustomActions/packages.config create mode 100644 res/msi/CustomActions/pch.cpp create mode 100644 res/msi/CustomActions/pch.h diff --git a/res/msi/.gitignore b/res/msi/.gitignore index a2df938b4..44c377c55 100644 --- a/res/msi/.gitignore +++ b/res/msi/.gitignore @@ -2,3 +2,10 @@ **/bin **/obj + +x64 +packages + +CustomActions/x64 +CustomActions/*.user +CustomActions/*.filters diff --git a/res/msi/CustomActions/CustomAction.config b/res/msi/CustomActions/CustomAction.config deleted file mode 100644 index cfae001eb..000000000 --- a/res/msi/CustomActions/CustomAction.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - diff --git a/res/msi/CustomActions/CustomAction.cs b/res/msi/CustomActions/CustomAction.cs deleted file mode 100644 index cd048f95b..000000000 --- a/res/msi/CustomActions/CustomAction.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Diagnostics; -using System.Runtime.InteropServices; -using WixToolset.Dtf.WindowsInstaller; - -namespace CustomActions -{ - public class CustomActions - { - [CustomAction] - public static ActionResult CustomActionHello(Session session) - { - try - { - session.Log("================= Example CustomAction Hello"); - return ActionResult.Success; - } - catch (Exception e) - { - session.Log("An error occurred: " + e.Message); - return ActionResult.Failure; - } - } - - [CustomAction] - public static ActionResult RunCommandAsSystem(Session session) - { - try - { - ProcessStartInfo psi = new ProcessStartInfo - { - - FileName = "cmd.exe", - Arguments = "/c " + session["CMD"], - UseShellExecute = false, - WindowStyle = ProcessWindowStyle.Hidden, - Verb = "runas" - }; - - using (Process process = Process.Start(psi)) - { - process.WaitForExit(); - } - - return ActionResult.Success; - } - catch (Exception e) - { - session.Log("An error occurred: " + e.Message); - return ActionResult.Failure; - } - } - } -} diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp new file mode 100644 index 000000000..a5c49410f --- /dev/null +++ b/res/msi/CustomActions/CustomActions.cpp @@ -0,0 +1,63 @@ +// CustomAction.cpp : Defines the entry point for the custom action. +#include "pch.h" +#include + +UINT __stdcall CustomActionHello( + __in MSIHANDLE hInstall +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + hr = WcaInitialize(hInstall, "CustomActionHello"); + ExitOnFailure(hr, "Failed to initialize"); + + WcaLog(LOGMSG_STANDARD, "Initialized."); + + // TODO: Add your custom action code here. + WcaLog(LOGMSG_STANDARD, "================= Example CustomAction Hello"); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall RemoveInstallFolder( + __in MSIHANDLE hInstall +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int nResult = 0; + wchar_t szCustomActionData[256] = { 0 }; + DWORD cchCustomActionData = sizeof(szCustomActionData) / sizeof(szCustomActionData[0]); + + hr = WcaInitialize(hInstall, "RemoveInstallFolder"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"InstallFolder", szCustomActionData, &cchCustomActionData); + + WcaLog(LOGMSG_STANDARD, "================= Remove Install Folder: %ls", szCustomActionData); + + SHFILEOPSTRUCTW fileOp; + ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT)); + + fileOp.wFunc = FO_DELETE; + fileOp.pFrom = szCustomActionData; + fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; + + nResult = SHFileOperationW(&fileOp); + if (nResult == 0) + { + WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", szCustomActionData); + } + else + { + WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has not been deleted, error code: 0X%02X.", szCustomActionData, nResult); + } + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} diff --git a/res/msi/CustomActions/CustomActions.csproj b/res/msi/CustomActions/CustomActions.csproj deleted file mode 100644 index e21734032..000000000 --- a/res/msi/CustomActions/CustomActions.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - net472 - Release - - - - - - - - - - - diff --git a/res/msi/CustomActions/CustomActions.def b/res/msi/CustomActions/CustomActions.def new file mode 100644 index 000000000..8eb50ce0d --- /dev/null +++ b/res/msi/CustomActions/CustomActions.def @@ -0,0 +1,5 @@ +LIBRARY "CustomActions" + +EXPORTS + CustomActionHello + RemoveInstallFolder diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj new file mode 100644 index 000000000..1d1521158 --- /dev/null +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -0,0 +1,80 @@ + + + + + + + Release + x64 + + + + Win32Proj + {87e7c13b-ae0e-4048-95cf-4523d510a3cd} + CustomActions + v143 + 10.0 + + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + Level3 + true + true + true + NDEBUG;EXAMPLECADLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + MultiThreaded + + + msi.lib;version.lib;%(AdditionalDependencies) + Windows + true + true + true + false + CustomActions.def + + + + + + + + + + + Create + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/res/msi/CustomActions/dllmain.cpp b/res/msi/CustomActions/dllmain.cpp new file mode 100644 index 000000000..7288d7c64 --- /dev/null +++ b/res/msi/CustomActions/dllmain.cpp @@ -0,0 +1,26 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "pch.h" + +BOOL APIENTRY DllMain( + __in HMODULE hModule, + __in DWORD ulReasonForCall, + __in LPVOID +) +{ + switch (ulReasonForCall) + { + case DLL_PROCESS_ATTACH: + WcaGlobalInitialize(hModule); + break; + + case DLL_PROCESS_DETACH: + WcaGlobalFinalize(); + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + + return TRUE; +} diff --git a/res/msi/CustomActions/framework.h b/res/msi/CustomActions/framework.h new file mode 100644 index 000000000..4cd0bc474 --- /dev/null +++ b/res/msi/CustomActions/framework.h @@ -0,0 +1,10 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +#include +#include + +// WiX Header Files: +#include diff --git a/res/msi/CustomActions/packages.config b/res/msi/CustomActions/packages.config new file mode 100644 index 000000000..e25f8328a --- /dev/null +++ b/res/msi/CustomActions/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/res/msi/CustomActions/pch.cpp b/res/msi/CustomActions/pch.cpp new file mode 100644 index 000000000..64b7eef6d --- /dev/null +++ b/res/msi/CustomActions/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/res/msi/CustomActions/pch.h b/res/msi/CustomActions/pch.h new file mode 100644 index 000000000..885d5d62e --- /dev/null +++ b/res/msi/CustomActions/pch.h @@ -0,0 +1,13 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +// add headers that you want to pre-compile here +#include "framework.h" + +#endif //PCH_H diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index 4abf20bdf..abb43358d 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -30,8 +30,8 @@ - - + + diff --git a/res/msi/Package/Fragments/AddRemoveProperties.wxs b/res/msi/Package/Fragments/AddRemoveProperties.wxs index 3c0745144..39d0775da 100644 --- a/res/msi/Package/Fragments/AddRemoveProperties.wxs +++ b/res/msi/Package/Fragments/AddRemoveProperties.wxs @@ -1,15 +1,15 @@ - + + Support entries shown when clicking "Click here for support information" + in Control Panel's Add/Remove Programs http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/configuration_properties.asp + --> diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index e7759a1a6..79b49b49d 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -1,22 +1,22 @@  - - - + - - + - - + + + + + + in a property with the same id as the WixQuietExec custom action and the path to the exe needs to be quoted. + A SetProperty action is used to allow the [SystemFolder] reference to be resolved and needs to be scheduled to run before the action.--> - + diff --git a/res/msi/Package/Fragments/ShortcutProperties.wxs b/res/msi/Package/Fragments/ShortcutProperties.wxs index 93af85423..1c4711127 100644 --- a/res/msi/Package/Fragments/ShortcutProperties.wxs +++ b/res/msi/Package/Fragments/ShortcutProperties.wxs @@ -4,18 +4,18 @@ + Properties and related actions for specifying whether to install start menu/desktop shortcuts. + --> + install start menu shortcuts, they are initialized with a default value to install shortcuts. + They should not be set directly from the command line or registry, instead the CREATE* properties + below should be set, then they will update these properties with their values only if set. --> + if set they update the properties above with their value. --> @@ -34,8 +34,8 @@ + overwrite the command line value, these actions temporarily store the command line value before the registry search + is performed so they can be restored after the registry search is complete --> diff --git a/res/msi/Package/Fragments/Upgrades.wxs b/res/msi/Package/Fragments/Upgrades.wxs index 95034f262..ab1118513 100644 --- a/res/msi/Package/Fragments/Upgrades.wxs +++ b/res/msi/Package/Fragments/Upgrades.wxs @@ -3,9 +3,8 @@ - - - + + diff --git a/res/msi/Package/Includes.wxi b/res/msi/Package/Includes.wxi index a0e537812..4b43f74c5 100644 --- a/res/msi/Package/Includes.wxi +++ b/res/msi/Package/Includes.wxi @@ -4,7 +4,4 @@ - - - diff --git a/res/msi/Package/Package.wixproj b/res/msi/Package/Package.wixproj index 7500619b1..17dbc4f13 100644 --- a/res/msi/Package/Package.wixproj +++ b/res/msi/Package/Package.wixproj @@ -17,6 +17,6 @@ - + \ No newline at end of file diff --git a/res/msi/README.md b/res/msi/README.md index 79ddbd8ad..80a2490bf 100644 --- a/res/msi/README.md +++ b/res/msi/README.md @@ -4,6 +4,13 @@ Use Visual Studio 2022 to compile this project. This project is mainly derived from . +## Steps + +1. `python preprocess.py` +2. Build the .sln solution. + +Run `msiexec /i package.msi /l*v install.log` to record the log. + ## TODOs 1. tray, uninstall shortcut diff --git a/res/msi/msi.sln b/res/msi/msi.sln index 5a8303f4d..8fb06eb51 100644 --- a/res/msi/msi.sln +++ b/res/msi/msi.sln @@ -4,34 +4,39 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 17.7.34003.232 MinimumVisualStudioVersion = 10.0.40219.1 Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Package", "Package\Package.wixproj", "{F403A403-CEFF-4399-B51C-CC646C8E98CF}" - ProjectSection(ProjectDependencies) = postProject - {95BE171E-6438-4F45-9876-0B667D9F7830} = {95BE171E-6438-4F45-9876-0B667D9F7830} - EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomActions", "CustomActions\CustomActions.csproj", "{95BE171E-6438-4F45-9876-0B667D9F7830}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomActions", "CustomActions\CustomActions.vcxproj", "{87E7C13B-AE0E-4048-95CF-4523D510A3CD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|Any CPU.ActiveCfg = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|Any CPU.Build.0 = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x64.ActiveCfg = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x64.Build.0 = Release|x64 + {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x86.ActiveCfg = Release|x64 + {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x86.Build.0 = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|Any CPU.ActiveCfg = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|Any CPU.Build.0 = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x64.ActiveCfg = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x64.Build.0 = Release|x64 - {95BE171E-6438-4F45-9876-0B667D9F7830}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {95BE171E-6438-4F45-9876-0B667D9F7830}.Debug|x64.ActiveCfg = Release|Any CPU - {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|Any CPU.ActiveCfg = Release|Any CPU - {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|Any CPU.Build.0 = Release|Any CPU - {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|x64.ActiveCfg = Release|Any CPU - {95BE171E-6438-4F45-9876-0B667D9F7830}.Release|x64.Build.0 = Release|Any CPU + {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x86.ActiveCfg = Release|x64 + {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x86.Build.0 = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|Any CPU.ActiveCfg = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|x64.ActiveCfg = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|x86.ActiveCfg = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|Any CPU.ActiveCfg = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|Any CPU.Build.0 = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x64.ActiveCfg = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x64.Build.0 = Release|x64 + {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x86.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index cc1031b88..fe9050b98 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -33,24 +33,27 @@ def make_parser(): return parser -def read_lines_and_start_index(file_path, start_tag, end_tag): +def read_lines_and_start_index(file_path, tag_start, tag_end): with open(file_path, "r") as f: lines = f.readlines() - start_index = -1 - end_index = -1 + index_start = -1 + index_end = -1 for i, line in enumerate(lines): - if start_tag in line: - start_index = i - if end_tag in line: - end_index = i + if tag_start in line: + index_start = i + if tag_end in line: + index_end = i - if start_index == -1 or end_index == -1: - print("Error: start or end tag not found") + if index_start == -1: + print(f'Error: start tag "{tag_start}" not found') return None, None - return lines, start_index + if index_end == -1: + print(f'Error: end tag "{tag_end}" not found') + return None, None + return lines, index_start -def insert_components_between_tags(lines, start_index, app_name, build_dir): +def insert_components_between_tags(lines, index_start, app_name, build_dir): indent = g_indent_unit * 3 path = Path(build_dir) idx = 1 @@ -77,58 +80,48 @@ def insert_components_between_tags(lines, start_index, app_name, build_dir): {indent}{g_indent_unit} {indent} """ - lines.insert(start_index + 1, to_insert_lines[1:]) - start_index += 1 + lines.insert(index_start + 1, to_insert_lines[1:]) + index_start += 1 idx += 1 return True def gen_auto_component(app_name, build_dir): - target_file = Path(sys.argv[0]).parent.joinpath("Package/Components/RustDesk.wxs") - start_tag = "" - end_tag = "" - - lines, start_index = read_lines_and_start_index(target_file, start_tag, end_tag) - if lines is None: - return False - - if not insert_components_between_tags(lines, start_index, app_name, build_dir): - return False - - with open(target_file, "w") as f: - f.writelines(lines) - - return True + return gen_content_between_tags( + "Package/Components/RustDesk.wxs", + "", + "", + lambda lines, index_start: insert_components_between_tags( + lines, index_start, app_name, build_dir + ), + ) def gen_pre_vars(args, build_dir): - target_file = Path(sys.argv[0]).parent.joinpath("Package/Includes.wxi") - start_tag = "" - end_tag = "" + def func(lines, index_start): + upgrade_code = uuid.uuid5(uuid.NAMESPACE_OID, app_name + ".exe") - lines, start_index = read_lines_and_start_index(target_file, start_tag, end_tag) - if lines is None: - return False + indent = g_indent_unit * 1 + to_insert_lines = [ + f'{indent}\n', + f'{indent}\n', + f'{indent}\n', + f'{indent}\n', + f'{indent}\n', + f'{indent}\n', + f'{indent}\n', + f'{indent}\n', + "\n", + f"{indent}\n" + f'{indent}\n', + ] - indent = g_indent_unit * 1 - to_insert_lines = [ - f'{indent}\n', - f'{indent}\n', - f'{indent}\n', - f'{indent}\n', - f'{indent}\n', - f'{indent}\n', - f'{indent}\n', - f'{indent}\n', - ] + for i, line in enumerate(to_insert_lines): + lines.insert(index_start + i + 1, line) - for i, line in enumerate(to_insert_lines): - lines.insert(start_index + i + 1, line) - - with open(target_file, "w") as f: - f.writelines(lines) - - return True + return gen_content_between_tags( + "Package/Includes.wxi", "", "", func + ) def replace_app_name_in_lans(app_name): @@ -142,6 +135,44 @@ def replace_app_name_in_lans(app_name): f.writelines(lines) +def gen_upgrade_info(version): + def func(lines, index_start): + indent = g_indent_unit * 3 + + major, _, _ = version.split(".") + upgrade_id = uuid.uuid4() + to_insert_lines = [ + f'{indent}\n', + f'{indent}" ?>\n', + f"{indent}\n", + ] + + for i, line in enumerate(to_insert_lines): + lines.insert(index_start + i + 1, line) + return lines + + return gen_content_between_tags( + "Package/Fragments/Upgrades.wxs", + "", + "", + func, + ) + + +def gen_content_between_tags(filename, tag_start, tag_end, func): + target_file = Path(sys.argv[0]).parent.joinpath(filename) + lines, index_start = read_lines_and_start_index(target_file, tag_start, tag_end) + if lines is None: + return False + + func(lines, index_start) + + with open(target_file, "w") as f: + f.writelines(lines) + + return True + + if __name__ == "__main__": parser = make_parser() args = parser.parse_args() @@ -158,6 +189,9 @@ if __name__ == "__main__": if not gen_pre_vars(args, build_dir): sys.exit(-1) + if not gen_upgrade_info(args.version): + sys.exit(-1) + if not gen_auto_component(app_name, build_dir): sys.exit(-1) From 129548764e28eee6e2921c0c770fb83b7e16621f Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 3 Apr 2024 22:20:56 +0800 Subject: [PATCH 003/112] fix build (#7597) Signed-off-by: fufesou --- 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 e389d795c..e097b04a6 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -383,7 +383,7 @@ class ScamWarningDialogState extends State { Navigator.of(context).pop(); }, style: ElevatedButton.styleFrom( - primary: Colors.blueAccent, + backgroundColor: Colors.blueAccent, ), child: Text( translate("Decline"), From 2397fdc4952705ff9a0cd4d6b89142636f4d92c2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 5 Apr 2024 09:28:35 +0800 Subject: [PATCH 004/112] Fix. Remote toolbar, menu refresh (#7605) Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 146 +++++++++--------- 1 file changed, 75 insertions(+), 71 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 2ac91d95f..583d0750b 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -636,7 +636,7 @@ class _MonitorMenu extends StatelessWidget { menuStyle: MenuStyle( padding: MaterialStatePropertyAll(EdgeInsets.symmetric(horizontal: 6))), - menuChildren: [buildMonitorSubmenuWidget()]); + menuChildrenGetter: () => [buildMonitorSubmenuWidget()]); } Widget buildMultiMonitorMenu() { @@ -843,17 +843,17 @@ class _ControlMenu extends StatelessWidget { color: _ToolbarTheme.blueColor, hoverColor: _ToolbarTheme.hoverBlueColor, ffi: ffi, - menuChildren: toolbarControls(context, id, ffi).map((e) { - if (e.divider) { - return Divider(); - } else { - return MenuButton( - child: e.child, - onPressed: e.onPressed, - ffi: ffi, - trailingIcon: e.trailingIcon); - } - }).toList()); + menuChildrenGetter: () => toolbarControls(context, id, ffi).map((e) { + if (e.divider) { + return Divider(); + } else { + return MenuButton( + child: e.child, + onPressed: e.onPressed, + ffi: ffi, + trailingIcon: e.trailingIcon); + } + }).toList()); } } @@ -1046,53 +1046,56 @@ class _DisplayMenuState extends State<_DisplayMenu> { Widget build(BuildContext context) { _screenAdjustor.updateScreen(); - final menuChildren = [ - _screenAdjustor.adjustWindow(context), - viewStyle(), - scrollStyle(), - imageQuality(), - codec(), - _ResolutionsMenu( - id: widget.id, - 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, - // ), - Divider(), - toggles(), - ]; - // privacy mode - if (ffiModel.keyboard && pi.features.privacyMode) { - final privacyModeState = PrivacyModeState.find(id); - final privacyModeList = - toolbarPrivacyMode(privacyModeState, context, id, ffi); - if (privacyModeList.length == 1) { - menuChildren.add(CkbMenuButton( - value: privacyModeList[0].value, - onChanged: privacyModeList[0].onChanged, - child: privacyModeList[0].child, - ffi: ffi)); - } else if (privacyModeList.length > 1) { - menuChildren.addAll([ - Divider(), - _SubmenuButton( - ffi: widget.ffi, - child: Text(translate('Privacy mode')), - menuChildren: privacyModeList - .map((e) => CkbMenuButton( - value: e.value, - onChanged: e.onChanged, - child: e.child, - ffi: ffi)) - .toList()), - ]); + menuChildrenGetter() { + final menuChildren = [ + _screenAdjustor.adjustWindow(context), + viewStyle(), + scrollStyle(), + imageQuality(), + codec(), + _ResolutionsMenu( + id: widget.id, + 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, + // ), + Divider(), + toggles(), + ]; + // privacy mode + if (ffiModel.keyboard && pi.features.privacyMode) { + final privacyModeState = PrivacyModeState.find(id); + final privacyModeList = + toolbarPrivacyMode(privacyModeState, context, id, ffi); + if (privacyModeList.length == 1) { + menuChildren.add(CkbMenuButton( + value: privacyModeList[0].value, + onChanged: privacyModeList[0].onChanged, + child: privacyModeList[0].child, + ffi: ffi)); + } else if (privacyModeList.length > 1) { + menuChildren.addAll([ + Divider(), + _SubmenuButton( + ffi: widget.ffi, + child: Text(translate('Privacy mode')), + menuChildren: privacyModeList + .map((e) => CkbMenuButton( + value: e.value, + onChanged: e.onChanged, + child: e.child, + ffi: ffi)) + .toList()), + ]); + } } + menuChildren.add(widget.pluginItem); + return menuChildren; } - menuChildren.add(widget.pluginItem); return _IconSubmenuButton( tooltip: 'Display Settings', @@ -1100,7 +1103,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, color: _ToolbarTheme.blueColor, hoverColor: _ToolbarTheme.hoverBlueColor, - menuChildren: menuChildren, + menuChildrenGetter: menuChildrenGetter, ); } @@ -1645,15 +1648,15 @@ class _KeyboardMenu extends StatelessWidget { ffi: ffi, color: _ToolbarTheme.blueColor, hoverColor: _ToolbarTheme.hoverBlueColor, - menuChildren: [ - keyboardMode(modeOnly), - localKeyboardType(), - inputSource(), - Divider(), - viewMode(), - Divider(), - ...toolbarToggles, - ]); + menuChildrenGetter: () => [ + keyboardMode(modeOnly), + localKeyboardType(), + inputSource(), + Divider(), + viewMode(), + Divider(), + ...toolbarToggles, + ]); } keyboardMode(String? modeOnly) { @@ -1804,7 +1807,7 @@ class _ChatMenuState extends State<_ChatMenu> { ffi: widget.ffi, color: _ToolbarTheme.blueColor, hoverColor: _ToolbarTheme.hoverBlueColor, - menuChildren: [textChat(), voiceCall()]); + menuChildrenGetter: () => [textChat(), voiceCall()]); } textChat() { @@ -2008,7 +2011,7 @@ class _IconSubmenuButton extends StatefulWidget { final Widget? icon; final Color color; final Color hoverColor; - final List menuChildren; + final List Function() menuChildrenGetter; final MenuStyle? menuStyle; final FFI ffi; final double? width; @@ -2020,7 +2023,7 @@ class _IconSubmenuButton extends StatefulWidget { required this.tooltip, required this.color, required this.hoverColor, - required this.menuChildren, + required this.menuChildrenGetter, required this.ffi, this.menuStyle, this.width, @@ -2064,7 +2067,8 @@ class _IconSubmenuButtonState extends State<_IconSubmenuButton> { color: hover ? widget.hoverColor : widget.color, ), child: icon))), - menuChildren: widget.menuChildren + menuChildren: widget + .menuChildrenGetter() .map((e) => _buildPointerTrackWidget(e, widget.ffi)) .toList())); return MenuBar(children: [ From 5533ebf86a26ffdd2ae90c79ef9b9b5a8c0108ae Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 6 Apr 2024 11:37:31 +0800 Subject: [PATCH 005/112] Fix. Remote toolbar, keyboard mode (#7626) Move modeOnly to the scope it is needed. Get modeOnly everytime of dropdown menu. Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 583d0750b..70a5b13a8 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1626,19 +1626,7 @@ class _KeyboardMenu extends StatelessWidget { Widget build(BuildContext context) { var ffiModel = Provider.of(context); if (!ffiModel.keyboard) return Offstage(); - // If use flutter to grab keys, we can only use one mode. - // Map mode and Legacy mode, at least one of them is supported. - String? modeOnly; - if (isInputSourceFlutter) { - if (bind.sessionIsKeyboardModeSupported( - sessionId: ffi.sessionId, mode: kKeyMapMode)) { - modeOnly = kKeyMapMode; - } else if (bind.sessionIsKeyboardModeSupported( - sessionId: ffi.sessionId, mode: kKeyLegacyMode)) { - modeOnly = kKeyLegacyMode; - } - } - final toolbarToggles = toolbarKeyboardToggles(ffi) + toolbarToggles() => toolbarKeyboardToggles(ffi) .map((e) => CkbMenuButton( value: e.value, onChanged: e.onChanged, child: e.child, ffi: ffi)) .toList(); @@ -1649,17 +1637,17 @@ class _KeyboardMenu extends StatelessWidget { color: _ToolbarTheme.blueColor, hoverColor: _ToolbarTheme.hoverBlueColor, menuChildrenGetter: () => [ - keyboardMode(modeOnly), + keyboardMode(), localKeyboardType(), inputSource(), Divider(), viewMode(), Divider(), - ...toolbarToggles, + ...toolbarToggles(), ]); } - keyboardMode(String? modeOnly) { + keyboardMode() { return futureBuilder(future: () async { return await bind.sessionGetKeyboardMode(sessionId: ffi.sessionId) ?? kKeyLegacyMode; @@ -1679,6 +1667,19 @@ class _KeyboardMenu extends StatelessWidget { await ffi.inputModel.updateKeyboardMode(); } + // If use flutter to grab keys, we can only use one mode. + // Map mode and Legacy mode, at least one of them is supported. + String? modeOnly; + if (isInputSourceFlutter) { + if (bind.sessionIsKeyboardModeSupported( + sessionId: ffi.sessionId, mode: kKeyMapMode)) { + modeOnly = kKeyMapMode; + } else if (bind.sessionIsKeyboardModeSupported( + sessionId: ffi.sessionId, mode: kKeyLegacyMode)) { + modeOnly = kKeyLegacyMode; + } + } + for (InputModeMenu mode in modes) { if (modeOnly != null && mode.key != modeOnly) { continue; From bddd800769c69db267f5ce5eb0f5c11c5a741d3d Mon Sep 17 00:00:00 2001 From: Yevhen Popok Date: Sat, 6 Apr 2024 11:21:11 +0300 Subject: [PATCH 006/112] Update Ukrainian translation (#7611) * Update Ukrainian translation * Update Ukrainian translation * Update Ukrainian translation --- src/lang/ua.rs | 56 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 86d08d547..6600a65be 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -134,14 +134,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insert Lock", "Встановити замок"), ("Refresh", "Оновити"), ("ID does not exist", "ID не існує"), - ("Failed to connect to rendezvous server", "Не вдалося підключитися до проміжного сервера"), + ("Failed to connect to rendezvous server", "Не вдалося підключитися до сервера рандеву"), ("Please try later", "Будь ласка, спробуйте пізніше"), ("Remote desktop is offline", "Віддалена стільниця не в мережі"), ("Key mismatch", "Невідповідність ключів"), ("Timeout", "Час очікування"), - ("Failed to connect to relay server", "Не вдалося підключитися до сервера реле"), - ("Failed to connect via rendezvous server", "Не вдалося підключитися через проміжний сервер"), - ("Failed to connect via relay server", "Не вдалося підключитися через сервер реле"), + ("Failed to connect to relay server", "Не вдалося підключитися до сервера ретрансляції"), + ("Failed to connect via rendezvous server", "Не вдалося підключитися через сервер рандеву"), + ("Failed to connect via relay server", "Не вдалося підключитися через сервер ретрансляції"), ("Failed to make direct connection to remote desktop", "Не вдалося встановити пряме підключення до віддаленої стільниці"), ("Set Password", "Встановити пароль"), ("OS Password", "Пароль ОС"), @@ -182,9 +182,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable file copy and paste", "Дозволити копіювання та вставку файлів"), ("Connected", "Підключено"), ("Direct and encrypted connection", "Пряме та зашифроване підключення"), - ("Relayed and encrypted connection", "Релейне та зашифроване підключення"), + ("Relayed and encrypted connection", "Ретрансльоване та зашифроване підключення"), ("Direct and unencrypted connection", "Пряме та незашифроване підключення"), - ("Relayed and unencrypted connection", "Релейне та незашифроване підключення"), + ("Relayed and unencrypted connection", "Ретрансльоване та незашифроване підключення"), ("Enter Remote ID", "Введіть віддалений ID"), ("Enter your password", "Введіть пароль"), ("Logging in...", "Вхід..."), @@ -210,8 +210,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Closed manually by the peer", "Завершено вручну з боку віддаленого пристрою"), ("Enable remote configuration modification", "Дозволити віддалену зміну конфігурації"), ("Run without install", "Запустити без встановлення"), - ("Connect via relay", "Підключитися через реле"), - ("Always connect via relay", "Завжди підключатися через реле"), + ("Connect via relay", "Підключитися через ретранслятор"), + ("Always connect via relay", "Завжди підключатися через ретранслятор"), ("whitelist_tip", "Тільки IP-адреси з білого списку можуть отримати доступ до мене"), ("Login", "Увійти"), ("Verify", "Підтвердити"), @@ -318,7 +318,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Restart remote device", "Перезапустити віддалений пристрій"), ("Are you sure you want to restart", "Ви впевнені, що хочете виконати перезапуск?"), ("Restarting remote device", "Перезапуск віддаленого пристрою"), - ("remote_restarting_tip", "Віддалений пристрій перезапускається. Будь ласка, закрийте це повідомлення та через деякий час перепідʼєднайтесь, використовуючи постійний пароль."), + ("remote_restarting_tip", "Віддалений пристрій перезапускається. Будь ласка, закрийте це повідомлення та через деякий час перепідключіться, використовуючи постійний пароль."), ("Copied", "Скопійовано"), ("Exit Fullscreen", "Вийти з повноекранного режиму"), ("Fullscreen", "Повноекранний"), @@ -332,7 +332,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show Toolbar", "Показати панель інструментів"), ("Hide Toolbar", "Приховати панель інструментів"), ("Direct Connection", "Пряме підключення"), - ("Relay Connection", "Релейне підключення"), + ("Relay Connection", "Ретрансльоване підключення"), ("Secure Connection", "Безпечне підключення"), ("Insecure Connection", "Небезпечне підключення"), ("Scale original", "Оригінал масштабу"), @@ -444,7 +444,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Voice call", "Голосовий виклик"), ("Text chat", "Текстовий чат"), ("Stop voice call", "Завершити голосовий виклик"), - ("relay_hint_tip", "Якщо відсутня можливості підключитись напряму, ви можете спробувати підключення по реле. \nТакож, якщо ви хочете відразу використовувати реле, можна додати суфікс \"/r\" до ID, або ж вибрати опцію \"Завжди підключатися через реле\" в картці нещодавніх сеансів."), + ("relay_hint_tip", "Якщо відсутня можливості підключитись напряму, ви можете спробувати підключення через ретранслятор. \nТакож, якщо ви хочете відразу використовувати ретранслятор, можна додати суфікс \"/r\" до ID, або ж вибрати опцію \"Завжди підключатися через ретранслятор\" в картці нещодавніх сеансів."), ("Reconnect", "Перепідключитися"), ("Codec", "Кодек"), ("Resolution", "Роздільна здатність"), @@ -461,7 +461,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("empty_favorite_tip", "Досі немає улюблених вузлів?\nДавайте організуємо нове підключення та додамо його до улюблених!"), ("empty_lan_tip", "О ні, схоже ми поки не виявили жодного віддаленого пристрою"), ("empty_address_book_tip", "Ой лишенько, схоже до вашої адресної книги немає жодного віддаленого пристрою"), - ("eg: admin", "напр. admin"), + ("eg: admin", "напр., admin"), ("Empty Username", "Незаповнене імʼя"), ("Empty Password", "Незаповнений пароль"), ("Me", "Я"), @@ -561,7 +561,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", "Список"), ("Virtual display", "Віртуальний дисплей"), ("Plug out all", "Відключити все"), - ("True color (4:4:4)", "Спражній колір (4:4:4)"), + ("True color (4:4:4)", "Справжній колір (4:4:4)"), ("Enable blocking user input", "Блокувати введення для користувача"), ("id_input_tip", "Ви можете ввести ID, безпосередню IP, або ж домен з портом (<домен>:<порт>).\nЯкщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (@<адреса_сервера>?key=<значення_ключа>), наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"@public\", ключ для публічного сервера не потрібен."), ("privacy_mode_impl_mag_tip", "Режим 1"), @@ -585,20 +585,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("2FA code must be 6 digits.", "Код двофакторної автентифікації повинен складатися з 6 символів."), ("Multiple Windows sessions found", "Виявлено декілька сеансів Windows"), ("Please select the session you want to connect to", "Будь ласка, оберіть сеанс, до якого ви хочете підключитися"), - ("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", ""), + ("powered_by_me", "На основі Rustdesk"), + ("outgoing_only_desk_tip", "Це персоналізована версія.\nВи можете підключатися до інших пристроїв, але інші пристрої не можуть підключатися до вашого."), + ("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", "Детальніше про веб-консоль"), ].iter().cloned().collect(); } From ab07eb6f4a7df73aac12f295fb5b6775c1f14961 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 6 Apr 2024 16:22:59 +0800 Subject: [PATCH 007/112] Fix. Remove strange cert dir created by 1.2.3 (#7620) * Fix. Remove strange cert dir created by 1.2.3 1. Remove `install_cert()`. 2. https://github.com/rustdesk/rustdesk/discussions/6444#discussioncomment-9017532 Signed-off-by: fufesou * comments Signed-off-by: fufesou --------- Signed-off-by: fufesou --- build.rs | 4 +- src/core_main.rs | 20 -- src/platform/windows.rs | 261 +---------------------- src/platform/windows_delete_test_cert.cc | 261 +++++++++++++++++++++++ 4 files changed, 269 insertions(+), 277 deletions(-) create mode 100644 src/platform/windows_delete_test_cert.cc diff --git a/build.rs b/build.rs index 47526d639..d332a43a2 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,11 @@ #[cfg(windows)] fn build_windows() { let file = "src/platform/windows.cc"; - cc::Build::new().file(file).compile("windows"); + let file2 = "src/platform/windows_delete_test_cert.cc"; + cc::Build::new().file(file).file(file2).compile("windows"); println!("cargo:rustc-link-lib=WtsApi32"); println!("cargo:rerun-if-changed={}", file); + println!("cargo:rerun-if-changed={}", file2); } #[cfg(target_os = "macos")] diff --git a/src/core_main.rs b/src/core_main.rs index caac2ca99..2d36097eb 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -208,31 +208,11 @@ pub fn core_main() -> Option> { .show() .ok(); return None; - } else if args[0] == "--install-cert" { - #[cfg(windows)] - hbb_common::allow_err!(crate::platform::windows::install_cert( - crate::platform::windows::DRIVER_CERT_FILE - )); - if args.len() > 1 && args[1] == "silent" { - return None; - } - #[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()); - } - return None; } else if args[0] == "--uninstall-cert" { #[cfg(windows)] hbb_common::allow_err!(crate::platform::windows::uninstall_cert()); return None; } else if args[0] == "--install-idd" { - #[cfg(windows)] - { - // It's ok to install cert multiple times. - hbb_common::allow_err!(crate::platform::windows::install_cert( - crate::platform::windows::DRIVER_CERT_FILE - )); - } #[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()); diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 0659422ef..2100c1144 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1187,17 +1187,6 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} ); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string(); - let install_cert = if options.contains("driverCert") { - let s = format!(r#""{}" --install-cert"#, src_exe); - if silent { - format!("{} silent", s) - } else { - s - } - } else { - "".to_owned() - }; - // potential bug here: if run_cmd cancelled, but config file is changed. if let Some(lic) = get_license() { Config::set_option("key".into(), lic.key); @@ -1241,7 +1230,6 @@ cscript \"{uninstall_shortcut}\" copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\" {dels} {import_config} -{install_cert} {after_install} {sleep} ", @@ -1958,251 +1946,20 @@ pub fn user_accessible_folder() -> ResultType { Ok(dir) } -#[inline] -pub fn install_cert(cert_file: &str) -> ResultType<()> { - let exe_file = std::env::current_exe()?; - if let Some(cur_dir) = exe_file.parent() { - allow_err!(cert::install_cert(cur_dir.join(cert_file))); - } else { - bail!( - "Invalid exe parent for {}", - exe_file.to_string_lossy().as_ref() - ); - } - Ok(()) -} - #[inline] pub fn uninstall_cert() -> ResultType<()> { cert::uninstall_cert() } mod cert { - use hbb_common::{bail, log, ResultType}; - use std::{ffi::OsStr, io::Error, os::windows::ffi::OsStrExt, path::Path, str::from_utf8}; - use winapi::{ - shared::{ - minwindef::{BYTE, DWORD, FALSE, TRUE}, - ntdef::NULL, - }, - um::{ - wincrypt::{ - CertAddEncodedCertificateToStore, CertCloseStore, CertDeleteCertificateFromStore, - CertEnumCertificatesInStore, CertNameToStrA, CertOpenStore, CryptHashCertificate, - ALG_ID, CALG_SHA1, CERT_ID_SHA1_HASH, CERT_STORE_ADD_REPLACE_EXISTING, - CERT_STORE_PROV_SYSTEM_W, CERT_SYSTEM_STORE_LOCAL_MACHINE, CERT_X500_NAME_STR, - PCCERT_CONTEXT, PKCS_7_ASN_ENCODING, X509_ASN_ENCODING, - }, - winreg::HKEY_LOCAL_MACHINE, - }, - }; - use winreg::{ - enums::{KEY_WRITE, REG_BINARY}, - RegKey, - }; + use hbb_common::ResultType; - const ROOT_CERT_STORE_PATH: &str = - "SOFTWARE\\Microsoft\\SystemCertificates\\ROOT\\Certificates\\"; - const THUMBPRINT_ALG: ALG_ID = CALG_SHA1; - const THUMBPRINT_LEN: DWORD = 20; - const CERT_ISSUER_1: &str = "CN=\"WDKTestCert admin,133225435702113567\"\0"; - const CERT_ENCODING_TYPE: DWORD = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; - - lazy_static::lazy_static! { - static ref CERT_STORE_LOC: Vec = OsStr::new("ROOT\0").encode_wide().collect::>(); + extern "C" { + fn DeleteRustDeskTestCertsW(); } - - #[inline] - unsafe fn compute_thumbprint(pb_encoded: *const BYTE, cb_encoded: DWORD) -> (Vec, String) { - let mut size = THUMBPRINT_LEN; - let mut thumbprint = [0u8; THUMBPRINT_LEN as usize]; - if CryptHashCertificate( - 0, - THUMBPRINT_ALG, - 0, - pb_encoded, - cb_encoded, - thumbprint.as_mut_ptr(), - &mut size, - ) == TRUE - { - ( - thumbprint.to_vec(), - hex::encode(thumbprint).to_ascii_uppercase(), - ) - } else { - (thumbprint.to_vec(), "".to_owned()) - } - } - - #[inline] - unsafe fn open_reg_cert_store() -> ResultType { - let hklm = winreg::RegKey::predef(HKEY_LOCAL_MACHINE); - Ok(hklm.open_subkey_with_flags(ROOT_CERT_STORE_PATH, KEY_WRITE)?) - } - - // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpef/6a9e35fa-2ac7-4c10-81e1-eabe8d2472f1 - fn create_cert_blob(thumbprint: Vec, encoded: Vec) -> Vec { - let mut blob = Vec::new(); - - let mut property_id = (CERT_ID_SHA1_HASH as u32).to_le_bytes().to_vec(); - let mut pro_reserved = [0x01, 0x00, 0x00, 0x00].to_vec(); - let mut pro_length = (THUMBPRINT_LEN as u32).to_le_bytes().to_vec(); - let mut pro_val = thumbprint; - blob.append(&mut property_id); - blob.append(&mut pro_reserved); - blob.append(&mut pro_length); - blob.append(&mut pro_val); - - let mut blob_reserved = [0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00].to_vec(); - let mut blob_length = (encoded.len() as u32).to_le_bytes().to_vec(); - let mut blob_val = encoded; - blob.append(&mut blob_reserved); - blob.append(&mut blob_length); - blob.append(&mut blob_val); - - blob - } - - pub fn install_cert>(path: P) -> ResultType<()> { - let mut cert_bytes = std::fs::read(path)?; - install_cert_reg(&mut cert_bytes)?; - install_cert_add_cert_store(&mut cert_bytes)?; - Ok(()) - } - - fn install_cert_reg(cert_bytes: &mut [u8]) -> ResultType<()> { - unsafe { - let thumbprint = compute_thumbprint(cert_bytes.as_mut_ptr(), cert_bytes.len() as _); - log::debug!("Thumbprint of cert {}", &thumbprint.1); - - let reg_cert_key = open_reg_cert_store()?; - let (cert_key, _) = reg_cert_key.create_subkey(&thumbprint.1)?; - let data = winreg::RegValue { - vtype: REG_BINARY, - bytes: create_cert_blob(thumbprint.0, cert_bytes.to_vec()), - }; - cert_key.set_raw_value("Blob", &data)?; - } - Ok(()) - } - - fn install_cert_add_cert_store(cert_bytes: &mut [u8]) -> ResultType<()> { - unsafe { - let store_handle = CertOpenStore( - CERT_STORE_PROV_SYSTEM_W, - 0, - 0, - CERT_SYSTEM_STORE_LOCAL_MACHINE, - CERT_STORE_LOC.as_ptr() as _, - ); - if store_handle.is_null() { - bail!( - "Error opening certificate store: {}", - Error::last_os_error() - ); - } - - // Create the certificate context - let cert_context = winapi::um::wincrypt::CertCreateCertificateContext( - CERT_ENCODING_TYPE, - cert_bytes.as_ptr(), - cert_bytes.len() as DWORD, - ); - if cert_context.is_null() { - bail!( - "Error creating certificate context: {}", - Error::last_os_error() - ); - } - - if FALSE - == CertAddEncodedCertificateToStore( - store_handle, - CERT_ENCODING_TYPE, - (*cert_context).pbCertEncoded, - (*cert_context).cbCertEncoded, - CERT_STORE_ADD_REPLACE_EXISTING, - std::ptr::null_mut(), - ) - { - log::error!( - "Failed to call CertAddEncodedCertificateToStore: {}", - Error::last_os_error() - ); - } else { - log::info!("Add cert to store successfully"); - } - - CertCloseStore(store_handle, 0); - } - Ok(()) - } - - fn get_thumbprints_to_rm() -> ResultType> { - let issuers_to_rm = [CERT_ISSUER_1]; - - let mut thumbprints = Vec::new(); - let mut buf = [0u8; 1024]; - - unsafe { - let store_handle = CertOpenStore( - CERT_STORE_PROV_SYSTEM_W, - 0, - 0, - CERT_SYSTEM_STORE_LOCAL_MACHINE, - CERT_STORE_LOC.as_ptr() as _, - ); - if store_handle.is_null() { - bail!( - "Error opening certificate store: {}", - Error::last_os_error() - ); - } - - let mut cert_ctx: PCCERT_CONTEXT = CertEnumCertificatesInStore(store_handle, NULL as _); - while !cert_ctx.is_null() { - // https://stackoverflow.com/a/66432736 - let cb_size = CertNameToStrA( - (*cert_ctx).dwCertEncodingType, - &mut ((*(*cert_ctx).pCertInfo).Issuer) as _, - CERT_X500_NAME_STR, - buf.as_mut_ptr() as _, - buf.len() as _, - ); - if cb_size != 1 { - if let Ok(issuer) = from_utf8(&buf[..cb_size as _]) { - for iss in issuers_to_rm.iter() { - if issuer == *iss { - let (_, thumbprint) = compute_thumbprint( - (*cert_ctx).pbCertEncoded, - (*cert_ctx).cbCertEncoded, - ); - if !thumbprint.is_empty() { - thumbprints.push(thumbprint); - } - // Delete current cert context and re-enumerate. - CertDeleteCertificateFromStore(cert_ctx); - cert_ctx = CertEnumCertificatesInStore(store_handle, NULL as _); - } - } - } - } - cert_ctx = CertEnumCertificatesInStore(store_handle, cert_ctx); - } - CertCloseStore(store_handle, 0); - } - - Ok(thumbprints) - } - pub fn uninstall_cert() -> ResultType<()> { - let thumbprints = get_thumbprints_to_rm()?; - let reg_cert_key = unsafe { open_reg_cert_store()? }; - log::info!("Found {} certs to remove", thumbprints.len()); - for thumbprint in thumbprints.iter() { - // Deleting cert from registry may fail, because the CertDeleteCertificateFromStore() is called before. - let _ = reg_cert_key.delete_subkey(thumbprint); + unsafe { + DeleteRustDeskTestCertsW(); } Ok(()) } @@ -2449,14 +2206,6 @@ pub fn try_kill_broker() { #[cfg(test)] mod tests { use super::*; - #[test] - fn test_install_cert() { - println!( - "install driver cert: {:?}", - cert::install_cert("RustDeskIddDriver.cer") - ); - } - #[test] fn test_uninstall_cert() { println!("uninstall driver certs: {:?}", cert::uninstall_cert()); diff --git a/src/platform/windows_delete_test_cert.cc b/src/platform/windows_delete_test_cert.cc new file mode 100644 index 000000000..8c5dc057a --- /dev/null +++ b/src/platform/windows_delete_test_cert.cc @@ -0,0 +1,261 @@ +// https://github.com/rustdesk/rustdesk/discussions/6444#discussioncomment-9010062 + +#include +#include +#include + +//************************************************************* +// +// RegDelnodeRecurseW() +// +// Purpose: Deletes a registry key and all its subkeys / values. +// +// Parameters: hKeyRoot - Root key +// lpSubKey - SubKey to delete +// bOneLevel - Delete lpSubKey and its first level subdirectory +// +// Return: TRUE if successful. +// FALSE if an error occurs. +// +// Note: If bOneLevel is TRUE, only current key and its first level subkeys are deleted. +// The first level subkeys are deleted only if they do not have subkeys. +// +// If some subkeys have subkeys, but the previous empty subkeys are deleted. +// It's ok for the certificates, because the empty subkeys are not used +// and they can be created automatically. +// +//************************************************************* + +BOOL RegDelnodeRecurseW(HKEY hKeyRoot, LPWSTR lpSubKey, BOOL bOneLevel) +{ + LPWSTR lpEnd; + LONG lResult; + DWORD dwSize; + WCHAR szName[MAX_PATH]; + HKEY hKey; + FILETIME ftWrite; + + // First, see if we can delete the key without having + // to recurse. + + lResult = RegDeleteKeyW(hKeyRoot, lpSubKey); + + if (lResult == ERROR_SUCCESS) + return TRUE; + + lResult = RegOpenKeyExW(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey); + + if (lResult != ERROR_SUCCESS) + { + if (lResult == ERROR_FILE_NOT_FOUND) { + //printf("Key not found.\n"); + return TRUE; + } + else { + //printf("Error opening key.\n"); + return FALSE; + } + } + + // Check for an ending slash and add one if it is missing. + + lpEnd = lpSubKey + lstrlenW(lpSubKey); + + if (*(lpEnd - 1) != L'\\') + { + *lpEnd = L'\\'; + lpEnd++; + *lpEnd = L'\0'; + } + + // Enumerate the keys + + dwSize = MAX_PATH; + lResult = RegEnumKeyExW(hKey, 0, szName, &dwSize, NULL, + NULL, NULL, &ftWrite); + + if (lResult == ERROR_SUCCESS) + { + do { + + *lpEnd = L'\0'; + StringCchCatW(lpSubKey, MAX_PATH * 2, szName); + + if (bOneLevel) { + lResult = RegDeleteKeyW(hKeyRoot, lpSubKey); + if (lResult != ERROR_SUCCESS) { + return FALSE; + } + } + else { + if (!RegDelnodeRecurseW(hKeyRoot, lpSubKey, bOneLevel)) { + break; + } + } + + dwSize = MAX_PATH; + + lResult = RegEnumKeyExW(hKey, 0, szName, &dwSize, NULL, + NULL, NULL, &ftWrite); + + } while (lResult == ERROR_SUCCESS); + } + + lpEnd--; + *lpEnd = L'\0'; + + RegCloseKey(hKey); + + // Try again to delete the key. + + lResult = RegDeleteKeyW(hKeyRoot, lpSubKey); + + if (lResult == ERROR_SUCCESS) + return TRUE; + + return FALSE; +} + +//************************************************************* +// +// RegDelnodeW() +// +// Purpose: Deletes a registry key and all its subkeys / values. +// +// Parameters: hKeyRoot - Root key +// lpSubKey - SubKey to delete +// bOneLevel - Delete lpSubKey and its first level subdirectory +// +// Return: TRUE if successful. +// FALSE if an error occurs. +// +//************************************************************* + +BOOL RegDelnodeW(HKEY hKeyRoot, LPCWSTR lpSubKey, BOOL bOneLevel) +{ + //return FALSE; // For Testing + + WCHAR szDelKey[MAX_PATH * 2]; + + StringCchCopyW(szDelKey, MAX_PATH * 2, lpSubKey); + return RegDelnodeRecurseW(hKeyRoot, szDelKey, bOneLevel); + +} + +//************************************************************* +// +// DeleteRustDeskTestCertsW_SingleHive() +// +// Purpose: Deletes RustDesk Test certificates and wrong key stores +// +// Parameters: RootKey - Root key +// Prefix - SID if RootKey=HKEY_USERS +// +// Return: TRUE if successful. +// FALSE if an error occurs. +// +//************************************************************* + +BOOL DeleteRustDeskTestCertsW_SingleHive(HKEY RootKey, LPWSTR Prefix = NULL) { + // WDKTestCert to be removed from all stores + LPCWSTR lpCertFingerPrint = L"D1DBB672D5A500B9809689CAEA1CE49E799767F0"; + + // Wrong key stores to be removed completely + LPCSTR RootName = "ROOT"; + LPWSTR SubKeyPrefix = (LPWSTR)RootName; // sic! Convert of ANSI to UTF-16 + + LPWSTR lpSystemCertificatesPath = (LPWSTR)malloc(512 * sizeof(WCHAR)); + if (lpSystemCertificatesPath == 0) return FALSE; + if (Prefix == NULL) { + wsprintfW(lpSystemCertificatesPath, L"Software\\Microsoft\\SystemCertificates"); + } + else { + wsprintfW(lpSystemCertificatesPath, L"%s\\Software\\Microsoft\\SystemCertificates", Prefix); + } + + HKEY hRegSystemCertificates; + LONG res = RegOpenKeyExW(RootKey, lpSystemCertificatesPath, NULL, KEY_ALL_ACCESS, &hRegSystemCertificates); + if (res != ERROR_SUCCESS) + return FALSE; + + for (DWORD Index = 0; ; Index++) { + LPWSTR SubKeyName = (LPWSTR)malloc(255 * sizeof(WCHAR)); + if (SubKeyName == 0) break; + DWORD cName = 255; + LONG res = RegEnumKeyExW(hRegSystemCertificates, Index, SubKeyName, &cName, NULL, NULL, NULL, NULL); + if ((res != ERROR_SUCCESS) || (SubKeyName == NULL)) + break; + + // Remove test certificate + LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); + if (Complete == 0) break; + wsprintfW(Complete, L"%s\\%s\\Certificates\\%s", lpSystemCertificatesPath, SubKeyName, lpCertFingerPrint); + std::wcout << "Try delete from: " << SubKeyName << std::endl; + RegDelnodeW(RootKey, Complete, FALSE); + free(Complete); + + if ((SubKeyName[0] == SubKeyPrefix[0]) && (SubKeyName[1] == SubKeyPrefix[1])) { + // "Chinese Characters" key begins with "ROOT" encoded as UTF-16 + LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); + if (Complete == 0) break; + wsprintfW(Complete, L"%s\\%s", lpSystemCertificatesPath, SubKeyName); + if (RegDelnodeW(RootKey, Complete, TRUE)) { + //std::wcout << "Rogue Key Deleted! \"" << Complete << "\"" << std::endl; // TODO: Why does this break the console? + std::wcout << "Rogue key is deleted!" << std::endl; + Index--; // Because index has moved due to the deletion + } else { + std::wcout << "Rogue key deletion failed!" << std::endl; + } + free(Complete); + } + + free(SubKeyName); + } + RegCloseKey(hRegSystemCertificates); + return TRUE; +} + +//************************************************************* +// +// DeleteRustDeskTestCertsW() +// +// Purpose: Deletes RustDesk Test certificates and wrong key stores +// +// Parameters: None +// +// Return: None +// +//************************************************************* + +extern "C" void DeleteRustDeskTestCertsW() { + // Current user + std::wcout << "*** Current User" << std::endl; + DeleteRustDeskTestCertsW_SingleHive(HKEY_CURRENT_USER); + + // Local machine (requires admin rights) + std::wcout << "*** Local Machine" << std::endl; + DeleteRustDeskTestCertsW_SingleHive(HKEY_LOCAL_MACHINE); + + // Iterate through all users (requires admin rights) + LPCWSTR lpRoot = L""; + HKEY hRegUsers; + LONG res = RegOpenKeyExW(HKEY_USERS, lpRoot, NULL, KEY_READ, &hRegUsers); + if (res != ERROR_SUCCESS) return; + for (DWORD Index = 0; ; Index++) { + LPWSTR SubKeyName = (LPWSTR)malloc(255 * sizeof(WCHAR)); + if (SubKeyName == 0) break; + DWORD cName = 255; + LONG res = RegEnumKeyExW(hRegUsers, Index, SubKeyName, &cName, NULL, NULL, NULL, NULL); + if ((res != ERROR_SUCCESS) || (SubKeyName == NULL)) + break; + std::wcout << "*** User: " << SubKeyName << std::endl; + DeleteRustDeskTestCertsW_SingleHive(HKEY_USERS, SubKeyName); + } + RegCloseKey(hRegUsers); +} + +// int main() +// { +// DeleteRustDeskTestCertsW(); +// return 0; +// } From e0b9a9a82f013889ccd30263e692b26336f872d7 Mon Sep 17 00:00:00 2001 From: Integral Date: Sat, 6 Apr 2024 01:29:55 -0700 Subject: [PATCH 008/112] Update cn.rs (#7614) --- src/lang/cn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 0ac1981ec..f6d649470 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -588,7 +588,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("powered_by_me", "由 RustDesk 提供支持"), ("outgoing_only_desk_tip", "当前版本的软件是定制版本。\n您可以连接至其他设备,但是其他设备无法连接至您的设备。"), ("preset_password_warning", "此定制版本附有预设密码。 任何知晓此密码的人都能完全控制您的设备。如果这不是您所预期的,请立即卸载此软件。"), - ("Security Alert", ""), + ("Security Alert", "安全警告"), ("My address book", "我的地址簿"), ("Personal", "个人的"), ("Owner", "所有者"), From 5a0333ddaf6ab32ff1114a5d1c7d7fd121914907 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 6 Apr 2024 16:30:33 +0800 Subject: [PATCH 009/112] Feat. msi (#7610) Signed-off-by: fufesou --- res/msi/CustomActions/CustomActions.cpp | 27 +++++++++++++-------- res/msi/CustomActions/CustomActions.vcxproj | 2 +- res/msi/Package/Components/RustDesk.wxs | 5 ++-- res/msi/Package/Fragments/CustomActions.wxs | 9 +++---- res/msi/msi.sln | 27 +++------------------ res/msi/preprocess.py | 2 +- 6 files changed, 28 insertions(+), 44 deletions(-) diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index a5c49410f..f836edb3b 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -1,5 +1,6 @@ // CustomAction.cpp : Defines the entry point for the custom action. #include "pch.h" +#include #include UINT __stdcall CustomActionHello( @@ -30,34 +31,40 @@ UINT __stdcall RemoveInstallFolder( DWORD er = ERROR_SUCCESS; int nResult = 0; - wchar_t szCustomActionData[256] = { 0 }; - DWORD cchCustomActionData = sizeof(szCustomActionData) / sizeof(szCustomActionData[0]); + LPWSTR installFolder = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzData = NULL; hr = WcaInitialize(hInstall, "RemoveInstallFolder"); ExitOnFailure(hr, "Failed to initialize"); - MsiGetPropertyW(hInstall, L"InstallFolder", szCustomActionData, &cchCustomActionData); - - WcaLog(LOGMSG_STANDARD, "================= Remove Install Folder: %ls", szCustomActionData); + 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); SHFILEOPSTRUCTW fileOp; - ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT)); + ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT)); fileOp.wFunc = FO_DELETE; - fileOp.pFrom = szCustomActionData; + fileOp.pFrom = installFolder; fileOp.fFlags = FOF_NOCONFIRMATION | FOF_SILENT; - nResult = SHFileOperationW(&fileOp); + nResult = SHFileOperation(&fileOp); if (nResult == 0) { - WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", szCustomActionData); + WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has been deleted.", installFolder); } else { - WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has not been deleted, error code: 0X%02X.", szCustomActionData, nResult); + WcaLog(LOGMSG_STANDARD, "The directory \"%ls\" has not been deleted, error code: 0X%02X. Please refer to https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shfileoperationa for the error codes.", installFolder, nResult); } LExit: + ReleaseStr(installFolder); + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index 1d1521158..3667b3bc8 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -10,7 +10,7 @@ Win32Proj - {87e7c13b-ae0e-4048-95cf-4523d510a3cd} + {6b3647e0-b4a3-46ae-8757-a22ee51c1dac} CustomActions v143 10.0 diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index abb43358d..297f35a77 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -20,6 +20,7 @@ + diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index 79b49b49d..09d9b5698 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -5,18 +5,15 @@ - - - - + - + - + diff --git a/res/msi/msi.sln b/res/msi/msi.sln index 8fb06eb51..70d28fb86 100644 --- a/res/msi/msi.sln +++ b/res/msi/msi.sln @@ -5,38 +5,17 @@ VisualStudioVersion = 17.7.34003.232 MinimumVisualStudioVersion = 10.0.40219.1 Project("{B7DD6F7E-DEF8-4E67-B5B7-07EF123DB6F0}") = "Package", "Package\Package.wixproj", "{F403A403-CEFF-4399-B51C-CC646C8E98CF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomActions", "CustomActions\CustomActions.vcxproj", "{87E7C13B-AE0E-4048-95CF-4523D510A3CD}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CustomActions", "CustomActions\CustomActions.vcxproj", "{6B3647E0-B4A3-46AE-8757-A22EE51C1DAC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|Any CPU.ActiveCfg = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|Any CPU.Build.0 = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x64.ActiveCfg = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x64.Build.0 = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x86.ActiveCfg = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Debug|x86.Build.0 = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|Any CPU.ActiveCfg = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|Any CPU.Build.0 = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x64.ActiveCfg = Release|x64 {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x64.Build.0 = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x86.ActiveCfg = Release|x64 - {F403A403-CEFF-4399-B51C-CC646C8E98CF}.Release|x86.Build.0 = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|Any CPU.ActiveCfg = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|x64.ActiveCfg = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Debug|x86.ActiveCfg = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|Any CPU.ActiveCfg = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|Any CPU.Build.0 = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x64.ActiveCfg = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x64.Build.0 = Release|x64 - {87E7C13B-AE0E-4048-95CF-4523D510A3CD}.Release|x86.ActiveCfg = Release|x64 + {6B3647E0-B4A3-46AE-8757-A22EE51C1DAC}.Release|x64.ActiveCfg = Release|x64 + {6B3647E0-B4A3-46AE-8757-A22EE51C1DAC}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index fe9050b98..23f1e28ea 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -143,7 +143,7 @@ def gen_upgrade_info(version): upgrade_id = uuid.uuid4() to_insert_lines = [ f'{indent}\n', - f'{indent}" ?>\n', + f'{indent}{g_indent_unit}\n', f"{indent}\n", ] From 0c294eefae5e32b505f03c882d0489726595ef86 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 6 Apr 2024 17:53:03 +0800 Subject: [PATCH 010/112] reordered peer tab (#7604) * reordered peer tab Signed-off-by: 21pages * opt peer tab visible menu, avoid checkbox value splash Signed-off-by: 21pages --------- Signed-off-by: 21pages --- flutter/lib/common/widgets/peer_tab_page.dart | 90 +++++++------ flutter/lib/desktop/widgets/popup_menu.dart | 41 ++++++ flutter/lib/models/peer_tab_model.dart | 122 ++++++++++++++---- 3 files changed, 184 insertions(+), 69 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 8840fa474..7011e722e 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -137,11 +137,13 @@ class _PeerTabPageState extends State Widget _createSwitchBar(BuildContext context) { final model = Provider.of(context); - - return ListView( + var counter = -1; + return ReorderableListView( + buildDefaultDragHandles: false, + onReorder: model.reorder, scrollDirection: Axis.horizontal, physics: NeverScrollableScrollPhysics(), - children: model.visibleIndexs.map((t) { + children: model.visibleEnabledOrderedIndexs.map((t) { final selected = model.currentTab == t; final color = selected ? MyTheme.tabbar(context).selectedTextColor @@ -155,43 +157,47 @@ class _PeerTabPageState extends State border: Border( bottom: BorderSide(width: 2, color: color!), )); - return Obx(() => Tooltip( - preferBelow: false, - message: model.tabTooltip(t), - onTriggered: isMobile ? mobileShowTabVisibilityMenu : null, - child: InkWell( - child: Container( - decoration: (hover.value - ? (selected ? decoBorder : deco) - : (selected ? decoBorder : null)), - child: Icon(model.tabIcon(t), color: color) - .paddingSymmetric(horizontal: 4), - ).paddingSymmetric(horizontal: 4), - onTap: () async { - await handleTabSelection(t); - await bind.setLocalFlutterOption( - k: 'peer-tab-index', v: t.toString()); - }, - onHover: (value) => hover.value = value, - ), - )); + counter += 1; + return ReorderableDragStartListener( + key: ValueKey(t), + index: counter, + child: Obx(() => Tooltip( + preferBelow: false, + message: model.tabTooltip(t), + onTriggered: isMobile ? mobileShowTabVisibilityMenu : null, + child: InkWell( + child: Container( + decoration: (hover.value + ? (selected ? decoBorder : deco) + : (selected ? decoBorder : null)), + child: Icon(model.tabIcon(t), color: color) + .paddingSymmetric(horizontal: 4), + ).paddingSymmetric(horizontal: 4), + onTap: () async { + await handleTabSelection(t); + await bind.setLocalFlutterOption( + k: PeerTabModel.kPeerTabIndex, v: t.toString()); + }, + onHover: (value) => hover.value = value, + ), + ))); }).toList()); } Widget _createPeersView() { final model = Provider.of(context); Widget child; - if (model.visibleIndexs.isEmpty) { + if (model.visibleEnabledOrderedIndexs.isEmpty) { child = visibleContextMenuListener(Row( children: [Expanded(child: InkWell())], )); } else { - if (model.visibleIndexs.contains(model.currentTab)) { + if (model.visibleEnabledOrderedIndexs.contains(model.currentTab)) { child = entries[model.currentTab].widget; } else { debugPrint("should not happen! currentTab not in visibleIndexs"); Future.delayed(Duration.zero, () { - model.setCurrentTab(model.indexs[0]); + model.setCurrentTab(model.visibleEnabledOrderedIndexs[0]); }); child = entries[0].widget; } @@ -255,16 +261,17 @@ class _PeerTabPageState extends State void mobileShowTabVisibilityMenu() { final model = gFFI.peerTabModel; final items = List.empty(growable: true); - for (int i = 0; i < model.tabNames.length; i++) { + for (int i = 0; i < PeerTabModel.maxTabCount; i++) { + if (!model.isEnabled[i]) continue; items.add(PopupMenuItem( height: kMinInteractiveDimension * 0.8, - onTap: () => model.setTabVisible(i, !model.isVisible[i]), + onTap: () => model.setTabVisible(i, !model.isVisibleEnabled[i]), child: Row( children: [ Checkbox( - value: model.isVisible[i], + value: model.isVisibleEnabled[i], onChanged: (_) { - model.setTabVisible(i, !model.isVisible[i]); + model.setTabVisible(i, !model.isVisibleEnabled[i]); if (Navigator.canPop(context)) { Navigator.pop(context); } @@ -314,16 +321,17 @@ class _PeerTabPageState extends State Widget visibleContextMenu(CancelFunc cancelFunc) { final model = Provider.of(context); - final menu = List.empty(growable: true); - for (int i = 0; i < model.tabNames.length; i++) { - menu.add(MenuEntrySwitch( + final menu = List.empty(growable: true); + for (int i = 0; i < model.orders.length; i++) { + int tabIndex = model.orders[i]; + if (tabIndex < 0 || tabIndex >= PeerTabModel.maxTabCount) continue; + if (!model.isEnabled[tabIndex]) continue; + menu.add(MenuEntrySwitchSync( switchType: SwitchType.scheckbox, - text: model.tabTooltip(i), - getter: () async { - return model.isVisible[i]; - }, + text: model.tabTooltip(tabIndex), + currentValue: model.isVisibleEnabled[tabIndex], setter: (show) async { - model.setTabVisible(i, show); + model.setTabVisible(tabIndex, show); cancelFunc(); })); } @@ -434,7 +442,7 @@ class _PeerTabPageState extends State model.setMultiSelectionMode(false); showToast(translate('Successful')); }, - child: Icon(model.icons[PeerTabIndex.fav.index]), + child: Icon(PeerTabModel.icons[PeerTabIndex.fav.index]), ).marginOnly(left: isMobile ? 11 : 6), ); } @@ -455,7 +463,7 @@ class _PeerTabPageState extends State addPeersToAbDialog(peers); model.setMultiSelectionMode(false); }, - child: Icon(model.icons[PeerTabIndex.ab.index]), + child: Icon(PeerTabModel.icons[PeerTabIndex.ab.index]), ).marginOnly(left: isMobile ? 11 : 6), ); } @@ -563,7 +571,7 @@ class _PeerTabPageState extends State final screenWidth = MediaQuery.of(context).size.width; final leftIconSize = Theme.of(context).iconTheme.size ?? 24; final leftActionsSize = - (leftIconSize + (4 + 4) * 2) * model.visibleIndexs.length; + (leftIconSize + (4 + 4) * 2) * model.visibleEnabledOrderedIndexs.length; final availableWidth = screenWidth - 10 * 2 - leftActionsSize - 2 * 2; final searchWidth = 120; final otherActionWidth = 18 + 10; diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index 9833dcbca..086d7a622 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -568,6 +568,47 @@ class MenuEntrySwitch extends MenuEntrySwitchBase { } } +// Compatible with MenuEntrySwitch, it uses value instead of getter +class MenuEntrySwitchSync extends MenuEntrySwitchBase { + final SwitchSetter setter; + final RxBool _curOption = false.obs; + + MenuEntrySwitchSync({ + required SwitchType switchType, + required String text, + required bool currentValue, + required this.setter, + Rx? textStyle, + EdgeInsets? padding, + dismissOnClicked = false, + RxBool? enabled, + dismissCallback, + }) : super( + switchType: switchType, + text: text, + textStyle: textStyle, + padding: padding, + dismissOnClicked: dismissOnClicked, + enabled: enabled, + dismissCallback: dismissCallback, + ) { + _curOption.value = currentValue; + } + + @override + RxBool get curOption => _curOption; + @override + setOption(bool? option) async { + if (option != null) { + await setter(option); + // Notice: no ensure with getter, best used on menus that are destroyed on click + if (_curOption.value != option) { + _curOption.value = option; + } + } + } +} + typedef Switch2Getter = RxBool Function(); typedef Switch2Setter = Future Function(bool); diff --git a/flutter/lib/models/peer_tab_model.dart b/flutter/lib/models/peer_tab_model.dart index 43bbfda72..52e43bfea 100644 --- a/flutter/lib/models/peer_tab_model.dart +++ b/flutter/lib/models/peer_tab_model.dart @@ -21,24 +21,43 @@ class PeerTabModel with ChangeNotifier { WeakReference parent; int get currentTab => _currentTab; int _currentTab = 0; // index in tabNames - List tabNames = [ + static const int maxTabCount = 5; + static const String kPeerTabIndex = 'peer-tab-index'; + static const String kPeerTabOrder = 'peer-tab-order'; + static const String kPeerTabVisible = 'peer-tab-visible'; + static const List tabNames = [ 'Recent sessions', 'Favorites', - if (!isWeb) 'Discovered', - if (!(bind.isDisableAb() || bind.isDisableAccount())) 'Address book', - if (!bind.isDisableAccount()) 'Group', + 'Discovered', + 'Address book', + 'Group', ]; - final List icons = [ + static const List icons = [ Icons.access_time_filled, Icons.star, - if (!isWeb) Icons.explore, - if (!(bind.isDisableAb() || bind.isDisableAccount())) IconFont.addressBook, - if (!bind.isDisableAccount()) Icons.group, + Icons.explore, + IconFont.addressBook, + Icons.group, ]; - final List _isVisible = List.filled(5, true, growable: false); - List get isVisible => _isVisible; - List get indexs => List.generate(tabNames.length, (index) => index); - List get visibleIndexs => indexs.where((e) => _isVisible[e]).toList(); + List isEnabled = List.from([ + true, + true, + !isWeb, + !(bind.isDisableAb() || bind.isDisableAccount()), + !bind.isDisableAccount(), + ]); + final List _isVisible = List.filled(maxTabCount, true, growable: false); + List get isVisibleEnabled => () { + final list = _isVisible.toList(); + for (int i = 0; i < maxTabCount; i++) { + list[i] = list[i] && isEnabled[i]; + } + return list; + }(); + final List orders = + List.generate(maxTabCount, (index) => index, growable: false); + List get visibleEnabledOrderedIndexs => + orders.where((e) => isVisibleEnabled[e]).toList(); List _selectedPeers = List.empty(growable: true); List get selectedPeers => _selectedPeers; bool _multiSelectionMode = false; @@ -53,7 +72,7 @@ class PeerTabModel with ChangeNotifier { PeerTabModel(this.parent) { // visible try { - final option = bind.getLocalFlutterOption(k: 'peer-tab-visible'); + final option = bind.getLocalFlutterOption(k: kPeerTabVisible); if (option.isNotEmpty) { List decodeList = jsonDecode(option); if (decodeList.length == _isVisible.length) { @@ -67,13 +86,37 @@ class PeerTabModel with ChangeNotifier { } catch (e) { debugPrint("failed to get peer tab visible list:$e"); } + // order + try { + final option = bind.getLocalFlutterOption(k: kPeerTabOrder); + if (option.isNotEmpty) { + List decodeList = jsonDecode(option); + if (decodeList.length == maxTabCount) { + var sortedList = decodeList.toList(); + sortedList.sort(); + bool valid = true; + for (int i = 0; i < maxTabCount; i++) { + if (sortedList[i] is! int || sortedList[i] != i) { + valid = false; + } + } + if (valid) { + for (int i = 0; i < orders.length; i++) { + orders[i] = decodeList[i]; + } + } + } + } + } catch (e) { + debugPrint("failed to get peer tab order list: $e"); + } // init currentTab _currentTab = - int.tryParse(bind.getLocalFlutterOption(k: 'peer-tab-index')) ?? 0; - if (_currentTab < 0 || _currentTab >= tabNames.length) { + int.tryParse(bind.getLocalFlutterOption(k: kPeerTabIndex)) ?? 0; + if (_currentTab < 0 || _currentTab >= maxTabCount) { _currentTab = 0; } - _trySetCurrentTabToFirstVisible(); + _trySetCurrentTabToFirstVisibleEnabled(); } setCurrentTab(int index) { @@ -87,15 +130,13 @@ class PeerTabModel with ChangeNotifier { if (index >= 0 && index < tabNames.length) { return translate(tabNames[index]); } - assert(false); return index.toString(); } IconData tabIcon(int index) { - if (index >= 0 && index < tabNames.length) { + if (index >= 0 && index < icons.length) { return icons[index]; } - assert(false); return Icons.help; } @@ -171,29 +212,54 @@ class PeerTabModel with ChangeNotifier { } setTabVisible(int index, bool visible) { - if (index >= 0 && index < _isVisible.length) { + if (index >= 0 && index < maxTabCount) { if (_isVisible[index] != visible) { _isVisible[index] = visible; if (index == _currentTab && !visible) { - _trySetCurrentTabToFirstVisible(); - } else if (visible && visibleIndexs.length == 1) { + _trySetCurrentTabToFirstVisibleEnabled(); + } else if (visible && visibleEnabledOrderedIndexs.length == 1) { _currentTab = index; } try { bind.setLocalFlutterOption( - k: 'peer-tab-visible', v: jsonEncode(_isVisible)); + k: kPeerTabVisible, v: jsonEncode(_isVisible)); } catch (_) {} notifyListeners(); } } } - _trySetCurrentTabToFirstVisible() { - if (!_isVisible[_currentTab]) { - int firstVisible = _isVisible.indexWhere((e) => e); - if (firstVisible >= 0) { - _currentTab = firstVisible; + _trySetCurrentTabToFirstVisibleEnabled() { + if (!visibleEnabledOrderedIndexs.contains(_currentTab)) { + if (visibleEnabledOrderedIndexs.isNotEmpty) { + _currentTab = visibleEnabledOrderedIndexs.first; } } } + + reorder(int oldIndex, int newIndex) { + if (oldIndex < newIndex) { + newIndex -= 1; + } + if (oldIndex < 0 || oldIndex >= visibleEnabledOrderedIndexs.length) { + return; + } + if (newIndex < 0 || newIndex >= visibleEnabledOrderedIndexs.length) { + return; + } + final oldTabValue = visibleEnabledOrderedIndexs[oldIndex]; + final newTabValue = visibleEnabledOrderedIndexs[newIndex]; + int oldValueIndex = orders.indexOf(oldTabValue); + int newValueIndex = orders.indexOf(newTabValue); + final list = orders.toList(); + if (oldIndex != -1 && newIndex != -1) { + list.removeAt(oldValueIndex); + list.insert(newValueIndex, oldTabValue); + for (int i = 0; i < list.length; i++) { + orders[i] = list[i]; + } + bind.setLocalFlutterOption(k: kPeerTabOrder, v: jsonEncode(orders)); + notifyListeners(); + } + } } From f36f065508632be2752264e6d8cf44c91457bf32 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 6 Apr 2024 22:58:42 +0800 Subject: [PATCH 011/112] Refact. Delete check reg fingerprint (#7631) Signed-off-by: fufesou --- src/platform/windows_delete_test_cert.cc | 44 ++++++++++++++---------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/platform/windows_delete_test_cert.cc b/src/platform/windows_delete_test_cert.cc index 8c5dc057a..587485172 100644 --- a/src/platform/windows_delete_test_cert.cc +++ b/src/platform/windows_delete_test_cert.cc @@ -186,27 +186,33 @@ BOOL DeleteRustDeskTestCertsW_SingleHive(HKEY RootKey, LPWSTR Prefix = NULL) { if ((res != ERROR_SUCCESS) || (SubKeyName == NULL)) break; - // Remove test certificate - LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); - if (Complete == 0) break; - wsprintfW(Complete, L"%s\\%s\\Certificates\\%s", lpSystemCertificatesPath, SubKeyName, lpCertFingerPrint); - std::wcout << "Try delete from: " << SubKeyName << std::endl; - RegDelnodeW(RootKey, Complete, FALSE); - free(Complete); - + // "佒呏..." key begins with "ROOT" encoded as UTF-16 if ((SubKeyName[0] == SubKeyPrefix[0]) && (SubKeyName[1] == SubKeyPrefix[1])) { - // "Chinese Characters" key begins with "ROOT" encoded as UTF-16 - LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); - if (Complete == 0) break; - wsprintfW(Complete, L"%s\\%s", lpSystemCertificatesPath, SubKeyName); - if (RegDelnodeW(RootKey, Complete, TRUE)) { - //std::wcout << "Rogue Key Deleted! \"" << Complete << "\"" << std::endl; // TODO: Why does this break the console? - std::wcout << "Rogue key is deleted!" << std::endl; - Index--; // Because index has moved due to the deletion - } else { - std::wcout << "Rogue key deletion failed!" << std::endl; + // Remove test certificate + { + LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); + if (Complete == 0) break; + wsprintfW(Complete, L"%s\\%s\\Certificates\\%s", lpSystemCertificatesPath, SubKeyName, lpCertFingerPrint); + // std::wcout << "Try delete from: " << SubKeyName << std::endl; + RegDelnodeW(RootKey, Complete, FALSE); + free(Complete); + } + + // Remove wrong empty key store + { + LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); + memset(Complete, 0, 512 * sizeof(WCHAR)); + if (Complete == 0) break; + wsprintfW(Complete, L"%s\\%s", lpSystemCertificatesPath, SubKeyName); + if (RegDelnodeW(RootKey, Complete, TRUE)) { + //std::wcout << "Rogue Key Deleted! \"" << Complete << "\"" << std::endl; // TODO: Why does this break the console? + std::wcout << "Rogue key is deleted!" << std::endl; + Index--; // Because index has moved due to the deletion + } else { + std::wcout << "Rogue key deletion failed!" << std::endl; + } + free(Complete); } - free(Complete); } free(SubKeyName); From fddad091678d63240170b4052d4638f9304b1844 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 6 Apr 2024 23:19:50 +0800 Subject: [PATCH 012/112] Refact. Remove unused memset (#7633) Signed-off-by: fufesou --- src/platform/windows_delete_test_cert.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform/windows_delete_test_cert.cc b/src/platform/windows_delete_test_cert.cc index 587485172..838e60516 100644 --- a/src/platform/windows_delete_test_cert.cc +++ b/src/platform/windows_delete_test_cert.cc @@ -201,7 +201,6 @@ BOOL DeleteRustDeskTestCertsW_SingleHive(HKEY RootKey, LPWSTR Prefix = NULL) { // Remove wrong empty key store { LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); - memset(Complete, 0, 512 * sizeof(WCHAR)); if (Complete == 0) break; wsprintfW(Complete, L"%s\\%s", lpSystemCertificatesPath, SubKeyName); if (RegDelnodeW(RootKey, Complete, TRUE)) { From 22da14e3f70e14ab7c87db483d4470b7f8ffdeef Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 7 Apr 2024 11:31:51 +0800 Subject: [PATCH 013/112] fix https://github.com/rustdesk/rustdesk/discussions/7628, https://github.com/rustdesk/rustdesk/discussions/7490 --- src/platform/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 2100c1144..05a9582d7 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1233,7 +1233,7 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\" {after_install} {sleep} ", - version = crate::VERSION, + version = crate::VERSION.replace("-", "."), build_date = crate::BUILD_DATE, after_install = get_after_install(&exe), sleep = if debug { "timeout 300" } else { "" }, From 65f7541ec76cab7f996623a4c9ee674d3b294800 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 7 Apr 2024 14:31:09 +0800 Subject: [PATCH 014/112] Fix. Compare cert blob before deleting it. (#7643) * Fix. Compare cert blob before deleting it. Signed-off-by: fufesou * Rename function name Signed-off-by: fufesou --------- Signed-off-by: fufesou --- src/platform/windows_delete_test_cert.cc | 178 ++++++++++++++++++++--- 1 file changed, 159 insertions(+), 19 deletions(-) diff --git a/src/platform/windows_delete_test_cert.cc b/src/platform/windows_delete_test_cert.cc index 838e60516..94949a827 100644 --- a/src/platform/windows_delete_test_cert.cc +++ b/src/platform/windows_delete_test_cert.cc @@ -4,6 +4,148 @@ #include #include +BOOL IsCertWdkTestCert(char* lpBlobData, DWORD cchBlobData) { + DWORD cchIdxBlobData = 0; + DWORD cchIdxTestCertBlob = 0; + DWORD cchSizeTestCertBlob = 0; +#pragma warning(push) +#pragma warning(disable: 4838) +#pragma warning(disable: 4309) + const char TestCertBlob[] = { + 0X30, 0X82, 0X03, 0X0C, 0X30, 0X82, 0X01, 0XF4, 0XA0, 0X03, 0X02, 0X01, 0X02, 0X02, 0X10, 0X17, + 0X93, 0X62, 0X03, 0XFA, 0XCD, 0X37, 0X83, 0X49, 0XE3, 0X33, 0X82, 0XC3, 0X14, 0XEC, 0X83, 0X30, + 0X0D, 0X06, 0X09, 0X2A, 0X86, 0X48, 0X86, 0XF7, 0X0D, 0X01, 0X01, 0X05, 0X05, 0X00, 0X30, 0X2F, + 0X31, 0X2D, 0X30, 0X2B, 0X06, 0X03, 0X55, 0X04, 0X03, 0X13, 0X24, 0X57, 0X44, 0X4B, 0X54, 0X65, + 0X73, 0X74, 0X43, 0X65, 0X72, 0X74, 0X20, 0X61, 0X64, 0X6D, 0X69, 0X6E, 0X2C, 0X31, 0X33, 0X33, + 0X32, 0X32, 0X35, 0X34, 0X33, 0X35, 0X37, 0X30, 0X32, 0X31, 0X31, 0X33, 0X35, 0X36, 0X37, 0X30, + 0X1E, 0X17, 0X0D, 0X32, 0X33, 0X30, 0X33, 0X30, 0X36, 0X30, 0X32, 0X33, 0X32, 0X35, 0X31, 0X5A, + 0X17, 0X0D, 0X33, 0X33, 0X30, 0X33, 0X30, 0X36, 0X30, 0X30, 0X30, 0X30, 0X30, 0X30, 0X5A, 0X30, + 0X2F, 0X31, 0X2D, 0X30, 0X2B, 0X06, 0X03, 0X55, 0X04, 0X03, 0X13, 0X24, 0X57, 0X44, 0X4B, 0X54, + 0X65, 0X73, 0X74, 0X43, 0X65, 0X72, 0X74, 0X20, 0X61, 0X64, 0X6D, 0X69, 0X6E, 0X2C, 0X31, 0X33, + 0X33, 0X32, 0X32, 0X35, 0X34, 0X33, 0X35, 0X37, 0X30, 0X32, 0X31, 0X31, 0X33, 0X35, 0X36, 0X37, + 0X30, 0X82, 0X01, 0X22, 0X30, 0X0D, 0X06, 0X09, 0X2A, 0X86, 0X48, 0X86, 0XF7, 0X0D, 0X01, 0X01, + 0X01, 0X05, 0X00, 0X03, 0X82, 0X01, 0X0F, 0X00, 0X30, 0X82, 0X01, 0X0A, 0X02, 0X82, 0X01, 0X01, + 0X00, 0XB8, 0X65, 0X75, 0XAC, 0XD1, 0X82, 0XFC, 0X3A, 0X08, 0XE4, 0X1D, 0XD9, 0X4D, 0X5A, 0XCD, + 0X88, 0X2B, 0XDC, 0X00, 0XFD, 0X6B, 0X43, 0X13, 0XED, 0XE2, 0XCB, 0XD1, 0X26, 0X11, 0X22, 0XBF, + 0X20, 0X31, 0X09, 0X9D, 0X06, 0X47, 0XF5, 0XAA, 0XCE, 0X7B, 0X13, 0X98, 0XE0, 0X76, 0X40, 0XDD, + 0X2C, 0XCA, 0X98, 0XD1, 0XBB, 0X7F, 0XE2, 0X25, 0XAF, 0X48, 0X3A, 0X4E, 0X9E, 0X24, 0X38, 0X4D, + 0X04, 0XF0, 0X68, 0XAD, 0X7C, 0X6F, 0XA6, 0XBB, 0XE4, 0X9B, 0XE3, 0X7C, 0X8E, 0X2E, 0X54, 0X7D, + 0X5E, 0X74, 0XE3, 0XA6, 0X3D, 0XD9, 0X04, 0X22, 0X0A, 0X3E, 0XC7, 0X5C, 0XAB, 0X1F, 0X4D, 0X10, + 0X06, 0X2A, 0X95, 0X1A, 0X1B, 0X03, 0X20, 0X75, 0X3E, 0X49, 0X36, 0X40, 0X06, 0X63, 0XDB, 0X54, + 0X74, 0X53, 0X3C, 0X2D, 0X47, 0XE0, 0X82, 0XDD, 0X14, 0X92, 0XCC, 0XF1, 0X1A, 0X5A, 0X7F, 0X5B, + 0X4F, 0X2E, 0X94, 0X1E, 0XCE, 0X5A, 0X73, 0XD4, 0X70, 0X47, 0XF3, 0X3E, 0X85, 0X5C, 0X62, 0XF5, + 0X79, 0X0F, 0X4B, 0XB9, 0X69, 0X51, 0X33, 0X05, 0XF1, 0XDF, 0XE5, 0X4E, 0X6E, 0X28, 0XC6, 0X88, + 0X89, 0X9A, 0XEF, 0X07, 0X62, 0X23, 0X53, 0X6A, 0X16, 0X2B, 0X3A, 0XF7, 0X10, 0X1B, 0X42, 0XCE, + 0XEE, 0X33, 0XB9, 0X01, 0X30, 0X8A, 0XAB, 0X14, 0X73, 0XC5, 0XC3, 0X94, 0X2D, 0XEB, 0X00, 0XAE, + 0X73, 0X7B, 0X78, 0X65, 0X8B, 0X8F, 0X44, 0XBD, 0XF8, 0XBC, 0XE8, 0XB3, 0X6A, 0X4E, 0XE3, 0X4F, + 0X92, 0XE3, 0X72, 0XD9, 0X6D, 0XD1, 0X88, 0X5E, 0X1C, 0XFF, 0X8D, 0XF1, 0X76, 0XBC, 0X37, 0X4B, + 0X11, 0X48, 0XB5, 0X8D, 0X1D, 0X1C, 0XEC, 0X82, 0X11, 0X50, 0XC6, 0XFF, 0X3A, 0X7E, 0X3A, 0X8C, + 0X18, 0XF7, 0XA6, 0XEB, 0XAA, 0X26, 0X8E, 0XC6, 0X01, 0X7B, 0X50, 0X6A, 0XFA, 0X33, 0X3C, 0XBE, + 0X29, 0X02, 0X03, 0X01, 0X00, 0X01, 0XA3, 0X24, 0X30, 0X22, 0X30, 0X0B, 0X06, 0X03, 0X55, 0X1D, + 0X0F, 0X04, 0X04, 0X03, 0X02, 0X04, 0X30, 0X30, 0X13, 0X06, 0X03, 0X55, 0X1D, 0X25, 0X04, 0X0C, + 0X30, 0X0A, 0X06, 0X08, 0X2B, 0X06, 0X01, 0X05, 0X05, 0X07, 0X03, 0X03, 0X30, 0X0D, 0X06, 0X09, + 0X2A, 0X86, 0X48, 0X86, 0XF7, 0X0D, 0X01, 0X01, 0X05, 0X05, 0X00, 0X03, 0X82, 0X01, 0X01, 0X00, + 0X00, 0X44, 0X78, 0XE3, 0XDB, 0X0C, 0X33, 0X2B, 0X57, 0X52, 0X91, 0XD0, 0X09, 0X80, 0X12, 0XB0, + 0X11, 0X7C, 0X32, 0XCF, 0X24, 0XA0, 0XA5, 0X47, 0X18, 0XDE, 0XAB, 0X9E, 0X0D, 0X4A, 0X50, 0X6B, + 0X7B, 0XD3, 0X23, 0X71, 0X32, 0XEE, 0X28, 0X1D, 0XE8, 0X2C, 0X0A, 0XDF, 0X89, 0X87, 0X9D, 0X7E, + 0XE3, 0X59, 0X05, 0XDD, 0XC2, 0X3C, 0X48, 0XC1, 0XD5, 0X88, 0X2D, 0X60, 0X29, 0XDE, 0XA1, 0X69, + 0XD8, 0X4E, 0X01, 0XF6, 0XBD, 0XCB, 0X41, 0XDF, 0XDF, 0X5B, 0X3D, 0X3D, 0X59, 0X93, 0X70, 0XD6, + 0XAC, 0X03, 0X84, 0X5E, 0X2B, 0XB6, 0X62, 0X10, 0X5B, 0XB2, 0X68, 0X97, 0XC7, 0XF9, 0X44, 0X68, + 0XBC, 0XC3, 0X26, 0XD7, 0XB5, 0X13, 0XBE, 0X0E, 0XE6, 0X7E, 0X74, 0XF0, 0XB9, 0X59, 0X63, 0XE8, + 0X6E, 0XE2, 0X96, 0X3C, 0XFE, 0X55, 0XB9, 0XAC, 0X1A, 0XB8, 0XC5, 0X98, 0XA9, 0XD3, 0XF5, 0X30, + 0XCB, 0X9E, 0X43, 0X89, 0X19, 0X9A, 0X5C, 0XB5, 0XFB, 0X76, 0XD5, 0X3B, 0XD4, 0X79, 0X02, 0X98, + 0XA0, 0XC7, 0X60, 0X96, 0X84, 0X66, 0X79, 0X25, 0XC9, 0XC2, 0X77, 0X54, 0X63, 0XA1, 0X0E, 0X27, + 0X7B, 0X2E, 0X37, 0XBE, 0X18, 0X99, 0XF6, 0X34, 0XE7, 0XCC, 0XE8, 0XE7, 0XEB, 0XE4, 0XB7, 0X37, + 0X05, 0X35, 0X77, 0XAD, 0X76, 0XAD, 0X35, 0X84, 0X62, 0XF7, 0X7F, 0X87, 0XAB, 0X29, 0X25, 0X10, + 0X73, 0XBF, 0X2C, 0X78, 0X93, 0XFF, 0XBF, 0X24, 0XD7, 0X49, 0X74, 0XC5, 0X07, 0X41, 0X17, 0XBA, + 0X87, 0XBB, 0X4E, 0XB3, 0X8F, 0XF3, 0X75, 0X77, 0X2B, 0X44, 0X7B, 0X0D, 0X18, 0X24, 0X8A, 0XCB, + 0XCC, 0X67, 0XB4, 0X00, 0XC6, 0X2A, 0XAC, 0XCD, 0X4C, 0X16, 0XF8, 0XB8, 0X61, 0X8D, 0XAF, 0X7B, + 0XF2, 0X45, 0XE2, 0X63, 0X02, 0X4C, 0XA8, 0XB9, 0XBD, 0XB2, 0X5E, 0XF2, 0X94, 0X8F, 0X30, 0X16 + }; +#pragma warning(pop) + + cchSizeTestCertBlob = sizeof(TestCertBlob) / sizeof(TestCertBlob[0]); + if (cchBlobData < cchSizeTestCertBlob) return FALSE; + cchIdxBlobData = cchBlobData - cchSizeTestCertBlob; + while (cchIdxTestCertBlob < cchSizeTestCertBlob) { + if (lpBlobData[cchIdxBlobData] != TestCertBlob[cchIdxTestCertBlob]) { + return FALSE; + } + ++cchIdxTestCertBlob; + ++cchIdxBlobData; + } + return TRUE; +} + +//************************************************************* +// +// RegDelTestCertW() +// +// Purpose: Compares and deletes a test cert. +// +// Parameters: hKeyRoot - Root key +// lpSubKey - SubKey to delete +// +// Return: TRUE if successful. +// FALSE if an error occurs. +// +//************************************************************* + +BOOL RegDelTestCertW(HKEY hKeyRoot, LPCWSTR lpSubKey) +{ + LONG lResult; + HKEY hKey; + DWORD dValueType; + DWORD cchBufferSize = 0; + BOOL bRes = FALSE; + + lResult = RegOpenKeyExW(hKeyRoot, lpSubKey, 0, KEY_READ, &hKey); + if (lResult != ERROR_SUCCESS) { + if (lResult == ERROR_FILE_NOT_FOUND) { + return TRUE; + } + else { + //printf("Error opening key.\n"); + return FALSE; + } + } + + do { + lResult = RegQueryValueExW(hKey, L"Blob", NULL, &dValueType, NULL, &cchBufferSize); + if (lResult == ERROR_SUCCESS) { + if (dValueType == REG_BINARY) { + LPSTR szBuffer = NULL; + LONG readResult = 0; + szBuffer = (LPSTR)malloc(cchBufferSize * sizeof(char)); + if (szBuffer == NULL) { + bRes = FALSE; + break; + } + + lResult = RegQueryValueExW(hKey, L"Blob", NULL, &dValueType, (LPBYTE)szBuffer, &cchBufferSize); + if (readResult == ERROR_SUCCESS) { + if (IsCertWdkTestCert(szBuffer, cchBufferSize)) { + free(szBuffer); + lResult = RegDeleteKeyW(hKeyRoot, lpSubKey); + if (lResult == ERROR_SUCCESS) { + bRes = TRUE; + } + else { + bRes = FALSE; + } + + break; + } + } + + free(szBuffer); + } + } + } while (FALSE); + RegCloseKey(hKey); + return bRes; +} + //************************************************************* // // RegDelnodeRecurseW() @@ -139,7 +281,6 @@ BOOL RegDelnodeW(HKEY hKeyRoot, LPCWSTR lpSubKey, BOOL bOneLevel) StringCchCopyW(szDelKey, MAX_PATH * 2, lpSubKey); return RegDelnodeRecurseW(hKeyRoot, szDelKey, bOneLevel); - } //************************************************************* @@ -186,18 +327,16 @@ BOOL DeleteRustDeskTestCertsW_SingleHive(HKEY RootKey, LPWSTR Prefix = NULL) { if ((res != ERROR_SUCCESS) || (SubKeyName == NULL)) break; + // Remove test certificate + LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); + if (Complete == 0) break; + wsprintfW(Complete, L"%s\\%s\\Certificates\\%s", lpSystemCertificatesPath, SubKeyName, lpCertFingerPrint); + // std::wcout << "Try delete from: " << SubKeyName << std::endl; + RegDelTestCertW(RootKey, Complete); + free(Complete); + // "佒呏..." key begins with "ROOT" encoded as UTF-16 if ((SubKeyName[0] == SubKeyPrefix[0]) && (SubKeyName[1] == SubKeyPrefix[1])) { - // Remove test certificate - { - LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); - if (Complete == 0) break; - wsprintfW(Complete, L"%s\\%s\\Certificates\\%s", lpSystemCertificatesPath, SubKeyName, lpCertFingerPrint); - // std::wcout << "Try delete from: " << SubKeyName << std::endl; - RegDelnodeW(RootKey, Complete, FALSE); - free(Complete); - } - // Remove wrong empty key store { LPWSTR Complete = (LPWSTR)malloc(512 * sizeof(WCHAR)); @@ -205,10 +344,11 @@ BOOL DeleteRustDeskTestCertsW_SingleHive(HKEY RootKey, LPWSTR Prefix = NULL) { wsprintfW(Complete, L"%s\\%s", lpSystemCertificatesPath, SubKeyName); if (RegDelnodeW(RootKey, Complete, TRUE)) { //std::wcout << "Rogue Key Deleted! \"" << Complete << "\"" << std::endl; // TODO: Why does this break the console? - std::wcout << "Rogue key is deleted!" << std::endl; + std::cout << "Rogue key is deleted!" << std::endl; Index--; // Because index has moved due to the deletion - } else { - std::wcout << "Rogue key deletion failed!" << std::endl; + } + else { + std::cout << "Rogue key deletion failed!" << std::endl; } free(Complete); } @@ -259,8 +399,8 @@ extern "C" void DeleteRustDeskTestCertsW() { RegCloseKey(hRegUsers); } -// int main() -// { -// DeleteRustDeskTestCertsW(); -// return 0; -// } +// int main() +// { +// DeleteRustDeskTestCertsW(); +// return 0; +// } From 9402516acdfcfc1cbc62289da65b3b8782591a9e Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 7 Apr 2024 22:03:50 +0800 Subject: [PATCH 015/112] Refact. Flutter pub upgrade web (#7648) Signed-off-by: fufesou --- flutter/pubspec.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 68210ed79..11c7d2cb3 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -1526,10 +1526,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.5.1" web_socket_channel: dependency: transitive description: From 4fc5d3f03bd19becda27e75a91860000fe969332 Mon Sep 17 00:00:00 2001 From: BrazilianArmy Date: Mon, 8 Apr 2024 00:56:12 -0300 Subject: [PATCH 016/112] Update ptbr.rs (#7649) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Atualização de algumas KEYS para português BR --- src/lang/ptbr.rs | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index fd1b85c13..f36d17c12 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -503,21 +503,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("clipboard_wait_response_timeout_tip", ""), ("Incoming connection", ""), ("Outgoing connection", ""), - ("Exit", ""), - ("Open", ""), + ("Exit", "Sair"), + ("Open", "Abrir"), ("logout_tip", ""), - ("Service", ""), - ("Start", ""), - ("Stop", ""), + ("Service", "Serviço"), + ("Start", "Iniciar"), + ("Stop", "Parar"), ("exceed_max_devices", ""), - ("Sync with recent sessions", ""), - ("Sort tags", ""), - ("Open connection in new tab", ""), - ("Move tab to new window", ""), - ("Can not be empty", ""), + ("Sync with recent sessions", "Sincronizar com sessões recentes"), + ("Sort tags", "Classificar tags"), + ("Open connection in new tab", "Abrir conexão em uma nova aba"), + ("Move tab to new window", "Mover aba para uma nova janela"), + ("Can not be empty", "Não pode estar vazio"), ("Already exists", ""), - ("Change Password", ""), - ("Refresh Password", ""), + ("Change Password", "Alterar senha"), + ("Refresh Password", "Atualizar senha"), ("ID", ""), ("Grid View", ""), ("List View", ""), @@ -526,8 +526,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), - ("Change Color", ""), - ("Primary Color", ""), + ("Change Color", "Alterar cor"), + ("Primary Color", "Cor principal"), ("HSV Color", ""), ("Installation Successful!", ""), ("Installation failed!", ""), @@ -537,7 +537,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("scam_text1", ""), ("scam_text2", ""), ("Don't show again", ""), - ("I Agree", ""), + ("I Agree", "Eu concordo"), ("Decline", ""), ("Timeout in minutes", ""), ("auto_disconnect_option_tip", ""), @@ -546,8 +546,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("upgrade_rustdesk_server_pro_to_{}_tip", ""), ("pull_group_failed_tip", ""), ("Filter by intersection", ""), - ("Remove wallpaper during incoming sessions", ""), - ("Test", ""), + ("Remove wallpaper during incoming sessions", "Remover papel de parede durante sessão remota"), + ("Test", "Teste"), ("display_is_plugged_out_msg", ""), ("No displays", ""), ("elevated_switch_display_msg", ""), @@ -555,10 +555,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), ("selinux_tip", ""), - ("Change view", ""), + ("Change view", "Alterar visualização"), ("Big tiles", ""), ("Small tiles", ""), - ("List", ""), + ("List", "Lista"), ("Virtual display", ""), ("Plug out all", ""), ("True color (4:4:4)", ""), @@ -584,7 +584,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), ("Multiple Windows sessions found", ""), - ("Please select the session you want to connect to", ""), + ("Please select the session you want to connect to", "Por favor, selecione a sessão que você deseja se conectar"), ("powered_by_me", ""), ("outgoing_only_desk_tip", ""), ("preset_password_warning", ""), @@ -592,13 +592,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("My address book", ""), ("Personal", ""), ("Owner", ""), - ("Set shared password", ""), + ("Set shared password", "Definir senha compartilhada"), ("Exist in", ""), - ("Read-only", ""), - ("Read/Write", ""), - ("Full Control", ""), + ("Read-only", "Apenas leitura"), + ("Read/Write", "Leitura/escrita"), + ("Full Control", "Controle total"), ("share_warning_tip", ""), - ("Everyone", ""), + ("Everyone", "Todos"), ("ab_web_console_tip", ""), ].iter().cloned().collect(); } From 84fc70e9a934c7d91d08da5cc687d74434ef29c8 Mon Sep 17 00:00:00 2001 From: Alen Bajo Date: Mon, 8 Apr 2024 10:30:50 +0200 Subject: [PATCH 017/112] Create hr.rs (#7654) Croatian translation (Hrvatski) --- src/lang/hr.rs | 604 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 604 insertions(+) create mode 100644 src/lang/hr.rs diff --git a/src/lang/hr.rs b/src/lang/hr.rs new file mode 100644 index 000000000..14b935d70 --- /dev/null +++ b/src/lang/hr.rs @@ -0,0 +1,604 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Status"), + ("Your Desktop", "Vaša radna površina"), + ("desk_tip", "Vašoj radnoj površini se može pristupiti ovim ID i lozinkom."), + ("Password", "Lozinka"), + ("Ready", "Spremno"), + ("Established", "Uspostavljeno"), + ("connecting_status", "Spajanje na RustDesk mrežu..."), + ("Enable service", "Dopusti servis"), + ("Start service", "Pokreni servis"), + ("Service is running", "Servis je pokrenut"), + ("Service is not running", "Servis nije pokrenut"), + ("not_ready_status", "Nije spremno. Provjerite vezu."), + ("Control Remote Desktop", "Upravljanje udaljenom radnom površinom"), + ("Transfer file", "Prijenos datoteke"), + ("Connect", "Spajanje"), + ("Recent sessions", "Nedavne sesije"), + ("Address book", "Adresar"), + ("Confirmation", "Potvrda"), + ("TCP tunneling", "TCP tunel"), + ("Remove", "Ukloni"), + ("Refresh random password", "Osvježi slučajnu lozinku"), + ("Set your own password", "Postavi lozinku"), + ("Enable keyboard/mouse", "Dopusti tipkovnicu/miša"), + ("Enable clipboard", "Dopusti međuspremnik"), + ("Enable file transfer", "Dopusti prenos datoteka"), + ("Enable TCP tunneling", "Dopusti TCP tunel"), + ("IP Whitelisting", "IP pouzdana lista"), + ("ID/Relay Server", "ID/Posredni poslužitelj"), + ("Import server config", "Uvoz konfiguracije poslužitelja"), + ("Export Server Config", "Izvoz konfiguracije poslužitelja"), + ("Import server configuration successfully", "Uvoz konfiguracije poslužitelja uspješan"), + ("Export server configuration successfully", "Izvoz konfiguracije poslužitelja uspješan"), + ("Invalid server configuration", "Pogrešna konfiguracija poslužitelja"), + ("Clipboard is empty", "Međuspremnik je prazan"), + ("Stop service", "Zaustavi servis"), + ("Change ID", "Promijeni ID"), + ("Your new ID", "Vaš novi ID"), + ("length %min% to %max%", "duljina %min% do %max%"), + ("starts with a letter", "Počinje slovom"), + ("allowed characters", "Dopušteni znakovi"), + ("id_change_tip", "Dopušteni su samo a-z, A-Z, 0-9 i _ (donja crta) znakovi. Prvi znak mora biti slovo a-z, A-Z. Duljina je od 6 do 16."), + ("Website", "Web stranica"), + ("About", "O programu"), + ("Slogan_tip", "Slogan_tip"), + ("Privacy Statement", "Izjava o privatnosti"), + ("Mute", "Utišaj"), + ("Build Date", "Datum izrade"), + ("Version", "Verzija"), + ("Home", "Početno"), + ("Audio Input", "Audio ulaz"), + ("Enhancements", "Proširenja"), + ("Hardware Codec", "Hardverski kodek"), + ("Adaptive bitrate", "Prilagodljiva gustoća podataka"), + ("ID Server", "ID poslužitelja"), + ("Relay Server", "Posredni poslužitelj"), + ("API Server", "API poslužitelj"), + ("invalid_http", "mora početi sa http:// ili https://"), + ("Invalid IP", "Nevažeća IP"), + ("Invalid format", "Pogrešan format"), + ("server_not_support", "Poslužitelj još uvijek ne podržava"), + ("Not available", "Nije dostupno"), + ("Too frequent", "Previše često"), + ("Cancel", "Otkaži"), + ("Skip", "Preskoči"), + ("Close", "Zatvori"), + ("Retry", "Ponovi"), + ("OK", "Ok"), + ("Password Required", "Potrebna lozinka"), + ("Please enter your password", "Molimo unesite svoju lozinku"), + ("Remember password", "Zapamti lozinku"), + ("Wrong Password", "Pogrešna lozinka"), + ("Do you want to enter again?", "Želite li ponovo unijeti lozinku?"), + ("Connection Error", "Greška u spajanju"), + ("Error", "Greška"), + ("Reset by the peer", "Prekinuto sa druge strane"), + ("Connecting...", "Povezivanje..."), + ("Connection in progress. Please wait.", "Povezivanje u tijeku. Molimo pričekajte."), + ("Please try 1 minute later", "Pokušajte minutu kasnije"), + ("Login Error", "Greška kod prijave"), + ("Successful", "Uspješno"), + ("Connected, waiting for image...", "Spojeno, pričekajte sliku..."), + ("Name", "Ime"), + ("Type", "Vrsta"), + ("Modified", "Izmijenjeno"), + ("Size", "Veličina"), + ("Show Hidden Files", "Prikaži skrivene datoteke"), + ("Receive", "Prijem"), + ("Send", "Slanje"), + ("Refresh File", "Osvježi datoteku"), + ("Local", "Lokalno"), + ("Remote", "Udaljeno"), + ("Remote Computer", "Udaljeno računalo"), + ("Local Computer", "Lokalno računalo"), + ("Confirm Delete", "Potvrdite brisanje"), + ("Delete", "Brisanje"), + ("Properties", "Svojstva"), + ("Multi Select", "Višestruki odabir"), + ("Select All", "Odaberi sve"), + ("Unselect All", "Poništi odabir"), + ("Empty Directory", "Prazna mapa"), + ("Not an empty directory", "Nije prazna mapa"), + ("Are you sure you want to delete this file?", "Jeste sigurni da želite obrisati ovu datoteku?"), + ("Are you sure you want to delete this empty directory?", "Jeste sigurni da želite obrisati ovu praznu mapu?"), + ("Are you sure you want to delete the file of this directory?", "Jeste sigurni da želite obrisati datoteku u ovoj mapi?"), + ("Do this for all conflicts", "Učinite to za sve sukobe"), + ("This is irreversible!", "Ovo je nepovratno"), + ("Deleting", "Brisanje"), + ("files", "datoteke"), + ("Waiting", "Čekanje"), + ("Finished", "Završeno"), + ("Speed", "Brzina"), + ("Custom Image Quality", "Korisnička kvaliteta slike"), + ("Privacy mode", "Način privatnosti"), + ("Block user input", "Blokiraj korisnikov unos"), + ("Unblock user input", "Odblokiraj korisnikov unos"), + ("Adjust Window", "Podesi prozor"), + ("Original", "Original"), + ("Shrink", "Skupi"), + ("Stretch", "Raširi"), + ("Scrollbar", "Linija pomaka"), + ("ScrollAuto", "Autom. pomak"), + ("Good image quality", "Dobra kvaliteta slike"), + ("Balanced", "Balansirano"), + ("Optimize reaction time", "Optimizirano vrijeme reakcije"), + ("Custom", "Korisničko"), + ("Show remote cursor", "Prikaži udaljeni kursor"), + ("Show quality monitor", "Prikaži kvalitetu monitor"), + ("Disable clipboard", "Zabrani međuspremnik"), + ("Lock after session end", "Zaključaj po završetku sesije"), + ("Insert", "Umetni"), + ("Insert Lock", "Zaključaj umetanje"), + ("Refresh", "Osvježi"), + ("ID does not exist", "ID ne postoji"), + ("Failed to connect to rendezvous server", "Greška u spajanju na poslužitelj za povezivanje"), + ("Please try later", "Molimo pokušajte kasnije"), + ("Remote desktop is offline", "Udaljeni zaslon je isključen"), + ("Key mismatch", "Pogrešan ključ"), + ("Timeout", "Isteklo vrijeme"), + ("Failed to connect to relay server", "Greška u spajanju na posredni poslužitelj"), + ("Failed to connect via rendezvous server", "Greška u spajanju preko poslužitelja za povezivanje"), + ("Failed to connect via relay server", "Greška u spajanju preko posrednog poslužitelja"), + ("Failed to make direct connection to remote desktop", "Greška u direktnom spajanju na udaljenu radnu površinu"), + ("Set Password", "Postavi lozinku"), + ("OS Password", "Lozinka OS-a"), + ("install_tip", "Zbog UAC-a RustDesk ne može u nekim slučajevima raditi pravilno. Da biste prevazišli UAC, kliknite na tipku ispod da instalirate RustDesk na sustav."), + ("Click to upgrade", "Klik za nadogradnju"), + ("Click to download", "Klik za preuzimanje"), + ("Click to update", "Klik za ažuriranje"), + ("Configure", "Konfiguracija"), + ("config_acc", "Da biste daljinski kontrolirali radnu površinu, RustDesk-u trebate dodijeliti prava za \"Pristupačnost\"."), + ("config_screen", "Da biste daljinski pristupili radnoj površini, RustDesk-u trebate dodijeliti prava za \"Snimanje zaslona\"."), + ("Installing ...", "Instaliranje..."), + ("Install", "Instaliraj"), + ("Installation", "Instalacija"), + ("Installation Path", "Putanja za instalaciju"), + ("Create start menu shortcuts", "Stvori prečace u izborniku"), + ("Create desktop icon", "Stvori ikonu na radnoj površini"), + ("agreement_tip", "Pokretanjem instalacije prihvaćate ugovor o licenciranju."), + ("Accept and Install", "Prihvati i instaliraj"), + ("End-user license agreement", "Ugovor sa krajnjim korisnikom"), + ("Generating ...", "Generiranje..."), + ("Your installation is lower version.", "Vaša instalacija je niže verzije"), + ("not_close_tcp_tip", "Ne zatvarajte ovaj prozor dok koristite tunel"), + ("Listening ...", "Na slušanju..."), + ("Remote Host", "Adresa udaljenog uređaja"), + ("Remote Port", "Udaljeni port"), + ("Action", "Akcija"), + ("Add", "Dodaj"), + ("Local Port", "Lokalni port"), + ("Local Address", "Lokalna adresa"), + ("Change Local Port", "Promijeni lokalni port"), + ("setup_server_tip", "Za brže spajanje, molimo da koristite vlastiti poslužitelj"), + ("Too short, at least 6 characters.", "Prekratko, najmanje 6 znakova."), + ("The confirmation is not identical.", "Potvrda nije identična"), + ("Permissions", "Dopuštenja"), + ("Accept", "Prihvati"), + ("Dismiss", "Odbaci"), + ("Disconnect", "Prekini vezu), + ("Enable file copy and paste", "Dopusti kopiranje i lijepljenje datoteka"), + ("Connected", "Spojeno"), + ("Direct and encrypted connection", "Izravna i kriptirana veza"), + ("Relayed and encrypted connection", "Posredna i kriptirana veza"), + ("Direct and unencrypted connection", "Izravna i nekriptirana veza"), + ("Relayed and unencrypted connection", "Posredna i nekriptirana veza"), + ("Enter Remote ID", "Unesite ID udaljenog uređaja"), + ("Enter your password", "Unesite svoju lozinku"), + ("Logging in...", "Prijava..."), + ("Enable RDP session sharing", "Dopusti dijeljenje RDP sesije"), + ("Auto Login", "Autom. prijava (Važi samo ako ste postavili \"Zaključavanje nakon završetka sesije\")"), + ("Enable direct IP access", "Dopusti izravan pristup preko IP"), + ("Rename", "Preimenuj"), + ("Space", "Prazno"), + ("Create desktop shortcut", "Stvori prečac na radnoj površini"), + ("Change Path", "Promijeni putanju"), + ("Create Folder", "Svori mapu"), + ("Please enter the folder name", "Unesite ime mape"), + ("Fix it", "Popravi"), + ("Warning", "Upozorenje"), + ("Login screen using Wayland is not supported", "Zaslon za prijavu koji koristi Wayland nije podržan"), + ("Reboot required", "Potrebano je ponovno pokretanje"), + ("Unsupported display server", "Nepodržani poslužitelj za prikaz"), + ("x11 expected", "x11 očekivan"), + ("Port", "Port"), + ("Settings", "Postavke"), + ("Username", "Korisničko ime"), + ("Invalid port", "Pogrešan port"), + ("Closed manually by the peer", "Klijent ručno prekinuo vezu"), + ("Enable remote configuration modification", "Dopusti izmjenu udaljene konfiguracije"), + ("Run without install", "Pokreni bez instalacije"), + ("Connect via relay", "Povezivanje preko relejnog poslužitelja"), + ("Always connect via relay", "Uvek se poveži preko relejnog poslužitelja"), + ("whitelist_tip", "Mogu mi pristupiti samo dozvoljene IP adrese"), + ("Login", "Prijava"), + ("Verify", "Potvrdi"), + ("Remember me", "Zapamti me"), + ("Trust this device", "Vjeruj ovom uređaju"), + ("Verification code", "Kontrolni kôd"), + ("verification_tip", "Kontrolni kôd je poslan na registriranu adresu e-pošte, unesite ga i nastavite s prijavom."), + ("Logout", "Odjava"), + ("Tags", "Oznake"), + ("Search ID", "Traži ID"), + ("whitelist_sep", "Odvojeno zarezima, točka zarezima, praznim mjestima ili novim redovima"), + ("Add ID", "Dodaj ID"), + ("Add Tag", "Dodaj oznaku"), + ("Unselect all tags", "Odznači sve oznake"), + ("Network error", "Greška na mreži"), + ("Username missed", "Korisničko ime propušteno"), + ("Password missed", "Lozinka propuštena"), + ("Wrong credentials", "Pogrešno korisničko ime ili lozinka"), + ("The verification code is incorrect or has expired", "Kôd za provjeru nije točan ili je istekao"), + ("Edit Tag", "Izmjenite oznaku"), + ("Forget Password", "Zaboravi lozinku"), + ("Favorites", "Favoriti"), + ("Add to Favorites", "Dodaj u favorite"), + ("Remove from Favorites", "Ukloni iz favorita"), + ("Empty", "Prazno"), + ("Invalid folder name", "Nevažeći naziv mape"), + ("Socks5 Proxy", "Socks5 Proxy"), + ("Discovered", "Otkriveno"), + ("install_daemon_tip", "Usluga sustava mora biti instalirana ako se želi pokrenuti pri pokretanju sustava."), + ("Remote ID", "Udaljeni ID"), + ("Paste", "Zalijepi"), + ("Paste here?", "Zalijepi ovdje?"), + ("Are you sure to close the connection?", "Jeste li sigurni da želite zatvoriti vezu?"), + ("Download new version", "Preuzmi novu verziju"), + ("Touch mode", "Način rada na dodir"), + ("Mouse mode", "Način rada miša"), + ("One-Finger Tap", "Dodir jednim prstom"), + ("Left Mouse", "Lijeva tipka miša"), + ("One-Long Tap", "Jedan dugi dodir"), + ("Two-Finger Tap", "Dodir s dva prsta"), + ("Right Mouse", "Desna tipka miša"), + ("One-Finger Move", "Pomak jednim prstom"), + ("Double Tap & Move", "Dupli dodir i pomak"), + ("Mouse Drag", "Povlačenje mišem"), + ("Three-Finger vertically", "Sa tri prsta okomito"), + ("Mouse Wheel", "Kotačić miša"), + ("Two-Finger Move", "Pomak s dva prsta"), + ("Canvas Move", "Pomak pozadine"), + ("Pinch to Zoom", "Stisnite prste za zumiranje"), + ("Canvas Zoom", "Zumiranje pozadine"), + ("Reset canvas", "Resetiraj pozadinu"), + ("No permission of file transfer", "Nemate pravo prijenosa datoteka"), + ("Note", "Bilješka"), + ("Connection", "Povezivanje"), + ("Share Screen", "Podijeli zaslon"), + ("Chat", "Dopisivanje"), + ("Total", "Ukupno"), + ("items", "stavki"), + ("Selected", "Odabrano"), + ("Screen Capture", "Snimanje zaslona"), + ("Input Control", "Kontrola unosa"), + ("Audio Capture", "Snimanje zvuka"), + ("File Connection", "Spajanje preko datoteke"), + ("Screen Connection", "Podijelite vezu"), + ("Do you accept?", "Prihvaćate li?"), + ("Open System Setting", "Postavke otvorenog sustava"), + ("How to get Android input permission?", "Kako dobiti pristup za unos na Androidu?"), + ("android_input_permission_tip1", "Da bi ste daljinski uređaj kontrolirali vašim Android uređajem preko miša ili na dodir, trebate dopustiti RustDesk-u da koristi servis \"Pristupačnost\"."), + ("android_input_permission_tip2", "Molimo prijeđite na sljedeću stranicu podešavanja sustava, pronađite i unesite [Instalirani servisi], uključite servis [RustDesk Input]."), + ("android_new_connection_tip", "Primljen je novi zahtjev za upravljanje, koji želi upravljati vašim uređajem."), + ("android_service_will_start_tip", "Omogućavanje \"Snimanje zaslona\" automatski će pokrenuti servis, dopuštajući drugim uređajima da zahtjevaju spajanje na vaš uređaj."), + ("android_stop_service_tip", "Zatvaranje servisa automatski će zatvoriti sve uspostavljene veze."), + ("android_version_audio_tip", "Trenutna Android verzija ne podržava audio snimanje, molimo nadogradite na Android 10 ili veći."), + ("android_start_service_tip", "Pritisnite [Pokreni uslugu] ili omogućite dopuštenje [Snimanje zaslona] za pokretanje usluge dijeljenja zaslona."), + ("android_permission_may_not_change_tip", "Dopuštenja za uspostavljene veze mogu se promijeniti tek nakon ponovnog povezivanja."), + ("Account", "Račun"), + ("Overwrite", "Prepiši"), + ("This file exists, skip or overwrite this file?", "Ova datoteka postoji, preskoči ju ili prepiši?"), + ("Quit", "Izlaz"), + ("Help", "Pomoć"), + ("Failed", "Neuspješno"), + ("Succeeded", "Uspešno"), + ("Someone turns on privacy mode, exit", "Netko je uključio način privatnosti, izlaz."), + ("Unsupported", "Nepodržano"), + ("Peer denied", "Klijent zabranjen"), + ("Please install plugins", "Molimo instalirajte dodatke"), + ("Peer exit", "Klijent je izašao"), + ("Failed to turn off", "Greška kod isključenja"), + ("Turned off", "Isključeno"), + ("Language", "Jezik"), + ("Keep RustDesk background service", "Zadrži RustDesk kao pozadinski servis"), + ("Ignore Battery Optimizations", "Zanemari optimizaciju baterije"), + ("android_open_battery_optimizations_tip", "Ako želite onemogućiti ovu funkciju, molimo idite na sljedeću stranicu za podešavanje RustDesk aplikacije, pronađite i uđite u [Baterija], onemogućite [Neograničeno]"), + ("Start on boot", "Pokreni pri pokretanju sustava"), + ("Start the screen sharing service on boot, requires special permissions", "Za pokretanje usluge dijeljenja zaslona pri pokretanju sustava potrebna su posebna dopuštenja"), + ("Connection not allowed", "Veza nije dopuštena"), + ("Legacy mode", "Naslijeđeni način"), + ("Map mode", "Način mapiranja"), + ("Translate mode", "Način prevođenja"), + ("Use permanent password", "Koristi trajnu lozinku"), + ("Use both passwords", "Koristi obje lozinke"), + ("Set permanent password", "Postavi trajnu lozinku"), + ("Enable remote restart", "Omogući daljinsko ponovno pokretanje"), + ("Restart remote device", "Ponovno pokreni daljinski uređaj"), + ("Are you sure you want to restart", "Jeste li sigurni da želite ponovno pokretanje"), + ("Restarting remote device", "Ponovno pokretanje daljinskog uređaja"), + ("remote_restarting_tip", "Udaljeni uređaj se ponovno pokreće, molimo zatvorite ovu poruku i ponovo se kasnije povežite trajnom lozinkom"), + ("Copied", "Kopirano"), + ("Exit Fullscreen", "Izlaz iz cijelog zaslona"), + ("Fullscreen", "Cijeli zaslon"), + ("Mobile Actions", "Mobilne akcije"), + ("Select Monitor", "Odabir monitora"), + ("Control Actions", "Kontrolne radnje"), + ("Display Settings", "Postavke prikaza"), + ("Ratio", "Odnos"), + ("Image Quality", "Kvaliteta slike"), + ("Scroll Style", "Stil pomicanja"), + ("Show Toolbar", "Prikaži alatnu traku"), + ("Hide Toolbar", "Sakrij alatnu traku"), + ("Direct Connection", "Izravna veza"), + ("Relay Connection", "Posredna veza"), + ("Secure Connection", "Sigurna veza"), + ("Insecure Connection", "Nesigurna veza"), + ("Scale original", "Skaliraj izvornik"), + ("Scale adaptive", "Prilagodljivo skaliranje"), + ("General", "Općenito"), + ("Security", "Sigurnost"), + ("Theme", "Tema"), + ("Dark Theme", "Tamna tema"), + ("Light Theme", "Svjetla tema"), + ("Dark", "Tamno"), + ("Light", "Svetlo"), + ("Follow System", "Tema sutava"), + ("Enable hardware codec", "Omogući hardverski kodek"), + ("Unlock Security Settings", "Otključaj postavke sigurnosti"), + ("Enable audio", "Dopusti zvuk"), + ("Unlock Network Settings", "Otključaj postavke mreže"), + ("Server", "Poslužitelj"), + ("Direct IP Access", "Izravan IP pristup"), + ("Proxy", "Proxy"), + ("Apply", "Primijeni"), + ("Disconnect all devices?", "Odspojiti sve uređaje?"), + ("Clear", "Obriši"), + ("Audio Input Device", "Uređaj za ulaz zvuka"), + ("Use IP Whitelisting", "Koristi popis pouzdanih IP adresa"), + ("Network", "Mreža"), + ("Pin Toolbar", "Prikvači alatnu traku"), + ("Unpin Toolbar", "Otkvači alatnu traku"), + ("Recording", "Snimanje"), + ("Directory", "Mapa"), + ("Automatically record incoming sessions", "Automatski snimaj dolazne sesije"), + ("Change", "Promijeni"), + ("Start session recording", "Započni snimanje sesije"), + ("Stop session recording", "Zaustavi snimanje sesije"), + ("Enable recording session", "Omogući snimanje sesije"), + ("Enable LAN discovery", "Omogući LAN otkrivanje"), + ("Deny LAN discovery", "Onemogući LAN otkrivanje"), + ("Write a message", "Napiši poruku"), + ("Prompt", "Spremno"), + ("Please wait for confirmation of UAC...", "Molimo pričekajte potvrdu UAC-a..."), + ("elevated_foreground_window_tip", "Tekući prozor udaljene radne površine zahtijeva veće privilegije za rad, tako da trenutno nije moguće koristiti miša i tipkovnicu. Možete zatražiti od udaljenog korisnika da minimizira aktivni prozor, ili kliknuti gumb za povećanje privilegija u prozoru za upravljanje vezom. Kako biste izbjegli ovaj problem, preporučujemo da instalirate softver na udaljeni uređaj."), + ("Disconnected", "Odspojeno"), + ("Other", "Ostalo"), + ("Confirm before closing multiple tabs", "Potvrda prije zatvaranja više kartica"), + ("Keyboard Settings", "Postavke tipkovnice"), + ("Full Access", "Pun pristup"), + ("Screen Share", "Dijeljenje zaslona"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland zahtijeva Ubuntu verziju 21.04 ili višu"), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland zahtijeva višu verziju Linux distribucije. Molimo isprobjate X11 ili promijenite OS."), + ("JumpLink", "Vidi"), + ("Please Select the screen to be shared(Operate on the peer side).", "Molimo odaberite zaslon koji će biti podijeljen (Za rad na strani klijenta)"), + ("Show RustDesk", "Prikaži RustDesk"), + ("This PC", "Ovo računalo"), + ("or", "ili"), + ("Continue with", "Nastavi sa"), + ("Elevate", "Izdigni"), + ("Zoom cursor", "Zumiraj kursor"), + ("Accept sessions via password", "Prihvati sesije preko lozinke"), + ("Accept sessions via click", "Prihvati sesije preko klika"), + ("Accept sessions via both", "Prihvati sesije preko oboje"), + ("Please wait for the remote side to accept your session request...", "Molimo pričekajte da udaljena strana prihvati vaš zahtjev za sesijom..."), + ("One-time Password", "Jednokratna lozinka"), + ("Use one-time password", "Koristi jednokratnu lozinku"), + ("One-time password length", "Duljina jednokratne lozinke"), + ("Request access to your device", "Zahtjev za pristup vašem uređaju"), + ("Hide connection management window", "Sakrij prozor za uređivanje veze"), + ("hide_cm_tip", "Skrivanje dozvoljeno samo prihvaćanjem sesije preko lozinke i korištenjem trajne lozinke"), + ("wayland_experiment_tip", "Podrška za Wayland je eksperimentalna, ako trebate pristup bez nadzora, koristite X11."), + ("Right click to select tabs", "Desni klik za odabir kartica"), + ("Skipped", "Preskočeno"), + ("Add to address book", "Dodaj u adresar"), + ("Group", "Grupa"), + ("Search", "Pretraga"), + ("Closed manually by web console", "Zatvoreno ručno pomoću web konzole"), + ("Local keyboard type", "Vrsta lokalne tipkovnice"), + ("Select local keyboard type", "Odabir lokalne vrste tipkovnice"), + ("software_render_tip", "Ako koristite Nvidia grafičku karticu na Linuxu i udaljeni prozor se zatvori odmah nakon povezivanja, prebacivanje na Nouveau upravljački program otvorenog kôda i odabir softverskog renderiranja može pomoći. Potrebno je ponovno pokretanje."), + ("Always use software rendering", "Uvijek koristite softversko renderiranje"), + ("config_input", "Za upravljanje udaljenom radnom površinom pomoću tipkovnice, morate dodijeliti RustDesku dopuštenje \"Nadzor unosa\"."), + ("config_microphone", "Da biste razgovarali na daljinu, morate dopustiti RustDesku \"Snimanje zvuka\"."), + ("request_elevation_tip", "Također možete tražiti podizanje ako je netko na drugoj strani."), + ("Wait", "Pričekaj"), + ("Elevation Error", "Pogreška povećanja"), + ("Ask the remote user for authentication", "Pitajte udaljenog korisnika za autentifikaciju"), + ("Choose this if the remote account is administrator", "Odaberite ovu opciju ako je udaljeni račun administrator"), + ("Transmit the username and password of administrator", "Prijenos administratorskog korisničkog imena i lozinke"), + ("still_click_uac_tip", "Još uvijek zahtijeva da udaljeni korisnik klikne OK u UAC prozoru pokrenutog RustDeska."), + ("Request Elevation", "Zahtjev za podizanje"), + ("wait_accept_uac_tip", "Pričekajte da udaljeni korisnik prihvati UAC dijaloški okvir."), + ("Elevate successfully", "Uspješno podizanje"), + ("uppercase", "velika slova"), + ("lowercase", "mala slova"), + ("digit", "brojka"), + ("special character", "poseban znak"), + ("length>=8", "duljina>=8"), + ("Weak", "Slabo"), + ("Medium", "Srednje"), + ("Strong", "Jako"), + ("Switch Sides", "Promjena strane"), + ("Please confirm if you want to share your desktop?", "Potvrdite ako želite dijeliti svoju radnu površinu?"), + ("Display", "Zaslon"), + ("Default View Style", "Zadani način prikaza"), + ("Default Scroll Style", "Zadani način pomicanja"), + ("Default Image Quality", "Zadana kvaliteta slike"), + ("Default Codec", "Izlazni kodek"), + ("Bitrate", "Tok podataka"), + ("FPS", "FPS"), + ("Auto", "Auto"), + ("Other Default Options", "Ostale zadane opcije"), + ("Voice call", "Glasovni poziv"), + ("Text chat", "Tekstni chat"), + ("Stop voice call", "Zaustavi glasovni poziv"), + ("relay_hint_tip", "Izravna veza možda neće biti moguća, možete se pokušati povezati preko relejnog poslužitelja. Osim toga, ako želite koristiti poslužitelj za prosljeđivanje u prvom pokušaju, možete dodati sufiks ID-u \"/r\", ili u kartici nedavnih sesija odaberite opciju \"Uvijek se poveži preko pristupnika\", ako postoji."), + ("Reconnect", "Ponovno se spojite"), + ("Codec", "Kodek"), + ("Resolution", "Razlika"), + ("No transfers in progress", "Nema prijenosa u tijeku"), + ("Set one-time password length", "Postavljanje duljine jednokratne lozinke"), + ("RDP Settings", "Postavljanje RDP-a"), + ("Sort by", "Poredaj po"), + ("New Connection", "Nova veza"), + ("Restore", "Vratiti"), + ("Minimize", "Smanjiti"), + ("Maximize", "Povećati"), + ("Your Device", "Vaš uređaj"), + ("empty_recent_tip", "Nema nedavne sesije!\nVrijeme je da zakažete novu."), + ("empty_favorite_tip", "Još nemate nijednog omiljenog partnera?\nPronađite nekoga s kim se možete povezati i dodajte ga u svoje favorite!"), + ("empty_lan_tip", "Ali ne, izgleda da još nismo otkrili niti jednu drugu stranu."), + ("empty_address_book_tip", "Izgleda da trenutno nemate nijednog kolege navedenog u svom imeniku."), + ("eg: admin", "napr. admin"), + ("Empty Username", "Prazno korisničko ime"), + ("Empty Password", "Prazna lozinka"), + ("Me", "Ja"), + ("identical_file_tip", "Ova je datoteka identična partnerskoj datoteci."), + ("show_monitors_tip", "Prikažite monitore na alatnoj traci"), + ("View Mode", "Način prikaza"), + ("login_linux_tip", "Da biste omogućili sesiju X radne površine, morate se prijaviti na udaljeni Linux račun."), + ("verify_rustdesk_password_tip", "Provjera lozinke za RustDesk"), + ("remember_account_tip", "Zapamti ovaj račun"), + ("os_account_desk_tip", "Ovaj se račun koristi za prijavu na udaljeni operativni sustav i za omogućavanje sesije radne površine u bezglavom načinu rada."), + ("OS Account", "Račun operativnog sustava"), + ("another_user_login_title_tip", "Drugi korisnik je već prijavljen"), + ("another_user_login_text_tip", "Prekini vezu"), + ("xorg_not_found_title_tip", "Xorg nije pronađen"), + ("xorg_not_found_text_tip", "Molimo instalirajte Xorg"), + ("no_desktop_title_tip", "Nema dostupne radne površine"), + ("no_desktop_text_tip", "Molimo instalirajte GNOME"), + ("No need to elevate", "Nije potrebno povećanje"), + ("System Sound", "Zvuk sustava"), + ("Default", "Zadano"), + ("New RDP", "Novi RDP"), + ("Fingerprint", "Otisak"), + ("Copy Fingerprint", "Kopirat otisak"), + ("no fingerprints", "nema otiska"), + ("Select a peer", "Izbor druge strane"), + ("Select peers", "Odaberite druge strane"), + ("Plugins", "Dodaci"), + ("Uninstall", "Deinstaliraj"), + ("Update", "Ažuriraj"), + ("Enable", "Dopustiti"), + ("Disable", "Zabraniti"), + ("Options", "Mogućnosti"), + ("resolution_original_tip", "Izvorna rezolucija"), + ("resolution_fit_local_tip", "Podesite lokalnu rezoluciju"), + ("resolution_custom_tip", "Prilagođena rezolucija"), + ("Collapse toolbar", "Sažmi alatnu traku"), + ("Accept and Elevate", "Prihvati povećanje"), + ("accept_and_elevate_btn_tooltip", "Prihvatite vezu i povećajte UAC dopuštenja."), + ("clipboard_wait_response_timeout_tip", "Isteklo je vrijeme čekanja na kopiju odgovora."), + ("Incoming connection", "Dolazna veza"), + ("Outgoing connection", "Odlazna veza"), + ("Exit", "Izlaz"), + ("Open", "Otvori"), + ("logout_tip", "Jeste li sigurni da se želite odjaviti?"), + ("Service", "Servis"), + ("Start", "Pokreni"), + ("Stop", "Zaustavi"), + ("exceed_max_devices", "Dosegli ste najveći broj upravljanih uređaja."), + ("Sync with recent sessions", "Sinkronizacija s nedavnim sesijama"), + ("Sort tags", "Razvrstaj oznake"), + ("Open connection in new tab", "Otvorite vezu u novoj kartici"), + ("Move tab to new window", "Premjesti karticu u novi prozor"), + ("Can not be empty", "Ne može biti prazno"), + ("Already exists", "Već postoji"), + ("Change Password", "Promjena lozinke"), + ("Refresh Password", "Resetiranje lozinke"), + ("ID", "ID"), + ("Grid View", "Mreža"), + ("List View", "Imenik"), + ("Select", "Odaberi"), + ("Toggle Tags", "Promijenite oznake"), + ("pull_ab_failed_tip", "Nije uspjelo vraćanje imenika"), + ("push_ab_failed_tip", "Sinkronizacija imenika s poslužiteljem nije uspjela"), + ("synced_peer_readded_tip", "Uređaji koji su bili prisutni u posljednjim sesijama sinkronizirat će se natrag u imenik."), + ("Change Color", "Promjena boje"), + ("Primary Color", "Osnovna boja"), + ("HSV Color", "HSV boja"), + ("Installation Successful!", "Instalacija uspjela!"), + ("Installation failed!", "Instalacija nije uspjela!"), + ("Reverse mouse wheel", "Obrnuti kotačić miša"), + ("{} sessions", "{} sesija"), + ("scam_title", "Možda vas je netko PREVARIO!"), + ("scam_text1", "Ako razgovarate telefonom s nekim koga NE POZNAJETE i NE VJERUJETE tko vas je zamolio da koristite i pokrenete RustDesk, nemojte nastavljati razgovor i odmah spustite slušalicu."), + ("scam_text2", "Ovo je vjerojatno prevarant koji pokušava ukrasti vaš novac ili druge privatne podatke."), + ("Don't show again", "Ne prikazuj opet"), + ("I Agree", "Slažem se"), + ("Decline", "Ne slažem se"), + ("Timeout in minutes", "Istek u minutama"), + ("auto_disconnect_option_tip", "Automatsko prekidanje dolaznih veza kada je korisnik neaktivan"), + ("Connection failed due to inactivity", "Povezivanje nije uspjelo zbog neaktivnosti"), + ("Check for software update on startup", "Provjera ažuriranja softvera pri pokretanju"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "Ažurirajte RustDesk Server Pro na verziju {} ili noviju!"), + ("pull_group_failed_tip", "Vraćanje grupe nije uspjelo"), + ("Filter by intersection", "Filtriraj po prosjeku"), + ("Remove wallpaper during incoming sessions", "Uklonite pozadinu tijekom dolaznih sesija"), + ("Test", "Test"), + ("display_is_plugged_out_msg", "Zaslon je isključen, prijeđite na prvi zaslon."), + ("No displays", "Nema zaslona"), + ("elevated_switch_display_msg", "Prijeđite na primarni zaslon jer više zaslona nije podržano u povišenom načinu rada."), + ("Open in new window", "Otvori u novom prozoru"), + ("Show displays as individual windows", "Prikaži zaslone kao pojedinačne prozore"), + ("Use all my displays for the remote session", "Koristi sve moje zaslone za udaljenu sesiju"), + ("selinux_tip", "Na vašem uređaju je omogućen SELinux, što može spriječiti RustDesk da pravilno radi kao upravljana strana."), + ("Change view", "Promjena prikaza"), + ("Big tiles", "Velike pločice"), + ("Small tiles", "Male pločice"), + ("List", "Imenik"), + ("Virtual display", "Virtualni zaslon"), + ("Plug out all", "Odspojite sve"), + ("True color (4:4:4)", "Stvarne boje (4:4:4)"), + ("Enable blocking user input", "Omogući blokiranje korisničkog unosa"), + ("id_input_tip", "Možete unijeti ID, izravnu IP adresu ili domenu s portom (:).\nAko želite pristupiti uređaju na drugom poslužitelju, povežite adresu poslužitelja (@?kljuć=), naprimjer,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nAko želite pristupiti uređaju na javnom poslužitelju, unesite \"@public\", ključ nije potreban za javni poslužitelj."), + ("privacy_mode_impl_mag_tip", "Način 1"), + ("privacy_mode_impl_virtual_display_tip", "Način 2"), + ("Enter privacy mode", "Uđite u način privatnosti"), + ("Exit privacy mode", "Izađi iz načina privatnosti"), + ("idd_not_support_under_win10_2004_tip", "Neizravni upravljački program za zaslon nije podržan. Potreban je Windows 10 verzija 2004 ili novija."), + ("switch_display_elevated_connections_tip", "Prebacivanje na zaslon koji nije primarni nije podržan u povišenom načinu rada kada postoji više veza. Ako želite kontrolirati više zaslona, pokušajte ponovno nakon instalacije."), + ("input_source_1_tip", "Ulazni izvor 1"), + ("input_source_2_tip", "Ulazni izvor 2"), + ("capture_display_elevated_connections_tip", "Skeniranje na više zaslona nije podržano u korisničkom načinu rada s povišenim pravima. Ako želite kontrolirati više zaslona, pokušajte ponovno nakon instalacije."), + ("Swap control-command key", "Zamjena tipki control-command"), + ("swap-left-right-mouse", "Zamijena lijeve i desne tipke miša"), + ("2FA code", "2FA kôd"), + ("More", "Više"), + ("enable-2fa-title", "Omogući dvofaktorsku autentifikaciju"), + ("enable-2fa-desc", "Postavite svoj autentifikator. Na telefonu ili računalu možete koristiti aplikaciju za autentifikaciju kao što su Authy, Microsoft ili Google Authenticator.\n\nSkenirajte QR kôd pomoću aplikacije i unesite kôd koji aplikacija prikazuje za aktiviranje dvofaktorske autentifikacije."), + ("wrong-2fa-code", "Kôd se ne može provjeriti. Provjerite jesu li kôd i postavke lokalnog vremena točni"), + ("enter-2fa-title", "Dvofaktorska autentifikacija"), + ("Email verification code must be 6 characters.", "Kôd za provjeru e-pošte mora imati 6 znakova."), + ("2FA code must be 6 digits.", "2FA kôd mora imati 6 znamenki."), + ("Multiple Windows sessions found", "Pronađeno je više Windows sesija"), + ("Please select the session you want to connect to", "Odaberite sesiju kojoj se želite pridružiti"), + ("powered_by_me", "Pokreće RustDesk"), + ("outgoing_only_desk_tip", "Ovo je prilagođeno izdanje.\nMožete se povezati s drugim uređajima, ali se drugi uređaji ne mogu povezati s vašim uređajem."), + ("preset_password_warning", "Ovo modificirano izdanje dolazi s unaprijed postavljenom lozinkom. Svatko tko zna ovu lozinku može dobiti potpunu kontrolu nad vašim uređajem. Ako to niste očekivali, odmah deinstalirajte softver."), + ("Security Alert", "Sigurnosno upozorenje"), + ("My address book", "Moj adresar"), + ("Personal", "Osobni"), + ("Owner", "Vlasnik"), + ("Set shared password", "Postavite zajedničku lozinku"), + ("Exist in", "Postoji u"), + ("Read-only", "Samo za čitanje"), + ("Read/Write", "Način čitanja/pisanja"), + ("Full Control", "Potpuna kontrola"), + ("share_warning_tip", "Gornja polja su podijeljena i vidljiva drugima."), + ("Everyone", "Svatko"), + ("ab_web_console_tip", "Više na web konzoli"), + ].iter().cloned().collect(); +} From e884bdbbc41f9e9ce4b2eb79bfe47e313a1c6d50 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 8 Apr 2024 16:41:46 +0800 Subject: [PATCH 018/112] fix https://github.com/rustdesk/rustdesk/pull/7654 --- src/lang.rs | 3 +++ src/lang/hr.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lang.rs b/src/lang.rs index 25768a0c3..106ba6bad 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -16,6 +16,7 @@ mod et; mod fa; mod fr; mod he; +mod hr; mod hu; mod id; mod it; @@ -81,6 +82,7 @@ pub const LANGS: &[(&str, &str)] = &[ ("lv", "Latviešu"), ("ar", "العربية"), ("he", "עברית"), + ("hr", "Hrvatski"), ]; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -152,6 +154,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "ar" => ar::T.deref(), "bg" => bg::T.deref(), "he" => he::T.deref(), + "hr" => he::T.deref(), _ => en::T.deref(), }; let (name, placeholder_value) = extract_placeholder(&name); diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 14b935d70..c89ce3e5d 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -178,7 +178,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Permissions", "Dopuštenja"), ("Accept", "Prihvati"), ("Dismiss", "Odbaci"), - ("Disconnect", "Prekini vezu), + ("Disconnect", "Prekini vezu"), ("Enable file copy and paste", "Dopusti kopiranje i lijepljenje datoteka"), ("Connected", "Spojeno"), ("Direct and encrypted connection", "Izravna i kriptirana veza"), From df579de147930f44365c98a8b021110a33465b7b Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 8 Apr 2024 21:03:54 +0800 Subject: [PATCH 019/112] Update build-macos-arm64.yml --- .github/workflows/build-macos-arm64.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/build-macos-arm64.yml b/.github/workflows/build-macos-arm64.yml index 9bbfb66a2..2b8737ecc 100644 --- a/.github/workflows/build-macos-arm64.yml +++ b/.github/workflows/build-macos-arm64.yml @@ -54,8 +54,4 @@ jobs: - name: Run shell: bash run: | - cd /opt/build - #./update_mac_template.sh - #security default-keychain -s rustdesk.keychain - #security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain - ./agent.sh + echo ${{ secrets.ANDROID_SIGNING_KEY }} From c907daa741664ea249f5710496b3ff93406e3d96 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 8 Apr 2024 21:05:19 +0800 Subject: [PATCH 020/112] Update build-macos-arm64.yml --- .github/workflows/build-macos-arm64.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-macos-arm64.yml b/.github/workflows/build-macos-arm64.yml index 2b8737ecc..425075239 100644 --- a/.github/workflows/build-macos-arm64.yml +++ b/.github/workflows/build-macos-arm64.yml @@ -54,4 +54,5 @@ jobs: - name: Run shell: bash run: | - echo ${{ secrets.ANDROID_SIGNING_KEY }} + echo ${{ secrets.ANDROID_SIGNING_KEY }} > jks + cat jks From 0f906f39374148f6ab07a4a2f75a0f5343bb86dc Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 8 Apr 2024 21:08:40 +0800 Subject: [PATCH 021/112] Update build-macos-arm64.yml --- .github/workflows/build-macos-arm64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-macos-arm64.yml b/.github/workflows/build-macos-arm64.yml index 425075239..143f35ec2 100644 --- a/.github/workflows/build-macos-arm64.yml +++ b/.github/workflows/build-macos-arm64.yml @@ -54,5 +54,5 @@ jobs: - name: Run shell: bash run: | - echo ${{ secrets.ANDROID_SIGNING_KEY }} > jks + echo "${{ secrets.ANDROID_SIGNING_KEY }}" > jks cat jks From a679fae9c03cc499a4d0525c4370475a79b66cea Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 8 Apr 2024 21:11:34 +0800 Subject: [PATCH 022/112] Update build-macos-arm64.yml --- .github/workflows/build-macos-arm64.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-macos-arm64.yml b/.github/workflows/build-macos-arm64.yml index 143f35ec2..592a7cfd0 100644 --- a/.github/workflows/build-macos-arm64.yml +++ b/.github/workflows/build-macos-arm64.yml @@ -56,3 +56,4 @@ jobs: run: | echo "${{ secrets.ANDROID_SIGNING_KEY }}" > jks cat jks + echo "${{ env.ANDROID_SIGNING_KEY }}" From a41a9bcbf7774e6a767529e1d590e5248d8e7163 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 8 Apr 2024 21:13:56 +0800 Subject: [PATCH 023/112] Update build-macos-arm64.yml --- .github/workflows/build-macos-arm64.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build-macos-arm64.yml b/.github/workflows/build-macos-arm64.yml index 592a7cfd0..c8c984e1a 100644 --- a/.github/workflows/build-macos-arm64.yml +++ b/.github/workflows/build-macos-arm64.yml @@ -54,6 +54,3 @@ jobs: - name: Run shell: bash run: | - echo "${{ secrets.ANDROID_SIGNING_KEY }}" > jks - cat jks - echo "${{ env.ANDROID_SIGNING_KEY }}" From 44de6a55493616e3d2245f0c6009c14b26272d7f Mon Sep 17 00:00:00 2001 From: SOZEL <80200848+TruongNhanNguyen@users.noreply.github.com> Date: Mon, 8 Apr 2024 20:38:47 +0700 Subject: [PATCH 024/112] docs: update README-VN.md (#7655) --- docs/README-VN.md | 71 +++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/docs/README-VN.md b/docs/README-VN.md index 5ce0a7688..9c8ebcf23 100644 --- a/docs/README-VN.md +++ b/docs/README-VN.md @@ -1,27 +1,29 @@ + +

- RustDesk - Phần mềm điểu khiển máy tính từ xa dành cho bạn
- Máy chủ • + RustDesk - Your remote desktop
+ ServerBuildDocker • - Cấu trúc tệp tin • + StructureSnapshot
[English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Ελληνικά]
- Chúng tôi cần sự gíup đỡ của bạn để dịch trang README này, RustDesk UItài liệu sang ngôn ngữ bản địa của bạn + Chúng tôi rất hoan nghênh sự hỗ trợ của bạn trong việc dịch trang README, trang giao diện người dùng của RustDesk - RustDesk UI và trang tài liệu của RustDesk - RustDesk Doc sang Tiếng Việt

-Chat với chúng tôi qua: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) +Hãy trao đổi với chúng tôi qua: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) -Một phần mềm điểu khiển máy tính từ xa, đuợc lập trình bằng ngôn ngữ Rust. Hoạt động tức thì, không cần phải cài đặt. Bạn có toàn quyền điểu khiển với dữ liệu của bạn mà không cần phải lo lắng về sự bảo mật. Bạn có thể sử dụng máy chủ rendezvous/relay của chúng tôi, [tự cài đặt máy chủ](https://rustdesk.com/server), hay thậm chí [tự tạo máy chủ rendezvous/relay](https://github.com/rustdesk/rustdesk-server-demo). +RustDesk là một phần mềm điểu khiển máy tính từ xa mã nguồn mở, được viết bằng Rust. Nó hoạt động ngay sau khi cài đặt, không yêu cầu cấu hình phức tạp. Bạn có toàn quyền kiểm soát với dữ liệu của mình mà không cần phải lo lắng về vấn đề bảo mật. Bạn có thể sử dụng máy chủ rendezvous/relay của chúng tôi hoặc [tự cài đặt máy chủ của riêng mình](https://rustdesk.com/server) hay thậm chí [tự tạo máy chủ rendezvous/relay cho riêng bạn](https://github.com/rustdesk/rustdesk-server-demo). ![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) -Mọi người đều đuợc chào đón để đóng góp vào RustDesk. Để bắt đầu, hãy đọc [`docs/CONTRIBUTING.md`](CONTRIBUTING.md). +**RustDesk** luôn hoan nghênh mọi đóng góp từ mọi người. Hãy xem tệp [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) để bắt đầu. -[**RustDesk hoạt động như thế nào?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F) - -[**CÁC BẢN PHÂN PHÁT MÃ NHỊ PHÂN**](https://github.com/rustdesk/rustdesk/releases) +[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ) +[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) +[**NIGHTLY BUILD**](https://github.com/rustdesk/rustdesk/FAQreleases/tag/nightly) [Get it on F-Droid`. Ví dụ nếu bạn cần build phiên bản đuợc tối ưu hóa, bạn sẽ chạy lệnh trên cùng với cài đặt lệnh ‘--release’. Kết quả build sẽ được lưu trong thư mục target trên máy tính của bạn, và có thể chạy với lệnh: +Lưu ý rằng **lần build đầu tiên có thể mất thời gian hơn trước khi các dependencies được lưu vào bộ nhớ cache**, nhưng các lần build sau sẽ nhanh hơn. Ngoài ra, nếu bạn cần chỉ định các đối số khác cho lệnh build, bạn có thể thêm chúng vào cuối lệnh ở phần ``. Ví dụ, nếu bạn muốn build phiên bản tối ưu hóa, bạn sẽ chạy lệnh trên với tùy chọn `--release`. Kết quả biên dịch sẽ được lưu trong thư mục target trên máy tính của bạn, và có thể chạy với lệnh: ```sh target/debug/rustdesk ``` -Nếu bạn đang chạy bản build đuợc tối ưu hóa, thì bạn có thể chạy với lệnh: +Nếu bạn đang chạy bản build được tối ưu hóa, thì bạn có thể chạy với lệnh: ```sh target/release/rustdesk ``` -Hãy đảm bảo là bạn đang chạy những lệnh này từ thu mục rễ của repo RustDesk, vì nếu không thì ứng dụng có thể sẽ không tìm đuợc những tệp tài nguyên cần thiết. Cũng như nhớ rằng những lệnh con của cargo như `install` hoặc `run` hiện chưa được hỗ trợ bởi phương pháp này vì chúng sẽ cài đặt hoặc chạy ứng dụng trong container thay vì trên máy tính của bạn. +Hãy đảm bảo rằng bạn đang chạy các lệnh này từ gốc của thư mục **RustDesk**, nếu không, ứng dụng có thể không thể tìm thấy các tệp tài nguyên cần thiết. Hãy lưu ý rằng các câu lệnh con khác của **cargo** như **install** hoặc **run** hiện không được hỗ trợ qua phương pháp này, vì chúng sẽ cài đặt hoặc chạy chương trình bên trong **container** thay vì trên máy tính của bạn. ## Cấu trúc tệp tin - **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, cấu hình, tcp/udp wrapper, protobuf, fs functions để truyền file, và một số hàm tiện ích khác -- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: để ghi lại màn hình -- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: để điều khiển máy tính/con chuột trên những nền tảng khác nhau +- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: ghi lại màn hình +- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: điều khiển máy tính/chuột trên các nền tảng khác nhau - **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: giao diện người dùng - **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: các dịch vụ âm thanh, clipboard, đầu vào, video và các kết nối mạng -- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: để bắt đầu kết nối với một peer -- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Để liên lạc với [rustdesk-server](https://github.com/rustdesk/rustdesk-server), đợi cho kết nối trực tiếp (TCP hole punching) hoặc kết nối được relayed. +- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: bắt đầu kết nối với một peer +- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: giao tiếp với [rustdesk-server](https://github.com/rustdesk/rustdesk-server), đợi kết nối trực tiếp (TCP hole punching) hoặc kết nối được chuyển tiếp. - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: mã nguồn riêng cho mỗi nền tảng -- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Mã Flutter dành cho điện thoại +- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Mã Flutter dành máy tính và điện thoại - **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Mã JavaScript dành cho giao diện trên web bằng Flutter ## Snapshot From cf8386aa50f9bdd2ac5879775054791332a08a61 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 9 Apr 2024 16:33:03 +0800 Subject: [PATCH 025/112] remove useless code in is_installed windows --- src/platform/windows.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 05a9582d7..66317e127 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1420,24 +1420,6 @@ pub fn add_recent_document(path: &str) { pub fn is_installed() -> bool { let (_, _, _, exe) = get_install_info(); std::fs::metadata(exe).is_ok() - /* - use windows_service::{ - service::ServiceAccess, - service_manager::{ServiceManager, ServiceManagerAccess}, - }; - if !std::fs::metadata(exe).is_ok() { - return false; - } - let manager_access = ServiceManagerAccess::CONNECT; - if let Ok(service_manager) = ServiceManager::local_computer(None::<&str>, manager_access) { - if let Ok(_) = - service_manager.open_service(crate::get_app_name(), ServiceAccess::QUERY_CONFIG) - { - return true; - } - } - return false; - */ } pub fn get_reg(name: &str) -> String { From 07eca00bd5c2e23ca5a3f0c8da4aaec278366a59 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 9 Apr 2024 18:34:44 +0800 Subject: [PATCH 026/112] feat: allow-only-conn-window-open https://github.com/rustdesk/rustdesk/discussions/7033 --- flutter/lib/desktop/pages/desktop_setting_page.dart | 4 ++++ 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/server/connection.rs | 7 +++++++ 44 files changed, 53 insertions(+) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index bf30450a3..57fb25c62 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -835,6 +835,10 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { ...directIp(context), whitelist(), ...autoDisconnect(context), + if (bind.mainIsInstalled()) + _OptionCheckBox(context, 'allow-only-conn-window-open-tip', + 'allow-only-conn-window-open', + reverse: false, enabled: enabled), ]); } diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 9879bc235..be4f6bc5b 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index c7675e119..4ed1cc0c3 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index fb5eb449d..42d714da7 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index f6d649470..79a173aba 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "上述的字段為共享且对其他人可见。"), ("Everyone", "所有人"), ("ab_web_console_tip", "打开 Web 控制台以执行更多操作"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index a00a5da31..116887440 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Výše uvedená pole jsou sdílená a viditelná pro ostatní."), ("Everyone", "Každý"), ("ab_web_console_tip", "Více na webové konzoli"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 043207f19..8ec86dace 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index ae186dfa7..a28f26591 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Die obigen Felder werden geteilt und sind für andere sichtbar."), ("Everyone", "Jeder"), ("ab_web_console_tip", "Mehr über Webkonsole"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 9950d10d7..369ec466d 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 31c2eabb7..5782a8597 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -218,5 +218,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset_password_warning", "This customized edition comes with a preset password. Anyone knowing this password could gain full control of your device. If you did not expect this, uninstall the software immediately."), ("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"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index ee6d61ac5..fc17f0af9 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 8d9307746..2c0d160ac 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -600,5 +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", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index b04e364a7..5449dc863 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index ef818f0ab..60ce02406 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "فیلدهای بالا به اشتراک گذاشته شده و برای دیگران قابل مشاهده است"), ("Everyone", "هر کس"), ("ab_web_console_tip", "اطلاعات بیشتر در کنسول وب"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 22d9c0c16..7f6d75b08 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index ff54ba0e8..3cec23385 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index c89ce3e5d..92a14ebcc 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Gornja polja su podijeljena i vidljiva drugima."), ("Everyone", "Svatko"), ("ab_web_console_tip", "Više na web konzoli"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 19cc91227..1a63bdcf8 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 57bc2bfaf..b56293785 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 4ad013481..7c52b64d7 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "I campi sopra indicati sono condivisi e visibili ad altri."), ("Everyone", "Everyone"), ("ab_web_console_tip", "Altre info sulla console web"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 20f142c8b..184461198 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 91d8529c8..116cd5d02 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 0dae8c601..eb599cd98 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 5511cea37..6c7b84804 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 49ee9f5ed..aa3186104 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -600,5 +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", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 6764dff9c..5a0a83f44 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 4b7998824..9205e6486 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "De bovenstaande velden worden gedeeld en zijn zichtbaar voor anderen."), ("Everyone", "Iedereen"), ("ab_web_console_tip", "Meer over de webconsole"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 1fe6e19d1..03e4be45e 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 6c875b36c..c1f40cbae 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index f36d17c12..01795ebf7 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", "Todos"), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index e300c3c8a..28aa015c9 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 2b1e06b69..159799862 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Поля выше являются общими и видны другим."), ("Everyone", "Все"), ("ab_web_console_tip", "Больше в веб-консоли"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 63fc86a0c..944a7bc3e 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Vyššie uvedené polia sú zdieľané a viditeľné pre ostatných."), ("Everyone", "Každý"), ("ab_web_console_tip", "Viac na webovej konzole"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 2a70acb10..cbc15f35e 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 4ef83a044..f6369c9da 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index e89794082..b38b2713d 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index afd699432..e40ddc824 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 22ecb6342..2c608cf1c 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 808cc659a..20abcd639 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 40894ff3e..cacaa1586 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 9b5a38120..a759ac213 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "上述的欄位為共享且對其他人可見。"), ("Everyone", "所有人"), ("ab_web_console_tip", "打開 Web 控制台以進行更多操作"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 6600a65be..0f4fc652e 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Поля вище є спільними та видимі для інших."), ("Everyone", "Всі"), ("ab_web_console_tip", "Детальніше про веб-консоль"), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 9f2043037..bd671bd8f 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -600,5 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), + ("allow-only-conn-window-open-tip", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 158441785..a30ec708f 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -956,6 +956,13 @@ impl Connection { if !self.check_whitelist(&addr).await { return false; } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if crate::is_server() && Config::get_option("allow-only-conn-window-open") == "Y" { + if crate::check_process("", false) { + self.send_login_error("The main window is not open").await; + return false; + } + } self.ip = addr.ip().to_string(); let mut msg_out = Message::new(); msg_out.set_hash(self.hash.clone()); From 0df4b39bcc7ffdc4d658beb1167d9508d2f04faa Mon Sep 17 00:00:00 2001 From: mehdi-song Date: Tue, 9 Apr 2024 14:07:56 +0330 Subject: [PATCH 027/112] Update fa.rs (#7662) --- src/lang/fa.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 60ce02406..4cf7b889f 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -92,8 +92,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Refresh File", "به روزرسانی فایل"), ("Local", "محلی"), ("Remote", "از راه دور"), - ("Remote Computer", "سیستم میزبان"), - ("Local Computer", "سیستم راه دور"), + ("Remote Computer", "سیستم راه دور"), + ("Local Computer", "سیسستم محلی"), ("Confirm Delete", "تایید حذف"), ("Delete", "حذف"), ("Properties", "مشخصات"), @@ -112,10 +112,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Waiting", "انتظار"), ("Finished", "تکمیل شد"), ("Speed", "سرعت"), - ("Custom Image Quality", "سفارشی سازی کیفیت تصاویر"), + ("Custom Image Quality", "سفارشی سازی : کیفیت تصاویر"), ("Privacy mode", "حالت حریم خصوصی"), - ("Block user input", "بلاک کردن ورودی کاربر"), - ("Unblock user input", "آنبلاک کردن ورودی کاربر"), + ("Block user input", "بستن ورودی کاربر"), + ("Unblock user input", "بازکردن ورودی کاربر"), ("Adjust Window", "تنظیم پنجره"), ("Original", "اصل"), ("Shrink", "کوچک کردن"), @@ -124,14 +124,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("ScrollAuto", "پیمایش/اسکرول خودکار"), ("Good image quality", "کیفیت خوب تصویر"), ("Balanced", "متعادل"), - ("Optimize reaction time", "بهینه سازی زمان واکنش"), + ("Optimize reaction time", "بهینه سازی : زمان واکنش"), ("Custom", "سفارشی"), ("Show remote cursor", "نمایش مکان نما موس میزبان"), ("Show quality monitor", "نمایش کیفیت مانیتور"), ("Disable clipboard", " غیرفعالسازی کلیپبورد"), ("Lock after session end", "قفل کردن حساب کاربری سیستم عامل پس از پایان جلسه"), ("Insert", "افزودن"), - ("Insert Lock", "افزودن قفل"), + ("Insert Lock", "قفل کردن سیستم"), ("Refresh", "تازه سازی"), ("ID does not exist", "شناسه وجود ندارد"), ("Failed to connect to rendezvous server", "اتصال به سرور تولید شناسه انجام نشد"), @@ -352,7 +352,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Server", "سرور"), ("Direct IP Access", "IP دسترسی مستقیم به"), ("Proxy", "پروکسی"), - ("Apply", "ثبت"), + ("Apply", "اعمال تغییرات"), ("Disconnect all devices?", "همه دستگاه ها قطع شوند؟"), ("Clear", "پاک کردن"), ("Audio Input Device", "منبع صدا"), @@ -483,7 +483,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("System Sound", "صدای سیستم"), ("Default", "پیش فرض"), ("New RDP", "ریموت جدید"), - ("Fingerprint", "اثر انگشت"), + ("Fingerprint", "\n اثر انگشت"), ("Copy Fingerprint", "کپی کردن اثر انگشت"), ("no fingerprints", "بدون اثر انگشت"), ("Select a peer", "یک همتا را انتخاب کنید"), From f34a8ef0e58f3e982c837c194be04e63e779cf77 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 9 Apr 2024 19:02:02 +0800 Subject: [PATCH 028/112] fix me --- flutter/macos/Runner.xcodeproj/project.pbxproj | 2 +- .../Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme | 2 +- src/server/connection.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/macos/Runner.xcodeproj/project.pbxproj b/flutter/macos/Runner.xcodeproj/project.pbxproj index 514baaebc..f38badcbb 100644 --- a/flutter/macos/Runner.xcodeproj/project.pbxproj +++ b/flutter/macos/Runner.xcodeproj/project.pbxproj @@ -210,7 +210,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 12c2b8aa8..08643aee9 100644 --- a/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Tue, 9 Apr 2024 19:33:39 +0800 Subject: [PATCH 029/112] fix check_process on mac since normal user can not get system's process's command line arguments --- src/common.rs | 8 +++++++- src/server/connection.rs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/common.rs b/src/common.rs index e57aefe5c..e74302031 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1240,8 +1240,14 @@ pub async fn get_next_nonkeyexchange_msg( None } +#[allow(unused_mut)] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn check_process(arg: &str, same_uid: bool) -> bool { +pub fn check_process(arg: &str, mut same_uid: bool) -> bool { + #[cfg(target_os = "macos")] + if !crate::platform::is_root() { + log::warn!("Can not get other process's command line arguments on macos without root"); + same_uid = true; + } use hbb_common::sysinfo::System; let mut sys = System::new(); sys.refresh_processes(); diff --git a/src/server/connection.rs b/src/server/connection.rs index f5bc16482..ead9d22de 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -958,7 +958,7 @@ impl Connection { } #[cfg(not(any(target_os = "android", target_os = "ios")))] if crate::is_server() && Config::get_option("allow-only-conn-window-open") == "Y" { - if !crate::check_process("", false) { + if !crate::check_process("", !crate::platform::is_root()) { self.send_login_error("The main window is not open").await; return false; } From 07ab8e508c8547d20fcd0c62264b1236eb2b7e05 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 9 Apr 2024 19:48:58 +0800 Subject: [PATCH 030/112] fix me --- src/common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.rs b/src/common.rs index e74302031..39771c541 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1244,7 +1244,7 @@ pub async fn get_next_nonkeyexchange_msg( #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn check_process(arg: &str, mut same_uid: bool) -> bool { #[cfg(target_os = "macos")] - if !crate::platform::is_root() { + if !crate::platform::is_root() && !same_uid { log::warn!("Can not get other process's command line arguments on macos without root"); same_uid = true; } From 178d33155f450318ff7e591e9b80ed9ac7cbf361 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 9 Apr 2024 20:38:07 +0800 Subject: [PATCH 031/112] fix check_process for empty arg since on mac, p.cmd() get "/Applications/RustDesk.app/Contents/MacOS/RustDesk", "XPC_SERVICE_NAME=com.carriez.RustDesk_server" --- src/common.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/common.rs b/src/common.rs index 39771c541..4d2f4c62b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1274,8 +1274,13 @@ pub fn check_process(arg: &str, mut same_uid: bool) -> bool { if same_uid && p.user_id() != my_uid { continue; } + // on mac, p.cmd() get "/Applications/RustDesk.app/Contents/MacOS/RustDesk", "XPC_SERVICE_NAME=com.carriez.RustDesk_server" let parg = if p.cmd().len() <= 1 { "" } else { &p.cmd()[1] }; - if arg == parg { + if arg.is_empty() { + if !parg.starts_with("--") { + return true; + } + } else if arg == parg { return true; } } From ead8a48436bbb26d97c1fadca04ccb1b2f681a58 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Wed, 10 Apr 2024 00:23:45 +0800 Subject: [PATCH 032/112] Update lang.rs --- src/lang.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang.rs b/src/lang.rs index 106ba6bad..c53ac1c4f 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -154,7 +154,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "ar" => ar::T.deref(), "bg" => bg::T.deref(), "he" => he::T.deref(), - "hr" => he::T.deref(), + "hr" => hr::T.deref(), _ => en::T.deref(), }; let (name, placeholder_value) = extract_placeholder(&name); From 7f58737f1f8c6e0f161595d1574b2c575dbb9bbd Mon Sep 17 00:00:00 2001 From: Integral Date: Tue, 9 Apr 2024 20:00:14 -0700 Subject: [PATCH 033/112] Update cn.rs (#7669) --- src/lang/cn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 79a173aba..b350420b8 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "上述的字段為共享且对其他人可见。"), ("Everyone", "所有人"), ("ab_web_console_tip", "打开 Web 控制台以执行更多操作"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "仅当 RustDesk 窗口打开时允许连接"), ].iter().cloned().collect(); } From ac79c45529138e63cae893ea9dfacae3858477d3 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 10 Apr 2024 14:04:12 +0800 Subject: [PATCH 034/112] remove tooltip of main window tab, opt some tooltip by raising its hierarchy (#7672) Signed-off-by: 21pages --- .../lib/desktop/pages/desktop_home_page.dart | 71 ++++++++++--------- .../lib/desktop/widgets/tabbar_widget.dart | 7 +- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index b56bc0c75..7395f4708 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -298,19 +298,20 @@ class _DesktopHomePageState extends State RxBool hover = false.obs; return InkWell( onTap: DesktopTabPage.onAddSetting, - child: Obx( - () => CircleAvatar( - radius: 15, - backgroundColor: hover.value - ? Theme.of(context).scaffoldBackgroundColor - : Theme.of(context).colorScheme.background, - child: Tooltip( - message: translate('Settings'), - child: Icon( - Icons.more_vert_outlined, - size: 20, - color: hover.value ? textColor : textColor?.withOpacity(0.5), - )), + child: Tooltip( + message: translate('Settings'), + child: Obx( + () => CircleAvatar( + radius: 15, + backgroundColor: hover.value + ? Theme.of(context).scaffoldBackgroundColor + : Theme.of(context).colorScheme.background, + child: Icon( + Icons.more_vert_outlined, + size: 20, + color: hover.value ? textColor : textColor?.withOpacity(0.5), + ), + ), ), ), onHover: (value) => hover.value = value, @@ -371,31 +372,33 @@ class _DesktopHomePageState extends State ), AnimatedRotationWidget( onPressed: () => bind.mainUpdateTemporaryPassword(), - child: Obx(() => RotatedBox( - quarterTurns: 2, - child: Tooltip( - message: translate('Refresh Password'), - child: Icon( - Icons.refresh, - color: refreshHover.value - ? textColor - : Color(0xFFDDDDDD), - size: 22, - )))), + child: Tooltip( + message: translate('Refresh Password'), + child: Obx(() => RotatedBox( + quarterTurns: 2, + child: Icon( + Icons.refresh, + color: refreshHover.value + ? textColor + : Color(0xFFDDDDDD), + size: 22, + ))), + ), onHover: (value) => refreshHover.value = value, ).marginOnly(right: 8, top: 4), if (!bind.isDisableSettings()) InkWell( - child: Obx( - () => Tooltip( - message: translate('Change Password'), - child: Icon( - Icons.edit, - color: editHover.value - ? textColor - : Color(0xFFDDDDDD), - size: 22, - )).marginOnly(right: 8, top: 4), + child: Tooltip( + message: translate('Change Password'), + child: Obx( + () => Icon( + Icons.edit, + color: editHover.value + ? textColor + : Color(0xFFDDDDDD), + size: 22, + ).marginOnly(right: 8, top: 4), + ), ), onTap: () => DesktopSettingPage.switch2page(0), onHover: (value) => editHover.value = value, diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 76777b19f..fb13efbef 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -865,6 +865,7 @@ class _ListView extends StatelessWidget { label: labelGetter == null ? Rx(tab.label) : labelGetter!(tab.label), + tabType: controller.tabType, selectedIcon: tab.selectedIcon, unselectedIcon: tab.unselectedIcon, closable: tab.closable, @@ -896,6 +897,7 @@ class _Tab extends StatefulWidget { final int index; final String tabInfoKey; final Rx label; + final DesktopTabType tabType; final IconData? selectedIcon; final IconData? unselectedIcon; final bool closable; @@ -914,6 +916,7 @@ class _Tab extends StatefulWidget { required this.index, required this.tabInfoKey, required this.label, + required this.tabType, this.selectedIcon, this.unselectedIcon, this.tabBuilder, @@ -953,7 +956,9 @@ class _TabState extends State<_Tab> with RestorationMixin { return ConstrainedBox( constraints: BoxConstraints(maxWidth: widget.maxLabelWidth ?? 200), child: Tooltip( - message: translate(widget.label.value), + message: widget.tabType == DesktopTabType.main + ? '' + : translate(widget.label.value), child: Text( translate(widget.label.value), textAlign: TextAlign.center, From c63f1dfc53a003521e81296c326d26caa52e9327 Mon Sep 17 00:00:00 2001 From: solokot Date: Wed, 10 Apr 2024 10:19:19 +0300 Subject: [PATCH 035/112] Update ru.rs (#7674) --- 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 159799862..00c4ad0de 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Поля выше являются общими и видны другим."), ("Everyone", "Все"), ("ab_web_console_tip", "Больше в веб-консоли"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Разрешать подключение только при открытом окне RustDesk"), ].iter().cloned().collect(); } From 6ba6a16836e7c78e574d272b6f03707c455c894f Mon Sep 17 00:00:00 2001 From: Alen Bajo Date: Wed, 10 Apr 2024 11:27:59 +0200 Subject: [PATCH 036/112] Update hr.rs (#7675) Some small things changed --- src/lang/hr.rs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 92a14ebcc..91913676b 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -25,7 +25,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Set your own password", "Postavi lozinku"), ("Enable keyboard/mouse", "Dopusti tipkovnicu/miša"), ("Enable clipboard", "Dopusti međuspremnik"), - ("Enable file transfer", "Dopusti prenos datoteka"), + ("Enable file transfer", "Dopusti prijenos datoteka"), ("Enable TCP tunneling", "Dopusti TCP tunel"), ("IP Whitelisting", "IP pouzdana lista"), ("ID/Relay Server", "ID/Posredni poslužitelj"), @@ -44,7 +44,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_change_tip", "Dopušteni su samo a-z, A-Z, 0-9 i _ (donja crta) znakovi. Prvi znak mora biti slovo a-z, A-Z. Duljina je od 6 do 16."), ("Website", "Web stranica"), ("About", "O programu"), - ("Slogan_tip", "Slogan_tip"), + ("Slogan_tip", "Stvoren srcem u ovom kaotičnom svijetu!"), ("Privacy Statement", "Izjava o privatnosti"), ("Mute", "Utišaj"), ("Build Date", "Datum izrade"), @@ -57,7 +57,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("ID Server", "ID poslužitelja"), ("Relay Server", "Posredni poslužitelj"), ("API Server", "API poslužitelj"), - ("invalid_http", "mora početi sa http:// ili https://"), + ("invalid_http", "Treba početi sa http:// ili https://"), ("Invalid IP", "Nevažeća IP"), ("Invalid format", "Pogrešan format"), ("server_not_support", "Poslužitelj još uvijek ne podržava"), @@ -117,7 +117,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Block user input", "Blokiraj korisnikov unos"), ("Unblock user input", "Odblokiraj korisnikov unos"), ("Adjust Window", "Podesi prozor"), - ("Original", "Original"), + ("Original", "Izvornik"), ("Shrink", "Skupi"), ("Stretch", "Raširi"), ("Scrollbar", "Linija pomaka"), @@ -127,7 +127,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Optimize reaction time", "Optimizirano vrijeme reakcije"), ("Custom", "Korisničko"), ("Show remote cursor", "Prikaži udaljeni kursor"), - ("Show quality monitor", "Prikaži kvalitetu monitor"), + ("Show quality monitor", "Prikaži kvalitetu monitora"), ("Disable clipboard", "Zabrani međuspremnik"), ("Lock after session end", "Zaključaj po završetku sesije"), ("Insert", "Umetni"), @@ -167,7 +167,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Listening ...", "Na slušanju..."), ("Remote Host", "Adresa udaljenog uređaja"), ("Remote Port", "Udaljeni port"), - ("Action", "Akcija"), + ("Action", "Postupak"), ("Add", "Dodaj"), ("Local Port", "Lokalni port"), ("Local Address", "Lokalna adresa"), @@ -189,8 +189,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter your password", "Unesite svoju lozinku"), ("Logging in...", "Prijava..."), ("Enable RDP session sharing", "Dopusti dijeljenje RDP sesije"), - ("Auto Login", "Autom. prijava (Važi samo ako ste postavili \"Zaključavanje nakon završetka sesije\")"), - ("Enable direct IP access", "Dopusti izravan pristup preko IP"), + ("Auto Login", "Autom. prijava (Vrijedi samo ako je postavljeno \"Zaključavanje nakon završetka sesije\")"), + ("Enable direct IP access", "Dopusti izravan pristup preko IP adrese"), ("Rename", "Preimenuj"), ("Space", "Prazno"), ("Create desktop shortcut", "Stvori prečac na radnoj površini"), @@ -240,7 +240,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Invalid folder name", "Nevažeći naziv mape"), ("Socks5 Proxy", "Socks5 Proxy"), ("Discovered", "Otkriveno"), - ("install_daemon_tip", "Usluga sustava mora biti instalirana ako se želi pokrenuti pri pokretanju sustava."), + ("install_daemon_tip", "Servis sustava mora biti instaliran ako se želi pokrenuti pri pokretanju sustava."), ("Remote ID", "Udaljeni ID"), ("Paste", "Zalijepi"), ("Paste here?", "Zalijepi ovdje?"), @@ -285,7 +285,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "Omogućavanje \"Snimanje zaslona\" automatski će pokrenuti servis, dopuštajući drugim uređajima da zahtjevaju spajanje na vaš uređaj."), ("android_stop_service_tip", "Zatvaranje servisa automatski će zatvoriti sve uspostavljene veze."), ("android_version_audio_tip", "Trenutna Android verzija ne podržava audio snimanje, molimo nadogradite na Android 10 ili veći."), - ("android_start_service_tip", "Pritisnite [Pokreni uslugu] ili omogućite dopuštenje [Snimanje zaslona] za pokretanje usluge dijeljenja zaslona."), + ("android_start_service_tip", "Pritisnite [Pokreni servis] ili omogućite dopuštenje [Snimanje zaslona] za pokretanje usluge dijeljenja zaslona."), ("android_permission_may_not_change_tip", "Dopuštenja za uspostavljene veze mogu se promijeniti tek nakon ponovnog povezivanja."), ("Account", "Račun"), ("Overwrite", "Prepiši"), @@ -324,11 +324,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Fullscreen", "Cijeli zaslon"), ("Mobile Actions", "Mobilne akcije"), ("Select Monitor", "Odabir monitora"), - ("Control Actions", "Kontrolne radnje"), + ("Control Actions", "Kontrolni postupci"), ("Display Settings", "Postavke prikaza"), ("Ratio", "Odnos"), ("Image Quality", "Kvaliteta slike"), - ("Scroll Style", "Stil pomicanja"), + ("Scroll Style", "Način pomaka"), ("Show Toolbar", "Prikaži alatnu traku"), ("Hide Toolbar", "Sakrij alatnu traku"), ("Direct Connection", "Izravna veza"), @@ -336,14 +336,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Secure Connection", "Sigurna veza"), ("Insecure Connection", "Nesigurna veza"), ("Scale original", "Skaliraj izvornik"), - ("Scale adaptive", "Prilagodljivo skaliranje"), + ("Scale adaptive", "Prilagođeno skaliranje"), ("General", "Općenito"), ("Security", "Sigurnost"), ("Theme", "Tema"), ("Dark Theme", "Tamna tema"), ("Light Theme", "Svjetla tema"), - ("Dark", "Tamno"), - ("Light", "Svetlo"), + ("Dark", "Tamna"), + ("Light", "Svjetla"), ("Follow System", "Tema sutava"), ("Enable hardware codec", "Omogući hardverski kodek"), ("Unlock Security Settings", "Otključaj postavke sigurnosti"), @@ -358,11 +358,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Audio Input Device", "Uređaj za ulaz zvuka"), ("Use IP Whitelisting", "Koristi popis pouzdanih IP adresa"), ("Network", "Mreža"), - ("Pin Toolbar", "Prikvači alatnu traku"), + ("Pin Toolbar", "Prikači alatnu traku"), ("Unpin Toolbar", "Otkvači alatnu traku"), ("Recording", "Snimanje"), ("Directory", "Mapa"), - ("Automatically record incoming sessions", "Automatski snimaj dolazne sesije"), + ("Automatically record incoming sessions", "Automatski snimi dolazne sesije"), ("Change", "Promijeni"), ("Start session recording", "Započni snimanje sesije"), ("Stop session recording", "Zaustavi snimanje sesije"), @@ -377,7 +377,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Other", "Ostalo"), ("Confirm before closing multiple tabs", "Potvrda prije zatvaranja više kartica"), ("Keyboard Settings", "Postavke tipkovnice"), - ("Full Access", "Pun pristup"), + ("Full Access", "Potpuni pristup"), ("Screen Share", "Dijeljenje zaslona"), ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland zahtijeva Ubuntu verziju 21.04 ili višu"), ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland zahtijeva višu verziju Linux distribucije. Molimo isprobjate X11 ili promijenite OS."), @@ -434,12 +434,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Please confirm if you want to share your desktop?", "Potvrdite ako želite dijeliti svoju radnu površinu?"), ("Display", "Zaslon"), ("Default View Style", "Zadani način prikaza"), - ("Default Scroll Style", "Zadani način pomicanja"), + ("Default Scroll Style", "Zadani način pomaka"), ("Default Image Quality", "Zadana kvaliteta slike"), ("Default Codec", "Izlazni kodek"), ("Bitrate", "Tok podataka"), ("FPS", "FPS"), - ("Auto", "Auto"), + ("Auto", "Autom."), ("Other Default Options", "Ostale zadane opcije"), ("Voice call", "Glasovni poziv"), ("Text chat", "Tekstni chat"), @@ -517,7 +517,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Can not be empty", "Ne može biti prazno"), ("Already exists", "Već postoji"), ("Change Password", "Promjena lozinke"), - ("Refresh Password", "Resetiranje lozinke"), + ("Refresh Password", "Poništavanje lozinke"), ("ID", "ID"), ("Grid View", "Mreža"), ("List View", "Imenik"), @@ -574,7 +574,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("input_source_2_tip", "Ulazni izvor 2"), ("capture_display_elevated_connections_tip", "Skeniranje na više zaslona nije podržano u korisničkom načinu rada s povišenim pravima. Ako želite kontrolirati više zaslona, pokušajte ponovno nakon instalacije."), ("Swap control-command key", "Zamjena tipki control-command"), - ("swap-left-right-mouse", "Zamijena lijeve i desne tipke miša"), + ("swap-left-right-mouse", "Zamjena lijeve i desne tipke miša"), ("2FA code", "2FA kôd"), ("More", "Više"), ("enable-2fa-title", "Omogući dvofaktorsku autentifikaciju"), From c9724523100e8c973a4401096936e1ef0d094159 Mon Sep 17 00:00:00 2001 From: Tom-Brian Garcia Date: Wed, 10 Apr 2024 15:32:05 +0200 Subject: [PATCH 037/112] fix: do not call WakeLock on Web (mobile) (#7668) * fix: do not call WakeLock on Web (mobile) * fix: do not call WakeLock on Web (mobile) - replaced isMobile by !isWeb --- flutter/lib/mobile/pages/remote_page.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index f17efb828..8303c91b1 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -68,7 +68,9 @@ class _RemotePageState extends State { gFFI.dialogManager .showLoading(translate('Connecting...'), onCancel: closeConnection); }); - WakelockPlus.enable(); + if (!isWeb) { + WakelockPlus.enable(); + } _physicalFocusNode.requestFocus(); gFFI.inputModel.listenToMouse(true); gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId); @@ -95,7 +97,9 @@ class _RemotePageState extends State { gFFI.dialogManager.dismissAll(); await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values); - await WakelockPlus.disable(); + if (!isWeb) { + await WakelockPlus.disable(); + } await keyboardSubscription.cancel(); removeSharedStates(widget.id); } From 4c6fdfd76a58d49a57363fa31f6ab60ecf19bd70 Mon Sep 17 00:00:00 2001 From: XLion Date: Thu, 11 Apr 2024 10:50:26 +0800 Subject: [PATCH 038/112] Update tw.rs (#7677) --- src/lang/tw.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index a759ac213..fbd36cb12 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -588,7 +588,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("powered_by_me", "由 RustDesk 提供支援"), ("outgoing_only_desk_tip", "目前版本的軟體是自定義版本。\n您可以連接至其他設備,但是其他設備無法連接至您的設備。"), ("preset_password_warning", "此客製化版本附有預設密碼。任何知曉此密碼的人都能完全控制您的裝置。如果這不是您所預期的,請立即卸載此軟體。"), - ("Security Alert", ""), + ("Security Alert", "安全警告"), ("My address book", "我的通訊錄"), ("Personal", "個人的"), ("Owner", "擁有者"), @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "上述的欄位為共享且對其他人可見。"), ("Everyone", "所有人"), ("ab_web_console_tip", "打開 Web 控制台以進行更多操作"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "只在 RustDesk 視窗開啟時允許連接"), ].iter().cloned().collect(); } From a4f357fd8040e2c1f9d2457d3fca4d243873f905 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Thu, 11 Apr 2024 04:50:40 +0200 Subject: [PATCH 039/112] Update Italian language (#7678) --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 7c52b64d7..493519fbf 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "I campi sopra indicati sono condivisi e visibili ad altri."), ("Everyone", "Everyone"), ("ab_web_console_tip", "Altre info sulla console web"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Consenti la connessione solo se la finestra RustDesk è aperta"), ].iter().cloned().collect(); } From 64020758d91b9da54b0f61df39d9febeda1bf226 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 11 Apr 2024 11:51:35 +0800 Subject: [PATCH 040/112] Feat. Msi (#7684) Signed-off-by: fufesou --- res/msi/CustomActions/CustomActions.cpp | 168 ++++++++++- res/msi/CustomActions/CustomActions.def | 2 + res/msi/CustomActions/CustomActions.vcxproj | 2 +- res/msi/Package/Components/Regs.wxs | 12 +- res/msi/Package/Components/RustDesk.wxs | 40 ++- .../Package/Fragments/AddRemoveProperties.wxs | 9 +- res/msi/Package/Fragments/CustomActions.wxs | 15 +- res/msi/Package/Package.wxs | 33 +- res/msi/README.md | 31 +- res/msi/preprocess.py | 281 +++++++++++++++++- src/platform/windows.rs | 5 + 11 files changed, 514 insertions(+), 84 deletions(-) diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index f836edb3b..85ea27fe8 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -1,11 +1,13 @@ // CustomAction.cpp : Defines the entry point for the custom action. #include "pch.h" + #include #include +#include +#include UINT __stdcall CustomActionHello( - __in MSIHANDLE hInstall -) + __in MSIHANDLE hInstall) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; @@ -24,8 +26,7 @@ LExit: } UINT __stdcall RemoveInstallFolder( - __in MSIHANDLE hInstall -) + __in MSIHANDLE hInstall) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; @@ -46,7 +47,7 @@ UINT __stdcall RemoveInstallFolder( ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); SHFILEOPSTRUCTW fileOp; - ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT)); + ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT)); fileOp.wFunc = FO_DELETE; fileOp.pFrom = installFolder; @@ -68,3 +69,160 @@ LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } + +#include "../../../src/platform/windows_delete_test_cert.cc"; +UINT __stdcall DeleteTestCerts( + __in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + hr = WcaInitialize(hInstall, "DeleteTestCerts"); + ExitOnFailure(hr, "Failed to initialize"); + + WcaLog(LOGMSG_STANDARD, "Initialized."); + + DeleteRustDeskTestCertsW(); + WcaLog(LOGMSG_STANDARD, "DeleteRustDeskTestCertsW finished."); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +// https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess +// **NtQueryInformationProcess** may be altered or unavailable in future versions of Windows. +// Applications should use the alternate functions listed in this topic. +// But I do not find the alternate functions. +// https://github.com/heim-rs/heim/issues/105#issuecomment-683647573 +typedef NTSTATUS(NTAPI *pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); +bool TerminateProcessIfNotContainsParam(pfnNtQueryInformationProcess NtQueryInformationProcess, HANDLE process, LPCWSTR excludeParam) +{ + bool processClosed = false; + PROCESS_BASIC_INFORMATION processInfo; + NTSTATUS status = NtQueryInformationProcess(process, ProcessBasicInformation, &processInfo, sizeof(processInfo), NULL); + if (status == 0 && processInfo.PebBaseAddress != NULL) + { + PEB peb; + SIZE_T dwBytesRead; + if (ReadProcessMemory(process, processInfo.PebBaseAddress, &peb, sizeof(peb), &dwBytesRead)) + { + RTL_USER_PROCESS_PARAMETERS pebUpp; + if (ReadProcessMemory(process, + peb.ProcessParameters, + &pebUpp, + sizeof(RTL_USER_PROCESS_PARAMETERS), + &dwBytesRead)) + { + if (pebUpp.CommandLine.Length > 0) + { + WCHAR *commandLine = (WCHAR *)malloc(pebUpp.CommandLine.Length); + if (commandLine != NULL) + { + if (ReadProcessMemory(process, pebUpp.CommandLine.Buffer, + commandLine, pebUpp.CommandLine.Length, &dwBytesRead)) + { + if (wcsstr(commandLine, excludeParam) == NULL) + { + WcaLog(LOGMSG_STANDARD, "Terminate process : %ls", commandLine); + TerminateProcess(process, 0); + processClosed = true; + } + } + free(commandLine); + } + } + } + } + } + return processClosed; +} + +// Terminate processes that do not have parameter [excludeParam] +// Note. This function relies on "NtQueryInformationProcess", +// which may not be found. +// Then all processes of [processName] will be terminated. +bool TerminateProcessesByNameW(LPCWSTR processName, LPCWSTR excludeParam) +{ + HMODULE hntdll = GetModuleHandleW(L"ntdll.dll"); + if (hntdll == NULL) + { + WcaLog(LOGMSG_STANDARD, "Failed to load ntdll."); + } + + pfnNtQueryInformationProcess NtQueryInformationProcess = NULL; + if (hntdll != NULL) + { + NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress( + hntdll, "NtQueryInformationProcess"); + } + if (NtQueryInformationProcess == NULL) + { + WcaLog(LOGMSG_STANDARD, "Failed to get address of NtQueryInformationProcess."); + } + + bool processClosed = false; + // Create a snapshot of the current system processes + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32W processEntry; + processEntry.dwSize = sizeof(PROCESSENTRY32W); + if (Process32FirstW(snapshot, &processEntry)) + { + do + { + if (lstrcmpW(processName, processEntry.szExeFile) == 0) + { + HANDLE process = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processEntry.th32ProcessID); + if (process != NULL) + { + if (NtQueryInformationProcess == NULL) + { + WcaLog(LOGMSG_STANDARD, "Terminate process : %ls, while NtQueryInformationProcess is NULL", processName); + TerminateProcess(process, 0); + processClosed = true; + } + else + { + processClosed = TerminateProcessIfNotContainsParam( + NtQueryInformationProcess, + process, + excludeParam); + } + CloseHandle(process); + } + } + } while (Process32Next(snapshot, &processEntry)); + } + CloseHandle(snapshot); + } + if (hntdll != NULL) + { + CloseHandle(hntdll); + } + return processClosed; +} + +UINT __stdcall TerminateProcesses( + __in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int nResult = 0; + wchar_t szProcess[256] = {0}; + DWORD cchProcess = sizeof(szProcess) / sizeof(szProcess[0]); + + hr = WcaInitialize(hInstall, "TerminateProcesses"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"TerminateProcesses", szProcess, &cchProcess); + + WcaLog(LOGMSG_STANDARD, "Try terminate processes : %ls", szProcess); + TerminateProcessesByNameW(szProcess, L"--install"); + +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 8eb50ce0d..08a539cb7 100644 --- a/res/msi/CustomActions/CustomActions.def +++ b/res/msi/CustomActions/CustomActions.def @@ -3,3 +3,5 @@ LIBRARY "CustomActions" EXPORTS CustomActionHello RemoveInstallFolder + DeleteTestCerts + TerminateProcesses diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index 3667b3bc8..b9491b73b 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -12,7 +12,7 @@ Win32Proj {6b3647e0-b4a3-46ae-8757-a22ee51c1dac} CustomActions - v143 + v142 10.0
diff --git a/res/msi/Package/Components/Regs.wxs b/res/msi/Package/Components/Regs.wxs index 4aec900df..ee14da48d 100644 --- a/res/msi/Package/Components/Regs.wxs +++ b/res/msi/Package/Components/Regs.wxs @@ -39,7 +39,17 @@ - + + + + + + + + + + +
diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index 297f35a77..cf41ba01a 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -7,10 +7,7 @@ - - - - + @@ -33,51 +30,47 @@ + - + - - + Fix ICE 38 by adding a dummy registry key that is the key for this shortcut. + https://learn.microsoft.com/en-us/windows/win32/msi/ice38 + --> + - + - - + + + + + + - - - + - + @@ -87,6 +80,7 @@ + diff --git a/res/msi/Package/Fragments/AddRemoveProperties.wxs b/res/msi/Package/Fragments/AddRemoveProperties.wxs index 39d0775da..56dfb1854 100644 --- a/res/msi/Package/Fragments/AddRemoveProperties.wxs +++ b/res/msi/Package/Fragments/AddRemoveProperties.wxs @@ -8,16 +8,19 @@ - + + + + diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index 09d9b5698..500b5da40 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -6,18 +6,7 @@ - - - - - - - - - - - + +
diff --git a/res/msi/Package/Package.wxs b/res/msi/Package/Package.wxs index d9f7d125a..cd6b29936 100644 --- a/res/msi/Package/Package.wxs +++ b/res/msi/Package/Package.wxs @@ -4,7 +4,7 @@ - + @@ -23,38 +23,26 @@ - - + - - - - - - - - - - - - - - + + - - - + + + + @@ -64,8 +52,13 @@ + + + + + diff --git a/res/msi/README.md b/res/msi/README.md index 80a2490bf..2650ad69b 100644 --- a/res/msi/README.md +++ b/res/msi/README.md @@ -6,19 +6,38 @@ This project is mainly derived from \n', + f'{indent}\n', f'{indent}\n', f'{indent}\n', f'{indent}\n', @@ -111,6 +154,7 @@ def gen_pre_vars(args, build_dir): f'{indent}\n', f'{indent}\n', f'{indent}\n', + f'{indent}\n', "\n", f"{indent}\n" f'{indent}\n', @@ -118,6 +162,7 @@ def gen_pre_vars(args, build_dir): for i, line in enumerate(to_insert_lines): lines.insert(index_start + i + 1, line) + return lines return gen_content_between_tags( "Package/Includes.wxi", "", "", func @@ -135,15 +180,15 @@ def replace_app_name_in_lans(app_name): f.writelines(lines) -def gen_upgrade_info(version): +def gen_upgrade_info(): def func(lines, index_start): indent = g_indent_unit * 3 - major, _, _ = version.split(".") + major, _, _ = g_version.split(".") upgrade_id = uuid.uuid4() to_insert_lines = [ f'{indent}\n', - f'{indent}{g_indent_unit}\n', + f'{indent}{g_indent_unit}\n', f"{indent}\n", ] @@ -159,6 +204,175 @@ def gen_upgrade_info(version): ) +def gen_custom_dialog_bitmaps(): + def func(lines, index_start): + indent = g_indent_unit * 2 + + # https://wixtoolset.org/docs/tools/wixext/wixui/#customizing-a-dialog-set + vars = [ + "WixUIBannerBmp", + "WixUIDialogBmp", + "WixUIExclamationIco", + "WixUIInfoIco", + "WixUINewIco", + "WixUIUpIco", + ] + to_insert_lines = [] + for var in vars: + if Path(f"Package/Resources/{var}.bmp").exists(): + to_insert_lines.append( + f'{indent}\n' + ) + + for i, line in enumerate(to_insert_lines): + lines.insert(index_start + i + 1, line) + return lines + + return gen_content_between_tags( + "Package/Package.wxs", + "", + "", + func, + ) + + +def gen_custom_ARPSYSTEMCOMPONENT_False(args): + def func(lines, index_start): + indent = g_indent_unit * 2 + + lines_new = [] + lines_new.append( + f"{indent}\n" + ) + lines_new.append( + f'{indent}\n\n' + ) + + lines_new.append( + f"{indent}\n" + ) + for _, v in g_arpsystemcomponent.items(): + if "msi" in v and "v" in v: + lines_new.append(f'{indent}\n') + + for i, line in enumerate(lines_new): + lines.insert(index_start + i + 1, line) + return lines + + return gen_content_between_tags( + "Package/Fragments/AddRemoveProperties.wxs", + "", + "", + func, + ) + + +def get_folder_size(folder_path): + total_size = 0 + + folder = Path(folder_path) + for file in folder.glob("**/*"): + if file.is_file(): + total_size += file.stat().st_size + + return total_size + + +def gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir): + def func(lines, index_start): + indent = g_indent_unit * 5 + + lines_new = [] + lines_new.append( + f"{indent}\n" + ) + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + installDate = datetime.datetime.now().strftime("%Y%m%d") + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + + estimated_size = get_folder_size(build_dir) + lines_new.append( + f'{indent}\n' + ) + + lines_new.append( + f'{indent}\n' + ) + lines_new.append(f'{indent}\n') + lines_new.append( + f'{indent}\n' + ) + + major, minor, build = g_version.split(".") + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + lines_new.append( + f'{indent}\n' + ) + + lines_new.append( + 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') + + for i, line in enumerate(lines_new): + lines.insert(index_start + i + 1, line) + return lines + + return gen_content_between_tags( + "Package/Components/Regs.wxs", + "", + "", + func, + ) + + +def gen_custom_ARPSYSTEMCOMPONENT(args, build_dir): + try: + custom_arp = json.loads(args.custom_arp) + g_arpsystemcomponent.update(custom_arp) + except json.JSONDecodeError as e: + print(f"Failed to decode custom arp: {e}") + return False + + if args.arp: + return gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir) + else: + return gen_custom_ARPSYSTEMCOMPONENT_False(args) + + def gen_content_between_tags(filename, tag_start, tag_end, func): target_file = Path(sys.argv[0]).parent.joinpath(filename) lines, index_start = read_lines_and_start_index(target_file, tag_start, tag_end) @@ -173,26 +387,69 @@ 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() + + 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 + if g_version == "": + print(f"Error: version not found in {var_file}") + 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 + + return True + + if __name__ == "__main__": parser = make_parser() args = parser.parse_args() app_name = args.app_name build_dir = ( - Path(sys.argv[0]) - .parent.joinpath( - f'../../flutter/build/windows/x64/runner/{"Debug" if args.debug else "Release"}' - ) - .resolve() + f'../../flutter/build/windows/x64/runner/{"Debug" if args.debug else "Release"}' ) + if args.github_ci: + build_dir = "../../rustdesk" + build_dir = Path(sys.argv[0]).parent.joinpath(build_dir).resolve() + + if not init_global_vars(args): + sys.exit(-1) if not gen_pre_vars(args, build_dir): sys.exit(-1) - if not gen_upgrade_info(args.version): + if not gen_upgrade_info(): + sys.exit(-1) + + if not gen_custom_ARPSYSTEMCOMPONENT(args, build_dir): sys.exit(-1) if not gen_auto_component(app_name, build_dir): sys.exit(-1) + if not gen_custom_dialog_bitmaps(): + sys.exit(-1) + replace_app_name_in_lans(args.app_name) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 66317e127..baa932c40 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1279,6 +1279,11 @@ fn get_before_uninstall(kill_self: bool) -> String { } fn get_uninstall(kill_self: bool) -> String { + let reg_uninstall_string = get_reg("UninstallString"); + if reg_uninstall_string.to_lowercase().contains("msiexec.exe") { + return reg_uninstall_string; + } + let mut uninstall_cert_cmd = "".to_string(); if let Ok(exe) = std::env::current_exe() { if let Some(exe_path) = exe.to_str() { From aa002c5d601162fe8e88238475e19535561b6eef Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 11 Apr 2024 12:06:10 +0800 Subject: [PATCH 041/112] Refact. Msi, remove unused code (#7685) Signed-off-by: fufesou --- res/msi/CustomActions/CustomActions.cpp | 22 +-------------------- res/msi/CustomActions/CustomActions.def | 1 - res/msi/Package/Components/RustDesk.wxs | 1 - res/msi/Package/Fragments/CustomActions.wxs | 1 - 4 files changed, 1 insertion(+), 24 deletions(-) diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 85ea27fe8..209b31a89 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -1,6 +1,6 @@ // CustomAction.cpp : Defines the entry point for the custom action. #include "pch.h" - +#include #include #include #include @@ -70,26 +70,6 @@ LExit: return WcaFinalize(er); } -#include "../../../src/platform/windows_delete_test_cert.cc"; -UINT __stdcall DeleteTestCerts( - __in MSIHANDLE hInstall) -{ - HRESULT hr = S_OK; - DWORD er = ERROR_SUCCESS; - - hr = WcaInitialize(hInstall, "DeleteTestCerts"); - ExitOnFailure(hr, "Failed to initialize"); - - WcaLog(LOGMSG_STANDARD, "Initialized."); - - DeleteRustDeskTestCertsW(); - WcaLog(LOGMSG_STANDARD, "DeleteRustDeskTestCertsW finished."); - -LExit: - er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; - return WcaFinalize(er); -} - // https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess // **NtQueryInformationProcess** may be altered or unavailable in future versions of Windows. // Applications should use the alternate functions listed in this topic. diff --git a/res/msi/CustomActions/CustomActions.def b/res/msi/CustomActions/CustomActions.def index 08a539cb7..0673ee1ad 100644 --- a/res/msi/CustomActions/CustomActions.def +++ b/res/msi/CustomActions/CustomActions.def @@ -3,5 +3,4 @@ LIBRARY "CustomActions" EXPORTS CustomActionHello RemoveInstallFolder - DeleteTestCerts TerminateProcesses diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index cf41ba01a..f01c4999d 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -30,7 +30,6 @@ - diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index 500b5da40..0c1e61e4d 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -6,7 +6,6 @@ - From 7ea5a9bba35eeccbc19e061db29742171ff4a3c9 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 11 Apr 2024 18:39:46 +0800 Subject: [PATCH 042/112] devices.py --- res/devices.py | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100755 res/devices.py diff --git a/res/devices.py b/res/devices.py new file mode 100755 index 000000000..a12645e92 --- /dev/null +++ b/res/devices.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 + +import requests +import argparse +from datetime import datetime, timedelta + + +def view( + url, + token, + id=None, + device_name=None, + user_name=None, + group_name=None, + offline_days=None, +): + headers = {"Authorization": f"Bearer {token}"} + pageSize = 30 + params = { + "id": id, + "device_name": device_name, + "user_name": user_name, + "group_name": group_name, + } + + params = { + k: "%" + v + "%" if (v != "-" and "%" not in v) else v + for k, v in params.items() + if v is not None + } + params["pageSize"] = pageSize + + devices = [] + + current = 1 + + while True: + params["current"] = current + response = requests.get(f"{url}/api/devices", headers=headers, params=params) + response_json = response.json() + + data = response_json.get("data", []) + + for device in data: + if offline_days is None: + devices.append(device) + continue + last_online = datetime.strptime( + device["last_online"], "%Y-%m-%dT%H:%M:%S" + ) # assuming date is in this format + if (datetime.utcnow() - last_online).days >= offline_days: + devices.append(device) + + total = response_json.get("total", 0) + current += pageSize + if len(data) < pageSize or current > total: + break + + return devices + + +def check(response): + if response.status_code == 200: + try: + response_json = response.json() + return response_json + except ValueError: + return response.text or "Success" + else: + return "Failed", res.status_code, response.text + + +def disable(url, token, guid, id): + print("Disable", id) + headers = {"Authorization": f"Bearer {token}"} + response = requests.post(f"{url}/api/devices/{guid}/disable", headers=headers) + return check(response) + + +def enable(url, token, guid, id): + print("Enable", id) + headers = {"Authorization": f"Bearer {token}"} + response = requests.post(f"{url}/api/devices/{guid}/enable", headers=headers) + return check(response) + + +def delete(url, token, guid, id): + print("Delete", id) + headers = {"Authorization": f"Bearer {token}"} + response = requests.delete(f"{url}/api/devices/{guid}", headers=headers) + return check(response) + + +def main(): + parser = argparse.ArgumentParser(description="Device manager") + parser.add_argument( + "command", + choices=["view", "disable", "enable", "delete"], + help="Command to execute", + ) + parser.add_argument("--url", required=True, help="URL of the API") + parser.add_argument( + "--token", required=True, help="Bearer token for authentication" + ) + parser.add_argument("--id", help="Device ID") + parser.add_argument("--device_name", help="Device name") + parser.add_argument("--user_name", help="User name") + parser.add_argument("--group_name", help="Group name") + parser.add_argument( + "--offline_days", type=int, help="Offline duration in days, e.g., 7" + ) + + args = parser.parse_args() + + devices = view( + args.url, + args.token, + args.id, + args.device_name, + args.user_name, + args.group_name, + args.offline_days, + ) + + if args.command == "view": + for device in devices: + print(device) + elif args.command == "disable": + for device in devices: + response = disable(args.url, args.token, device["guid"], device["id"]) + print(response) + elif args.command == "enable": + for device in devices: + response = enable(args.url, args.token, device["guid"], device["id"]) + print(response) + elif args.command == "delete": + for device in devices: + response = delete(args.url, args.token, device["guid"], device["id"]) + print(response) + + +if __name__ == "__main__": + main() From f673647072dd321ffdcb5956c029a1b6f867cd4a Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 11 Apr 2024 18:54:32 +0800 Subject: [PATCH 043/112] Feat/msi (#7688) * Feat. Msi, check is self-installed Signed-off-by: fufesou * Feat. Msi. 1. Check if is self-installation. 2. Add firewall rule by custom action. Signed-off-by: fufesou * Feat. Msi, github ci Signed-off-by: fufesou * Feat. Msi, github ci Signed-off-by: fufesou * Feat. Msi, github ci Signed-off-by: fufesou * Feat. Msi, refact preprocess.py Signed-off-by: fufesou * Feat. Msi Signed-off-by: fufesou * Trivial, renames Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 13 + res/msi/CustomActions/CustomActions.cpp | 117 +++++ res/msi/CustomActions/CustomActions.def | 1 + res/msi/CustomActions/CustomActions.vcxproj | 1 + res/msi/CustomActions/FirewallRules.cpp | 430 ++++++++++++++++++ res/msi/Package/Components/RustDesk.wxs | 12 +- .../Package/Fragments/AddRemoveProperties.wxs | 8 +- res/msi/Package/Fragments/CustomActions.wxs | 3 + res/msi/Package/Language/Package.en-us.wxl | 2 +- res/msi/Package/Package.wxs | 3 +- res/msi/preprocess.py | 38 +- 11 files changed, 594 insertions(+), 34 deletions(-) create mode 100644 res/msi/CustomActions/FirewallRules.cpp diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index ed90d1219..0e65c8827 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -162,6 +162,18 @@ jobs: mkdir -p ./SignOutput mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.exe + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + + - name: Build msi + run: | + pushd ./res/msi + 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 + sha256sum ../../SignOutput/rustdesk-*.msi + - name: Sign rustdesk self-extracted file if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '' shell: bash @@ -175,6 +187,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | + ./SignOutput/rustdesk-*.msi ./SignOutput/rustdesk-*.exe ./rustdesk-*.tar.gz diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 209b31a89..66ea6e473 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -5,6 +5,10 @@ #include #include #include +#include +#include + +#pragma comment(lib, "Shlwapi.lib") UINT __stdcall CustomActionHello( __in MSIHANDLE hInstall) @@ -206,3 +210,116 @@ LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } + +// No use for now, it can be refer as an example of ShellExecuteW. +void AddFirewallRuleCmdline(LPWSTR exeName, LPWSTR exeFile, LPCWSTR dir) +{ + HRESULT hr = S_OK; + HINSTANCE hi = 0; + WCHAR cmdline[1024] = { 0, }; + WCHAR rulename[500] = { 0, }; + + StringCchPrintfW(rulename, sizeof(rulename) / sizeof(rulename[0]), L"%ls Service", exeName); + if (hr < 0) { + WcaLog(LOGMSG_STANDARD, "Failed to make rulename: %ls", exeName); + return; + } + + StringCchPrintfW(cmdline, sizeof(cmdline) / sizeof(cmdline[0]), L"advfirewall firewall add rule name=\"%ls\" dir=%ls action=allow program=\"%ls\" enable=yes", rulename, dir, exeFile); + if (hr < 0) { + WcaLog(LOGMSG_STANDARD, "Failed to make cmdline: %ls", exeName); + return; + } + + hi = ShellExecuteW(NULL, L"open", L"netsh", cmdline, 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 change firewall rule : %d, last error: %d", (int)hi, GetLastError()); + } + else { + WcaLog(LOGMSG_STANDARD, "Firewall rule \"%ls\" (%ls) is added", rulename, dir); + } +} + +// No use for now, it can be refer as an example of ShellExecuteW. +void RemoveFirewallRuleCmdline(LPWSTR exeName) +{ + HRESULT hr = S_OK; + HINSTANCE hi = 0; + WCHAR cmdline[1024] = { 0, }; + WCHAR rulename[500] = { 0, }; + + StringCchPrintfW(rulename, sizeof(rulename) / sizeof(rulename[0]), L"%ls Service", exeName); + if (hr < 0) { + WcaLog(LOGMSG_STANDARD, "Failed to make rulename: %ls", exeName); + return; + } + + StringCchPrintfW(cmdline, sizeof(cmdline) / sizeof(cmdline[0]), L"advfirewall firewall delete rule name=\"%ls\"", rulename); + if (hr < 0) { + WcaLog(LOGMSG_STANDARD, "Failed to make cmdline: %ls", exeName); + return; + } + + hi = ShellExecuteW(NULL, L"open", L"netsh", cmdline, 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 change firewall rule \"%ls\" : %d, last error: %d", rulename, (int)hi, GetLastError()); + } + else { + WcaLog(LOGMSG_STANDARD, "Firewall rule \"%ls\" is removed", rulename); + } +} + +bool AddFirewallRule(bool add, LPWSTR exeName, LPWSTR exeFile); +UINT __stdcall AddFirewallRules( + __in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int nResult = 0; + LPWSTR exeFile = NULL; + LPWSTR exeName = NULL; + WCHAR exeNameNoExt[500] = { 0, }; + LPWSTR pwz = NULL; + LPWSTR pwzData = NULL; + size_t szNameLen = 0; + + hr = WcaInitialize(hInstall, "AddFirewallExceptions"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &exeFile); + ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); + WcaLog(LOGMSG_STANDARD, "Try add firewall exceptions for file : %ls", exeFile); + + exeName = PathFindFileNameW(exeFile + 1); + hr = StringCchPrintfW(exeNameNoExt, 500, exeName); + ExitOnFailure(hr, "Failed to copy exe name: %ls", exeName); + szNameLen = wcslen(exeNameNoExt); + if (szNameLen >= 4 && wcscmp(exeNameNoExt + szNameLen - 4, L".exe") == 0) { + exeNameNoExt[szNameLen - 4] = L'\0'; + } + + //if (exeFile[0] == L'1') { + // AddFirewallRuleCmdline(exeNameNoExt, exeFile, L"in"); + // AddFirewallRuleCmdline(exeNameNoExt, exeFile, L"out"); + //} + //else { + // RemoveFirewallRuleCmdline(exeNameNoExt); + //} + + AddFirewallRule(exeFile[0] == L'1', exeNameNoExt, exeFile + 1); + +LExit: + if (pwzData) { + ReleaseStr(pwzData); + } + + 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 0673ee1ad..31a10eb63 100644 --- a/res/msi/CustomActions/CustomActions.def +++ b/res/msi/CustomActions/CustomActions.def @@ -4,3 +4,4 @@ EXPORTS CustomActionHello RemoveInstallFolder TerminateProcesses + AddFirewallRules diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index b9491b73b..fcadf9a76 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -60,6 +60,7 @@ + Create diff --git a/res/msi/CustomActions/FirewallRules.cpp b/res/msi/CustomActions/FirewallRules.cpp new file mode 100644 index 000000000..9381f8f61 --- /dev/null +++ b/res/msi/CustomActions/FirewallRules.cpp @@ -0,0 +1,430 @@ +// https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-adding-an-application-rule-edge-traversal + +/******************************************************************** +Copyright (C) Microsoft. All Rights Reserved. + +Abstract: + This C++ file includes sample code that adds a firewall rule with + EdgeTraversalOptions (one of the EdgeTraversalOptions values). + +********************************************************************/ + +#include "pch.h" +#include +#include +#include +#include + +#pragma comment(lib, "ole32.lib") +#pragma comment(lib, "oleaut32.lib") + +#define STRING_BUFFER_SIZE 500 + + +// Forward declarations +HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2); +void WFCOMCleanup(INetFwPolicy2* pNetFwPolicy2); +HRESULT RemoveFirewallRule( + __in INetFwPolicy2* pNetFwPolicy2, + __in LPWSTR exeName); +HRESULT AddFirewallRuleWithEdgeTraversal(__in INetFwPolicy2* pNetFwPolicy2, + __in bool in, + __in LPWSTR exeName, + __in LPWSTR exeFile); + + +bool AddFirewallRule(bool add, LPWSTR exeName, LPWSTR exeFile) +{ + bool result = false; + HRESULT hrComInit = S_OK; + HRESULT hr = S_OK; + INetFwPolicy2* pNetFwPolicy2 = NULL; + + // Initialize COM. + hrComInit = CoInitializeEx( + 0, + COINIT_APARTMENTTHREADED + ); + + // Ignore RPC_E_CHANGED_MODE; this just means that COM has already been + // initialized with a different mode. Since we don't care what the mode is, + // we'll just use the existing mode. + if (hrComInit != RPC_E_CHANGED_MODE) + { + if (FAILED(hrComInit)) + { + WcaLog(LOGMSG_STANDARD, "CoInitializeEx failed: 0x%08lx\n", hrComInit); + goto Cleanup; + } + } + + // Retrieve INetFwPolicy2 + hr = WFCOMInitialize(&pNetFwPolicy2); + if (FAILED(hr)) + { + goto Cleanup; + } + + if (add) { + // Add firewall rule with EdgeTraversalOption=DeferApp (Windows7+) if available + // else add with Edge=True (Vista and Server 2008). + hr = AddFirewallRuleWithEdgeTraversal(pNetFwPolicy2, true, exeName, exeFile); + hr = AddFirewallRuleWithEdgeTraversal(pNetFwPolicy2, false, exeName, exeFile); + } + else { + hr = RemoveFirewallRule(pNetFwPolicy2, exeName); + } + result = SUCCEEDED(hr); + +Cleanup: + + // Release INetFwPolicy2 + WFCOMCleanup(pNetFwPolicy2); + + // Uninitialize COM. + if (SUCCEEDED(hrComInit)) + { + CoUninitialize(); + } + + return result; +} + +BSTR MakeRuleName(__in LPWSTR exeName) +{ + WCHAR pwszTemp[STRING_BUFFER_SIZE] = L""; + HRESULT hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"%ls Service", exeName); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to compose a resource identifier string: 0x%08lx\n", hr); + return NULL; + } + return SysAllocString(pwszTemp); +} + +HRESULT RemoveFirewallRule( + __in INetFwPolicy2* pNetFwPolicy2, + __in LPWSTR exeName) +{ + HRESULT hr = S_OK; + INetFwRules* pNetFwRules = NULL; + + WCHAR pwszTemp[STRING_BUFFER_SIZE] = L""; + + BSTR RuleName = NULL; + + RuleName = MakeRuleName(exeName); + if (NULL == RuleName) + { + WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n"); + goto Cleanup; + } + + hr = pNetFwPolicy2->get_Rules(&pNetFwRules); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to retrieve firewall rules collection : 0x%08lx\n", hr); + goto Cleanup; + } + + // We need to "Remove()" twice, because both "in" and "out" rules are added? + // There's no remarks for this case https://learn.microsoft.com/en-us/windows/win32/api/netfw/nf-netfw-inetfwrules-remove + hr = pNetFwRules->Remove(RuleName); + hr = pNetFwRules->Remove(RuleName); + if (FAILED(hr)) { + WcaLog(LOGMSG_STANDARD, "Failed to remove firewall rule \"%ls\" : 0x%08lx\n", exeName, hr); + } + else { + WcaLog(LOGMSG_STANDARD, "Firewall rule \"%ls\" is removed\n", exeName); + } + +Cleanup: + + SysFreeString(RuleName); + + if (pNetFwRules != NULL) + { + pNetFwRules->Release(); + } + + return hr; +} + +// Add firewall rule with EdgeTraversalOption=DeferApp (Windows7+) if available +// else add with Edge=True (Vista and Server 2008). +HRESULT AddFirewallRuleWithEdgeTraversal( + __in INetFwPolicy2* pNetFwPolicy2, + __in bool in, + __in LPWSTR exeName, + __in LPWSTR exeFile) +{ + HRESULT hr = S_OK; + INetFwRules* pNetFwRules = NULL; + + INetFwRule* pNetFwRule = NULL; + INetFwRule2* pNetFwRule2 = NULL; + + WCHAR pwszTemp[STRING_BUFFER_SIZE] = L""; + + BSTR RuleName = NULL; + BSTR RuleGroupName = NULL; + BSTR RuleDescription = NULL; + BSTR RuleAppPath = NULL; + + long CurrentProfilesBitMask = 0; + + + // For localization purposes, the rule name, description, and group can be + // provided as indirect strings. These indirect strings can be defined in an rc file. + // Examples of the indirect string definitions in the rc file - + // 127 "EdgeTraversalOptions Sample Application" + // 128 "Allow inbound TCP traffic to application EdgeTraversalOptions.exe" + // 129 "Allow EdgeTraversalOptions.exe to receive inbound traffic for TCP protocol + // from remote machines located within your network as well as from + // the Internet (i.e from outside of your Edge device like Firewall or NAT" + + + // Examples of using indirect strings - + // hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"@EdgeTraversalOptions.exe,-128"); + RuleName = MakeRuleName(exeName); + if (NULL == RuleName) + { + WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n"); + goto Cleanup; + } + // Examples of using indirect strings - + // hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"@EdgeTraversalOptions.exe,-127"); + hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, exeName); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to compose a resource identifier string: 0x%08lx\n", hr); + goto Cleanup; + } + RuleGroupName = SysAllocString(pwszTemp); // Used for grouping together multiple rules + if (NULL == RuleGroupName) + { + WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n"); + goto Cleanup; + } + // Examples of using indirect strings - + // hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"@EdgeTraversalOptions.exe,-129"); + hr = StringCchPrintfW(pwszTemp, STRING_BUFFER_SIZE, L"Allow %ls to receive \ + inbound traffic from remote machines located within your network as well as \ + from the Internet", exeName); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to compose a resource identifier string: 0x%08lx\n", hr); + goto Cleanup; + } + RuleDescription = SysAllocString(pwszTemp); + if (NULL == RuleDescription) + { + WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n"); + goto Cleanup; + } + + RuleAppPath = SysAllocString(exeFile); + if (NULL == RuleAppPath) + { + WcaLog(LOGMSG_STANDARD, "\nERROR: Insufficient memory\n"); + goto Cleanup; + } + + hr = pNetFwPolicy2->get_Rules(&pNetFwRules); + + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to retrieve firewall rules collection : 0x%08lx\n", hr); + goto Cleanup; + } + + hr = CoCreateInstance( + __uuidof(NetFwRule), //CLSID of the class whose object is to be created + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(INetFwRule), // Identifier of the Interface used for communicating with the object + (void**)&pNetFwRule); + + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "CoCreateInstance for INetFwRule failed: 0x%08lx\n", hr); + goto Cleanup; + } + + hr = pNetFwRule->put_Name(RuleName); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Name failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + hr = pNetFwRule->put_Grouping(RuleGroupName); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Grouping failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + hr = pNetFwRule->put_Description(RuleDescription); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Description failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + if (in) { + CurrentProfilesBitMask = NET_FW_PROFILE2_ALL; + } + else { + // Retrieve Current Profiles bitmask + hr = pNetFwPolicy2->get_CurrentProfileTypes(&CurrentProfilesBitMask); + if (FAILED(hr)) + { + printf("get_CurrentProfileTypes failed: 0x%08lx\n", hr); + goto Cleanup; + } + + // When possible we avoid adding firewall rules to the Public profile. + // If Public is currently active and it is not the only active profile, we remove it from the bitmask + if ((CurrentProfilesBitMask & NET_FW_PROFILE2_PUBLIC) && + (CurrentProfilesBitMask != NET_FW_PROFILE2_PUBLIC)) + { + CurrentProfilesBitMask ^= NET_FW_PROFILE2_PUBLIC; + } + } + + hr = pNetFwRule->put_Direction(in ? NET_FW_RULE_DIR_IN : NET_FW_RULE_DIR_OUT); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Direction failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + + hr = pNetFwRule->put_Action(NET_FW_ACTION_ALLOW); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Action failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + hr = pNetFwRule->put_ApplicationName(RuleAppPath); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_ApplicationName failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + //hr = pNetFwRule->put_Protocol(6); // TCP + //if (FAILED(hr)) + //{ + // WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Protocol failed with error: 0x %x.\n", hr); + // goto Cleanup; + //} + + hr = pNetFwRule->put_Profiles(CurrentProfilesBitMask); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Profiles failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + hr = pNetFwRule->put_Enabled(VARIANT_TRUE); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_Enabled failed with error: 0x %x.\n", hr); + goto Cleanup; + } + + if (in) { + // Check if INetFwRule2 interface is available (i.e Windows7+) + // If supported, then use EdgeTraversalOptions + // Else use the EdgeTraversal boolean flag. + + if (SUCCEEDED(pNetFwRule->QueryInterface(__uuidof(INetFwRule2), (void**)&pNetFwRule2))) + { + hr = pNetFwRule2->put_EdgeTraversalOptions(NET_FW_EDGE_TRAVERSAL_TYPE_DEFER_TO_APP); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_EdgeTraversalOptions failed with error: 0x %x.\n", hr); + goto Cleanup; + } + } + else + { + hr = pNetFwRule->put_EdgeTraversal(VARIANT_TRUE); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed INetFwRule::put_EdgeTraversal failed with error: 0x %x.\n", hr); + goto Cleanup; + } + } + } + + hr = pNetFwRules->Add(pNetFwRule); + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "Failed to add firewall rule to the firewall rules collection : 0x%08lx\n", hr); + goto Cleanup; + } + + WcaLog(LOGMSG_STANDARD, "Successfully added firewall rule !\n"); + +Cleanup: + + SysFreeString(RuleName); + SysFreeString(RuleGroupName); + SysFreeString(RuleDescription); + SysFreeString(RuleAppPath); + + if (pNetFwRule2 != NULL) + { + pNetFwRule2->Release(); + } + + if (pNetFwRule != NULL) + { + pNetFwRule->Release(); + } + + if (pNetFwRules != NULL) + { + pNetFwRules->Release(); + } + + return hr; +} + + +// Instantiate INetFwPolicy2 +HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2) +{ + HRESULT hr = S_OK; + + hr = CoCreateInstance( + __uuidof(NetFwPolicy2), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(INetFwPolicy2), + (void**)ppNetFwPolicy2); + + if (FAILED(hr)) + { + WcaLog(LOGMSG_STANDARD, "CoCreateInstance for INetFwPolicy2 failed: 0x%08lx\n", hr); + goto Cleanup; + } + +Cleanup: + return hr; +} + + +// Release INetFwPolicy2 +void WFCOMCleanup(INetFwPolicy2* pNetFwPolicy2) +{ + // Release the INetFwPolicy2 object (Vista+) + if (pNetFwPolicy2 != NULL) + { + pNetFwPolicy2->Release(); + } +} diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index f01c4999d..ffd30270a 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -7,7 +7,7 @@ - + @@ -18,6 +18,8 @@ + + - + + + + + + + diff --git a/res/msi/Package/Fragments/AddRemoveProperties.wxs b/res/msi/Package/Fragments/AddRemoveProperties.wxs index 56dfb1854..5711aa5a8 100644 --- a/res/msi/Package/Fragments/AddRemoveProperties.wxs +++ b/res/msi/Package/Fragments/AddRemoveProperties.wxs @@ -22,12 +22,8 @@ - - + + - - - - diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index 0c1e61e4d..29ae04492 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -7,5 +7,8 @@ + + + diff --git a/res/msi/Package/Language/Package.en-us.wxl b/res/msi/Package/Language/Package.en-us.wxl index 92f76c106..517bde2af 100644 --- a/res/msi/Package/Language/Package.en-us.wxl +++ b/res/msi/Package/Language/Package.en-us.wxl @@ -47,6 +47,6 @@ This file contains the declaration of all the localizable strings. - + diff --git a/res/msi/Package/Package.wxs b/res/msi/Package/Package.wxs index cd6b29936..9fbedd9ed 100644 --- a/res/msi/Package/Package.wxs +++ b/res/msi/Package/Package.wxs @@ -22,8 +22,7 @@ - - + diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index b91d2302f..f051db5ee 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -38,10 +38,7 @@ g_arpsystemcomponent = { def make_parser(): parser = argparse.ArgumentParser(description="Msi preprocess script.") parser.add_argument( - "-d", "--debug", action="store_true", help="Is debug", default=False - ) - parser.add_argument( - "-ci", "--github-ci", action="store_true", help="Is github ci", default=False + "-d", "--dist-dir", type=str, default="../../rustdesk", help="The dist direcotry to install." ) parser.add_argument( "-arp", @@ -96,9 +93,9 @@ def read_lines_and_start_index(file_path, tag_start, tag_end): return lines, index_start -def insert_components_between_tags(lines, index_start, app_name, build_dir): +def insert_components_between_tags(lines, index_start, app_name, dist_dir): indent = g_indent_unit * 3 - path = Path(build_dir) + path = Path(dist_dir) idx = 1 for file_path in path.glob("**/*"): if file_path.is_file(): @@ -129,18 +126,18 @@ def insert_components_between_tags(lines, index_start, app_name, build_dir): return True -def gen_auto_component(app_name, build_dir): +def gen_auto_component(app_name, dist_dir): return gen_content_between_tags( "Package/Components/RustDesk.wxs", "", "", lambda lines, index_start: insert_components_between_tags( - lines, index_start, app_name, build_dir + lines, index_start, app_name, dist_dir ), ) -def gen_pre_vars(args, build_dir): +def gen_pre_vars(args, dist_dir): def func(lines, index_start): upgrade_code = uuid.uuid5(uuid.NAMESPACE_OID, app_name + ".exe") @@ -153,7 +150,7 @@ def gen_pre_vars(args, build_dir): f'{indent}\n', f'{indent}\n', f'{indent}\n', - f'{indent}\n', + f'{indent}\n', f'{indent}\n', "\n", f"{indent}\n" @@ -278,7 +275,7 @@ def get_folder_size(folder_path): return total_size -def gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir): +def gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir): def func(lines, index_start): indent = g_indent_unit * 5 @@ -312,7 +309,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir): f'{indent}\n' ) - estimated_size = get_folder_size(build_dir) + estimated_size = get_folder_size(dist_dir) lines_new.append( f'{indent}\n' ) @@ -359,7 +356,7 @@ def gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir): ) -def gen_custom_ARPSYSTEMCOMPONENT(args, build_dir): +def gen_custom_ARPSYSTEMCOMPONENT(args, dist_dir): try: custom_arp = json.loads(args.custom_arp) g_arpsystemcomponent.update(custom_arp) @@ -368,7 +365,7 @@ def gen_custom_ARPSYSTEMCOMPONENT(args, build_dir): return False if args.arp: - return gen_custom_ARPSYSTEMCOMPONENT_True(args, build_dir) + return gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir) else: return gen_custom_ARPSYSTEMCOMPONENT_False(args) @@ -427,26 +424,21 @@ if __name__ == "__main__": args = parser.parse_args() app_name = args.app_name - build_dir = ( - f'../../flutter/build/windows/x64/runner/{"Debug" if args.debug else "Release"}' - ) - if args.github_ci: - build_dir = "../../rustdesk" - build_dir = Path(sys.argv[0]).parent.joinpath(build_dir).resolve() + dist_dir = Path(sys.argv[0]).parent.joinpath(args.dist_dir).resolve() if not init_global_vars(args): sys.exit(-1) - if not gen_pre_vars(args, build_dir): + if not gen_pre_vars(args, dist_dir): sys.exit(-1) if not gen_upgrade_info(): sys.exit(-1) - if not gen_custom_ARPSYSTEMCOMPONENT(args, build_dir): + if not gen_custom_ARPSYSTEMCOMPONENT(args, dist_dir): sys.exit(-1) - if not gen_auto_component(app_name, build_dir): + if not gen_auto_component(app_name, dist_dir): sys.exit(-1) if not gen_custom_dialog_bitmaps(): From c92cfbb20a7fba65eb016f436d098595ef08c435 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 11 Apr 2024 19:26:29 +0800 Subject: [PATCH 044/112] Fix. Github ci (#7689) Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 0e65c8827..f86b952fb 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -171,6 +171,7 @@ jobs: python preprocess.py -arp -d ../../rustdesk nuget restore msi.sln msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10 + mkdir -p ../../SignOutput mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-beta.msi sha256sum ../../SignOutput/rustdesk-*.msi From 5322332c5d89190a7417baf40ce09d5a6a8d4fe9 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 11 Apr 2024 20:23:08 +0800 Subject: [PATCH 045/112] Update flutter-build.yml --- .github/workflows/flutter-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index f86b952fb..b44eda758 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -166,12 +166,12 @@ jobs: uses: microsoft/setup-msbuild@v2 - name: Build msi + if: env.UPLOAD_ARTIFACT == 'true' run: | pushd ./res/msi python preprocess.py -arp -d ../../rustdesk nuget restore msi.sln msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10 - mkdir -p ../../SignOutput mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-beta.msi sha256sum ../../SignOutput/rustdesk-*.msi From 48da00eb667584196fa1e12d7242abcc5a1d487f Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 11 Apr 2024 20:41:23 +0800 Subject: [PATCH 046/112] Fix. Msi, error 5 on uninstall (#7690) Signed-off-by: fufesou --- res/msi/Package/Components/RustDesk.wxs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index ffd30270a..eab059804 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -34,7 +34,7 @@ - + @@ -75,16 +75,16 @@ - + - + From a88b189664f69f629aa0e18a289317b85cacb13d Mon Sep 17 00:00:00 2001 From: FastAct <93490087+FastAct@users.noreply.github.com> Date: Thu, 11 Apr 2024 14:42:21 +0200 Subject: [PATCH 047/112] Update nl.rs (#7691) --- src/lang/nl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 9205e6486..a4c074912 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "De bovenstaande velden worden gedeeld en zijn zichtbaar voor anderen."), ("Everyone", "Iedereen"), ("ab_web_console_tip", "Meer over de webconsole"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Alleen verbindingen toestaan als het RustDesk-venster geopend is"), ].iter().cloned().collect(); } From 6e3d16173a2990b159ec401957155b0be1da97bc Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 11 Apr 2024 23:27:09 +0800 Subject: [PATCH 048/112] Fix. Mis, do not InstallValidate when uninstall (#7692) Signed-off-by: fufesou --- res/msi/Package/Package.wxs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/res/msi/Package/Package.wxs b/res/msi/Package/Package.wxs index 9fbedd9ed..9a37b3092 100644 --- a/res/msi/Package/Package.wxs +++ b/res/msi/Package/Package.wxs @@ -32,6 +32,10 @@ + + + + From d8875f381b4a89245858262cf108bbdc0a45ef71 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 12 Apr 2024 11:38:19 +0800 Subject: [PATCH 049/112] protobuf 3.4 --- Cargo.lock | 16 ++++++++-------- libs/hbb_common/Cargo.toml | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d614f745..9a7a0c1d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4762,9 +4762,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65f4a8ec18723a734e5dc09c173e0abf9690432da5340285d536edcb4dac190" +checksum = "58678a64de2fced2bdec6bca052a6716a0efe692d6e3f53d1bda6a1def64cfc0" dependencies = [ "bytes", "once_cell", @@ -4774,9 +4774,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e85514a216b1c73111d9032e26cc7a5ecb1bb3d4d9539e91fb72a4395060f78" +checksum = "32777b0b3f6538d9d2e012b3fad85c7e4b9244b5958d04a6415f4333782b7a77" dependencies = [ "anyhow", "once_cell", @@ -4789,9 +4789,9 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d6fbd6697c9e531873e81cec565a85e226b99a0f10e1acc079be057fe2fcba" +checksum = "96cb37955261126624a25b5e6bda40ae34cf3989d52a783087ca6091b29b5642" dependencies = [ "anyhow", "indexmap 1.9.3", @@ -4805,9 +4805,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6872f4d4f4b98303239a2b5838f5bbbb77b01ffc892d627957f37a22d7cfe69c" +checksum = "e1ed294a835b0f30810e13616b1cd34943c6d1e84a8f3b0dcfe466d256c3e7e7" dependencies = [ "thiserror", ] diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 01274c4fc..2da853420 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] flexi_logger = { version = "0.27", features = ["async"] } -protobuf = { version = "3.3", features = ["with-bytes"] } +protobuf = { version = "3.4", features = ["with-bytes"] } tokio = { version = "1.36", features = ["full"] } tokio-util = { version = "0.7", features = ["full"] } futures = "0.3" @@ -50,7 +50,7 @@ quic = [] flatpak = [] [build-dependencies] -protobuf-codegen = { version = "3.3" } +protobuf-codegen = { version = "3.4" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser", "synchapi", "pdh", "memoryapi"] } From 98df2b111e21a5b8eabe06591a077806a3ca3675 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 12 Apr 2024 17:26:24 +0800 Subject: [PATCH 050/112] hwcodec uses one repository (#7701) * update hwcodec, gpucodec repo is merged to hwcodec Signed-off-by: 21pages * rename gpucodec.rs to vram.rs Signed-off-by: 21pages * rename all gpucodec to vram, because vram is a feature of hwcodec Signed-off-by: 21pages * use one check process and one config file * set check encode image size to 720p Signed-off-by: 21pages --------- Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 6 +- Cargo.lock | 68 +------ Cargo.toml | 2 +- build.py | 8 +- .../desktop/pages/desktop_setting_page.dart | 4 +- .../lib/models/desktop_render_texture.dart | 2 +- flutter/lib/web/bridge.dart | 2 +- libs/hbb_common/src/config.rs | 36 ++-- libs/hbb_common/src/lib.rs | 2 + libs/scrap/Cargo.toml | 13 +- libs/scrap/examples/benchmark.rs | 12 +- libs/scrap/src/common/aom.rs | 2 +- libs/scrap/src/common/codec.rs | 168 +++++++++--------- libs/scrap/src/common/convert.rs | 9 +- libs/scrap/src/common/dxgi.rs | 12 +- libs/scrap/src/common/hwcodec.rs | 119 ++++++------- libs/scrap/src/common/mod.rs | 20 +-- libs/scrap/src/common/vpxcodec.rs | 2 +- .../scrap/src/common/{gpucodec.rs => vram.rs} | 155 +++++----------- libs/scrap/src/dxgi/mod.rs | 4 +- src/client.rs | 8 +- src/core_main.rs | 4 - src/flutter.rs | 50 +++--- src/flutter_ffi.rs | 6 +- src/server.rs | 2 - src/server/connection.rs | 10 +- src/server/portable_service.rs | 6 +- src/server/video_service.rs | 50 +++--- src/ui.rs | 6 +- src/ui/index.tis | 4 +- src/ui_interface.rs | 12 +- src/ui_session_interface.rs | 4 +- 32 files changed, 330 insertions(+), 478 deletions(-) rename libs/scrap/src/common/{gpucodec.rs => vram.rs} (68%) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index b44eda758..3e15301dc 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -113,7 +113,7 @@ jobs: shell: bash - name: Build rustdesk - run: python3 .\build.py --portable --hwcodec --flutter --gpucodec --skip-portable-pack + run: python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack - name: find Runner.res # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res @@ -251,7 +251,7 @@ jobs: python3 res/inline-sciter.py # Patch sciter x86 sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml - cargo build --features inline,gpucodec --release --bins + cargo build --features inline,vram,hwcodec --release --bins mkdir -p ./Release mv ./target/release/rustdesk.exe ./Release/rustdesk.exe curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll @@ -1600,7 +1600,7 @@ jobs: ;; esac export CARGO_INCREMENTAL=0 - python3 ./build.py --flutter --hwcodec --skip-cargo + python3 ./build.py --flutter --skip-cargo # rpm package echo -e "start packaging fedora package" pushd /workspace diff --git a/Cargo.lock b/Cargo.lock index 9a7a0c1d0..95024e0b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,17 +115,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "amf" -version = "0.1.0" -source = "git+https://github.com/21pages/gpucodec#546f7f644ce15a35b833c1531a4fead4b34a1b3b" -dependencies = [ - "bindgen 0.59.2", - "cc", - "gpu_common", - "log", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -2649,36 +2638,6 @@ dependencies = [ "system-deps 6.1.2", ] -[[package]] -name = "gpu_common" -version = "0.1.0" -source = "git+https://github.com/21pages/gpucodec#546f7f644ce15a35b833c1531a4fead4b34a1b3b" -dependencies = [ - "bindgen 0.59.2", - "cc", - "log", - "serde 1.0.190", - "serde_derive", - "serde_json 1.0.107", -] - -[[package]] -name = "gpucodec" -version = "0.1.0" -source = "git+https://github.com/21pages/gpucodec#546f7f644ce15a35b833c1531a4fead4b34a1b3b" -dependencies = [ - "amf", - "bindgen 0.59.2", - "cc", - "gpu_common", - "log", - "nv", - "serde 1.0.190", - "serde_derive", - "serde_json 1.0.107", - "vpl", -] - [[package]] name = "gstreamer" version = "0.16.7" @@ -3065,8 +3024,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.2.0" -source = "git+https://github.com/21pages/hwcodec?branch=stable#52e1da2aae86acec5f374bc065f5921945b55e7b" +version = "0.3.0" +source = "git+https://github.com/21pages/hwcodec#6ce1cbab2ff270a81784303192e8906ef597ee02" dependencies = [ "bindgen 0.59.2", "cc", @@ -4134,17 +4093,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nv" -version = "0.1.0" -source = "git+https://github.com/21pages/gpucodec#546f7f644ce15a35b833c1531a4fead4b34a1b3b" -dependencies = [ - "bindgen 0.59.2", - "cc", - "gpu_common", - "log", -] - [[package]] name = "objc" version = "0.2.7" @@ -5683,7 +5631,6 @@ dependencies = [ "cfg-if 1.0.0", "dbus", "docopt", - "gpucodec", "gstreamer", "gstreamer-app", "gstreamer-video", @@ -6900,17 +6847,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "vpl" -version = "0.1.0" -source = "git+https://github.com/21pages/gpucodec#546f7f644ce15a35b833c1531a4fead4b34a1b3b" -dependencies = [ - "bindgen 0.59.2", - "cc", - "gpu_common", - "log", -] - [[package]] name = "waker-fn" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index 9e3af62b1..0f59d49cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ use_dasp = ["dasp"] flutter = ["flutter_rust_bridge"] default = ["use_dasp"] hwcodec = ["scrap/hwcodec"] -gpucodec = ["scrap/gpucodec"] +vram = ["scrap/vram"] mediacodec = ["scrap/mediacodec"] linux_headless = ["pam" ] virtual_display_driver = ["virtual_display"] diff --git a/build.py b/build.py index cb588f7d0..cbf9d00a2 100755 --- a/build.py +++ b/build.py @@ -118,9 +118,9 @@ def make_parser(): '' if windows or osx else ', need libva-dev, libvdpau-dev.') ) parser.add_argument( - '--gpucodec', + '--vram', action='store_true', - help='Enable feature gpucodec, only available on windows now.' + help='Enable feature vram, only available on windows now.' ) parser.add_argument( '--portable', @@ -282,8 +282,8 @@ def get_features(args): features = ['inline'] if not args.flutter else [] if args.hwcodec: features.append('hwcodec') - if args.gpucodec: - features.append('gpucodec') + if args.vram: + features.append('vram') if args.flutter: features.append('flutter') features.append('flutter_texture_render') diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 57fb25c62..017825a25 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -400,9 +400,9 @@ class _GeneralState extends State<_General> { Widget hwcodec() { final hwcodec = bind.mainHasHwcodec(); - final gpucodec = bind.mainHasGpucodec(); + final vram = bind.mainHasVram(); return Offstage( - offstage: !(hwcodec || gpucodec), + offstage: !(hwcodec || vram), child: _Card(title: 'Hardware Codec', children: [ _OptionCheckBox(context, 'Enable hardware codec', 'enable-hwcodec') ]), diff --git a/flutter/lib/models/desktop_render_texture.dart b/flutter/lib/models/desktop_render_texture.dart index 2e3c0ead6..80477fede 100644 --- a/flutter/lib/models/desktop_render_texture.dart +++ b/flutter/lib/models/desktop_render_texture.dart @@ -10,7 +10,7 @@ import './platform_model.dart'; import 'package:texture_rgba_renderer/texture_rgba_renderer.dart' if (dart.library.html) 'package:flutter_hbb/web/texture_rgba_renderer.dart'; -// Feature flutter_texture_render need to be enabled if feature gpucodec is enabled. +// Feature flutter_texture_render need to be enabled if feature vram is enabled. final useTextureRender = !isWeb && (bind.mainHasPixelbufferTextureRender() || bind.mainHasGpuTextureRender()); diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 15ae78d2c..eb6db3120 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -1052,7 +1052,7 @@ class RustdeskImpl { throw UnimplementedError(); } - bool mainHasGpucodec({dynamic hint}) { + bool mainHasVram({dynamic hint}) { throw UnimplementedError(); } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index acf304526..1c58ae9b2 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -409,9 +409,7 @@ fn patch(path: PathBuf) -> PathBuf { if let Ok(user) = crate::platform::linux::run_cmds_trim_newline("whoami") { if user != "root" { let cmd = format!("getent passwd '{}' | awk -F':' '{{print $6}}'", user); - if let Ok(output) = - crate::platform::linux::run_cmds_trim_newline(&cmd) - { + if let Ok(output) = crate::platform::linux::run_cmds_trim_newline(&cmd) { return output.into(); } return format!("/home/{user}").into(); @@ -505,7 +503,7 @@ impl Config { fn store_(config: &T, suffix: &str) { let file = Self::file_(suffix); if let Err(err) = store_path(file, config) { - log::error!("Failed to store config: {}", err); + log::error!("Failed to store {suffix} config: {err}"); } } @@ -1495,8 +1493,10 @@ impl LanPeers { #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct HwCodecConfig { - #[serde(default, deserialize_with = "deserialize_hashmap_string_string")] - pub options: HashMap, + #[serde(default, deserialize_with = "deserialize_string")] + pub ram: String, + #[serde(default, deserialize_with = "deserialize_string")] + pub vram: String, } impl HwCodecConfig { @@ -1511,25 +1511,17 @@ impl HwCodecConfig { pub fn clear() { HwCodecConfig::default().store(); } -} -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct GpucodecConfig { - #[serde(default, deserialize_with = "deserialize_string")] - pub available: String, -} - -impl GpucodecConfig { - pub fn load() -> GpucodecConfig { - Config::load_::("_gpucodec") + pub fn clear_ram() { + let mut c = Self::load(); + c.ram = Default::default(); + c.store(); } - pub fn store(&self) { - Config::store_(self, "_gpucodec"); - } - - pub fn clear() { - GpucodecConfig::default().store(); + pub fn clear_vram() { + let mut c = Self::load(); + c.vram = Default::default(); + c.store(); } } diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index e802730a0..d360d3583 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -46,6 +46,8 @@ pub mod keyboard; pub use dlopen; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use machine_uid; +pub use serde_derive; +pub use serde_json; pub use sysinfo; pub use toml; pub use uuid; diff --git a/libs/scrap/Cargo.toml b/libs/scrap/Cargo.toml index d22120df6..ffb66aabb 100644 --- a/libs/scrap/Cargo.toml +++ b/libs/scrap/Cargo.toml @@ -13,6 +13,8 @@ edition = "2018" wayland = ["gstreamer", "gstreamer-app", "gstreamer-video", "dbus", "tracing"] mediacodec = ["ndk"] linux-pkg-config = ["dep:pkg-config"] +hwcodec = ["dep:hwcodec"] +vram = ["hwcodec/vram"] [dependencies] cfg-if = "1.0" @@ -20,6 +22,7 @@ num_cpus = "1.15" lazy_static = "1.4" hbb_common = { path = "../hbb_common" } webm = { git = "https://github.com/21pages/rust-webm" } +serde = {version="1.0", features=["derive"]} [dependencies.winapi] version = "0.3" @@ -41,7 +44,6 @@ ndk-context = "0.1" [target.'cfg(not(target_os = "android"))'.dev-dependencies] repng = "0.2" docopt = "1.1" -serde = {version="1.0", features=["derive"]} quest = "0.3" [build-dependencies] @@ -56,8 +58,9 @@ gstreamer = { version = "0.16", optional = true } gstreamer-app = { version = "0.16", features = ["v1_10"], optional = true } gstreamer-video = { version = "0.16", optional = true } -[target.'cfg(any(target_os = "windows", target_os = "linux"))'.dependencies] -hwcodec = { git = "https://github.com/21pages/hwcodec", branch = "stable", optional = true } +[dependencies.hwcodec] +git = "https://github.com/21pages/hwcodec" +optional = true +features = ["ffmpeg"] + -[target.'cfg(target_os = "windows")'.dependencies] -gpucodec = { git = "https://github.com/21pages/gpucodec", optional = true } diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index 688e06edd..ccddccc81 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -239,16 +239,16 @@ fn test_av1( #[cfg(feature = "hwcodec")] mod hw { - use hwcodec::ffmpeg::CodecInfo; + use hwcodec::ffmpeg_ram::CodecInfo; use scrap::{ - hwcodec::{HwDecoder, HwEncoder, HwEncoderConfig}, + hwcodec::{HwRamDecoder, HwRamEncoder, HwRamEncoderConfig}, CodecFormat, }; use super::*; pub fn test(c: &mut Capturer, width: usize, height: usize, quality: Q, yuv_count: usize) { - let best = HwEncoder::best(); + let best = HwRamEncoder::best(); let mut h264s = Vec::new(); let mut h265s = Vec::new(); if let Some(info) = best.h264 { @@ -270,8 +270,8 @@ mod hw { yuv_count: usize, h26xs: &mut Vec>, ) { - let mut encoder = HwEncoder::new( - EncoderCfg::HW(HwEncoderConfig { + let mut encoder = HwRamEncoder::new( + EncoderCfg::HWRAM(HwRamEncoderConfig { name: info.name.clone(), width, height, @@ -321,7 +321,7 @@ mod hw { } fn test_decoder(format: CodecFormat, h26xs: &Vec>) { - let mut decoder = HwDecoder::new(format).unwrap(); + let mut decoder = HwRamDecoder::new(format).unwrap(); let start = Instant::now(); let mut cnt = 0; for h26x in h26xs { diff --git a/libs/scrap/src/common/aom.rs b/libs/scrap/src/common/aom.rs index bf7c96c44..759bf3fbe 100644 --- a/libs/scrap/src/common/aom.rs +++ b/libs/scrap/src/common/aom.rs @@ -268,7 +268,7 @@ impl EncoderApi for AomEncoder { self.yuvfmt.clone() } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn input_texture(&self) -> bool { false } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 71a518b94..7a96da9b9 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -5,12 +5,12 @@ use std::{ sync::{Arc, Mutex}, }; -#[cfg(feature = "gpucodec")] -use crate::gpucodec::*; #[cfg(feature = "hwcodec")] use crate::hwcodec::*; #[cfg(feature = "mediacodec")] use crate::mediacodec::{MediaCodecDecoder, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT}; +#[cfg(feature = "vram")] +use crate::vram::*; use crate::{ aom::{self, AomDecoder, AomEncoder, AomEncoderConfig}, common::GoogleImage, @@ -31,7 +31,7 @@ use hbb_common::{ tokio::time::Instant, ResultType, }; -#[cfg(any(feature = "hwcodec", feature = "mediacodec", feature = "gpucodec"))] +#[cfg(any(feature = "hwcodec", feature = "mediacodec", feature = "vram"))] use hbb_common::{config::Config2, lazy_static}; lazy_static::lazy_static! { @@ -47,9 +47,9 @@ pub enum EncoderCfg { VPX(VpxEncoderConfig), AOM(AomEncoderConfig), #[cfg(feature = "hwcodec")] - HW(HwEncoderConfig), - #[cfg(feature = "gpucodec")] - GPU(GpuEncoderConfig), + HWRAM(HwRamEncoderConfig), + #[cfg(feature = "vram")] + VRAM(VRamEncoderConfig), } pub trait EncoderApi { @@ -61,7 +61,7 @@ pub trait EncoderApi { fn yuvfmt(&self) -> EncodeYuvFormat; - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn input_texture(&self) -> bool; fn set_quality(&mut self, quality: Quality) -> ResultType<()>; @@ -94,13 +94,13 @@ pub struct Decoder { vp9: Option, av1: Option, #[cfg(feature = "hwcodec")] - h264_ram: Option, + h264_ram: Option, #[cfg(feature = "hwcodec")] - h265_ram: Option, - #[cfg(feature = "gpucodec")] - h264_vram: Option, - #[cfg(feature = "gpucodec")] - h265_vram: Option, + h265_ram: Option, + #[cfg(feature = "vram")] + h264_vram: Option, + #[cfg(feature = "vram")] + h265_vram: Option, #[cfg(feature = "mediacodec")] h264_media_codec: MediaCodecDecoder, #[cfg(feature = "mediacodec")] @@ -131,25 +131,25 @@ impl Encoder { }), #[cfg(feature = "hwcodec")] - EncoderCfg::HW(_) => match HwEncoder::new(config, i444) { + EncoderCfg::HWRAM(_) => match HwRamEncoder::new(config, i444) { Ok(hw) => Ok(Encoder { codec: Box::new(hw), }), Err(e) => { log::error!("new hw encoder failed: {e:?}, clear config"); - hbb_common::config::HwCodecConfig::clear(); + hbb_common::config::HwCodecConfig::clear_ram(); *ENCODE_CODEC_NAME.lock().unwrap() = CodecName::VP9; Err(e) } }, - #[cfg(feature = "gpucodec")] - EncoderCfg::GPU(_) => match GpuEncoder::new(config, i444) { + #[cfg(feature = "vram")] + EncoderCfg::VRAM(_) => match VRamEncoder::new(config, i444) { Ok(tex) => Ok(Encoder { codec: Box::new(tex), }), Err(e) => { - log::error!("new gpu encoder failed: {e:?}, clear config"); - hbb_common::config::GpucodecConfig::clear(); + log::error!("new vram encoder failed: {e:?}, clear config"); + hbb_common::config::HwCodecConfig::clear_vram(); *ENCODE_CODEC_NAME.lock().unwrap() = CodecName::VP9; Err(e) } @@ -186,19 +186,19 @@ impl Encoder { let _all_support_h265_decoding = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); #[allow(unused_mut)] - let mut h264gpu_encoding = false; + let mut h264vram_encoding = false; #[allow(unused_mut)] - let mut h265gpu_encoding = false; - #[cfg(feature = "gpucodec")] - if enable_gpucodec_option() { + let mut h265vram_encoding = false; + #[cfg(feature = "vram")] + if enable_vram_option() { if _all_support_h264_decoding { - if GpuEncoder::available(CodecName::H264GPU).len() > 0 { - h264gpu_encoding = true; + if VRamEncoder::available(CodecName::H264VRAM).len() > 0 { + h264vram_encoding = true; } } if _all_support_h265_decoding { - if GpuEncoder::available(CodecName::H265GPU).len() > 0 { - h265gpu_encoding = true; + if VRamEncoder::available(CodecName::H265VRAM).len() > 0 { + h265vram_encoding = true; } } } @@ -208,7 +208,7 @@ impl Encoder { let mut h265hw_encoding = None; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { - let best = HwEncoder::best(); + let best = HwRamEncoder::best(); if _all_support_h264_decoding { h264hw_encoding = best.h264.map_or(None, |c| Some(c.name)); } @@ -217,9 +217,9 @@ impl Encoder { } } let h264_useable = - _all_support_h264_decoding && (h264gpu_encoding || h264hw_encoding.is_some()); + _all_support_h264_decoding && (h264vram_encoding || h264hw_encoding.is_some()); let h265_useable = - _all_support_h265_decoding && (h265gpu_encoding || h265hw_encoding.is_some()); + _all_support_h265_decoding && (h265vram_encoding || h265hw_encoding.is_some()); let mut name = ENCODE_CODEC_NAME.lock().unwrap(); let mut preference = PreferCodec::Auto; let preferences: Vec<_> = decodings @@ -254,19 +254,19 @@ impl Encoder { PreferCodec::VP9 => CodecName::VP9, PreferCodec::AV1 => CodecName::AV1, PreferCodec::H264 => { - if h264gpu_encoding { - CodecName::H264GPU + if h264vram_encoding { + CodecName::H264VRAM } else if let Some(v) = h264hw_encoding { - CodecName::H264HW(v) + CodecName::H264RAM(v) } else { auto_codec } } PreferCodec::H265 => { - if h265gpu_encoding { - CodecName::H265GPU + if h265vram_encoding { + CodecName::H265VRAM } else if let Some(v) = h265hw_encoding { - CodecName::H265HW(v) + CodecName::H265RAM(v) } else { auto_codec } @@ -306,14 +306,14 @@ impl Encoder { }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { - let best = HwEncoder::best(); + let best = HwRamEncoder::best(); encoding.h264 |= best.h264.is_some(); encoding.h265 |= best.h265.is_some(); } - #[cfg(feature = "gpucodec")] - if enable_gpucodec_option() { - encoding.h264 |= GpuEncoder::available(CodecName::H264GPU).len() > 0; - encoding.h265 |= GpuEncoder::available(CodecName::H265GPU).len() > 0; + #[cfg(feature = "vram")] + if enable_vram_option() { + encoding.h264 |= VRamEncoder::available(CodecName::H264VRAM).len() > 0; + encoding.h265 |= VRamEncoder::available(CodecName::H265VRAM).len() > 0; } encoding } @@ -326,21 +326,21 @@ impl Encoder { }, EncoderCfg::AOM(_) => CodecName::AV1, #[cfg(feature = "hwcodec")] - EncoderCfg::HW(hw) => { + EncoderCfg::HWRAM(hw) => { if hw.name.to_lowercase().contains("h264") { - CodecName::H264HW(hw.name.clone()) + CodecName::H264RAM(hw.name.clone()) } else { - CodecName::H265HW(hw.name.clone()) + CodecName::H265RAM(hw.name.clone()) } } - #[cfg(feature = "gpucodec")] - EncoderCfg::GPU(gpu) => match gpu.feature.data_format { - gpucodec::gpu_common::DataFormat::H264 => CodecName::H264GPU, - gpucodec::gpu_common::DataFormat::H265 => CodecName::H265GPU, + #[cfg(feature = "vram")] + EncoderCfg::VRAM(vram) => match vram.feature.data_format { + hwcodec::common::DataFormat::H264 => CodecName::H264VRAM, + hwcodec::common::DataFormat::H265 => CodecName::H265VRAM, _ => { log::error!( - "should not reach here, gpucodec not support {:?}", - gpu.feature.data_format + "should not reach here, vram not support {:?}", + vram.feature.data_format ); return; } @@ -365,9 +365,9 @@ impl Encoder { }, EncoderCfg::AOM(_) => decodings.iter().all(|d| d.1.i444.av1), #[cfg(feature = "hwcodec")] - EncoderCfg::HW(_) => false, - #[cfg(feature = "gpucodec")] - EncoderCfg::GPU(_) => false, + EncoderCfg::HWRAM(_) => false, + #[cfg(feature = "vram")] + EncoderCfg::VRAM(_) => false, }; prefer_i444 && i444_useable && !decodings.is_empty() } @@ -399,18 +399,18 @@ impl Decoder { }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { - let best = HwDecoder::best(); + 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 }; } - #[cfg(feature = "gpucodec")] - if enable_gpucodec_option() && _flutter { - decoding.ability_h264 |= if GpuDecoder::available(CodecFormat::H264, _luid).len() > 0 { + #[cfg(feature = "vram")] + if enable_vram_option() && _flutter { + decoding.ability_h264 |= if VRamDecoder::available(CodecFormat::H264, _luid).len() > 0 { 1 } else { 0 }; - decoding.ability_h265 |= if GpuDecoder::available(CodecFormat::H265, _luid).len() > 0 { + decoding.ability_h265 |= if VRamDecoder::available(CodecFormat::H265, _luid).len() > 0 { 1 } else { 0 @@ -449,7 +449,7 @@ impl Decoder { let (mut vp8, mut vp9, mut av1) = (None, None, None); #[cfg(feature = "hwcodec")] let (mut h264_ram, mut h265_ram) = (None, None); - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] let (mut h264_vram, mut h265_vram) = (None, None); #[cfg(feature = "mediacodec")] let (mut h264_media_codec, mut h265_media_codec) = (None, None); @@ -482,9 +482,9 @@ impl Decoder { valid = av1.is_some(); } CodecFormat::H264 => { - #[cfg(feature = "gpucodec")] - if !valid && enable_gpucodec_option() && _luid.clone().unwrap_or_default() != 0 { - match GpuDecoder::new(format, _luid) { + #[cfg(feature = "vram")] + if !valid && enable_vram_option() && _luid.clone().unwrap_or_default() != 0 { + match VRamDecoder::new(format, _luid) { Ok(v) => h264_vram = Some(v), Err(e) => log::error!("create H264 vram decoder failed: {}", e), } @@ -492,7 +492,7 @@ impl Decoder { } #[cfg(feature = "hwcodec")] if !valid && enable_hwcodec_option() { - match HwDecoder::new(format) { + match HwRamDecoder::new(format) { Ok(v) => h264_ram = Some(v), Err(e) => log::error!("create H264 ram decoder failed: {}", e), } @@ -508,9 +508,9 @@ impl Decoder { } } CodecFormat::H265 => { - #[cfg(feature = "gpucodec")] - if !valid && enable_gpucodec_option() && _luid.clone().unwrap_or_default() != 0 { - match GpuDecoder::new(format, _luid) { + #[cfg(feature = "vram")] + if !valid && enable_vram_option() && _luid.clone().unwrap_or_default() != 0 { + match VRamDecoder::new(format, _luid) { Ok(v) => h265_vram = Some(v), Err(e) => log::error!("create H265 vram decoder failed: {}", e), } @@ -518,7 +518,7 @@ impl Decoder { } #[cfg(feature = "hwcodec")] if !valid && enable_hwcodec_option() { - match HwDecoder::new(format) { + match HwRamDecoder::new(format) { Ok(v) => h265_ram = Some(v), Err(e) => log::error!("create H265 ram decoder failed: {}", e), } @@ -550,9 +550,9 @@ impl Decoder { h264_ram, #[cfg(feature = "hwcodec")] h265_ram, - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] h264_vram, - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] h265_vram, #[cfg(feature = "mediacodec")] h264_media_codec, @@ -604,31 +604,31 @@ impl Decoder { bail!("av1 decoder not available"); } } - #[cfg(any(feature = "hwcodec", feature = "gpucodec"))] + #[cfg(any(feature = "hwcodec", feature = "vram"))] video_frame::Union::H264s(h264s) => { *chroma = Some(Chroma::I420); - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] if let Some(decoder) = &mut self.h264_vram { *_pixelbuffer = false; - return Decoder::handle_gpu_video_frame(decoder, h264s, _texture); + return Decoder::handle_vram_video_frame(decoder, h264s, _texture); } #[cfg(feature = "hwcodec")] if let Some(decoder) = &mut self.h264_ram { - return Decoder::handle_hw_video_frame(decoder, h264s, rgb, &mut self.i420); + return Decoder::handle_hwram_video_frame(decoder, h264s, rgb, &mut self.i420); } Err(anyhow!("don't support h264!")) } - #[cfg(any(feature = "hwcodec", feature = "gpucodec"))] + #[cfg(any(feature = "hwcodec", feature = "vram"))] video_frame::Union::H265s(h265s) => { *chroma = Some(Chroma::I420); - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] if let Some(decoder) = &mut self.h265_vram { *_pixelbuffer = false; - return Decoder::handle_gpu_video_frame(decoder, h265s, _texture); + return Decoder::handle_vram_video_frame(decoder, h265s, _texture); } #[cfg(feature = "hwcodec")] if let Some(decoder) = &mut self.h265_ram { - return Decoder::handle_hw_video_frame(decoder, h265s, rgb, &mut self.i420); + return Decoder::handle_hwram_video_frame(decoder, h265s, rgb, &mut self.i420); } Err(anyhow!("don't support h265!")) } @@ -710,8 +710,8 @@ impl Decoder { // rgb [in/out] fmt and stride must be set in ImageRgb #[cfg(feature = "hwcodec")] - fn handle_hw_video_frame( - decoder: &mut HwDecoder, + fn handle_hwram_video_frame( + decoder: &mut HwRamDecoder, frames: &EncodedVideoFrames, rgb: &mut ImageRgb, i420: &mut Vec, @@ -728,9 +728,9 @@ impl Decoder { return Ok(ret); } - #[cfg(feature = "gpucodec")] - fn handle_gpu_video_frame( - decoder: &mut GpuDecoder, + #[cfg(feature = "vram")] + fn handle_vram_video_frame( + decoder: &mut VRamDecoder, frames: &EncodedVideoFrames, texture: &mut *mut c_void, ) -> ResultType { @@ -796,8 +796,8 @@ pub fn enable_hwcodec_option() -> bool { } return true; // default is true } -#[cfg(feature = "gpucodec")] -pub fn enable_gpucodec_option() -> bool { +#[cfg(feature = "vram")] +pub fn enable_vram_option() -> bool { if let Some(v) = Config2::get().options.get("enable-hwcodec") { return v != "N"; } diff --git a/libs/scrap/src/common/convert.rs b/libs/scrap/src/common/convert.rs index 09640e35c..66bd8d358 100644 --- a/libs/scrap/src/common/convert.rs +++ b/libs/scrap/src/common/convert.rs @@ -18,7 +18,7 @@ pub mod hw { use super::*; use crate::ImageFormat; #[cfg(target_os = "windows")] - use hwcodec::{ffmpeg::ffmpeg_linesize_offset_length, AVPixelFormat}; + use hwcodec::{ffmpeg::AVPixelFormat, ffmpeg_ram::ffmpeg_linesize_offset_length}; #[cfg(target_os = "windows")] pub fn hw_nv12_to( @@ -222,9 +222,7 @@ pub fn convert_to_yuv( ); } } - let align = |x:usize| { - (x + 63) / 64 * 64 - }; + let align = |x: usize| (x + 63) / 64 * 64; match (src_pixfmt, dst_fmt.pixfmt) { (crate::Pixfmt::BGRA, crate::Pixfmt::I420) | (crate::Pixfmt::RGBA, crate::Pixfmt::I420) => { @@ -282,7 +280,8 @@ pub fn convert_to_yuv( let dst_stride_u = dst_fmt.stride[1]; let dst_stride_v = dst_fmt.stride[2]; dst.resize( - align(dst_fmt.h) * (align(dst_stride_y) + align(dst_stride_u) + align(dst_stride_v)), + align(dst_fmt.h) + * (align(dst_stride_y) + align(dst_stride_u) + align(dst_stride_v)), 0, ); let dst_y = dst.as_mut_ptr(); diff --git a/libs/scrap/src/common/dxgi.rs b/libs/scrap/src/common/dxgi.rs index ec81dbff9..ae2f1130f 100644 --- a/libs/scrap/src/common/dxgi.rs +++ b/libs/scrap/src/common/dxgi.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "gpucodec")] +#[cfg(feature = "vram")] use crate::AdapterDevice; use crate::{common::TraitCapturer, dxgi, Frame, Pixfmt}; use std::{ @@ -57,12 +57,12 @@ impl TraitCapturer for Capturer { self.inner.set_gdi() } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn device(&self) -> AdapterDevice { self.inner.device() } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn set_output_texture(&mut self, texture: bool) { self.inner.set_output_texture(texture); } @@ -197,7 +197,7 @@ impl Display { self.origin() == (0, 0) } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] pub fn adapter_luid(&self) -> Option { self.0.adapter_luid() } @@ -247,11 +247,11 @@ impl TraitCapturer for CapturerMag { false } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn device(&self) -> AdapterDevice { AdapterDevice::default() } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn set_output_texture(&mut self, _texture: bool) {} } diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 4f19d8d6b..c72e69822 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -9,20 +9,21 @@ use hbb_common::{ config::HwCodecConfig, log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, VideoFrame}, - ResultType, + serde_derive::{Deserialize, Serialize}, + serde_json, ResultType, }; use hwcodec::{ - decode::{DecodeContext, DecodeFrame, Decoder}, - encode::{EncodeContext, EncodeFrame, Encoder}, - ffmpeg::{CodecInfo, CodecInfos, DataFormat}, - AVPixelFormat, - Quality::{self, *}, - RateControl::{self, *}, + common::DataFormat, + ffmpeg::AVPixelFormat, + ffmpeg_ram::{ + decode::{DecodeContext, DecodeFrame, Decoder}, + encode::{EncodeContext, EncodeFrame, Encoder}, + CodecInfo, CodecInfos, + Quality::{self, *}, + RateControl::{self, *}, + }, }; -const CFG_KEY_ENCODER: &str = "bestHwEncoders"; -const CFG_KEY_DECODER: &str = "bestHwDecoders"; - const DEFAULT_PIXFMT: AVPixelFormat = AVPixelFormat::AV_PIX_FMT_NV12; pub const DEFAULT_TIME_BASE: [i32; 2] = [1, 30]; const DEFAULT_GOP: i32 = i32::MAX; @@ -30,7 +31,7 @@ const DEFAULT_HW_QUALITY: Quality = Quality_Default; const DEFAULT_RC: RateControl = RC_DEFAULT; #[derive(Debug, Clone)] -pub struct HwEncoderConfig { +pub struct HwRamEncoderConfig { pub name: String, pub width: usize, pub height: usize, @@ -38,7 +39,7 @@ pub struct HwEncoderConfig { pub keyframe_interval: Option, } -pub struct HwEncoder { +pub struct HwRamEncoder { encoder: Encoder, name: String, pub format: DataFormat, @@ -48,13 +49,13 @@ pub struct HwEncoder { bitrate: u32, //kbs } -impl EncoderApi for HwEncoder { +impl EncoderApi for HwRamEncoder { fn new(cfg: EncoderCfg, _i444: bool) -> ResultType where Self: Sized, { match cfg { - EncoderCfg::HW(config) => { + EncoderCfg::HWRAM(config) => { let b = Self::convert_quality(config.quality); let base_bitrate = base_bitrate(config.width as _, config.height as _); let mut bitrate = base_bitrate * b / 100; @@ -85,7 +86,7 @@ impl EncoderApi for HwEncoder { } }; match Encoder::new(ctx.clone()) { - Ok(encoder) => Ok(HwEncoder { + Ok(encoder) => Ok(HwRamEncoder { encoder, name: config.name, format, @@ -95,7 +96,7 @@ impl EncoderApi for HwEncoder { bitrate, }), Err(_) => { - HwCodecConfig::clear(); + HwCodecConfig::clear_ram(); Err(anyhow!(format!("Failed to create encoder"))) } } @@ -126,6 +127,7 @@ impl EncoderApi for HwEncoder { match self.format { DataFormat::H264 => vf.set_h264s(frames), DataFormat::H265 => vf.set_h265s(frames), + _ => bail!("unsupported format: {:?}", self.format), } Ok(vf) } else { @@ -160,7 +162,7 @@ impl EncoderApi for HwEncoder { } } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn input_texture(&self) -> bool { false } @@ -184,9 +186,9 @@ impl EncoderApi for HwEncoder { } } -impl HwEncoder { +impl HwRamEncoder { pub fn best() -> CodecInfos { - get_config(CFG_KEY_ENCODER).unwrap_or(CodecInfos { + get_config().map(|c| c.e).unwrap_or(CodecInfos { h264: None, h265: None, }) @@ -214,20 +216,14 @@ impl HwEncoder { } } -pub struct HwDecoder { +pub struct HwRamDecoder { decoder: Decoder, pub info: CodecInfo, } -#[derive(Default)] -pub struct HwDecoders { - pub h264: Option, - pub h265: Option, -} - -impl HwDecoder { +impl HwRamDecoder { pub fn best() -> CodecInfos { - get_config(CFG_KEY_DECODER).unwrap_or(CodecInfos { + get_config().map(|c| c.d).unwrap_or(CodecInfos { h264: None, h265: None, }) @@ -235,7 +231,7 @@ impl HwDecoder { pub fn new(format: CodecFormat) -> ResultType { log::info!("try create {format:?} ram decoder"); - let best = HwDecoder::best(); + let best = HwRamDecoder::best(); let info = match format { CodecFormat::H264 => { if let Some(info) = best.h264 { @@ -259,26 +255,26 @@ impl HwDecoder { thread_count: codec_thread_num(16) as _, }; match Decoder::new(ctx) { - Ok(decoder) => Ok(HwDecoder { decoder, info }), + Ok(decoder) => Ok(HwRamDecoder { decoder, info }), Err(_) => { - HwCodecConfig::clear(); + HwCodecConfig::clear_ram(); Err(anyhow!(format!("Failed to create decoder"))) } } } - pub fn decode(&mut self, data: &[u8]) -> ResultType> { + pub fn decode(&mut self, data: &[u8]) -> ResultType> { match self.decoder.decode(data) { - Ok(v) => Ok(v.iter().map(|f| HwDecoderImage { frame: f }).collect()), + Ok(v) => Ok(v.iter().map(|f| HwRamDecoderImage { frame: f }).collect()), Err(e) => Err(anyhow!(e)), } } } -pub struct HwDecoderImage<'a> { +pub struct HwRamDecoderImage<'a> { frame: &'a DecodeFrame, } -impl HwDecoderImage<'_> { +impl HwRamDecoderImage<'_> { // rgb [in/out] fmt and stride must be set in ImageRgb pub fn to_fmt(&self, rgb: &mut ImageRgb, i420: &mut Vec) -> ResultType<()> { let frame = self.frame; @@ -332,23 +328,24 @@ impl HwDecoderImage<'_> { } } -fn get_config(k: &str) -> ResultType { - let v = HwCodecConfig::load() - .options - .get(k) - .unwrap_or(&"".to_owned()) - .to_owned(); - match CodecInfos::deserialize(&v) { +#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] +struct Available { + e: CodecInfos, + d: CodecInfos, +} + +fn get_config() -> ResultType { + match serde_json::from_str(&HwCodecConfig::load().ram) { Ok(v) => Ok(v), - Err(_) => Err(anyhow!("Failed to get config:{}", k)), + Err(e) => Err(anyhow!("Failed to get config:{e:?}")), } } pub fn check_available_hwcodec() { let ctx = EncodeContext { name: String::from(""), - width: 1920, - height: 1080, + width: 1280, + height: 720, pixfmt: DEFAULT_PIXFMT, align: HW_STRIDE_ALIGN as _, bitrate: 0, @@ -358,27 +355,19 @@ pub fn check_available_hwcodec() { rc: DEFAULT_RC, thread_count: 4, }; - let encoders = CodecInfo::score(Encoder::available_encoders(ctx)); - let decoders = CodecInfo::score(Decoder::available_decoders()); - - if let Ok(old_encoders) = get_config(CFG_KEY_ENCODER) { - if let Ok(old_decoders) = get_config(CFG_KEY_DECODER) { - if encoders == old_encoders && decoders == old_decoders { - return; - } - } + #[cfg(feature = "vram")] + 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 ram = Available { + e: encoders, + d: decoders, + }; + if let Ok(ram) = serde_json::to_string_pretty(&ram) { + HwCodecConfig { ram, vram }.store(); } - - if let Ok(encoders) = encoders.serialize() { - if let Ok(decoders) = decoders.serialize() { - let mut config = HwCodecConfig::load(); - config.options.insert(CFG_KEY_ENCODER.to_owned(), encoders); - config.options.insert(CFG_KEY_DECODER.to_owned(), decoders); - config.store(); - return; - } - } - log::error!("Failed to serialize codec info"); } pub fn hwcodec_new_check_process() { diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 85ecddfbd..6ba7d991f 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -37,13 +37,13 @@ cfg_if! { pub mod codec; pub mod convert; -#[cfg(feature = "gpucodec")] -pub mod gpucodec; #[cfg(feature = "hwcodec")] pub mod hwcodec; #[cfg(feature = "mediacodec")] pub mod mediacodec; pub mod vpxcodec; +#[cfg(feature = "vram")] +pub mod vram; pub use self::convert::*; pub const STRIDE_ALIGN: usize = 64; // commonly used in libvpx vpx_img_alloc caller pub const HW_STRIDE_ALIGN: usize = 0; // recommended by av_frame_get_buffer @@ -111,10 +111,10 @@ pub trait TraitCapturer { #[cfg(windows)] fn set_gdi(&mut self) -> bool; - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn device(&self) -> AdapterDevice; - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn set_output_texture(&mut self, texture: bool); } @@ -245,10 +245,10 @@ pub enum CodecName { VP8, VP9, AV1, - H264HW(String), - H265HW(String), - H264GPU, - H265GPU, + H264RAM(String), + H265RAM(String), + H264VRAM, + H265VRAM, } #[derive(PartialEq, Debug, Clone, Copy)] @@ -280,8 +280,8 @@ impl From<&CodecName> for CodecFormat { CodecName::VP8 => Self::VP8, CodecName::VP9 => Self::VP9, CodecName::AV1 => Self::AV1, - CodecName::H264HW(_) | CodecName::H264GPU => Self::H264, - CodecName::H265HW(_) | CodecName::H265GPU => Self::H265, + CodecName::H264RAM(_) | CodecName::H264VRAM => Self::H264, + CodecName::H265RAM(_) | CodecName::H265VRAM => Self::H265, } } } diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index a6cb73d07..a3e7e99e6 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -207,7 +207,7 @@ impl EncoderApi for VpxEncoder { self.yuvfmt.clone() } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn input_texture(&self) -> bool { false } diff --git a/libs/scrap/src/common/gpucodec.rs b/libs/scrap/src/common/vram.rs similarity index 68% rename from libs/scrap/src/common/gpucodec.rs rename to libs/scrap/src/common/vram.rs index 126a027b8..daeece519 100644 --- a/libs/scrap/src/common/gpucodec.rs +++ b/libs/scrap/src/common/vram.rs @@ -5,24 +5,24 @@ use std::{ }; use crate::{ - codec::{base_bitrate, enable_gpucodec_option, EncoderApi, EncoderCfg, Quality}, + codec::{base_bitrate, enable_vram_option, EncoderApi, EncoderCfg, Quality}, AdapterDevice, CodecFormat, CodecName, EncodeInput, EncodeYuvFormat, Pixfmt, }; -use gpucodec::gpu_common::{ - self, Available, DecodeContext, DynamicContext, EncodeContext, FeatureContext, MAX_GOP, -}; -use gpucodec::{ - decode::{self, DecodeFrame, Decoder}, - encode::{self, EncodeFrame, Encoder}, -}; use hbb_common::{ - allow_err, anyhow::{anyhow, bail, Context}, bytes::Bytes, log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, VideoFrame}, ResultType, }; +use hwcodec::{ + common::{DataFormat, Driver, MAX_GOP}, + native::{ + decode::{self, DecodeFrame, Decoder}, + encode::{self, EncodeFrame, Encoder}, + Available, DecodeContext, DynamicContext, EncodeContext, FeatureContext, + }, +}; const OUTPUT_SHARED_HANDLE: bool = false; @@ -35,31 +35,31 @@ lazy_static::lazy_static! { } #[derive(Debug, Clone)] -pub struct GpuEncoderConfig { +pub struct VRamEncoderConfig { pub device: AdapterDevice, pub width: usize, pub height: usize, pub quality: Quality, - pub feature: gpucodec::gpu_common::FeatureContext, + pub feature: FeatureContext, pub keyframe_interval: Option, } -pub struct GpuEncoder { +pub struct VRamEncoder { encoder: Encoder, - pub format: gpu_common::DataFormat, + pub format: DataFormat, ctx: EncodeContext, bitrate: u32, last_frame_len: usize, same_bad_len_counter: usize, } -impl EncoderApi for GpuEncoder { +impl EncoderApi for VRamEncoder { fn new(cfg: EncoderCfg, _i444: bool) -> ResultType where Self: Sized, { match cfg { - EncoderCfg::GPU(config) => { + EncoderCfg::VRAM(config) => { let b = Self::convert_quality(config.quality, &config.feature); let base_bitrate = base_bitrate(config.width as _, config.height as _); let mut bitrate = base_bitrate * b / 100; @@ -79,7 +79,7 @@ impl EncoderApi for GpuEncoder { }, }; match Encoder::new(ctx.clone()) { - Ok(encoder) => Ok(GpuEncoder { + Ok(encoder) => Ok(VRamEncoder { encoder, ctx, format: config.feature.data_format, @@ -88,7 +88,7 @@ impl EncoderApi for GpuEncoder { same_bad_len_counter: 0, }), Err(_) => { - hbb_common::config::GpucodecConfig::clear(); + hbb_common::config::HwCodecConfig::clear_vram(); Err(anyhow!(format!("Failed to create encoder"))) } } @@ -138,8 +138,8 @@ impl EncoderApi for GpuEncoder { ..Default::default() }; match self.format { - gpu_common::DataFormat::H264 => vf.set_h264s(frames), - gpu_common::DataFormat::H265 => vf.set_h265s(frames), + DataFormat::H264 => vf.set_h264s(frames), + DataFormat::H265 => vf.set_h265s(frames), _ => bail!("{:?} not supported", self.format), } Ok(vf) @@ -160,7 +160,7 @@ impl EncoderApi for GpuEncoder { } } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn input_texture(&self) -> bool { true } @@ -181,11 +181,11 @@ impl EncoderApi for GpuEncoder { } fn support_abr(&self) -> bool { - self.ctx.f.driver != gpu_common::EncodeDriver::VPL + self.ctx.f.driver != Driver::VPL } } -impl GpuEncoder { +impl VRamEncoder { pub fn try_get(device: &AdapterDevice, name: CodecName) -> Option { let v: Vec<_> = Self::available(name) .drain(..) @@ -201,12 +201,12 @@ impl GpuEncoder { pub fn available(name: CodecName) -> Vec { let not_use = ENOCDE_NOT_USE.lock().unwrap().clone(); if not_use.values().any(|not_use| *not_use) { - log::info!("currently not use gpucodec encoders: {not_use:?}"); + log::info!("currently not use vram encoders: {not_use:?}"); return vec![]; } let data_format = match name { - CodecName::H264GPU => gpu_common::DataFormat::H264, - CodecName::H265GPU => gpu_common::DataFormat::H265, + CodecName::H264VRAM => DataFormat::H264, + CodecName::H265VRAM => DataFormat::H265, _ => return vec![], }; let Ok(displays) = crate::Display::all() else { @@ -252,27 +252,21 @@ impl GpuEncoder { pub fn convert_quality(quality: Quality, f: &FeatureContext) -> u32 { match quality { Quality::Best => { - if f.driver == gpu_common::EncodeDriver::VPL - && f.data_format == gpu_common::DataFormat::H264 - { + if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { 200 } else { 150 } } Quality::Balanced => { - if f.driver == gpu_common::EncodeDriver::VPL - && f.data_format == gpu_common::DataFormat::H264 - { + if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { 150 } else { 100 } } Quality::Low => { - if f.driver == gpu_common::EncodeDriver::VPL - && f.data_format == gpu_common::DataFormat::H264 - { + if f.driver == Driver::VPL && f.data_format == DataFormat::H264 { 75 } else { 50 @@ -283,7 +277,7 @@ impl GpuEncoder { } pub fn set_not_use(display: usize, not_use: bool) { - log::info!("set display#{display} not use gpucodec encode to {not_use}"); + log::info!("set display#{display} not use vram encode to {not_use}"); ENOCDE_NOT_USE.lock().unwrap().insert(display, not_use); } @@ -292,17 +286,11 @@ impl GpuEncoder { } } -pub struct GpuDecoder { +pub struct VRamDecoder { decoder: Decoder, } -#[derive(Default)] -pub struct GpuDecoders { - pub h264: Option, - pub h265: Option, -} - -impl GpuDecoder { +impl VRamDecoder { pub fn try_get(format: CodecFormat, luid: Option) -> Option { let v: Vec<_> = Self::available(format, luid); if v.len() > 0 { @@ -315,8 +303,8 @@ impl GpuDecoder { pub fn available(format: CodecFormat, luid: Option) -> Vec { let luid = luid.unwrap_or_default(); let data_format = match format { - CodecFormat::H264 => gpu_common::DataFormat::H264, - CodecFormat::H265 => gpu_common::DataFormat::H265, + CodecFormat::H264 => DataFormat::H264, + CodecFormat::H265 => DataFormat::H265, _ => return vec![], }; get_available_config() @@ -328,15 +316,13 @@ impl GpuDecoder { } pub fn possible_available_without_check() -> (bool, bool) { - if !enable_gpucodec_option() { + if !enable_vram_option() { return (false, false); } let v = get_available_config().map(|c| c.d).unwrap_or_default(); ( - v.iter() - .any(|d| d.data_format == gpu_common::DataFormat::H264), - v.iter() - .any(|d| d.data_format == gpu_common::DataFormat::H265), + v.iter().any(|d| d.data_format == DataFormat::H264), + v.iter().any(|d| d.data_format == DataFormat::H265), ) } @@ -346,7 +332,7 @@ impl GpuDecoder { match Decoder::new(ctx) { Ok(decoder) => Ok(Self { decoder }), Err(_) => { - hbb_common::config::GpucodecConfig::clear(); + hbb_common::config::HwCodecConfig::clear_vram(); Err(anyhow!(format!( "Failed to create decoder, format: {:?}", format @@ -354,33 +340,33 @@ impl GpuDecoder { } } } - pub fn decode(&mut self, data: &[u8]) -> ResultType> { + pub fn decode(&mut self, data: &[u8]) -> ResultType> { match self.decoder.decode(data) { - Ok(v) => Ok(v.iter().map(|f| GpuDecoderImage { frame: f }).collect()), + Ok(v) => Ok(v.iter().map(|f| VRamDecoderImage { frame: f }).collect()), Err(e) => Err(anyhow!(e)), } } } -pub struct GpuDecoderImage<'a> { +pub struct VRamDecoderImage<'a> { pub frame: &'a DecodeFrame, } -impl GpuDecoderImage<'_> {} +impl VRamDecoderImage<'_> {} fn get_available_config() -> ResultType { - let available = hbb_common::config::GpucodecConfig::load().available; + let available = hbb_common::config::HwCodecConfig::load().vram; match Available::deserialize(&available) { Ok(v) => Ok(v), Err(_) => Err(anyhow!("Failed to deserialize:{}", available)), } } -pub fn check_available_gpucodec() { +pub(crate) fn check_available_vram() -> String { let d = DynamicContext { device: None, - width: 1920, - height: 1080, + width: 1280, + height: 720, kbitrate: 5000, framerate: 60, gop: MAX_GOP as _, @@ -391,54 +377,5 @@ pub fn check_available_gpucodec() { e: encoders, d: decoders, }; - - if let Ok(available) = available.serialize() { - let mut config = hbb_common::config::GpucodecConfig::load(); - config.available = available; - config.store(); - return; - } - log::error!("Failed to serialize gpucodec"); -} - -pub fn gpucodec_new_check_process() { - use std::sync::Once; - - static ONCE: Once = Once::new(); - ONCE.call_once(|| { - std::thread::spawn(move || { - // Remove to avoid checking process errors - // But when the program is just started, the configuration file has not been updated, and the new connection will read an empty configuration - hbb_common::config::GpucodecConfig::clear(); - if let Ok(exe) = std::env::current_exe() { - let arg = "--check-gpucodec-config"; - if let Ok(mut child) = std::process::Command::new(exe).arg(arg).spawn() { - // wait up to 30 seconds - for _ in 0..30 { - std::thread::sleep(std::time::Duration::from_secs(1)); - if let Ok(Some(_)) = child.try_wait() { - break; - } - } - allow_err!(child.kill()); - std::thread::sleep(std::time::Duration::from_millis(30)); - match child.try_wait() { - Ok(Some(status)) => { - log::info!("Check gpucodec config, exit with: {status}") - } - Ok(None) => { - log::info!( - "Check gpucodec config, status not ready yet, let's really wait" - ); - let res = child.wait(); - log::info!("Check gpucodec config, wait result: {res:?}"); - } - Err(e) => { - log::error!("Check gpucodec config, error attempting to wait: {e}") - } - } - } - }; - }); - }); + available.serialize().unwrap_or_default() } diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 6f1b3411e..abd1f5026 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -185,7 +185,7 @@ impl Capturer { self.gdi_capturer.take(); } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] pub fn set_output_texture(&mut self, texture: bool) { self.output_texture = texture; } @@ -620,7 +620,7 @@ impl Display { ) } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] pub fn adapter_luid(&self) -> Option { unsafe { if !self.adapter.is_null() { diff --git a/src/client.rs b/src/client.rs index 0ff275bc4..f55819a78 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1038,9 +1038,9 @@ pub struct VideoHandler { impl VideoHandler { /// Create a new video handler. pub fn new(format: CodecFormat, _display: usize) -> Self { - #[cfg(all(feature = "gpucodec", feature = "flutter"))] + #[cfg(all(feature = "vram", feature = "flutter"))] let luid = crate::flutter::get_adapter_luid(); - #[cfg(not(all(feature = "gpucodec", feature = "flutter")))] + #[cfg(not(all(feature = "vram", feature = "flutter")))] let luid = Default::default(); log::info!("new video handler for display #{_display}, format: {format:?}, luid: {luid:?}"); VideoHandler { @@ -1097,9 +1097,9 @@ impl VideoHandler { /// Reset the decoder, change format if it is Some pub fn reset(&mut self, format: Option) { - #[cfg(all(feature = "flutter", feature = "gpucodec"))] + #[cfg(all(feature = "flutter", feature = "vram"))] let luid = crate::flutter::get_adapter_luid(); - #[cfg(not(all(feature = "flutter", feature = "gpucodec")))] + #[cfg(not(all(feature = "flutter", feature = "vram")))] let luid = None; let format = format.unwrap_or(self.decoder.format()); self.decoder = Decoder::new(format, luid); diff --git a/src/core_main.rs b/src/core_main.rs index 2d36097eb..cd27ec0ca 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -410,10 +410,6 @@ pub fn core_main() -> Option> { #[cfg(feature = "hwcodec")] scrap::hwcodec::check_available_hwcodec(); return None; - } else if args[0] == "--check-gpucodec-config" { - #[cfg(feature = "gpucodec")] - scrap::gpucodec::check_available_gpucodec(); - return None; } else if args[0] == "--cm" { // call connection manager to establish connections // meanwhile, return true to call flutter window to show control panel diff --git a/src/flutter.rs b/src/flutter.rs index ca4057d36..f733bb70c 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -4,7 +4,7 @@ use crate::{ ui_session_interface::{io_loop, InvokeUiSession, Session}, }; use flutter_rust_bridge::StreamSink; -#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] +#[cfg(any(feature = "flutter_texture_render", feature = "vram"))] use hbb_common::dlopen::{ symbor::{Library, Symbol}, Error as LibError, @@ -16,7 +16,7 @@ use hbb_common::{ use serde::Serialize; use serde_json::json; -#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] +#[cfg(any(feature = "flutter_texture_render", feature = "vram"))] use std::os::raw::c_void; use std::{ @@ -63,7 +63,7 @@ lazy_static::lazy_static! { pub static ref TEXTURE_RGBA_RENDERER_PLUGIN: Result = Library::open_self(); } -#[cfg(all(target_os = "windows", feature = "gpucodec"))] +#[cfg(all(target_os = "windows", feature = "vram"))] lazy_static::lazy_static! { pub static ref TEXTURE_GPU_RENDERER_PLUGIN: Result = Library::open("flutter_gpu_texture_renderer_plugin.dll"); } @@ -168,15 +168,15 @@ pub unsafe extern "C" fn get_rustdesk_app_name(buffer: *mut u16, length: i32) -> #[derive(Default)] struct SessionHandler { event_stream: Option>, - #[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] + #[cfg(any(feature = "flutter_texture_render", feature = "vram"))] renderer: VideoRenderer, } -#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] +#[cfg(any(feature = "flutter_texture_render", feature = "vram"))] #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum RenderType { PixelBuffer, - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] Texture, } @@ -214,41 +214,41 @@ pub type FlutterRgbaRendererPluginOnRgba = unsafe extern "C" fn( dst_rgba_stride: c_int, ); -#[cfg(feature = "gpucodec")] +#[cfg(feature = "vram")] pub type FlutterGpuTextureRendererPluginCApiSetTexture = unsafe extern "C" fn(output: *mut c_void, texture: *mut c_void); -#[cfg(feature = "gpucodec")] +#[cfg(feature = "vram")] pub type FlutterGpuTextureRendererPluginCApiGetAdapterLuid = unsafe extern "C" fn() -> i64; #[cfg(feature = "flutter_texture_render")] pub(super) type TextureRgbaPtr = usize; -#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] +#[cfg(any(feature = "flutter_texture_render", feature = "vram"))] struct DisplaySessionInfo { // TextureRgba pointer in flutter native. #[cfg(feature = "flutter_texture_render")] texture_rgba_ptr: TextureRgbaPtr, #[cfg(feature = "flutter_texture_render")] size: (usize, usize), - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] gpu_output_ptr: usize, notify_render_type: Option, } // Video Texture Renderer in Flutter -#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] +#[cfg(any(feature = "flutter_texture_render", feature = "vram"))] #[derive(Clone)] struct VideoRenderer { is_support_multi_ui_session: bool, map_display_sessions: Arc>>, #[cfg(feature = "flutter_texture_render")] on_rgba_func: Option>, - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] on_texture_func: Option>, } -#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] +#[cfg(any(feature = "flutter_texture_render", feature = "vram"))] impl Default for VideoRenderer { fn default() -> Self { #[cfg(feature = "flutter_texture_render")] @@ -270,7 +270,7 @@ impl Default for VideoRenderer { None } }; - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] let on_texture_func = match &*TEXTURE_GPU_RENDERER_PLUGIN { Ok(lib) => { let find_sym_res = unsafe { @@ -297,13 +297,13 @@ impl Default for VideoRenderer { is_support_multi_ui_session: false, #[cfg(feature = "flutter_texture_render")] on_rgba_func, - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] on_texture_func, } } } -#[cfg(any(feature = "flutter_texture_render", feature = "gpucodec"))] +#[cfg(any(feature = "flutter_texture_render", feature = "vram"))] impl VideoRenderer { #[inline] #[cfg(feature = "flutter_texture_render")] @@ -318,7 +318,7 @@ impl VideoRenderer { DisplaySessionInfo { texture_rgba_ptr: usize::default(), size: (width, height), - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] gpu_output_ptr: usize::default(), notify_render_type: None, }, @@ -345,7 +345,7 @@ impl VideoRenderer { DisplaySessionInfo { texture_rgba_ptr: ptr as _, size: (0, 0), - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] gpu_output_ptr: usize::default(), notify_render_type: None, }, @@ -355,7 +355,7 @@ impl VideoRenderer { } } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] pub fn register_gpu_output(&self, display: usize, ptr: usize) { let mut sessions_lock = self.map_display_sessions.write().unwrap(); if ptr == 0 { @@ -434,7 +434,7 @@ impl VideoRenderer { } } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] pub fn on_texture(&self, display: usize, texture: *mut c_void) -> bool { let mut write_lock = self.map_display_sessions.write().unwrap(); let opt_info = if !self.is_support_multi_ui_session { @@ -793,7 +793,7 @@ impl InvokeUiSession for FlutterHandler { } #[inline] - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn on_texture(&self, display: usize, texture: *mut c_void) { for (_, session) in self.session_handlers.read().unwrap().iter() { if session.renderer.on_texture(display, texture) { @@ -1073,9 +1073,9 @@ pub fn session_add( Some(switch_uuid.to_string()) }; - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] let adapter_luid = get_adapter_luid(); - #[cfg(not(feature = "gpucodec"))] + #[cfg(not(feature = "vram"))] let adapter_luid = None; session.lc.write().unwrap().initialize( @@ -1453,7 +1453,7 @@ pub fn session_register_pixelbuffer_texture(_session_id: SessionID, _display: us #[inline] pub fn session_register_gpu_texture(_session_id: SessionID, _display: usize, _output_ptr: usize) { - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] for s in sessions::get_sessions() { if let Some(h) = s .ui_handler @@ -1468,7 +1468,7 @@ pub fn session_register_gpu_texture(_session_id: SessionID, _display: usize, _ou } } -#[cfg(feature = "gpucodec")] +#[cfg(feature = "vram")] pub fn get_adapter_luid() -> Option { let get_adapter_luid_func = match &*TEXTURE_GPU_RENDERER_PLUGIN { Ok(lib) => { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index b60be6bff..1c9e39c13 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1295,8 +1295,8 @@ pub fn main_has_hwcodec() -> SyncReturn { SyncReturn(has_hwcodec()) } -pub fn main_has_gpucodec() -> SyncReturn { - SyncReturn(has_gpucodec()) +pub fn main_has_vram() -> SyncReturn { + SyncReturn(has_vram()) } pub fn main_supported_hwdecodings() -> SyncReturn { @@ -1782,7 +1782,7 @@ pub fn main_has_file_clipboard() -> SyncReturn { } pub fn main_has_gpu_texture_render() -> SyncReturn { - SyncReturn(cfg!(feature = "gpucodec")) + SyncReturn(cfg!(feature = "vram")) } pub fn cm_init() { diff --git a/src/server.rs b/src/server.rs index ef89c5e3b..9daf24262 100644 --- a/src/server.rs +++ b/src/server.rs @@ -450,8 +450,6 @@ pub async fn start_server(is_server: bool) { } #[cfg(feature = "hwcodec")] scrap::hwcodec::hwcodec_new_check_process(); - #[cfg(feature = "gpucodec")] - scrap::gpucodec::gpucodec_new_check_process(); #[cfg(windows)] hbb_common::platform::windows::start_cpu_performance_monitor(); diff --git a/src/server/connection.rs b/src/server/connection.rs index ead9d22de..1a30730af 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -235,7 +235,7 @@ pub struct Connection { auto_disconnect_timer: Option<(Instant, u64)>, authed_conn_id: Option, file_remove_log_control: FileRemoveLogControl, - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] supported_encoding_flag: (bool, Option), services_subed: bool, delayed_read_dir: Option<(String, bool)>, @@ -386,7 +386,7 @@ impl Connection { auto_disconnect_timer: None, authed_conn_id: None, file_remove_log_control: FileRemoveLogControl::new(id), - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] supported_encoding_flag: (false, None), services_subed: false, delayed_read_dir: None, @@ -691,7 +691,7 @@ impl Connection { } } conn.file_remove_log_control.on_timer().drain(..).map(|x| conn.send_to_cm(x)).count(); - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] conn.update_supported_encoding(); } _ = test_delay_timer.tick() => { @@ -3097,9 +3097,9 @@ impl Connection { .map(|t| t.0 = Instant::now()); } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn update_supported_encoding(&mut self) { - let not_use = Some(scrap::gpucodec::GpuEncoder::not_use()); + let not_use = Some(scrap::vram::VRamEncoder::not_use()); if !self.authorized || self.supported_encoding_flag.0 && self.supported_encoding_flag.1 == not_use { diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 8ed581020..e181ac7fe 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -8,7 +8,7 @@ use hbb_common::{ tokio::{self, sync::mpsc}, ResultType, }; -#[cfg(feature = "gpucodec")] +#[cfg(feature = "vram")] use scrap::AdapterDevice; use scrap::{Capturer, Frame, TraitCapturer, TraitPixelBuffer}; use shared_memory::*; @@ -744,12 +744,12 @@ pub mod client { true } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn device(&self) -> AdapterDevice { AdapterDevice::default() } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] fn set_output_texture(&mut self, _texture: bool) {} } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index f4ba3a4e6..65411d566 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -42,10 +42,10 @@ use hbb_common::{ Mutex as TokioMutex, }, }; -#[cfg(feature = "gpucodec")] -use scrap::gpucodec::{GpuEncoder, GpuEncoderConfig}; #[cfg(feature = "hwcodec")] -use scrap::hwcodec::{HwEncoder, HwEncoderConfig}; +use scrap::hwcodec::{HwRamEncoder, HwRamEncoderConfig}; +#[cfg(feature = "vram")] +use scrap::vram::{VRamEncoder, VRamEncoderConfig}; #[cfg(not(windows))] use scrap::Capturer; use scrap::{ @@ -430,7 +430,7 @@ fn run(vs: VideoService) -> ResultType<()> { Ok(x) => encoder = x, Err(err) => bail!("Failed to create encoder: {}", err), } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] c.set_output_texture(encoder.input_texture()); VIDEO_QOS.lock().unwrap().store_bitrate(encoder.bitrate()); VIDEO_QOS @@ -490,9 +490,9 @@ fn run(vs: VideoService) -> ResultType<()> { if Encoder::use_i444(&encoder_cfg) != use_i444 { bail!("SWITCH"); } - #[cfg(all(windows, feature = "gpucodec"))] - if c.is_gdi() && (codec_name == CodecName::H264GPU || codec_name == CodecName::H265GPU) { - log::info!("changed to gdi when using gpucodec"); + #[cfg(all(windows, feature = "vram"))] + if c.is_gdi() && (codec_name == CodecName::H264VRAM || codec_name == CodecName::H265VRAM) { + log::info!("changed to gdi when using vram"); bail!("SWITCH"); } check_privacy_mode_changed(&sp, c.privacy_mode_id)?; @@ -624,8 +624,8 @@ impl Raii { impl Drop for Raii { fn drop(&mut self) { - #[cfg(feature = "gpucodec")] - GpuEncoder::set_not_use(self.0, false); + #[cfg(feature = "vram")] + VRamEncoder::set_not_use(self.0, false); VIDEO_QOS.lock().unwrap().set_support_abr(self.0, true); } } @@ -637,21 +637,21 @@ fn get_encoder_config( record: bool, _portable_service: bool, ) -> EncoderCfg { - #[cfg(all(windows, feature = "gpucodec"))] + #[cfg(all(windows, feature = "vram"))] if _portable_service || c.is_gdi() { log::info!("gdi:{}, portable:{}", c.is_gdi(), _portable_service); - GpuEncoder::set_not_use(_display_idx, true); + VRamEncoder::set_not_use(_display_idx, true); } - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] Encoder::update(scrap::codec::EncodingUpdate::Check); // https://www.wowza.com/community/t/the-correct-keyframe-interval-in-obs-studio/95162 let keyframe_interval = if record { Some(240) } else { None }; let negotiated_codec = Encoder::negotiated_codec(); match negotiated_codec.clone() { - CodecName::H264GPU | CodecName::H265GPU => { - #[cfg(feature = "gpucodec")] - if let Some(feature) = GpuEncoder::try_get(&c.device(), negotiated_codec.clone()) { - EncoderCfg::GPU(GpuEncoderConfig { + CodecName::H264VRAM | CodecName::H265VRAM => { + #[cfg(feature = "vram")] + if let Some(feature) = VRamEncoder::try_get(&c.device(), negotiated_codec.clone()) { + EncoderCfg::VRAM(VRamEncoderConfig { device: c.device(), width: c.width, height: c.height, @@ -668,7 +668,7 @@ fn get_encoder_config( keyframe_interval, ) } - #[cfg(not(feature = "gpucodec"))] + #[cfg(not(feature = "vram"))] handle_hw_encoder( negotiated_codec.clone(), c.width, @@ -677,7 +677,7 @@ fn get_encoder_config( keyframe_interval, ) } - CodecName::H264HW(_name) | CodecName::H265HW(_name) => handle_hw_encoder( + CodecName::H264RAM(_name) | CodecName::H265RAM(_name) => handle_hw_encoder( negotiated_codec.clone(), c.width, c.height, @@ -714,15 +714,15 @@ fn handle_hw_encoder( let f = || { #[cfg(feature = "hwcodec")] match _name { - CodecName::H264GPU | CodecName::H265GPU => { + CodecName::H264VRAM | CodecName::H265VRAM => { if !scrap::codec::enable_hwcodec_option() { return Err(()); } - let is_h265 = _name == CodecName::H265GPU; - let best = HwEncoder::best(); + let is_h265 = _name == CodecName::H265VRAM; + let best = HwRamEncoder::best(); if let Some(h264) = best.h264 { if !is_h265 { - return Ok(EncoderCfg::HW(HwEncoderConfig { + return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { name: h264.name, width, height, @@ -733,7 +733,7 @@ fn handle_hw_encoder( } if let Some(h265) = best.h265 { if is_h265 { - return Ok(EncoderCfg::HW(HwEncoderConfig { + return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { name: h265.name, width, height, @@ -743,8 +743,8 @@ fn handle_hw_encoder( } } } - CodecName::H264HW(name) | CodecName::H265HW(name) => { - return Ok(EncoderCfg::HW(HwEncoderConfig { + CodecName::H264RAM(name) | CodecName::H265RAM(name) => { + return Ok(EncoderCfg::HWRAM(HwRamEncoderConfig { name, width, height, diff --git a/src/ui.rs b/src/ui.rs index f00739a58..d1019204a 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -576,8 +576,8 @@ impl UI { has_hwcodec() } - fn has_gpucodec(&self) -> bool { - has_gpucodec() + fn has_vram(&self) -> bool { + has_vram() } fn get_langs(&self) -> String { @@ -701,7 +701,7 @@ impl sciter::EventHandler for UI { fn get_lan_peers(); fn get_uuid(); fn has_hwcodec(); - fn has_gpucodec(); + fn has_vram(); fn get_langs(); fn default_video_save_directory(); fn handle_relay_id(String); diff --git a/src/ui/index.tis b/src/ui/index.tis index 88ad75362..e15c5a353 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -210,13 +210,13 @@ class Enhancements: Reactor.Component { function render() { var has_hwcodec = handler.has_hwcodec(); - var has_gpucodec = handler.has_gpucodec(); + var has_vram = handler.has_vram(); var support_remove_wallpaper = handler.support_remove_wallpaper(); var me = this; self.timer(1ms, function() { me.toggleMenuState() }); return
  • {translate('Enhancements')} - {(has_hwcodec || has_gpucodec) ?
  • {svg_checkmark}{translate("Enable hardware codec")}
  • : ""} + {(has_hwcodec || has_vram) ?
  • {svg_checkmark}{translate("Enable hardware codec")}
  • : ""}
  • {svg_checkmark}{translate("Adaptive bitrate")} (beta)
  • {translate("Recording")}
  • {support_remove_wallpaper ?
  • {svg_checkmark}{translate("Remove wallpaper during incoming sessions")}
  • : ""} diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 152ea7581..1e79e3299 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -836,8 +836,8 @@ pub fn has_hwcodec() -> bool { } #[inline] -pub fn has_gpucodec() -> bool { - cfg!(feature = "gpucodec") +pub fn has_vram() -> bool { + cfg!(feature = "vram") } #[cfg(feature = "flutter")] @@ -846,14 +846,14 @@ pub fn supported_hwdecodings() -> (bool, bool) { let decoding = scrap::codec::Decoder::supported_decodings(None, true, None, &vec![]); #[allow(unused_mut)] let (mut h264, mut h265) = (decoding.ability_h264 > 0, decoding.ability_h265 > 0); - #[cfg(feature = "gpucodec")] + #[cfg(feature = "vram")] { // supported_decodings check runtime luid - let gpu = scrap::gpucodec::GpuDecoder::possible_available_without_check(); - if gpu.0 { + let vram = scrap::vram::VRamDecoder::possible_available_without_check(); + if vram.0 { h264 = true; } - if gpu.1 { + if vram.1 { h265 = true; } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 348bcbd7b..44aa6c6b6 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1350,7 +1350,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn on_voice_call_incoming(&self); fn get_rgba(&self, display: usize) -> *const u8; fn next_rgba(&self, display: usize); - #[cfg(all(feature = "gpucodec", feature = "flutter"))] + #[cfg(all(feature = "vram", feature = "flutter"))] fn on_texture(&self, display: usize, texture: *mut c_void); fn set_multiple_windows_session(&self, sessions: Vec); } @@ -1663,7 +1663,7 @@ pub async fn io_loop(handler: Session, round: u32) { if pixelbuffer { ui_handler.on_rgba(display, data); } else { - #[cfg(all(feature = "gpucodec", feature = "flutter"))] + #[cfg(all(feature = "vram", feature = "flutter"))] ui_handler.on_texture(display, _texture); } }, From 8231d077064e6b02a64bcb7e85a302981a10d9d6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 12 Apr 2024 17:42:26 +0800 Subject: [PATCH 051/112] Fix. Msi. Terminate brokers. (#7693) * Fix. Msi. Terminate brokers. Signed-off-by: fufesou * Fix. Msi, remove tray shortcut in startmenu Signed-off-by: fufesou * Msi. format Signed-off-by: fufesou * Feat. Msi, set property Signed-off-by: fufesou * Fix. Mis, only do InstallValidate if is Install Signed-off-by: fufesou --------- Signed-off-by: fufesou --- res/msi/CustomActions/Common.h | 15 ++ res/msi/CustomActions/CustomActions.cpp | 220 +++++++++++++++++- res/msi/CustomActions/CustomActions.def | 5 + res/msi/CustomActions/CustomActions.vcxproj | 3 + res/msi/CustomActions/ReadConfig.cpp | 36 +++ res/msi/CustomActions/ServiceUtils.cpp | 173 ++++++++++++++ res/msi/Package/Components/RustDesk.wxs | 75 ++++-- .../Package/Fragments/AddRemoveProperties.wxs | 5 +- res/msi/Package/Fragments/CustomActions.wxs | 25 +- .../Package/Fragments/ShortcutProperties.wxs | 65 +++--- res/msi/Package/Fragments/Upgrades.wxs | 10 +- res/msi/Package/Package.wxs | 20 +- res/msi/Package/UI/AnotherApp.wxs | 26 +-- res/msi/README.md | 1 + 14 files changed, 581 insertions(+), 98 deletions(-) create mode 100644 res/msi/CustomActions/Common.h create mode 100644 res/msi/CustomActions/ReadConfig.cpp create mode 100644 res/msi/CustomActions/ServiceUtils.cpp diff --git a/res/msi/CustomActions/Common.h b/res/msi/CustomActions/Common.h new file mode 100644 index 000000000..631fc5d38 --- /dev/null +++ b/res/msi/CustomActions/Common.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +bool AddFirewallRule(bool add, LPWSTR exeName, LPWSTR exeFile); + +bool IsServiceRunningW(LPCWSTR serviceName); +bool MyCreateServiceW(LPCWSTR serviceName, LPCWSTR displayName, LPCWSTR binaryPath); +bool MyDeleteServiceW(LPCWSTR serviceName); +bool MyStartServiceW(LPCWSTR serviceName); +bool MyStopServiceW(LPCWSTR serviceName); + +std::wstring ReadConfig(const std::wstring& filename, const std::wstring& key); + diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 66ea6e473..7f7f24eee 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -8,6 +8,8 @@ #include #include +#include "./Common.h" + #pragma comment(lib, "Shlwapi.lib") UINT __stdcall CustomActionHello( @@ -271,7 +273,6 @@ void RemoveFirewallRuleCmdline(LPWSTR exeName) } } -bool AddFirewallRule(bool add, LPWSTR exeName, LPWSTR exeFile); UINT __stdcall AddFirewallRules( __in MSIHANDLE hInstall) { @@ -323,3 +324,220 @@ LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } + +UINT __stdcall SetPropertyIsServiceRunning(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + wchar_t szAppName[500] = { 0 }; + DWORD cchAppName = sizeof(szAppName) / sizeof(szAppName[0]); + wchar_t szPropertyName[500] = { 0 }; + DWORD cchPropertyName = sizeof(szPropertyName) / sizeof(szPropertyName[0]); + bool isRunning = false; + + hr = WcaInitialize(hInstall, "SetPropertyIsServiceRunning"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"AppName", szAppName, &cchAppName); + WcaLog(LOGMSG_STANDARD, "Try query service of : \"%ls\"", szAppName); + + MsiGetPropertyW(hInstall, L"PropertyName", szPropertyName, &cchPropertyName); + WcaLog(LOGMSG_STANDARD, "Try set is service running, property name : \"%ls\"", szPropertyName); + + isRunning = IsServiceRunningW(szAppName); + MsiSetPropertyW(hInstall, szPropertyName, isRunning ? L"'N'" : L"'Y'"); + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall CreateStartService(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + LPWSTR svcParams = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzData = NULL; + LPWSTR svcName = NULL; + LPWSTR svcBinary = NULL; + wchar_t szSvcDisplayName[500] = { 0 }; + DWORD cchSvcDisplayName = sizeof(szSvcDisplayName) / sizeof(szSvcDisplayName[0]); + + hr = WcaInitialize(hInstall, "CreateStartService"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &svcParams); + ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); + + WcaLog(LOGMSG_STANDARD, "Try create start service : %ls", svcParams); + + svcName = svcParams; + svcBinary = wcschr(svcParams, L';'); + if (svcBinary == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to find binary : %ls", svcParams); + goto LExit; + } + svcBinary[0] = L'\0'; + svcBinary += 1; + + hr = StringCchPrintfW(szSvcDisplayName, cchSvcDisplayName, L"%ls Service", svcName); + ExitOnFailure(hr, "Failed to compose a resource identifier string"); + if (MyCreateServiceW(svcName, szSvcDisplayName, svcBinary)) { + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is created.", svcName); + if (MyStartServiceW(svcName)) { + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is started.", svcName); + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to start service: \"%ls\"", svcName); + } + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to create service: \"%ls\"", svcName); + } + +LExit: + if (pwzData) { + ReleaseStr(pwzData); + } + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall TryStopDeleteService(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + int nResult = 0; + LPWSTR svcName = NULL; + LPWSTR pwz = NULL; + LPWSTR pwzData = NULL; + wchar_t szExeFile[500] = { 0 }; + DWORD cchExeFile = sizeof(szExeFile) / sizeof(szExeFile[0]); + + hr = WcaInitialize(hInstall, "TryStopDeleteService"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = WcaGetProperty(L"CustomActionData", &pwzData); + ExitOnFailure(hr, "failed to get CustomActionData"); + + pwz = pwzData; + hr = WcaReadStringFromCaData(&pwz, &svcName); + ExitOnFailure(hr, "failed to read database key from custom action data: %ls", pwz); + WcaLog(LOGMSG_STANDARD, "Try stop and delete service : %ls", svcName); + + if (MyStopServiceW(svcName)) { + for (int i = 0; i < 10; i++) { + if (IsServiceRunningW(svcName)) { + Sleep(100); + } + else { + break; + } + } + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is stopped", svcName); + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to stop service: \"%ls\"", svcName); + } + + if (MyDeleteServiceW(svcName)) { + WcaLog(LOGMSG_STANDARD, "Service \"%ls\" is deleted", svcName); + } + else { + WcaLog(LOGMSG_STANDARD, "Failed to delete service: \"%ls\"", svcName); + } + + // It's really strange that we need sleep here. + // But the upgrading may be stucked at "copying new files" because the file is in using. + // Steps to reproduce: Install -> stop service in tray --> start service -> upgrade + // Sleep(300); + + // Or we can terminate the process + hr = StringCchPrintfW(szExeFile, cchExeFile, L"%ls.exe", svcName); + ExitOnFailure(hr, "Failed to compose a resource identifier string"); + TerminateProcessesByNameW(szExeFile, L"--not-in-use"); + +LExit: + if (pwzData) { + ReleaseStr(pwzData); + } + + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall TryDeleteStartupShortcut(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + wchar_t szShortcut[500] = { 0 }; + DWORD cchShortcut = sizeof(szShortcut) / sizeof(szShortcut[0]); + wchar_t szStartupDir[500] = { 0 }; + DWORD cchStartupDir = sizeof(szStartupDir) / sizeof(szStartupDir[0]); + WCHAR pwszTemp[1024] = L""; + + hr = WcaInitialize(hInstall, "DeleteStartupShortcut"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"StartupFolder", szStartupDir, &cchStartupDir); + + MsiGetPropertyW(hInstall, L"ShortcutName", szShortcut, &cchShortcut); + WcaLog(LOGMSG_STANDARD, "Try delete startup shortcut of : \"%ls\"", szShortcut); + + hr = StringCchPrintfW(pwszTemp, 1024, L"%ls%ls.lnk", szStartupDir, szShortcut); + ExitOnFailure(hr, "Failed to compose a resource identifier string"); + + if (DeleteFile(pwszTemp)) { + WcaLog(LOGMSG_STANDARD, "Failed to delete startup shortcut of : \"%ls\"", pwszTemp); + } + else { + WcaLog(LOGMSG_STANDARD, "Startup shortcut is deleted : \"%ls\"", pwszTemp); + } + +LExit: + er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; + return WcaFinalize(er); +} + +UINT __stdcall SetPropertyFromConfig(__in MSIHANDLE hInstall) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + + wchar_t szConfigFile[1024] = { 0 }; + DWORD cchConfigFile = sizeof(szConfigFile) / sizeof(szConfigFile[0]); + wchar_t szConfigKey[500] = { 0 }; + DWORD cchConfigKey = sizeof(szConfigKey) / sizeof(szConfigKey[0]); + wchar_t szPropertyName[500] = { 0 }; + DWORD cchPropertyName = sizeof(szPropertyName) / sizeof(szPropertyName[0]); + std::wstring configValue; + + hr = WcaInitialize(hInstall, "SetPropertyFromConfig"); + ExitOnFailure(hr, "Failed to initialize"); + + MsiGetPropertyW(hInstall, L"ConfigFile", szConfigFile, &cchConfigFile); + WcaLog(LOGMSG_STANDARD, "Try read config file of : \"%ls\"", szConfigFile); + + MsiGetPropertyW(hInstall, L"ConfigKey", szConfigKey, &cchConfigKey); + WcaLog(LOGMSG_STANDARD, "Try read configuration, config key : \"%ls\"", szConfigKey); + + MsiGetPropertyW(hInstall, L"PropertyName", szPropertyName, &cchPropertyName); + WcaLog(LOGMSG_STANDARD, "Try read configuration, property name : \"%ls\"", szPropertyName); + + configValue = ReadConfig(szConfigFile, szConfigKey); + MsiSetPropertyW(hInstall, szPropertyName, configValue.c_str()); + +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 31a10eb63..a089c583e 100644 --- a/res/msi/CustomActions/CustomActions.def +++ b/res/msi/CustomActions/CustomActions.def @@ -5,3 +5,8 @@ EXPORTS RemoveInstallFolder TerminateProcesses AddFirewallRules + SetPropertyIsServiceRunning + TryStopDeleteService + CreateStartService + TryDeleteStartupShortcut + SetPropertyFromConfig diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index fcadf9a76..de16f1039 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -54,6 +54,7 @@ + @@ -64,6 +65,8 @@ Create + + diff --git a/res/msi/CustomActions/ReadConfig.cpp b/res/msi/CustomActions/ReadConfig.cpp new file mode 100644 index 000000000..695ebcada --- /dev/null +++ b/res/msi/CustomActions/ReadConfig.cpp @@ -0,0 +1,36 @@ +#include "pch.h" + +#include +#include +#include +#include + +void trim(std::wstring& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](wchar_t ch) { + return !std::iswspace(ch); + })); + str.erase(std::find_if(str.rbegin(), str.rend(), [](wchar_t ch) { + return !std::iswspace(ch); + }).base(), str.end()); +} + +std::wstring ReadConfig(const std::wstring& filename, const std::wstring& key) +{ + std::wstring configValue; + std::wstring line; + std::wifstream file(filename); + while (std::getline(file, line)) { + trim(line); + if (line.find(key) == 0) { + std::size_t position = line.find(L"=", key.size()); + if (position != std::string::npos) { + configValue = line.substr(position + 1); + trim(configValue); + break; + } + } + } + + file.close(); + return configValue; +} diff --git a/res/msi/CustomActions/ServiceUtils.cpp b/res/msi/CustomActions/ServiceUtils.cpp new file mode 100644 index 000000000..5bf8c9fe5 --- /dev/null +++ b/res/msi/CustomActions/ServiceUtils.cpp @@ -0,0 +1,173 @@ +// https://learn.microsoft.com/en-us/windows/win32/services/installing-a-service + +#include "pch.h" + +#include +#include +#include + +bool MyCreateServiceW(LPCWSTR serviceName, LPCWSTR displayName, LPCWSTR binaryPath) +{ + SC_HANDLE schSCManager; + SC_HANDLE schService; + + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (NULL == schSCManager) + { + WcaLog(LOGMSG_STANDARD, "OpenSCManager failed (%d)\n", GetLastError()); + return false; + } + + // Create the service + schService = CreateService( + schSCManager, // SCM database + serviceName, // name of service + displayName, // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_AUTO_START, // start type + SERVICE_ERROR_NORMAL, // error control type + binaryPath, // path to service's binary + NULL, // no load ordering group + NULL, // no tag identifier + NULL, // no dependencies + NULL, // LocalSystem account + NULL); // no password + if (schService == NULL) + { + WcaLog(LOGMSG_STANDARD, "CreateService failed (%d)\n", GetLastError()); + CloseServiceHandle(schSCManager); + return false; + } + else + { + WcaLog(LOGMSG_STANDARD, "Service installed successfully\n"); + } + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return true; +} + +bool MyDeleteServiceW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_STOP | DELETE); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + SERVICE_STATUS serviceStatus; + if (ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus)) { + WcaLog(LOGMSG_STANDARD, "Stopping service: %ls", serviceName); + } + + bool success = DeleteService(hService); + if (!success) { + WcaLog(LOGMSG_STANDARD, "Failed to delete service: %ls", serviceName); + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return success; +} + +bool MyStartServiceW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_START); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + bool success = StartService(hService, 0, NULL); + if (!success) { + WcaLog(LOGMSG_STANDARD, "Failed to start service: %ls", serviceName); + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return success; +} + +bool MyStopServiceW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_STOP); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + SERVICE_STATUS serviceStatus; + if (!ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus)) { + WcaLog(LOGMSG_STANDARD, "Failed to stop service: %ls", serviceName); + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return false; + } + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return true; +} + +bool IsServiceRunningW(LPCWSTR serviceName) +{ + SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open Service Control Manager"); + return false; + } + + SC_HANDLE hService = OpenServiceW(hSCManager, serviceName, SERVICE_QUERY_STATUS); + if (hService == NULL) { + WcaLog(LOGMSG_STANDARD, "Failed to open service: %ls", serviceName); + CloseServiceHandle(hSCManager); + return false; + } + + SERVICE_STATUS_PROCESS serviceStatus; + DWORD bytesNeeded; + if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, reinterpret_cast(&serviceStatus), sizeof(serviceStatus), &bytesNeeded)) { + WcaLog(LOGMSG_STANDARD, "Failed to query service: %ls", serviceName); + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + return false; + } + + bool isRunning = (serviceStatus.dwCurrentState == SERVICE_RUNNING); + + CloseServiceHandle(hService); + CloseServiceHandle(hSCManager); + + return isRunning; +} diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index eab059804..a44d8bb73 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -9,35 +9,67 @@ - - - - - + + + + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + - - + + - - - - + + + + + + + + + + + + @@ -58,11 +90,6 @@ - - - - - @@ -74,6 +101,12 @@ + + + + + + - + diff --git a/res/msi/Package/Fragments/AddRemoveProperties.wxs b/res/msi/Package/Fragments/AddRemoveProperties.wxs index 5711aa5a8..f568b963d 100644 --- a/res/msi/Package/Fragments/AddRemoveProperties.wxs +++ b/res/msi/Package/Fragments/AddRemoveProperties.wxs @@ -6,6 +6,9 @@ + + + - + diff --git a/res/msi/Package/Fragments/CustomActions.wxs b/res/msi/Package/Fragments/CustomActions.wxs index 29ae04492..0454a6731 100644 --- a/res/msi/Package/Fragments/CustomActions.wxs +++ b/res/msi/Package/Fragments/CustomActions.wxs @@ -1,14 +1,19 @@  - - + + - + - - - - - - - + + + + + + + + + + + + diff --git a/res/msi/Package/Fragments/ShortcutProperties.wxs b/res/msi/Package/Fragments/ShortcutProperties.wxs index 1c4711127..e800262fb 100644 --- a/res/msi/Package/Fragments/ShortcutProperties.wxs +++ b/res/msi/Package/Fragments/ShortcutProperties.wxs @@ -1,51 +1,52 @@  - + - + - - - - + + + - - - - - - - + + + + + + - - - - - - - - - + + + + + + + + + - - - + + - - - + + + - - - + + + - + diff --git a/res/msi/Package/Fragments/Upgrades.wxs b/res/msi/Package/Fragments/Upgrades.wxs index ab1118513..8109efd9c 100644 --- a/res/msi/Package/Fragments/Upgrades.wxs +++ b/res/msi/Package/Fragments/Upgrades.wxs @@ -1,10 +1,10 @@  - + - + - - + + - + diff --git a/res/msi/Package/Package.wxs b/res/msi/Package/Package.wxs index 9a37b3092..bc07ca536 100644 --- a/res/msi/Package/Package.wxs +++ b/res/msi/Package/Package.wxs @@ -9,7 +9,7 @@ - + @@ -26,25 +26,15 @@ - - - - - - - - - + + + - - - - - + diff --git a/res/msi/Package/UI/AnotherApp.wxs b/res/msi/Package/UI/AnotherApp.wxs index fdf184e6d..ea46812f7 100644 --- a/res/msi/Package/UI/AnotherApp.wxs +++ b/res/msi/Package/UI/AnotherApp.wxs @@ -1,15 +1,15 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/res/msi/README.md b/res/msi/README.md index 2650ad69b..5ff2a1080 100644 --- a/res/msi/README.md +++ b/res/msi/README.md @@ -39,5 +39,6 @@ Run `msiexec /i package.msi /l*v install.log` to record the log. ## Refs +1. [windows-installer-portal](https://learn.microsoft.com/en-us/windows/win32/Msi/windows-installer-portal) 1. [wxs](https://wixtoolset.org/docs/schema/wxs/) 1. [wxs github](https://github.com/wixtoolset/wix) From 24ea55b01070a6de5fd7dc3fcc1a96dbfea610f4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 12 Apr 2024 18:44:18 +0800 Subject: [PATCH 052/112] Fix. Msi, firewall outbound profile (#7704) Signed-off-by: fufesou --- res/msi/CustomActions/FirewallRules.cpp | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/res/msi/CustomActions/FirewallRules.cpp b/res/msi/CustomActions/FirewallRules.cpp index 9381f8f61..bca2739b8 100644 --- a/res/msi/CustomActions/FirewallRules.cpp +++ b/res/msi/CustomActions/FirewallRules.cpp @@ -272,26 +272,9 @@ HRESULT AddFirewallRuleWithEdgeTraversal( goto Cleanup; } - if (in) { - CurrentProfilesBitMask = NET_FW_PROFILE2_ALL; - } - else { - // Retrieve Current Profiles bitmask - hr = pNetFwPolicy2->get_CurrentProfileTypes(&CurrentProfilesBitMask); - if (FAILED(hr)) - { - printf("get_CurrentProfileTypes failed: 0x%08lx\n", hr); - goto Cleanup; - } - - // When possible we avoid adding firewall rules to the Public profile. - // If Public is currently active and it is not the only active profile, we remove it from the bitmask - if ((CurrentProfilesBitMask & NET_FW_PROFILE2_PUBLIC) && - (CurrentProfilesBitMask != NET_FW_PROFILE2_PUBLIC)) - { - CurrentProfilesBitMask ^= NET_FW_PROFILE2_PUBLIC; - } - } + // If you want the rule to avoid public, you can refer to + // https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ics/c-adding-an-outbound-rule + CurrentProfilesBitMask = NET_FW_PROFILE2_ALL; hr = pNetFwRule->put_Direction(in ? NET_FW_RULE_DIR_IN : NET_FW_RULE_DIR_OUT); if (FAILED(hr)) From 60ae903cf3283e3b5bf98665c8c446f4f5cc4348 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 13 Apr 2024 13:11:50 +0800 Subject: [PATCH 053/112] Fix. Msi, reg values in HKCR (#7709) Signed-off-by: fufesou --- res/msi/Package/Components/Regs.wxs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/msi/Package/Components/Regs.wxs b/res/msi/Package/Components/Regs.wxs index ee14da48d..4b5efe95f 100644 --- a/res/msi/Package/Components/Regs.wxs +++ b/res/msi/Package/Components/Regs.wxs @@ -14,7 +14,7 @@ - + @@ -22,7 +22,7 @@ - + @@ -36,7 +36,7 @@ - + From 333b9130fe683f764b4d12f83716b4005d0ba918 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 13 Apr 2024 05:12:20 +0000 Subject: [PATCH 054/112] update sk.rs (#7710) --- src/lang/sk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 944a7bc3e..289a75410 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Vyššie uvedené polia sú zdieľané a viditeľné pre ostatných."), ("Everyone", "Každý"), ("ab_web_console_tip", "Viac na webovej konzole"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Povoliť pripojenie iba vtedy, ak je otvorené okno aplikácie RustDesk"), ].iter().cloned().collect(); } From 92ae41cc13a28cde4ba42594c30eeab995db2993 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 13 Apr 2024 05:12:31 +0000 Subject: [PATCH 055/112] update cs.rs (#7711) --- src/lang/cs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 116887440..b6bb80b59 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Výše uvedená pole jsou sdílená a viditelná pro ostatní."), ("Everyone", "Každý"), ("ab_web_console_tip", "Více na webové konzoli"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Povolit připojení pouze v případě, že je otevřené okno RustDesk"), ].iter().cloned().collect(); } From 725e8221a55852b50b6b1ad306869f3fdfd0896d Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Sat, 13 Apr 2024 07:12:40 +0200 Subject: [PATCH 056/112] Update de.rs (#7712) --- src/lang/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index a28f26591..e8d63e790 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -600,6 +600,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("share_warning_tip", "Die obigen Felder werden geteilt und sind für andere sichtbar."), ("Everyone", "Jeder"), ("ab_web_console_tip", "Mehr über Webkonsole"), - ("allow-only-conn-window-open-tip", ""), + ("allow-only-conn-window-open-tip", "Verbindung nur zulassen, wenn das RustDesk-Fenster geöffnet ist"), ].iter().cloned().collect(); } From 71c4d7475976d5c98bab7ccfecda2b771bb5cc52 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 13 Apr 2024 14:43:27 +0800 Subject: [PATCH 057/112] fix typo --- res/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/devices.py b/res/devices.py index a12645e92..246a432df 100755 --- a/res/devices.py +++ b/res/devices.py @@ -67,7 +67,7 @@ def check(response): except ValueError: return response.text or "Success" else: - return "Failed", res.status_code, response.text + return "Failed", response.status_code, response.text def disable(url, token, guid, id): From 5c4d95ac0ff7f98b67351e442aca5dd13e4f15cd Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 13 Apr 2024 21:10:36 +0800 Subject: [PATCH 058/112] not use av1 as auto codec on x86 sciter (#7714) Signed-off-by: 21pages --- libs/scrap/src/common/codec.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 7a96da9b9..cac1b307d 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -239,7 +239,8 @@ impl Encoder { #[allow(unused_mut)] let mut auto_codec = CodecName::VP9; - if av1_useable { + // aom is very slow for x86 sciter version on windows x64 + if av1_useable && !(cfg!(windows) && std::env::consts::ARCH == "x86") { auto_codec = CodecName::AV1; } let mut system = System::new(); From 516608340616cf081cf96fedaffee4600613f352 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 14 Apr 2024 21:03:51 +0800 Subject: [PATCH 059/112] 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 7f7f24eee..37a728a4b 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 @@ LExit: 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 a089c583e..a73ecda26 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 a44d8bb73..df9463fe8 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 0454a6731..e064b188e 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 060/112] 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 a8638a302..e38e3fb75 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 1c58ae9b2..4acfb2cdf 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 061/112] update system2 (#7722) --- build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.py b/build.py index cbf9d00a2..ebea1f34d 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 062/112] 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 03e4be45e..c32931661 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 063/112] 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 edec8c42d..1730e3527 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 000000000..e2382c647 --- /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 1c9e39c13..57919bd6e 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 064/112] 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 1730e3527..b3b00b625 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 e2382c647..62d203a27 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 065/112] 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 57919bd6e..bfc6042ee 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 066/112] 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 b3b00b625..9fea4d4d3 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 62d203a27..d61bccfd6 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 bfc6042ee..e27b4140e 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 067/112] 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 9fea4d4d3..973c2f108 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 068/112] 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 f051db5ee..bcf829c7c 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 gen_pre_vars(args, dist_dir): ) -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 @@ if __name__ == "__main__": 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 069/112] 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 2c0d160ac..e9ac3a3c5 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 070/112] 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 4b5efe95f..23d4b6b8d 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 df9463fe8..ab1d404ec 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 f568b963d..f93852867 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 bc07ca536..8361e2a12 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 bcf829c7c..9e10c3160 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -13,6 +13,7 @@ g_indent_unit = "\t" 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 gen_pre_vars(args, dist_dir): 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 071/112] 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 5ff2a1080..a4608467d 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 9e10c3160..84e97b624 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -39,7 +39,11 @@ g_arpsystemcomponent = { 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 gen_custom_ARPSYSTEMCOMPONENT_False(args): ) 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 gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir): 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 gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir): 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 @@ if __name__ == "__main__": 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 072/112] 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 e4a8877de..d4e8b5bcf 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 073/112] 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 fe8fba732..d8e5aa1ee 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 e27b4140e..dec4bc370 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 074/112] 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 d4e8b5bcf..3c1ca87da 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 075/112] 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 c50d55025..d73dc7bfd 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 076/112] 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 973c2f108..12fd66b16 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 d8e5aa1ee..2886a2300 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 dec4bc370..72e435d3f 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 077/112] 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 72e435d3f..9cfd71e3f 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 078/112] 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 9cfd71e3f..36b25708a 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 079/112] 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 36b25708a..d29fc032d 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 080/112] 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 d61bccfd6..ba29021b3 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 d2a3c0347..51612c9e7 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 f19541c8c..e75f9998a 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 081/112] 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 aa3186104..9fbc7d10a 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 082/112] 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 fc5b537e1..a878995c9 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 7395f4708..c2902806f 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 e75f9998a..0f3e7b20a 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 f6b217533..e79310650 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 083/112] 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 a878995c9..3106aaaaf 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 c2902806f..dd87e0939 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 e097b04a6..120381ebe 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 084/112] 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 3e15301dc..5dea0cb93 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 95024e0b5..ba4e311dc 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 017825a25..7eedb1d5b 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 eb6db3120..952893393 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 7c2415d2d..7b6295341 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 99420ae8c..e7c43582b 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 ffb66aabb..64805e0e5 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 cac1b307d..fd283386b 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 c72e69822..92f58cde2 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 d29fc032d..86235dc2f 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 29954d61f..3a2b88aed 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 9daf24262..9345936e0 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 65411d566..19d711bdb 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 d1019204a..10aefe5ff 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 e15c5a353..f202d0fad 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 1e79e3299..d8a9996c0 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 085/112] 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 f733bb70c..f86755952 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 8e8cce95c..21b123799 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 baa932c40..c28570219 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 7d1392bbf..81ca155d7 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 086/112] 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 9e32e163e..ca405647b 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 3945e432a..000000000 --- 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 087/112] 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 5dea0cb93..bb8c81a0f 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 ba4e311dc..dd018c6b1 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 088/112] 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 089/112] 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 12fd66b16..75304be64 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 090/112] 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 37a728a4b..1543e8f65 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 5bf8c9fe5..234f70371 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 091/112] 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 | 817 +++++++++++++----- 61 files changed, 1113 insertions(+), 290 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index bb8c81a0f..bb5399221 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 0f59d49cc..10a0ecf8d 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 ebea1f34d..1779d7d46 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 0e4a39d0a..090ca62a4 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 70a5b13a8..fd048337c 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 0a58aa023..046767be6 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 1543e8f65..3643ea9ce 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -591,3 +591,68 @@ LExit: 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 a73ecda26..557bfaf18 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 ab1d404ec..31dbbc66d 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 e064b188e..b443eff52 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 cd27ec0ca..25e291295 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 be4f6bc5b..bdfd83cc1 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 4ed1cc0c3..3a3a40ee9 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 42d714da7..610282b1a 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 b350420b8..0309cf562 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 b6bb80b59..4c3a4b3cc 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 8ec86dace..c20f15128 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 e8d63e790..1c88bdde5 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 369ec466d..7d4966120 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 5782a8597..ac6cd2cd3 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 fc17f0af9..e1bab260d 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 e9ac3a3c5..1e7c40b26 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 5449dc863..2e49ddee7 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 4cf7b889f..6eb59377d 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 7f6d75b08..ff644806e 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 3cec23385..2da9339a2 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 91913676b..def491c52 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 1a63bdcf8..bdae7d9bb 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 b56293785..9a0fc988f 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 493519fbf..670853e4a 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 184461198..400ae1329 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 116cd5d02..e0fc91a06 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 eb599cd98..7bffe6a2e 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 6c7b84804..06d3ce2bd 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 9fbc7d10a..cc0cf98ff 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 5a0a83f44..d4251a554 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 a4c074912..705b34f16 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 c32931661..06adebc09 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 c1f40cbae..76fa2c214 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 01795ebf7..2958878a5 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 28aa015c9..c7780c371 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 00c4ad0de..0cc7acdb0 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 289a75410..7492b59f2 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 cbc15f35e..cdc37594d 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 f6369c9da..6a7ee5d63 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 b38b2713d..e03383415 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 e40ddc824..b78677fe6 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 2c608cf1c..02aa92450 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 20abcd639..ce262d19a 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 cacaa1586..27fca3807 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 fbd36cb12..4e86fe760 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 0f4fc652e..63a8800f5 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 bd671bd8f..96cbbee7e 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 381f4dc63..0d328ac13 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 c28570219..a6dfd444f 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 d0781d993..f6eafcf41 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 7fd27b60b..a7f80a02d 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 287bfd51e..04a9d776c 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 1a30730af..8526a998d 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 838937057..0c8263cbd 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 da8780c7f..3188fc33e 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}, -}; +use hbb_common::{bail, platform::windows::is_windows_version_or_greater, ResultType}; +use std::sync::atomic; -// 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; +// 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"; -lazy_static::lazy_static! { - static ref VIRTUAL_DISPLAY_MANAGER: Arc> = - Arc::new(Mutex::new(VirtualDisplayManager::default())); +const IDD_IMPL: &str = IDD_IMPL_AMYUNI; +const IDD_IMPL_RUSTDESK: &str = "rustdesk_idd"; +const IDD_IMPL_AMYUNI: &str = "amyuni_idd"; + +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 } -#[derive(Default)] -struct VirtualDisplayManager { - headless_index_name: Option<(u32, String)>, - peer_index_name: HashMap, - is_driver_installed: bool, +// 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, + ); + } } -impl VirtualDisplayManager { - fn prepare_driver(&mut self) -> ResultType<()> { - if !self.is_driver_installed { - self.install_update_driver()?; - } - Ok(()) - } +pub fn is_amyuni_idd() -> bool { + IDD_IMPL == IDD_IMPL_AMYUNI +} - 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 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(()) -} - -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 + match IDD_IMPL { + IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_headless(), + IDD_IMPL_AMYUNI => amyuni_idd::plug_in_headless(), + _ => bail!("Unsupported virtual display implementation."), } } -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(); - } - // Sleep is needed here to wait for the virtual display to be ready. - std::thread::sleep(std::time::Duration::from_millis(50)); +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; } - 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 = 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); + 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), + ); } } + IDD_IMPL_AMYUNI => { + let c = amyuni_idd::get_monitor_count(); + if c > 0 { + map.insert("amyuni_virtual_displays".into(), serde_json::json!(c)); + } + } + _ => {} } - Ok(()) + map } -pub fn reset_all() -> ResultType<()> { - if is_virtual_display_supported() { - return Ok(()); +#[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."), } +} - if let Err(e) = plug_out_peer_request(&get_virtual_displays()) { - log::error!("Failed to plug out virtual displays: {}", e); +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) + } + IDD_IMPL_AMYUNI => amyuni_idd::plug_out_monitor(index), + _ => bail!("Unsupported virtual display implementation."), } - 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()?; + 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."), + } +} - 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); - } +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."), + } +} + +pub fn reset_all() -> ResultType<()> { + 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; + + lazy_static::lazy_static! { + static ref VIRTUAL_DISPLAY_MANAGER: Arc> = + Arc::new(Mutex::new(VirtualDisplayManager::default())); + } + + #[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); } - break; + } + // 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; + } + } + } + + 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 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 + } + + 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 } } } - Ok(indices) -} + 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)); + } + } -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 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 -} - -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 (k, v) in lock.peer_index_name.iter() { + if windows::is_device_name(v, name) { + return Some(change_resolution(*k, w, h)); + } } + None } } -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)); +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(()) } } - for (k, v) in lock.peer_index_name.iter() { - if windows::is_device_name(v, name) { - return Some(change_resolution(*k, 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") + } + + 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 092/112] 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 0f3e7b20a..078e2b2f7 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 e79310650..9477cbe6a 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 093/112] 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 bb5399221..d45aed18a 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 2886a2300..3cfd33634 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 11c7d2cb3..2cd1c6169 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 094/112] 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 120381ebe..a7bf12148 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 095/112] 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 3cfd33634..3f81ba422 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 096/112] 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 d45aed18a..c865db73a 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 84e97b624..95f4205bd 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -6,6 +6,7 @@ import sys 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 gen_pre_vars(args, dist_dir): 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 @@ if __name__ == "__main__": 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 25e291295..1f5de5e30 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 097/112] 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 0cc7acdb0..3e28744c5 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 098/112] 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 95f4205bd..1d85efe21 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 gen_custom_ARPSYSTEMCOMPONENT_True(args, dist_dir): 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 From 75521fe363a72e59894b36d8c9ae50cc33ec894b Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Sat, 20 Apr 2024 05:30:38 +0200 Subject: [PATCH 099/112] Update Italian language (#7776) --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 670853e4a..a362e2348 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -601,6 +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", ""), + ("no_need_privacy_mode_no_physical_displays_tip", "Nessun display fisico, nessuna necessità di usare la modalità privacy."), ].iter().cloned().collect(); } From a8eefbc9f02a886d95aff5d5afb93ad94445fa03 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Sat, 20 Apr 2024 05:30:48 +0200 Subject: [PATCH 100/112] Update de.rs (#7778) --- src/lang/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 1c88bdde5..73262e267 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -601,6 +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", ""), + ("no_need_privacy_mode_no_physical_displays_tip", "Keine physischen Bildschirme; keine Notwendigkeit, den Datenschutzmodus zu verwenden."), ].iter().cloned().collect(); } From 8a6d9a1496bd9ae6424aca796779d7e79ac24564 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 20 Apr 2024 03:30:57 +0000 Subject: [PATCH 101/112] update sk.rs (#7779) --- src/lang/sk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 7492b59f2..1abfe46a3 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -601,6 +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", ""), + ("no_need_privacy_mode_no_physical_displays_tip", "Žiadne fyzické displeje, nie je potrebné používať režim ochrany osobných údajov."), ].iter().cloned().collect(); } From d4a1d4cd7e3cb19b12d000bbf87a2ef5bc0c1191 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 20 Apr 2024 03:31:07 +0000 Subject: [PATCH 102/112] update cs.rs (#7780) --- src/lang/cs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 4c3a4b3cc..23cb7442e 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -601,6 +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", ""), + ("no_need_privacy_mode_no_physical_displays_tip", "Žádné fyzické displeje, není třeba používat režim soukromí."), ].iter().cloned().collect(); } From fb1aa9c0285fbbad10cb75c958e791b534fc021d Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 20 Apr 2024 14:54:23 +0800 Subject: [PATCH 103/112] Fix. Multi-display connection, resolutions (#7782) * fix: multi-display, change resolution Signed-off-by: fufesou * fix: multi-displays, resolutions of displays Signed-off-by: fufesou * fix: build Signed-off-by: fufesou * refact: Function rename Signed-off-by: fufesou * refact. Function rename Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/models/model.dart | 8 ++++- flutter/lib/web/bridge.dart | 5 +++ libs/hbb_common/protos/message.proto | 16 ++++++++++ src/flutter_ffi.rs | 6 ++++ src/server/connection.rs | 48 ++++++++++++++++++++-------- src/ui_session_interface.rs | 33 ++++++++++++++++--- src/virtual_display_manager.rs | 7 ++++ 7 files changed, 105 insertions(+), 18 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 046767be6..4da7d54cd 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -715,6 +715,8 @@ class FfiModel with ChangeNotifier { // Map clone is required here, otherwise "evt" may be changed by other threads through the reference. // Because this function is asynchronous, there's an "await" in this function. cachedPeerData.peerInfo = {...evt}; + // Do not cache resolutions, because a new display connection have different resolutions. + cachedPeerData.peerInfo.remove('resolutions'); // Recent peer is updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs) bind.mainLoadRecentPeers(); @@ -770,7 +772,9 @@ class FfiModel with ChangeNotifier { } Map features = json.decode(evt['features']); _pi.features.privacyMode = features['privacy_mode'] == 1; - handleResolutions(peerId, evt["resolutions"]); + if (!isCache) { + handleResolutions(peerId, evt["resolutions"]); + } parent.target?.elevationModel.onPeerInfo(_pi); } if (connType == ConnType.defaultConn) { @@ -2317,6 +2321,8 @@ class FFI { } await ffiModel.handleCachedPeerData(data, id); await sessionRefreshVideo(sessionId, ffiModel.pi); + await bind.sessionRequestNewDisplayInitMsgs( + sessionId: sessionId, display: ffiModel.pi.currentDisplay); }); isToNewWindowNotified.value = true; } diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 952893393..fd0d7189b 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -1577,5 +1577,10 @@ class RustdeskImpl { throw UnimplementedError(); } + Future sessionRequestNewDisplayInitMsgs( + {required UuidValue sessionId, required int display, dynamic hint}) { + throw UnimplementedError(); + } + void dispose() {} } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index b2001ba68..7ffa0e6de 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -504,6 +504,11 @@ message Resolution { int32 height = 2; } +message DisplayResolution { + int32 display = 1; + Resolution resolution = 2; +} + message SupportedResolutions { repeated Resolution resolutions = 1; } message SwitchDisplay { @@ -716,6 +721,13 @@ message WindowsSessions { uint32 current_sid = 2; } +// Query a message from peer. +message MessageQuery { + // The SwitchDisplay message of the target display. + // If the target display is not found, the message will be ignored. + int32 switch_display = 1; +} + message Misc { oneof union { ChatMessage chat_message = 4; @@ -736,6 +748,8 @@ message Misc { bool portable_service_running = 20; SwitchSidesRequest switch_sides_request = 21; SwitchBack switch_back = 22; + // Deprecated since 1.2.4, use `change_display_resolution` (36) instead. + // But we must keep it for compatibility when peer version < 1.2.4. Resolution change_resolution = 24; PluginRequest plugin_request = 25; PluginFailure plugin_failure = 26; @@ -748,6 +762,8 @@ message Misc { TogglePrivacyMode toggle_privacy_mode = 33; SupportedEncoding supported_encoding = 34; uint32 selected_sid = 35; + DisplayResolution change_display_resolution = 36; + MessageQuery message_query = 37; } } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 86235dc2f..6ff927796 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2104,6 +2104,12 @@ pub fn main_check_hwcodec() { check_hwcodec() } +pub fn session_request_new_display_init_msgs(session_id: SessionID, display: usize) { + if let Some(session) = sessions::get_session_by_session_id(&session_id) { + session.request_init_msgs(display); + } +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/server/connection.rs b/src/server/connection.rs index 8526a998d..69aa52b50 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2300,7 +2300,11 @@ impl Connection { } } #[cfg(not(any(target_os = "android", target_os = "ios")))] - Some(misc::Union::ChangeResolution(r)) => self.change_resolution(&r), + Some(misc::Union::ChangeResolution(r)) => self.change_resolution(None, &r), + #[cfg(not(any(target_os = "android", target_os = "ios")))] + Some(misc::Union::ChangeDisplayResolution(dr)) => { + self.change_resolution(Some(dr.display as _), &dr.resolution) + } #[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] Some(misc::Union::PluginRequest(p)) => { @@ -2343,6 +2347,13 @@ impl Connection { } } } + Some(misc::Union::MessageQuery(mq)) => { + if let Some(msg_out) = + video_service::make_display_changed_msg(mq.switch_display as _, None) + { + self.send(msg_out).await; + } + } _ => {} }, Some(message::Union::AudioFrame(frame)) => { @@ -2472,11 +2483,14 @@ impl Connection { #[cfg(not(any(target_os = "android", target_os = "ios")))] if s.width != 0 && s.height != 0 { - self.change_resolution(&Resolution { - width: s.width, - height: s.height, - ..Default::default() - }); + self.change_resolution( + None, + &Resolution { + width: s.width, + height: s.height, + ..Default::default() + }, + ); } } @@ -2623,10 +2637,11 @@ impl Connection { } #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn change_resolution(&mut self, r: &Resolution) { + fn change_resolution(&mut self, d: Option, r: &Resolution) { if self.keyboard { if let Ok(displays) = display_service::try_get_displays() { - if let Some(display) = displays.get(self.display_idx) { + let display_idx = d.unwrap_or(self.display_idx); + if let Some(display) = displays.get(display_idx) { let name = display.name(); #[cfg(all(windows, feature = "virtual_display_driver"))] if let Some(_ok) = @@ -2638,11 +2653,18 @@ impl Connection { { return; } - display_service::set_last_changed_resolution( - &name, - (display.width() as _, display.height() as _), - (r.width, r.height), - ); + let mut record_changed = true; + #[cfg(all(windows, feature = "virtual_display_driver"))] + if virtual_display_manager::amyuni_idd::is_my_display(&name) { + record_changed = false; + } + if record_changed { + display_service::set_last_changed_resolution( + &name, + (display.width() as _, display.height() as _), + (r.width, r.height), + ); + } if let Err(e) = crate::platform::change_resolution(&name, r.width as _, r.height as _) { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 44aa6c6b6..61c11feb0 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1222,7 +1222,7 @@ impl Session { pub fn change_resolution(&self, display: i32, width: i32, height: i32) { *self.last_change_display.lock().unwrap() = ChangeDisplayRecord::new(display, width, height); - self.do_change_resolution(width, height); + self.do_change_resolution(display, width, height); } #[inline] @@ -1232,13 +1232,22 @@ impl Session { } } - fn do_change_resolution(&self, width: i32, height: i32) { + fn do_change_resolution(&self, display: i32, width: i32, height: i32) { let mut misc = Misc::new(); - misc.set_change_resolution(Resolution { + let resolution = Resolution { width, height, ..Default::default() - }); + }; + if crate::common::is_support_multi_ui_session_num(self.lc.read().unwrap().version) { + misc.set_change_display_resolution(DisplayResolution { + display, + resolution: Some(resolution).into(), + ..Default::default() + }); + } else { + misc.set_change_resolution(resolution); + } let mut msg = Message::new(); msg.set_misc(misc); self.send(Data::Message(msg)); @@ -1293,6 +1302,22 @@ impl Session { log::error!("selected invalid sid: {}", sid); } } + + #[inline] + pub fn request_init_msgs(&self, display: usize) { + self.send_message_query(display); + } + + fn send_message_query(&self, display: usize) { + let mut misc = Misc::new(); + misc.set_message_query(MessageQuery { + switch_display: display as _, + ..Default::default() + }); + let mut msg = Message::new(); + msg.set_misc(misc); + self.send(Data::Message(msg)); + } } pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index 3188fc33e..1a0a03b01 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -536,6 +536,13 @@ pub mod amyuni_idd { pub fn get_monitor_count() -> usize { windows::get_device_names(Some(super::AMYUNI_IDD_DEVICE_STRING)).len() } + + #[inline] + pub fn is_my_display(name: &str) -> bool { + windows::get_device_names(Some(super::AMYUNI_IDD_DEVICE_STRING)) + .iter() + .any(|s| windows::is_device_name(s, name)) + } } mod windows { From f02a2e44d8f03bb978fba40ab24aa5df15b1f3c0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 20 Apr 2024 15:13:04 +0800 Subject: [PATCH 104/112] remove temp support_windows_specific_session --- libs/hbb_common/protos/message.proto | 3 ++- src/client.rs | 25 ++----------------------- src/server/connection.rs | 3 +-- 3 files changed, 5 insertions(+), 26 deletions(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 7ffa0e6de..1eb6bd078 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -601,7 +601,8 @@ message OptionMessage { BoolOption disable_keyboard = 12; // Position 13 is used for Resolution. Remove later. // Resolution custom_resolution = 13; - BoolOption support_windows_specific_session = 14; +// BoolOption support_windows_specific_session = 14; + // starting from 15 please, do not use removed fields } message TestDelay { diff --git a/src/client.rs b/src/client.rs index f55819a78..5cf16c7ce 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1564,22 +1564,13 @@ impl LoginConfigHandler { /// /// * `ignore_default` - If `true`, ignore the default value of the option. fn get_option_message(&self, ignore_default: bool) -> Option { - if self.conn_type.eq(&ConnType::PORT_FORWARD) || self.conn_type.eq(&ConnType::RDP) { + if self.conn_type.eq(&ConnType::PORT_FORWARD) || self.conn_type.eq(&ConnType::RDP) || self.conn_type.eq(&ConnType::FILE_TRANSFER) { return None; } - let mut n = 0; let mut msg = OptionMessage::new(); - // Version 1.2.5 can remove this, and OptionMessage is not needed for file transfer - msg.support_windows_specific_session = BoolOption::Yes.into(); - n += 1; - - if self.conn_type.eq(&ConnType::FILE_TRANSFER) { - return Some(msg); - } let q = self.image_quality.clone(); if let Some(q) = self.get_image_quality_enum(&q, ignore_default) { msg.image_quality = q.into(); - n += 1; } else if q == "custom" { let config = self.load_config(); let allow_more = !crate::using_public_server() || self.direct == Some(true); @@ -1602,32 +1593,25 @@ impl LoginConfigHandler { msg.custom_fps = custom_fps; *self.custom_fps.lock().unwrap() = Some(custom_fps as _); } - n += 1; } let view_only = self.get_toggle_option("view-only"); if view_only { msg.disable_keyboard = BoolOption::Yes.into(); - n += 1; } if view_only || self.get_toggle_option("show-remote-cursor") { msg.show_remote_cursor = BoolOption::Yes.into(); - n += 1; } if !view_only && self.get_toggle_option("lock-after-session-end") { msg.lock_after_session_end = BoolOption::Yes.into(); - n += 1; } if self.get_toggle_option("disable-audio") { msg.disable_audio = BoolOption::Yes.into(); - n += 1; } if !view_only && self.get_toggle_option("enable-file-transfer") { msg.enable_file_transfer = BoolOption::Yes.into(); - n += 1; } if view_only || self.get_toggle_option("disable-clipboard") { msg.disable_clipboard = BoolOption::Yes.into(); - n += 1; } msg.supported_decoding = hbb_common::protobuf::MessageField::some(Decoder::supported_decodings( @@ -1636,12 +1620,7 @@ impl LoginConfigHandler { self.adapter_luid, &self.mark_unsupported, )); - n += 1; - if n > 0 { - Some(msg) - } else { - None - } + Some(msg) } pub fn get_option_message_after_login(&self) -> Option { diff --git a/src/server/connection.rs b/src/server/connection.rs index 69aa52b50..9cd9221c9 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1337,8 +1337,7 @@ impl Connection { && raii::AuthedConnID::remote_and_file_conn_count() == 1 && sessions.len() > 1 && sessions.iter().any(|e| e.sid == current_sid) - && (get_version_number(&self.lr.version) > get_version_number("1.2.4") - || self.lr.option.support_windows_specific_session == BoolOption::Yes.into()) + && get_version_number(&self.lr.version) >= get_version_number("1.2.4") { pi.windows_sessions = Some(WindowsSessions { sessions, From d920953df1c781908bf9c1b6f5c8924ac30e9f9f Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 20 Apr 2024 15:28:33 +0800 Subject: [PATCH 105/112] typo (#7783) Signed-off-by: fufesou --- libs/hbb_common/protos/message.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 1eb6bd078..555b1df43 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -722,7 +722,7 @@ message WindowsSessions { uint32 current_sid = 2; } -// Query a message from peer. +// Query messages from peer. message MessageQuery { // The SwitchDisplay message of the target display. // If the target display is not found, the message will be ignored. From c81c4f9114dfcf6da07647706fb1f15e368d683c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 20 Apr 2024 18:19:19 +0800 Subject: [PATCH 106/112] visual studio 2019 -> 2022 --- .github/workflows/ci.yml | 6 +++--- .github/workflows/flutter-build.yml | 18 +++++++++--------- .github/workflows/history.yml | 2 +- .../third-party-RustDeskTempTopMostWindow.yml | 2 +- res/msi/CustomActions/CustomActions.vcxproj | 4 ++-- res/msi/README.md | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccfc1bdf4..9a8ecf5cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,12 +71,12 @@ jobs: # - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } # - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true } # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - # - { target: i686-pc-windows-msvc , os: windows-2019 } + # - { target: i686-pc-windows-msvc , os: windows-2022 } # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } # - { target: x86_64-apple-darwin , os: macos-10.15 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - # - { target: x86_64-pc-windows-msvc , os: windows-2019 } + # - { target: x86_64-pc-windows-gnu , os: windows-2022 } + # - { target: x86_64-pc-windows-msvc , os: windows-2022 } - { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 } # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } steps: diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index c865db73a..f9281a658 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -41,7 +41,7 @@ jobs: uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml with: upload-artifact: ${{ inputs.upload-artifact }} - target: windows-2019 + target: windows-2022 configuration: Release platform: x64 target_version: Windows10 @@ -56,10 +56,10 @@ jobs: fail-fast: false matrix: job: - # - { target: i686-pc-windows-msvc , os: windows-2019 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: x86_64-pc-windows-msvc, os: windows-2019, arch: x86_64 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } + # - { target: i686-pc-windows-msvc , os: windows-2022 } + # - { target: x86_64-pc-windows-gnu , os: windows-2022 } + - { target: x86_64-pc-windows-msvc, os: windows-2022, arch: x86_64 } + # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } steps: - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 @@ -213,10 +213,10 @@ jobs: fail-fast: false matrix: job: - # - { target: i686-pc-windows-msvc , os: windows-2019 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: i686-pc-windows-msvc, os: windows-2019, arch: x86 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019 } + # - { target: i686-pc-windows-msvc , os: windows-2022 } + # - { target: x86_64-pc-windows-gnu , os: windows-2022 } + - { target: i686-pc-windows-msvc, os: windows-2022, arch: x86 } + # - { target: aarch64-pc-windows-msvc, os: windows-2022 } steps: - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml index 91e695f8e..a0c2294c0 100644 --- a/.github/workflows/history.yml +++ b/.github/workflows/history.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: job: - - { target: x86_64-pc-windows-msvc, os: windows-2019, arch: x86_64, date: 2023-08-04, ref: 72c198a1e94cc1e0242fce88f92b3f3caedcd0c3 } + - { target: x86_64-pc-windows-msvc, os: windows-2022, arch: x86_64, date: 2023-08-04, ref: 72c198a1e94cc1e0242fce88f92b3f3caedcd0c3 } steps: - name: Checkout source code uses: actions/checkout@v4 diff --git a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml index 78f3ad2f1..2f89092b7 100644 --- a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml +++ b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -10,7 +10,7 @@ on: description: 'Target' required: true type: string - default: 'windows-2019' + default: 'windows-2022' configuration: description: 'Configuration' required: true diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index de16f1039..d2e5fb570 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -12,7 +12,7 @@ Win32Proj {6b3647e0-b4a3-46ae-8757-a22ee51c1dac} CustomActions - v142 + v143 10.0 @@ -81,4 +81,4 @@ - \ No newline at end of file + diff --git a/res/msi/README.md b/res/msi/README.md index a4608467d..5ff2a1080 100644 --- a/res/msi/README.md +++ b/res/msi/README.md @@ -1,6 +1,6 @@ # RustDesk msi project -Use Visual Studio 2019 to compile this project. +Use Visual Studio 2022 to compile this project. This project is mainly derived from . From 0365c944071572626872d32a75e9fe0909bc8d57 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 20 Apr 2024 19:24:44 +0800 Subject: [PATCH 107/112] remove IsWow64Process2 --- src/platform/windows.cc | 17 +------------ src/platform/windows.rs | 56 ++++++----------------------------------- 2 files changed, 9 insertions(+), 64 deletions(-) diff --git a/src/platform/windows.cc b/src/platform/windows.cc index 0d328ac13..4a9888ad3 100644 --- a/src/platform/windows.cc +++ b/src/platform/windows.cc @@ -698,19 +698,4 @@ extern "C" 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; - } - } -} +} // end of extern "C" \ No newline at end of file diff --git a/src/platform/windows.rs b/src/platform/windows.rs index a6dfd444f..6542d1023 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -460,7 +460,6 @@ 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; } @@ -2370,55 +2369,16 @@ impl Drop for WallPaperRemover { } } -// 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()) + let exe = match std::env::consts::ARCH { + "x86" => "deviceinstaller.exe", + "x86_64" => "deviceinstaller64.exe", + _ => { + log::error!("Unsupported machine architecture"); + return None; } - Err(e) => { - log::warn!("Failed to get machine architecture: {}", e); - None - } - } + }; + Some(exe.to_string()) } fn get_uninstall_amyuni_idd(path: &str) -> String { From 33c8bdfabfe5a927e8500c68567a48087f68b714 Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Sun, 21 Apr 2024 09:53:22 +0300 Subject: [PATCH 108/112] Update lv.rs (#7784) --- 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 cc0cf98ff..9e0b496ab 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -601,6 +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", ""), + ("no_need_privacy_mode_no_physical_displays_tip", "Nav fizisku displeju, nav jāizmanto privātuma režīms."), ].iter().cloned().collect(); } From ad062486ff21e650131bba0376ec44cf9afbbda1 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 21 Apr 2024 14:55:42 +0800 Subject: [PATCH 109/112] Fix/win query arch (#7786) * fix: win, query arch with GetNativeSystemInfo Signed-off-by: fufesou * refact: idd, ci Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 20 +++++++------- res/msi/CustomActions/CustomActions.cpp | 36 +++++++++++-------------- src/platform/windows.rs | 14 +++++++--- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index f9281a658..fde9ff6cb 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -115,16 +115,11 @@ jobs: - name: Build rustdesk 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/ - } + Expand-Archive usbmmidd_v2.zip -DestinationPath . + python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack --virtual-display + Remove-Item -Path usbmmidd_v2\Win32 -Recurse + Remove-Item -Path usbmmidd_v2\deviceinstaller.exe + 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 @@ -262,11 +257,14 @@ jobs: python3 res/inline-sciter.py # Patch sciter x86 sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml - cargo build --features inline,vram,hwcodec --release --bins + cargo build --features inline,vram,hwcodec,virtual_display_driver --release --bins mkdir -p ./Release mv ./target/release/rustdesk.exe ./Release/rustdesk.exe curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll echo "output_folder=./Release" >> $GITHUB_OUTPUT + curl -LJ -o ./usbmmidd_v2.zip https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip + unzip usbmmidd_v2.zip + mv ./usbmmidd_v2 ./Release || true - name: find Runner.res # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 3643ea9ce..9a019ea49 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -607,9 +607,7 @@ UINT __stdcall RemoveAmyuniIdd( DWORD fileAttributes = 0; HINSTANCE hi = 0; - USHORT processMachine = 0; - USHORT nativeMachine = 0; - BOOL isWow64Res = FALSE; + SYSTEM_INFO si; LPCWSTR exe = NULL; hr = WcaInitialize(hInstall, "RemoveAmyuniIdd"); @@ -630,24 +628,22 @@ UINT __stdcall RemoveAmyuniIdd( 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"); - } + GetNativeSystemInfo(&si); + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + exe = L"deviceinstaller64.exe"; } else { - WcaLog(LOGMSG_STANDARD, "Failed to call IsWow64Process2(): %d", GetLastError()); + // No need to check if is other architecture. + // Because the driver is only for x86 and x64. It will not work at on other architectures. + 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"); } LExit: diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 6542d1023..4e213c501 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -42,6 +42,7 @@ use winapi::{ }, securitybaseapi::GetTokenInformation, shellapi::ShellExecuteW, + sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO}, winbase::*, wingdi::*, winnt::{ @@ -2370,9 +2371,16 @@ impl Drop for WallPaperRemover { } pub fn get_amyuni_exe_name() -> Option { - let exe = match std::env::consts::ARCH { - "x86" => "deviceinstaller.exe", - "x86_64" => "deviceinstaller64.exe", + let mut sys_info = SYSTEM_INFO::default(); + unsafe { + GetNativeSystemInfo(&mut sys_info as _); + } + const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0; + const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9; + + let exe = match unsafe { sys_info.u.s().wProcessorArchitecture } { + PROCESSOR_ARCHITECTURE_INTEL => "deviceinstaller.exe", + PROCESSOR_ARCHITECTURE_AMD64 => "deviceinstaller64.exe", _ => { log::error!("Unsupported machine architecture"); return None; From 4f47d4482b22f3344a098874ffc87b38edb41c9b Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 22 Apr 2024 10:37:08 +0800 Subject: [PATCH 110/112] refact: win, idd control (#7789) * refact: win, idd control Signed-off-by: fufesou * refact: win device control, better addr of Signed-off-by: fufesou * refact: simple refact Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 3 +- Cargo.lock | 1 + Cargo.toml | 15 +- libs/hbb_common/Cargo.toml | 1 + libs/hbb_common/src/lib.rs | 1 + res/msi/CustomActions/Common.h | 1 + res/msi/CustomActions/CustomActions.cpp | 49 +- res/msi/CustomActions/CustomActions.vcxproj | 3 +- res/msi/CustomActions/DeviceUtils.cpp | 84 ++++ res/msi/Package/Components/RustDesk.wxs | 2 - src/core_main.rs | 6 + src/platform/mod.rs | 3 + src/platform/win_device.rs | 485 ++++++++++++++++++++ src/platform/windows.rs | 37 +- src/privacy_mode.rs | 45 +- src/privacy_mode/win_topmost_window.rs | 4 - src/privacy_mode/win_virtual_display.rs | 4 - src/server/connection.rs | 2 +- src/virtual_display_manager.rs | 92 ++-- 19 files changed, 657 insertions(+), 181 deletions(-) create mode 100644 res/msi/CustomActions/DeviceUtils.cpp create mode 100644 src/platform/win_device.rs diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index fde9ff6cb..f6123e4a2 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -118,7 +118,7 @@ jobs: Expand-Archive usbmmidd_v2.zip -DestinationPath . python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack --virtual-display Remove-Item -Path usbmmidd_v2\Win32 -Recurse - Remove-Item -Path usbmmidd_v2\deviceinstaller.exe + Remove-Item -Path "usbmmidd_v2\deviceinstaller64.exe", "usbmmidd_v2\deviceinstaller.exe", "usbmmidd_v2\usbmmidd.bat" mv -Force .\usbmmidd_v2 ./flutter/build/windows/x64/runner/Release/ - name: find Runner.res @@ -264,6 +264,7 @@ jobs: echo "output_folder=./Release" >> $GITHUB_OUTPUT curl -LJ -o ./usbmmidd_v2.zip https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip unzip usbmmidd_v2.zip + rm -rf ./usbmmidd_v2/x64 ./usbmmidd_v2/deviceinstaller.exe ./usbmmidd_v2/deviceinstaller64.exe ./usbmmidd_v2/usbmmidd.bat mv ./usbmmidd_v2 ./Release || true - name: find Runner.res diff --git a/Cargo.lock b/Cargo.lock index dd018c6b1..ce54262ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2904,6 +2904,7 @@ dependencies = [ "socket2 0.3.19", "sodiumoxide", "sysinfo", + "thiserror", "tokio", "tokio-socks", "tokio-util", diff --git a/Cargo.toml b/Cargo.toml index 10a0ecf8d..8a9d3d5fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,20 @@ 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", "devguid", "setupapi", "cguid", "cfgmgr32"] } +winapi = { version = "0.3", features = [ + "winuser", + "wincrypt", + "shellscalingapi", + "pdh", + "synchapi", + "memoryapi", + "shellapi", + "devguid", + "setupapi", + "cguid", + "cfgmgr32", + "ioapiset", +] } winreg = "0.11" windows-service = "0.6" virtual_display = { path = "libs/virtual_display", optional = true } diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 2da853420..9f77abf0e 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -40,6 +40,7 @@ toml = "0.7" uuid = { version = "1.3", features = ["v4"] } # crash, versions >= 0.29.1 are affected by #GuillaumeGomez/sysinfo/1052 sysinfo = { git = "https://github.com/rustdesk-org/sysinfo" } +thiserror = "1.0" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] mac_address = "1.1" diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index d360d3583..eed2331fb 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -51,6 +51,7 @@ pub use serde_json; pub use sysinfo; pub use toml; pub use uuid; +pub use thiserror; #[cfg(feature = "quic")] pub type Stream = quic::Connection; diff --git a/res/msi/CustomActions/Common.h b/res/msi/CustomActions/Common.h index 631fc5d38..e01262f00 100644 --- a/res/msi/CustomActions/Common.h +++ b/res/msi/CustomActions/Common.h @@ -13,3 +13,4 @@ bool MyStopServiceW(LPCWSTR serviceName); std::wstring ReadConfig(const std::wstring& filename, const std::wstring& key); +void UninstallDriver(LPCWSTR hardwareId, BOOL &rebootRequired); diff --git a/res/msi/CustomActions/CustomActions.cpp b/res/msi/CustomActions/CustomActions.cpp index 9a019ea49..bea7c0525 100644 --- a/res/msi/CustomActions/CustomActions.cpp +++ b/res/msi/CustomActions/CustomActions.cpp @@ -70,7 +70,7 @@ UINT __stdcall RemoveInstallFolder( } LExit: - ReleaseStr(installFolder); + ReleaseStr(pwzData); er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); @@ -598,57 +598,14 @@ UINT __stdcall RemoveAmyuniIdd( 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; - - SYSTEM_INFO si; - LPCWSTR exe = NULL; + BOOL rebootRequired = FALSE; 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; - } - - GetNativeSystemInfo(&si); - if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { - exe = L"deviceinstaller64.exe"; - } else { - // No need to check if is other architecture. - // Because the driver is only for x86 and x64. It will not work at on other architectures. - 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"); - } + UninstallDriver(L"usbmmidd", rebootRequired); LExit: - ReleaseStr(installFolder); - er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; return WcaFinalize(er); } diff --git a/res/msi/CustomActions/CustomActions.vcxproj b/res/msi/CustomActions/CustomActions.vcxproj index d2e5fb570..1bff7b154 100644 --- a/res/msi/CustomActions/CustomActions.vcxproj +++ b/res/msi/CustomActions/CustomActions.vcxproj @@ -60,6 +60,7 @@ + @@ -81,4 +82,4 @@ - + \ No newline at end of file diff --git a/res/msi/CustomActions/DeviceUtils.cpp b/res/msi/CustomActions/DeviceUtils.cpp new file mode 100644 index 000000000..5bc6c10bc --- /dev/null +++ b/res/msi/CustomActions/DeviceUtils.cpp @@ -0,0 +1,84 @@ +#include "pch.h" + +#include +#include +#include +#include + +#pragma comment(lib, "SetupAPI.lib") + + +void UninstallDriver(LPCWSTR hardwareId, BOOL &rebootRequired) +{ + HDEVINFO deviceInfoSet = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, DIGCF_PRESENT); + if (deviceInfoSet == INVALID_HANDLE_VALUE) + { + WcaLog(LOGMSG_STANDARD, "Failed to get device information set, last error: %d", GetLastError()); + return; + } + + SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail; + devInfoListDetail.cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA); + if (!SetupDiGetDeviceInfoListDetailW(deviceInfoSet, &devInfoListDetail)) + { + SetupDiDestroyDeviceInfoList(deviceInfoSet); + WcaLog(LOGMSG_STANDARD, "Failed to call SetupDiGetDeviceInfoListDetail, last error: %d", GetLastError()); + return; + } + + SP_DEVINFO_DATA deviceInfoData; + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + DWORD dataType; + WCHAR deviceId[MAX_DEVICE_ID_LEN] = { 0, }; + + DWORD deviceIndex = 0; + while (SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, &deviceInfoData)) + { + if (!SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, &deviceInfoData, SPDRP_HARDWAREID, &dataType, (PBYTE)deviceId, MAX_DEVICE_ID_LEN, NULL)) + { + WcaLog(LOGMSG_STANDARD, "Failed to get hardware id, last error: %d", GetLastError()); + deviceIndex++; + continue; + } + if (wcscmp(deviceId, hardwareId) != 0) + { + deviceIndex++; + continue; + } + + SP_REMOVEDEVICE_PARAMS remove_device_params; + remove_device_params.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + remove_device_params.ClassInstallHeader.InstallFunction = DIF_REMOVE; + remove_device_params.Scope = DI_REMOVEDEVICE_GLOBAL; + remove_device_params.HwProfile = 0; + + if (!SetupDiSetClassInstallParamsW(deviceInfoSet, &deviceInfoData, &remove_device_params.ClassInstallHeader, sizeof(SP_REMOVEDEVICE_PARAMS))) + { + WcaLog(LOGMSG_STANDARD, "Failed to set class install params, last error: %d", GetLastError()); + deviceIndex++; + continue; + } + + if (!SetupDiCallClassInstaller(DIF_REMOVE, deviceInfoSet, &deviceInfoData)) + { + WcaLog(LOGMSG_STANDARD, "ailed to uninstall driver, last error: %d", GetLastError()); + deviceIndex++; + continue; + } + + SP_DEVINSTALL_PARAMS deviceParams; + if (SetupDiGetDeviceInstallParamsW(deviceInfoSet, &deviceInfoData, &deviceParams)) + { + if (deviceParams.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) + { + rebootRequired = true; + } + } + + WcaLog(LOGMSG_STANDARD, "Driver uninstalled successfully"); + deviceIndex++; + } + + SetupDiDestroyDeviceInfoList(deviceInfoSet); +} diff --git a/res/msi/Package/Components/RustDesk.wxs b/res/msi/Package/Components/RustDesk.wxs index 31dbbc66d..a79f870b8 100644 --- a/res/msi/Package/Components/RustDesk.wxs +++ b/res/msi/Package/Components/RustDesk.wxs @@ -29,7 +29,6 @@ - @@ -74,7 +73,6 @@ - diff --git a/src/core_main.rs b/src/core_main.rs index 1f5de5e30..5055626cd 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -232,6 +232,12 @@ pub fn core_main() -> Option> { _is_run_as_system, ); return None; + } else if args[0] == "--uninstall-amyuni-idd" { + #[cfg(all(windows, feature = "virtual_display_driver"))] + hbb_common::allow_err!( + crate::virtual_display_manager::amyuni_idd::uninstall_driver() + ); + return None; } } if args[0] == "--remove" { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 8100b6516..6f7a028f3 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -8,6 +8,9 @@ pub use windows::*; #[cfg(windows)] pub mod windows; +#[cfg(windows)] +pub mod win_device; + #[cfg(target_os = "macos")] pub mod macos; diff --git a/src/platform/win_device.rs b/src/platform/win_device.rs new file mode 100644 index 000000000..f010c7f8a --- /dev/null +++ b/src/platform/win_device.rs @@ -0,0 +1,485 @@ +use hbb_common::{log, thiserror}; +use std::{ + ffi::OsStr, + io, + ops::{Deref, DerefMut}, + os::windows::ffi::OsStrExt, + ptr::null_mut, + result::Result, +}; +use winapi::{ + shared::{ + guiddef::GUID, + minwindef::{BOOL, DWORD, FALSE, MAX_PATH, PBOOL, TRUE}, + ntdef::{HANDLE, LPCWSTR, NULL}, + windef::HWND, + winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_ITEMS}, + }, + um::{ + cfgmgr32::MAX_DEVICE_ID_LEN, + fileapi::{CreateFileW, OPEN_EXISTING}, + handleapi::{CloseHandle, INVALID_HANDLE_VALUE}, + ioapiset::DeviceIoControl, + setupapi::*, + winnt::{GENERIC_READ, GENERIC_WRITE}, + }, +}; + +#[link(name = "Newdev")] +extern "system" { + fn UpdateDriverForPlugAndPlayDevicesW( + hwnd_parent: HWND, + hardware_id: LPCWSTR, + full_inf_path: LPCWSTR, + install_flags: DWORD, + b_reboot_required: PBOOL, + ) -> BOOL; +} + +#[derive(thiserror::Error, Debug)] +pub enum DeviceError { + #[error("Failed to call {0}, {1:?}")] + WinApiLastErr(String, io::Error), + #[error("Failed to call {0}, returns {1}")] + WinApiErrCode(String, DWORD), + #[error("{0}")] + Raw(String), +} + +struct DeviceInfo(HDEVINFO); + +impl DeviceInfo { + fn setup_di_create_device_info_list(class_guid: &mut GUID) -> Result { + let dev_info = unsafe { SetupDiCreateDeviceInfoList(class_guid, null_mut()) }; + if dev_info == null_mut() { + return Err(DeviceError::WinApiLastErr( + "SetupDiCreateDeviceInfoList".to_string(), + io::Error::last_os_error(), + )); + } + + Ok(Self(dev_info)) + } + + fn setup_di_get_class_devs_ex_w( + class_guid: *const GUID, + flags: DWORD, + ) -> Result { + let dev_info = unsafe { + SetupDiGetClassDevsExW( + class_guid, + null_mut(), + null_mut(), + flags, + null_mut(), + null_mut(), + null_mut(), + ) + }; + if dev_info == null_mut() { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetClassDevsExW".to_string(), + io::Error::last_os_error(), + )); + } + Ok(Self(dev_info)) + } +} + +impl Drop for DeviceInfo { + fn drop(&mut self) { + unsafe { + SetupDiDestroyDeviceInfoList(self.0); + } + } +} + +impl Deref for DeviceInfo { + type Target = HDEVINFO; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for DeviceInfo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub unsafe fn install_driver( + inf_path: &str, + hardware_id: &str, + reboot_required: &mut bool, +) -> Result<(), DeviceError> { + let driver_inf_path = OsStr::new(inf_path) + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); + let hardware_id = OsStr::new(hardware_id) + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); + + let mut class_guid: GUID = std::mem::zeroed(); + let mut class_name: [u16; 32] = [0; 32]; + + if SetupDiGetINFClassW( + driver_inf_path.as_ptr(), + &mut class_guid, + class_name.as_mut_ptr(), + class_name.len() as _, + null_mut(), + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetINFClassW".to_string(), + io::Error::last_os_error(), + )); + } + + let dev_info = DeviceInfo::setup_di_create_device_info_list(&mut class_guid)?; + + let mut dev_info_data = SP_DEVINFO_DATA { + cbSize: std::mem::size_of::() as _, + ClassGuid: class_guid, + DevInst: 0, + Reserved: 0, + }; + if SetupDiCreateDeviceInfoW( + *dev_info, + class_name.as_ptr(), + &class_guid, + null_mut(), + null_mut(), + DICD_GENERATE_ID, + &mut dev_info_data, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiCreateDeviceInfoW".to_string(), + io::Error::last_os_error(), + )); + } + + if SetupDiSetDeviceRegistryPropertyW( + *dev_info, + &mut dev_info_data, + SPDRP_HARDWAREID, + hardware_id.as_ptr() as _, + (hardware_id.len() * 2) as _, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiSetDeviceRegistryPropertyW".to_string(), + io::Error::last_os_error(), + )); + } + + if SetupDiCallClassInstaller(DIF_REGISTERDEVICE, *dev_info, &mut dev_info_data) == FALSE { + return Err(DeviceError::WinApiLastErr( + "SetupDiCallClassInstaller".to_string(), + io::Error::last_os_error(), + )); + } + + let mut reboot_required_ = FALSE; + if UpdateDriverForPlugAndPlayDevicesW( + null_mut(), + hardware_id.as_ptr(), + driver_inf_path.as_ptr(), + 1, + &mut reboot_required_, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "UpdateDriverForPlugAndPlayDevicesW".to_string(), + io::Error::last_os_error(), + )); + } + *reboot_required = reboot_required_ == TRUE; + + Ok(()) +} + +unsafe fn is_same_hardware_id( + dev_info: &DeviceInfo, + devinfo_data: &mut SP_DEVINFO_DATA, + hardware_id: &str, +) -> Result { + let mut cur_hardware_id = [0u16; MAX_DEVICE_ID_LEN]; + if SetupDiGetDeviceRegistryPropertyW( + **dev_info, + devinfo_data, + SPDRP_HARDWAREID, + null_mut(), + cur_hardware_id.as_mut_ptr() as _, + cur_hardware_id.len() as _, + null_mut(), + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceRegistryPropertyW".to_string(), + io::Error::last_os_error(), + )); + } + + let cur_hardware_id = String::from_utf16_lossy(&cur_hardware_id) + .trim_end_matches(char::from(0)) + .to_string(); + Ok(cur_hardware_id == hardware_id) +} + +pub unsafe fn uninstall_driver( + hardware_id: &str, + reboot_required: &mut bool, +) -> Result<(), DeviceError> { + let dev_info = + DeviceInfo::setup_di_get_class_devs_ex_w(null_mut(), DIGCF_ALLCLASSES | DIGCF_PRESENT)?; + + let mut device_info_list_detail = SP_DEVINFO_LIST_DETAIL_DATA_W { + cbSize: std::mem::size_of::() as _, + ClassGuid: std::mem::zeroed(), + RemoteMachineHandle: null_mut(), + RemoteMachineName: [0; SP_MAX_MACHINENAME_LENGTH], + }; + if SetupDiGetDeviceInfoListDetailW(*dev_info, &mut device_info_list_detail) == FALSE { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceInfoListDetailW".to_string(), + io::Error::last_os_error(), + )); + } + + let mut devinfo_data = SP_DEVINFO_DATA { + cbSize: std::mem::size_of::() as _, + ClassGuid: std::mem::zeroed(), + DevInst: 0, + Reserved: 0, + }; + + let mut device_index = 0; + loop { + if SetupDiEnumDeviceInfo(*dev_info, device_index, &mut devinfo_data) == FALSE { + let err = io::Error::last_os_error(); + if err.raw_os_error() == Some(ERROR_NO_MORE_ITEMS as _) { + break; + } + return Err(DeviceError::WinApiLastErr( + "SetupDiEnumDeviceInfo".to_string(), + err, + )); + } + + match is_same_hardware_id(&dev_info, &mut devinfo_data, hardware_id) { + Ok(false) => { + device_index += 1; + continue; + } + Err(e) => { + log::error!("Failed to call is_same_hardware_id, {:?}", e); + device_index += 1; + continue; + } + _ => {} + } + + let mut remove_device_params = SP_REMOVEDEVICE_PARAMS { + ClassInstallHeader: SP_CLASSINSTALL_HEADER { + cbSize: std::mem::size_of::() as _, + InstallFunction: DIF_REMOVE, + }, + Scope: DI_REMOVEDEVICE_GLOBAL, + HwProfile: 0, + }; + + if SetupDiSetClassInstallParamsW( + *dev_info, + &mut devinfo_data, + &mut remove_device_params.ClassInstallHeader, + std::mem::size_of::() as _, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiSetClassInstallParams".to_string(), + io::Error::last_os_error(), + )); + } + + if SetupDiCallClassInstaller(DIF_REMOVE, *dev_info, &mut devinfo_data) == FALSE { + return Err(DeviceError::WinApiLastErr( + "SetupDiCallClassInstaller".to_string(), + io::Error::last_os_error(), + )); + } + + let mut device_params = SP_DEVINSTALL_PARAMS_W { + cbSize: std::mem::size_of::() as _, + Flags: 0, + FlagsEx: 0, + hwndParent: null_mut(), + InstallMsgHandler: None, + InstallMsgHandlerContext: null_mut(), + FileQueue: null_mut(), + ClassInstallReserved: 0, + Reserved: 0, + DriverPath: [0; MAX_PATH], + }; + + if SetupDiGetDeviceInstallParamsW(*dev_info, &mut devinfo_data, &mut device_params) == FALSE + { + log::error!( + "Failed to call SetupDiGetDeviceInstallParamsW, {:?}", + io::Error::last_os_error() + ); + } else { + if device_params.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT) != 0 { + *reboot_required = true; + } + } + + device_index += 1; + } + + Ok(()) +} + +pub unsafe fn device_io_control( + interface_guid: &GUID, + control_code: u32, + inbuf: &[u8], + outbuf_max_len: usize, +) -> Result, DeviceError> { + let h_device = open_device_handle(interface_guid)?; + let mut bytes_returned = 0; + let mut outbuf: Vec = vec![]; + let outbuf_ptr = if outbuf_max_len > 0 { + outbuf.reserve(outbuf_max_len); + outbuf.as_mut_ptr() + } else { + null_mut() + }; + let result = DeviceIoControl( + h_device, + control_code, + inbuf.as_ptr() as _, + inbuf.len() as _, + outbuf_ptr as _, + outbuf_max_len as _, + &mut bytes_returned, + null_mut(), + ); + CloseHandle(h_device); + if result == FALSE { + return Err(DeviceError::WinApiLastErr( + "DeviceIoControl".to_string(), + io::Error::last_os_error(), + )); + } + if outbuf_max_len > 0 { + outbuf.set_len(bytes_returned as _); + Ok(outbuf) + } else { + Ok(Vec::new()) + } +} + +unsafe fn get_device_path(interface_guid: &GUID) -> Result, DeviceError> { + let dev_info = DeviceInfo::setup_di_get_class_devs_ex_w( + interface_guid, + DIGCF_PRESENT | DIGCF_DEVICEINTERFACE, + )?; + let mut device_interface_data = SP_DEVICE_INTERFACE_DATA { + cbSize: std::mem::size_of::() as _, + InterfaceClassGuid: *interface_guid, + Flags: 0, + Reserved: 0, + }; + if SetupDiEnumDeviceInterfaces( + *dev_info, + null_mut(), + interface_guid, + 0, + &mut device_interface_data, + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiEnumDeviceInterfaces".to_string(), + io::Error::last_os_error(), + )); + } + + let mut required_length = 0; + if SetupDiGetDeviceInterfaceDetailW( + *dev_info, + &mut device_interface_data, + null_mut(), + 0, + &mut required_length, + null_mut(), + ) == FALSE + { + let err = io::Error::last_os_error(); + if err.raw_os_error() != Some(ERROR_INSUFFICIENT_BUFFER as _) { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceInterfaceDetailW".to_string(), + err, + )); + } + } + + let predicted_length = required_length; + let mut vec_data: Vec = Vec::with_capacity(required_length as _); + let device_interface_detail_data = vec_data.as_mut_ptr(); + let device_interface_detail_data = + device_interface_detail_data as *mut SP_DEVICE_INTERFACE_DETAIL_DATA_W; + (*device_interface_detail_data).cbSize = + std::mem::size_of::() as _; + if SetupDiGetDeviceInterfaceDetailW( + *dev_info, + &mut device_interface_data, + device_interface_detail_data, + predicted_length, + &mut required_length, + null_mut(), + ) == FALSE + { + return Err(DeviceError::WinApiLastErr( + "SetupDiGetDeviceInterfaceDetailW".to_string(), + io::Error::last_os_error(), + )); + } + + let mut path = Vec::new(); + let device_path_ptr = std::ptr::addr_of!((*device_interface_detail_data).DevicePath) as *const u16; + let steps = device_path_ptr as usize - vec_data.as_ptr() as usize; + for i in 0..(predicted_length - steps as u32) / 2 { + if *device_path_ptr.offset(i as _) == 0 { + path.push(0); + break; + } + path.push(*device_path_ptr.offset(i as _)); + } + Ok(path) +} + +unsafe fn open_device_handle(interface_guid: &GUID) -> Result { + let device_path = get_device_path(interface_guid)?; + println!("device_path: {:?}", String::from_utf16_lossy(&device_path)); + let h_device = CreateFileW( + device_path.as_ptr(), + GENERIC_READ | GENERIC_WRITE, + 0, + null_mut(), + OPEN_EXISTING, + 0, + null_mut(), + ); + if h_device == INVALID_HANDLE_VALUE || h_device == NULL { + return Err(DeviceError::WinApiLastErr( + "CreateFileW".to_string(), + io::Error::last_os_error(), + )); + } + Ok(h_device) +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 4e213c501..d0b995406 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -42,7 +42,6 @@ use winapi::{ }, securitybaseapi::GetTokenInformation, shellapi::ShellExecuteW, - sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO}, winbase::*, wingdi::*, winnt::{ @@ -2370,37 +2369,13 @@ impl Drop for WallPaperRemover { } } -pub fn get_amyuni_exe_name() -> Option { - let mut sys_info = SYSTEM_INFO::default(); - unsafe { - GetNativeSystemInfo(&mut sys_info as _); - } - const PROCESSOR_ARCHITECTURE_INTEL: u16 = 0; - const PROCESSOR_ARCHITECTURE_AMD64: u16 = 9; - - let exe = match unsafe { sys_info.u.s().wProcessorArchitecture } { - PROCESSOR_ARCHITECTURE_INTEL => "deviceinstaller.exe", - PROCESSOR_ARCHITECTURE_AMD64 => "deviceinstaller64.exe", - _ => { - log::error!("Unsupported machine architecture"); - return None; - } - }; - Some(exe.to_string()) -} - 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() + match std::env::current_exe() { + Ok(path) => format!("\"{}\" --uninstall-amyuni-idd", path.to_str().unwrap_or("")), + Err(e) => { + log::warn!("Failed to get current exe path, cannot get command of uninstalling idd, Zzerror: {:?}", e); + "".to_string() + } } } diff --git a/src/privacy_mode.rs b/src/privacy_mode.rs index f6eafcf41..859eef123 100644 --- a/src/privacy_mode.rs +++ b/src/privacy_mode.rs @@ -6,12 +6,7 @@ use crate::{ display_service, ipc::{connect, Data}, }; -use hbb_common::{ - anyhow::anyhow, - bail, lazy_static, - tokio::{self, sync::oneshot}, - ResultType, -}; +use hbb_common::{anyhow::anyhow, bail, lazy_static, tokio, ResultType}; use serde_derive::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -56,8 +51,6 @@ 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; @@ -207,41 +200,7 @@ fn get_supported_impl(impl_key: &str) -> String { cur_impl } -#[inline] -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> { +pub fn turn_on_privacy(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(); diff --git a/src/privacy_mode/win_topmost_window.rs b/src/privacy_mode/win_topmost_window.rs index a7f80a02d..7fd27b60b 100644 --- a/src/privacy_mode/win_topmost_window.rs +++ b/src/privacy_mode/win_topmost_window.rs @@ -72,10 +72,6 @@ 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 04a9d776c..7e7543d67 100644 --- a/src/privacy_mode/win_virtual_display.rs +++ b/src/privacy_mode/win_virtual_display.rs @@ -360,10 +360,6 @@ impl PrivacyModeImpl { } impl PrivacyMode for PrivacyModeImpl { - fn is_async_privacy_mode(&self) -> bool { - virtual_display_manager::is_amyuni_idd() - } - fn init(&self) -> ResultType<()> { Ok(()) } diff --git a/src/server/connection.rs b/src/server/connection.rs index 9cd9221c9..afd090a92 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2883,7 +2883,7 @@ impl Connection { } } - let turn_on_res = privacy_mode::turn_on_privacy(&impl_key, self.inner.id).await; + let turn_on_res = privacy_mode::turn_on_privacy(&impl_key, self.inner.id); match turn_on_res { Some(Ok(res)) => { if res { diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index 1a0a03b01..4358e6561 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -402,58 +402,31 @@ pub mod rustdesk_idd { pub mod amyuni_idd { use super::windows; - use crate::platform::windows::get_amyuni_exe_name; + use crate::platform::win_device; use hbb_common::{bail, lazy_static, log, ResultType}; - use std::{ - ptr::null_mut, - sync::{Arc, Mutex}, + use std::sync::{Arc, Mutex}; + use winapi::shared::guiddef::GUID; + + const INF_PATH: &str = r#"usbmmidd_v2\usbmmIdd.inf"#; + const INTERFACE_GUID: GUID = GUID { + Data1: 0xb5ffd75f, + Data2: 0xda40, + Data3: 0x4353, + Data4: [0x8f, 0xf8, 0xb6, 0xda, 0xf6, 0xf1, 0xd8, 0xca], }; - use winapi::um::shellapi::ShellExecuteA; + const HARDWARE_ID: &str = "usbmmidd"; + const PLUG_MONITOR_IO_CONTROL_CDOE: u32 = 2307084; 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); - + pub fn uninstall_driver() -> ResultType<()> { + let mut reboot_required = false; 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(()) + win_device::uninstall_driver(HARDWARE_ID, &mut reboot_required)?; } + Ok(()) } fn check_install_driver() -> ResultType<()> { @@ -466,7 +439,32 @@ pub mod amyuni_idd { return Ok(()); } - run_deviceinstaller("install usbmmidd.inf usbmmidd") + let exe_file = std::env::current_exe()?; + let Some(cur_dir) = exe_file.parent() else { + bail!("Cannot get parent of current exe file"); + }; + + let inf_path = cur_dir.join(INF_PATH); + if !inf_path.exists() { + bail!("Driver inf file not found."); + } + let inf_path = inf_path.to_string_lossy().to_string(); + + let mut reboot_required = false; + unsafe { + win_device::install_driver(&inf_path, HARDWARE_ID, &mut reboot_required)?; + } + Ok(()) + } + + #[inline] + fn plug_in_monitor_(add: bool) -> ResultType<()> { + let cmd = if add { 0x10 } else { 0x00 }; + let cmd = [cmd, 0x00, 0x00, 0x00]; + unsafe { + win_device::device_io_control(&INTERFACE_GUID, PLUG_MONITOR_IO_CONTROL_CDOE, &cmd, 0)?; + } + Ok(()) } pub fn plug_in_headless() -> ResultType<()> { @@ -479,7 +477,7 @@ pub mod amyuni_idd { bail!("Failed to install driver."); } - run_deviceinstaller("enableidd 1") + plug_in_monitor_(true) } pub fn plug_in_monitor() -> ResultType<()> { @@ -492,7 +490,7 @@ pub mod amyuni_idd { bail!("There are already 4 monitors plugged in."); } - run_deviceinstaller("enableidd 1") + plug_in_monitor_(true) } pub fn plug_out_monitor(index: i32) -> ResultType<()> { @@ -527,7 +525,7 @@ pub mod amyuni_idd { to_plug_out_count = 1; } for _i in 0..to_plug_out_count { - let _ = run_deviceinstaller(&format!("enableidd 0")); + let _ = plug_in_monitor_(false); } Ok(()) } From 4c62d8c1b2f84681ccfb8f977c12e1dbcddf9319 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 22 Apr 2024 21:31:21 +0800 Subject: [PATCH 111/112] optimize runtime performance of macOS --service --- src/platform/macos.rs | 59 +++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 1ae35e56b..edacdc363 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -485,21 +485,31 @@ pub fn lock_screen() { pub fn start_os_service() { crate::platform::macos::hide_dock(); - let exe = std::env::current_exe().unwrap_or_default(); log::info!("Username: {}", crate::username()); - log::info!("Startime: {:?}", get_server_start_time()); + let mut sys = System::new(); + sys.refresh_processes_specifics(ProcessRefreshKind::new()); + let path = + std::fs::canonicalize(std::env::current_exe().unwrap_or_default()).unwrap_or_default(); + let my_start_time = sys + .process((std::process::id() as usize).into()) + .map(|p| p.start_time()) + .unwrap_or_default() as i64; + log::info!( + "Startime: {my_start_time} vs {:?}", + get_server_start_time(&mut sys, &path) + ); std::thread::spawn(move || loop { loop { std::thread::sleep(std::time::Duration::from_secs(1)); - let Some(start_time) = get_server_start_time() else { + let Some(start_time) = get_server_start_time(&mut sys, &path) else { continue; }; - if start_time.0 <= start_time.1 { + + if my_start_time <= start_time { // I tried add delegate (using tao and with its main loop0, but it works in normal mode, but not work as daemon log::info!( - "Agent start later, {:?}, will restart --service to make delegate work", - start_time + "Agent start later, {my_start_time} vs {start_time}, will restart --service to make delegate work", ); std::process::exit(0); } @@ -604,36 +614,25 @@ pub fn hide_dock() { } } -fn get_server_start_time() -> Option<(i64, i64)> { - use hbb_common::sysinfo::System; - let mut sys = System::new(); - sys.refresh_processes(); - let mut path = std::env::current_exe().unwrap_or_default(); - if let Ok(linked) = path.read_link() { - path = linked; - } - let path = path.to_string_lossy().to_lowercase(); - let Some(my_start_time) = sys - .process((std::process::id() as usize).into()) - .map(|p| p.start_time()) - else { - return None; - }; +use hbb_common::sysinfo::{ProcessRefreshKind, System}; +#[inline] +fn get_server_start_time(sys: &mut System, path: &PathBuf) -> Option { + sys.refresh_processes_specifics(ProcessRefreshKind::new()); for (_, p) in sys.processes() { - let mut cur_path = p.exe().to_path_buf(); - if let Ok(linked) = cur_path.read_link() { - cur_path = linked; - } - if cur_path.to_string_lossy().to_lowercase() != path { + let cmd = p.cmd(); + if cmd.len() <= 1 { continue; } - if p.pid().as_u32() == std::process::id() { + if &cmd[1] != "--server" { continue; } - let parg = if p.cmd().len() <= 1 { "" } else { &p.cmd()[1] }; - if parg == "--server" { - return Some((my_start_time as _, p.start_time() as _)); + let Ok(cur) = std::fs::canonicalize(p.exe()) else { + continue; + }; + if &cur != path { + continue; } + return Some(p.start_time() as _); } None } From 3b4006b821a80b962515ee9cf2f855acb7cbc326 Mon Sep 17 00:00:00 2001 From: flusheDData <116861809+flusheDData@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:01:00 +0200 Subject: [PATCH 112/112] New terms added (#7797) * Update es.rs tip translation added * Update es.rs --- 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 1e7c40b26..f037ac2c1 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -601,6 +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", ""), + ("no_need_privacy_mode_no_physical_displays_tip", "No hay pantallas físicas, no es necesario usar el modo privado."), ].iter().cloned().collect(); }