mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-27 14:59:02 +08:00
refact: win, virtual display (#7767)
* refact: win, virtual display Signed-off-by: fufesou <shuanglongchen@yeah.net> * Update flutter-build.yml --------- Signed-off-by: fufesou <shuanglongchen@yeah.net> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
parent
a3c0911529
commit
e83c28bf54
13
.github/workflows/flutter-build.yml
vendored
13
.github/workflows/flutter-build.yml
vendored
@ -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
|
||||
|
@ -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 }
|
||||
|
9
build.py
9
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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 = <Widget>[];
|
||||
@ -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 = <Widget>[
|
||||
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;
|
||||
|
@ -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<String, dynamic>;
|
||||
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<int> get virtualDisplays => List<int>.from(
|
||||
platformAdditions[kPlatformAdditionsVirtualDisplays] ?? []);
|
||||
List<int> get RustDeskVirtualDisplays => List<int>.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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -11,3 +11,4 @@ EXPORTS
|
||||
TryDeleteStartupShortcut
|
||||
SetPropertyFromConfig
|
||||
AddRegSoftwareSASGeneration
|
||||
RemoveAmyuniIdd
|
||||
|
@ -29,6 +29,7 @@
|
||||
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" />
|
||||
<CustomAction Id="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
|
||||
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
|
||||
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER]" />
|
||||
<InstallExecuteSequence>
|
||||
|
||||
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
|
||||
@ -68,10 +69,12 @@
|
||||
<Custom Action="RemoveFirewallRules" Before="RemoveFiles"/>
|
||||
<Custom Action="RemoveFirewallRules.SetParam" Before="RemoveFirewallRules"/>
|
||||
|
||||
<Custom Action="TerminateProcesses" Before="RemoveFiles"/>
|
||||
<Custom Action="TerminateProcesses" Before="RemoveInstallFolder"/>
|
||||
<Custom Action="TerminateProcesses.SetParam" Before="TerminateProcesses"/>
|
||||
<Custom Action="TerminateBrokers" Before="RemoveFiles"/>
|
||||
<Custom Action="TerminateBrokers" Before="RemoveInstallFolder"/>
|
||||
<Custom Action="TerminateBrokers.SetParam" Before="TerminateBrokers"/>
|
||||
<Custom Action="RemoveAmyuniIdd" Before="RemoveInstallFolder"/>
|
||||
<Custom Action="RemoveAmyuniIdd.SetParam" Before="RemoveAmyuniIdd"/>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<!-- Shortcuts -->
|
||||
|
@ -16,5 +16,6 @@
|
||||
<CustomAction Id="TryDeleteStartupShortcut" DllEntry="TryDeleteStartupShortcut" Impersonate="yes" Execute="immediate" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||
<CustomAction Id="SetPropertyServiceStop" DllEntry="SetPropertyFromConfig" Impersonate="yes" Execute="immediate" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||
<CustomAction Id="AddRegSoftwareSASGeneration" DllEntry="AddRegSoftwareSASGeneration" Impersonate="no" Execute="deferred" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||
<CustomAction Id="RemoveAmyuniIdd" DllEntry="RemoveAmyuniIdd" Impersonate="no" Execute="deferred" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
@ -215,7 +215,9 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
} 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<Vec<String>> {
|
||||
} 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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<LPBYTE>(&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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<MachineArch, io::Error> {
|
||||
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<String> {
|
||||
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 _)
|
||||
}
|
||||
}
|
||||
|
@ -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<bool>;
|
||||
@ -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<ResultType<bool>> {
|
||||
pub async fn turn_on_privacy(impl_key: &str, conn_id: i32) -> Option<ResultType<bool>> {
|
||||
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<ResultType<bool>> {
|
||||
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<ResultType<bool>> {
|
||||
// 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",
|
||||
|
@ -72,6 +72,10 @@ pub struct PrivacyModeImpl {
|
||||
}
|
||||
|
||||
impl PrivacyMode for PrivacyModeImpl {
|
||||
fn is_async_privacy_mode(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn init(&self) -> ResultType<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -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::<reg_display_settings::RegRecovery>(&config_recovery_value)
|
||||
{
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -160,15 +160,8 @@ fn displays_to_msg(displays: Vec<DisplayInfo>) -> 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<Resolution> {
|
||||
#[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<Vec<Display>> {
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
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");
|
||||
|
@ -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<Mutex<VirtualDisplayManager>> =
|
||||
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<u32, String>,
|
||||
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>) -> 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<String, serde_json::Value> {
|
||||
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<u32> {
|
||||
VIRTUAL_DISPLAY_MANAGER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.peer_index_name
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn plug_in_index_modes(
|
||||
idx: u32,
|
||||
mut modes: Vec<virtual_display::MonitorMode>,
|
||||
) -> 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<virtual_display::MonitorMode>) -> 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<Vec<virtual_display::MonitorMode>>) -> ResultType<Vec<u32>> {
|
||||
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<u32> = 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<Mutex<VirtualDisplayManager>> =
|
||||
Arc::new(Mutex::new(VirtualDisplayManager::default()));
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct VirtualDisplayManager {
|
||||
headless_index_name: Option<(u32, String)>,
|
||||
peer_index_name: HashMap<u32, String>,
|
||||
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<String> {
|
||||
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>) -> String {
|
||||
for _ in 0..3 {
|
||||
let device_names_af: HashSet<String> = 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<u32> {
|
||||
VIRTUAL_DISPLAY_MANAGER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.peer_index_name
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn plug_in_index_modes(
|
||||
idx: u32,
|
||||
mut modes: Vec<virtual_display::MonitorMode>,
|
||||
) -> 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<Vec<virtual_display::MonitorMode>>,
|
||||
) -> ResultType<Vec<u32>> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
|
||||
let mut indices: Vec<u32> = 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<bool> {
|
||||
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<bool> {
|
||||
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<Mutex<()>> = 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::<Vec<_>>();
|
||||
args.push(0);
|
||||
let mut exe_name = exe_name.bytes().collect::<Vec<_>>();
|
||||
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<String> {
|
||||
let mut device_names = HashSet::new();
|
||||
pub(super) fn get_device_names(device_string: Option<&str>) -> Vec<String> {
|
||||
let mut device_names = Vec::new();
|
||||
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() };
|
||||
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() 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::<SP_DEVINFO_DATA>() 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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user