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
|
shell: bash
|
||||||
|
|
||||||
- name: Build rustdesk
|
- 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
|
- name: find Runner.res
|
||||||
# Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./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"
|
qrcode-generator = "4.1"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[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"
|
winreg = "0.11"
|
||||||
windows-service = "0.6"
|
windows-service = "0.6"
|
||||||
virtual_display = { path = "libs/virtual_display", optional = true }
|
virtual_display = { path = "libs/virtual_display", optional = true }
|
||||||
|
9
build.py
9
build.py
@ -153,6 +153,12 @@ def make_parser():
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help='Skip packing, only flutter version + Windows supported'
|
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(
|
parser.add_argument(
|
||||||
"--package",
|
"--package",
|
||||||
type=str
|
type=str
|
||||||
@ -293,6 +299,9 @@ def get_features(args):
|
|||||||
features.append('appimage')
|
features.append('appimage')
|
||||||
if args.unix_file_copy_paste:
|
if args.unix_file_copy_paste:
|
||||||
features.append('unix-file-copy-paste')
|
features.append('unix-file-copy-paste')
|
||||||
|
if windows:
|
||||||
|
if args.virtual_display:
|
||||||
|
features.append('virtual_display_driver')
|
||||||
print("features:", features)
|
print("features:", features)
|
||||||
return features
|
return features
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@ const kKeyTranslateMode = 'translate';
|
|||||||
const String kPlatformAdditionsIsWayland = "is_wayland";
|
const String kPlatformAdditionsIsWayland = "is_wayland";
|
||||||
const String kPlatformAdditionsHeadless = "headless";
|
const String kPlatformAdditionsHeadless = "headless";
|
||||||
const String kPlatformAdditionsIsInstalled = "is_installed";
|
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 kPlatformAdditionsHasFileClipboard = "has_file_clipboard";
|
||||||
const String kPlatformAdditionsSupportedPrivacyModeImpl =
|
const String kPlatformAdditionsSupportedPrivacyModeImpl =
|
||||||
"supported_privacy_mode_impl";
|
"supported_privacy_mode_impl";
|
||||||
@ -121,12 +123,11 @@ double kNewWindowOffset = isWindows
|
|||||||
? 30.0
|
? 30.0
|
||||||
: 50.0;
|
: 50.0;
|
||||||
|
|
||||||
EdgeInsets get kDragToResizeAreaPadding =>
|
EdgeInsets get kDragToResizeAreaPadding => !kUseCompatibleUiMode && isLinux
|
||||||
!kUseCompatibleUiMode && isLinux
|
? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value
|
||||||
? stateGlobal.fullscreen.isTrue || stateGlobal.isMaximized.value
|
? EdgeInsets.zero
|
||||||
? EdgeInsets.zero
|
: EdgeInsets.all(5.0)
|
||||||
: EdgeInsets.all(5.0)
|
: EdgeInsets.zero;
|
||||||
: EdgeInsets.zero;
|
|
||||||
// https://en.wikipedia.org/wiki/Non-breaking_space
|
// https://en.wikipedia.org/wiki/Non-breaking_space
|
||||||
const int $nbsp = 0x00A0;
|
const int $nbsp = 0x00A0;
|
||||||
|
|
||||||
|
@ -1058,11 +1058,16 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
screenAdjustor: _screenAdjustor,
|
screenAdjustor: _screenAdjustor,
|
||||||
),
|
),
|
||||||
// We may add this feature if it is needed and we have an EV certificate.
|
if (pi.isRustDeskIdd)
|
||||||
// _VirtualDisplayMenu(
|
_RustDeskVirtualDisplayMenu(
|
||||||
// id: widget.id,
|
id: widget.id,
|
||||||
// ffi: widget.ffi,
|
ffi: widget.ffi,
|
||||||
// ),
|
),
|
||||||
|
if (pi.isAmyuniIdd)
|
||||||
|
_AmyuniVirtualDisplayMenu(
|
||||||
|
id: widget.id,
|
||||||
|
ffi: widget.ffi,
|
||||||
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
toggles(),
|
toggles(),
|
||||||
];
|
];
|
||||||
@ -1540,21 +1545,23 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VirtualDisplayMenu extends StatefulWidget {
|
class _RustDeskVirtualDisplayMenu extends StatefulWidget {
|
||||||
final String id;
|
final String id;
|
||||||
final FFI ffi;
|
final FFI ffi;
|
||||||
|
|
||||||
_VirtualDisplayMenu({
|
_RustDeskVirtualDisplayMenu({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.ffi,
|
required this.ffi,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_VirtualDisplayMenu> createState() => _VirtualDisplayMenuState();
|
State<_RustDeskVirtualDisplayMenu> createState() =>
|
||||||
|
_RustDeskVirtualDisplayMenuState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VirtualDisplayMenuState extends State<_VirtualDisplayMenu> {
|
class _RustDeskVirtualDisplayMenuState
|
||||||
|
extends State<_RustDeskVirtualDisplayMenu> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -1569,7 +1576,7 @@ class _VirtualDisplayMenuState extends State<_VirtualDisplayMenu> {
|
|||||||
return Offstage();
|
return Offstage();
|
||||||
}
|
}
|
||||||
|
|
||||||
final virtualDisplays = widget.ffi.ffiModel.pi.virtualDisplays;
|
final virtualDisplays = widget.ffi.ffiModel.pi.RustDeskVirtualDisplays;
|
||||||
final privacyModeState = PrivacyModeState.find(widget.id);
|
final privacyModeState = PrivacyModeState.find(widget.id);
|
||||||
|
|
||||||
final children = <Widget>[];
|
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 {
|
class _KeyboardMenu extends StatelessWidget {
|
||||||
final String id;
|
final String id;
|
||||||
final FFI ffi;
|
final FFI ffi;
|
||||||
|
@ -561,8 +561,12 @@ class FfiModel with ChangeNotifier {
|
|||||||
showRelayHintDialog(sessionId, type, title, text, dialogManager, peerId);
|
showRelayHintDialog(sessionId, type, title, text, dialogManager, peerId);
|
||||||
} else if (text == 'Connected, waiting for image...') {
|
} else if (text == 'Connected, waiting for image...') {
|
||||||
showConnectedWaitingForImage(dialogManager, sessionId, type, title, text);
|
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 {
|
} else {
|
||||||
var hasRetry = evt['hasRetry'] == 'true';
|
final hasRetry = evt['hasRetry'] == 'true';
|
||||||
showMsgBox(sessionId, type, title, text, link, hasRetry, dialogManager);
|
showMsgBox(sessionId, type, title, text, link, hasRetry, dialogManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,6 +661,27 @@ class FfiModel with ChangeNotifier {
|
|||||||
bind.sessionOnWaitingForImageDialogShow(sessionId: sessionId);
|
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) {
|
_updateSessionWidthHeight(SessionID sessionId) {
|
||||||
if (_rect == null) return;
|
if (_rect == null) return;
|
||||||
if (_rect!.width <= 0 || _rect!.height <= 0) {
|
if (_rect!.width <= 0 || _rect!.height <= 0) {
|
||||||
@ -986,15 +1011,21 @@ class FfiModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updateData.isEmpty) {
|
if (updateData.isEmpty) {
|
||||||
_pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays);
|
_pi.platformAdditions.remove(kPlatformAdditionsRustDeskVirtualDisplays);
|
||||||
|
_pi.platformAdditions.remove(kPlatformAdditionsAmyuniVirtualDisplays);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
final updateJson = json.decode(updateData) as Map<String, dynamic>;
|
final updateJson = json.decode(updateData) as Map<String, dynamic>;
|
||||||
for (final key in updateJson.keys) {
|
for (final key in updateJson.keys) {
|
||||||
_pi.platformAdditions[key] = updateJson[key];
|
_pi.platformAdditions[key] = updateJson[key];
|
||||||
}
|
}
|
||||||
if (!updateJson.containsKey(kPlatformAdditionsVirtualDisplays)) {
|
if (!updateJson
|
||||||
_pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays);
|
.containsKey(kPlatformAdditionsRustDeskVirtualDisplays)) {
|
||||||
|
_pi.platformAdditions
|
||||||
|
.remove(kPlatformAdditionsRustDeskVirtualDisplays);
|
||||||
|
}
|
||||||
|
if (!updateJson.containsKey(kPlatformAdditionsAmyuniVirtualDisplays)) {
|
||||||
|
_pi.platformAdditions.remove(kPlatformAdditionsAmyuniVirtualDisplays);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Failed to decode platformAdditions $e');
|
debugPrint('Failed to decode platformAdditions $e');
|
||||||
@ -2490,14 +2521,21 @@ class PeerInfo with ChangeNotifier {
|
|||||||
bool get isInstalled =>
|
bool get isInstalled =>
|
||||||
platform != kPeerPlatformWindows ||
|
platform != kPeerPlatformWindows ||
|
||||||
platformAdditions[kPlatformAdditionsIsInstalled] == true;
|
platformAdditions[kPlatformAdditionsIsInstalled] == true;
|
||||||
List<int> get virtualDisplays => List<int>.from(
|
List<int> get RustDeskVirtualDisplays => List<int>.from(
|
||||||
platformAdditions[kPlatformAdditionsVirtualDisplays] ?? []);
|
platformAdditions[kPlatformAdditionsRustDeskVirtualDisplays] ?? []);
|
||||||
|
int get amyuniVirtualDisplayCount =>
|
||||||
|
platformAdditions[kPlatformAdditionsAmyuniVirtualDisplays] ?? 0;
|
||||||
|
|
||||||
bool get isSupportMultiDisplay =>
|
bool get isSupportMultiDisplay =>
|
||||||
(isDesktop || isWebDesktop) && isSupportMultiUiSession;
|
(isDesktop || isWebDesktop) && isSupportMultiUiSession;
|
||||||
|
|
||||||
bool get cursorEmbedded => tryGetDisplay()?.cursorEmbedded ?? false;
|
bool get cursorEmbedded => tryGetDisplay()?.cursorEmbedded ?? false;
|
||||||
|
|
||||||
|
bool get isRustDeskIdd =>
|
||||||
|
platformAdditions[kPlatformAdditionsIddImpl] == 'rustdesk_idd';
|
||||||
|
bool get isAmyuniIdd =>
|
||||||
|
platformAdditions[kPlatformAdditionsIddImpl] == 'amyuni_idd';
|
||||||
|
|
||||||
Display? tryGetDisplay() {
|
Display? tryGetDisplay() {
|
||||||
if (displays.isEmpty) {
|
if (displays.isEmpty) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -591,3 +591,68 @@ LExit:
|
|||||||
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||||
return WcaFinalize(er);
|
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
|
TryDeleteStartupShortcut
|
||||||
SetPropertyFromConfig
|
SetPropertyFromConfig
|
||||||
AddRegSoftwareSASGeneration
|
AddRegSoftwareSASGeneration
|
||||||
|
RemoveAmyuniIdd
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
<CustomAction Id="SetPropertyServiceStop.SetParam.ConfigKey" Return="check" Property="ConfigKey" Value="stop-service" />
|
<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="SetPropertyServiceStop.SetParam.PropertyName" Return="check" Property="PropertyName" Value="STOP_SERVICE" />
|
||||||
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
|
<CustomAction Id="TryDeleteStartupShortcut.SetParam" Return="check" Property="ShortcutName" Value="$(var.Product) Tray" />
|
||||||
|
<CustomAction Id="RemoveAmyuniIdd.SetParam" Return="check" Property="RemoveAmyuniIdd" Value="[INSTALLFOLDER]" />
|
||||||
<InstallExecuteSequence>
|
<InstallExecuteSequence>
|
||||||
|
|
||||||
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
|
<Custom Action="SetPropertyIsServiceRunning" After="InstallInitialize" Condition="Installed" />
|
||||||
@ -68,10 +69,12 @@
|
|||||||
<Custom Action="RemoveFirewallRules" Before="RemoveFiles"/>
|
<Custom Action="RemoveFirewallRules" Before="RemoveFiles"/>
|
||||||
<Custom Action="RemoveFirewallRules.SetParam" Before="RemoveFirewallRules"/>
|
<Custom Action="RemoveFirewallRules.SetParam" Before="RemoveFirewallRules"/>
|
||||||
|
|
||||||
<Custom Action="TerminateProcesses" Before="RemoveFiles"/>
|
<Custom Action="TerminateProcesses" Before="RemoveInstallFolder"/>
|
||||||
<Custom Action="TerminateProcesses.SetParam" Before="TerminateProcesses"/>
|
<Custom Action="TerminateProcesses.SetParam" Before="TerminateProcesses"/>
|
||||||
<Custom Action="TerminateBrokers" Before="RemoveFiles"/>
|
<Custom Action="TerminateBrokers" Before="RemoveInstallFolder"/>
|
||||||
<Custom Action="TerminateBrokers.SetParam" Before="TerminateBrokers"/>
|
<Custom Action="TerminateBrokers.SetParam" Before="TerminateBrokers"/>
|
||||||
|
<Custom Action="RemoveAmyuniIdd" Before="RemoveInstallFolder"/>
|
||||||
|
<Custom Action="RemoveAmyuniIdd.SetParam" Before="RemoveAmyuniIdd"/>
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
<!-- Shortcuts -->
|
<!-- Shortcuts -->
|
||||||
|
@ -16,5 +16,6 @@
|
|||||||
<CustomAction Id="TryDeleteStartupShortcut" DllEntry="TryDeleteStartupShortcut" Impersonate="yes" Execute="immediate" Return="ignore" BinaryRef="Custom_Actions_Dll"/>
|
<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="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="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>
|
</Fragment>
|
||||||
</Wix>
|
</Wix>
|
||||||
|
@ -215,7 +215,9 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
} else if args[0] == "--install-idd" {
|
} else if args[0] == "--install-idd" {
|
||||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||||
if crate::virtual_display_manager::is_virtual_display_supported() {
|
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;
|
return None;
|
||||||
} else if args[0] == "--portable-service" {
|
} else if args[0] == "--portable-service" {
|
||||||
@ -254,7 +256,7 @@ pub fn core_main() -> Option<Vec<String>> {
|
|||||||
} else if args[0] == "--server" {
|
} else if args[0] == "--server" {
|
||||||
log::info!("start --server with user {}", crate::username());
|
log::info!("start --server with user {}", crate::username());
|
||||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
#[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"))]
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
{
|
{
|
||||||
crate::start_server(true);
|
crate::start_server(true);
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "所有人"),
|
("Everyone", "所有人"),
|
||||||
("ab_web_console_tip", "打开 Web 控制台以执行更多操作"),
|
("ab_web_console_tip", "打开 Web 控制台以执行更多操作"),
|
||||||
("allow-only-conn-window-open-tip", "仅当 RustDesk 窗口打开时允许连接"),
|
("allow-only-conn-window-open-tip", "仅当 RustDesk 窗口打开时允许连接"),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", "没有物理显示器,没必要使用隐私模式。"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Každý"),
|
("Everyone", "Každý"),
|
||||||
("ab_web_console_tip", "Více na webové konzoli"),
|
("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"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Jeder"),
|
("Everyone", "Jeder"),
|
||||||
("ab_web_console_tip", "Mehr über Webkonsole"),
|
("ab_web_console_tip", "Mehr über Webkonsole"),
|
||||||
("allow-only-conn-window-open-tip", "Verbindung nur zulassen, wenn das RustDesk-Fenster geöffnet ist"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].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."),
|
("share_warning_tip", "The fields above are shared and visible to others."),
|
||||||
("ab_web_console_tip", "More on web console"),
|
("ab_web_console_tip", "More on web console"),
|
||||||
("allow-only-conn-window-open-tip", "Only allow connection if RustDesk window is open"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Todos"),
|
("Everyone", "Todos"),
|
||||||
("ab_web_console_tip", "Más en consola web"),
|
("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"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "هر کس"),
|
("Everyone", "هر کس"),
|
||||||
("ab_web_console_tip", "اطلاعات بیشتر در کنسول وب"),
|
("ab_web_console_tip", "اطلاعات بیشتر در کنسول وب"),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Svatko"),
|
("Everyone", "Svatko"),
|
||||||
("ab_web_console_tip", "Više na web konzoli"),
|
("ab_web_console_tip", "Više na web konzoli"),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Everyone"),
|
("Everyone", "Everyone"),
|
||||||
("ab_web_console_tip", "Altre info sulla console web"),
|
("ab_web_console_tip", "Altre info sulla console web"),
|
||||||
("allow-only-conn-window-open-tip", "Consenti la connessione solo se la finestra RustDesk è aperta"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Visi"),
|
("Everyone", "Visi"),
|
||||||
("ab_web_console_tip", "Vairāk par tīmekļa konsoli"),
|
("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"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Iedereen"),
|
("Everyone", "Iedereen"),
|
||||||
("ab_web_console_tip", "Meer over de webconsole"),
|
("ab_web_console_tip", "Meer over de webconsole"),
|
||||||
("allow-only-conn-window-open-tip", "Alleen verbindingen toestaan als het RustDesk-venster geopend is"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Wszyscy"),
|
("Everyone", "Wszyscy"),
|
||||||
("ab_web_console_tip", "Więcej w konsoli web"),
|
("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"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Todos"),
|
("Everyone", "Todos"),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Все"),
|
("Everyone", "Все"),
|
||||||
("ab_web_console_tip", "Больше в веб-консоли"),
|
("ab_web_console_tip", "Больше в веб-консоли"),
|
||||||
("allow-only-conn-window-open-tip", "Разрешать подключение только при открытом окне RustDesk"),
|
("allow-only-conn-window-open-tip", "Разрешать подключение только при открытом окне RustDesk"),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Každý"),
|
("Everyone", "Každý"),
|
||||||
("ab_web_console_tip", "Viac na webovej konzole"),
|
("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"),
|
("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();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "所有人"),
|
("Everyone", "所有人"),
|
||||||
("ab_web_console_tip", "打開 Web 控制台以進行更多操作"),
|
("ab_web_console_tip", "打開 Web 控制台以進行更多操作"),
|
||||||
("allow-only-conn-window-open-tip", "只在 RustDesk 視窗開啟時允許連接"),
|
("allow-only-conn-window-open-tip", "只在 RustDesk 視窗開啟時允許連接"),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", "Всі"),
|
("Everyone", "Всі"),
|
||||||
("ab_web_console_tip", "Детальніше про веб-консоль"),
|
("ab_web_console_tip", "Детальніше про веб-консоль"),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -601,5 +601,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Everyone", ""),
|
("Everyone", ""),
|
||||||
("ab_web_console_tip", ""),
|
("ab_web_console_tip", ""),
|
||||||
("allow-only-conn-window-open-tip", ""),
|
("allow-only-conn-window-open-tip", ""),
|
||||||
|
("no_need_privacy_mode_no_physical_displays_tip", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -669,4 +669,48 @@ extern "C"
|
|||||||
AllocConsole();
|
AllocConsole();
|
||||||
freopen("CONOUT$", "w", stdout);
|
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"
|
} // 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::enums::*;
|
||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
|
|
||||||
pub const DRIVER_CERT_FILE: &str = "RustDeskIddDriver.cer";
|
|
||||||
|
|
||||||
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[allow(invalid_value)]
|
#[allow(invalid_value)]
|
||||||
@ -462,6 +460,8 @@ extern "C" {
|
|||||||
fn is_win_down() -> BOOL;
|
fn is_win_down() -> BOOL;
|
||||||
fn is_local_system() -> BOOL;
|
fn is_local_system() -> BOOL;
|
||||||
fn alloc_console_and_redirect();
|
fn alloc_console_and_redirect();
|
||||||
|
fn get_native_machine() -> i32;
|
||||||
|
fn is_service_running_w(svc_name: *const u16) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "system" {
|
extern "system" {
|
||||||
@ -1296,12 +1296,14 @@ fn get_uninstall(kill_self: bool) -> String {
|
|||||||
{before_uninstall}
|
{before_uninstall}
|
||||||
{uninstall_cert_cmd}
|
{uninstall_cert_cmd}
|
||||||
reg delete {subkey} /f
|
reg delete {subkey} /f
|
||||||
|
{uninstall_amyuni_idd}
|
||||||
if exist \"{path}\" rd /s /q \"{path}\"
|
if exist \"{path}\" rd /s /q \"{path}\"
|
||||||
if exist \"{start_menu}\" rd /s /q \"{start_menu}\"
|
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 \"%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\"
|
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),
|
before_uninstall=get_before_uninstall(kill_self),
|
||||||
|
uninstall_amyuni_idd=get_uninstall_amyuni_idd(&path),
|
||||||
app_name = crate::get_app_name(),
|
app_name = crate::get_app_name(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -2367,3 +2369,81 @@ impl Drop for WallPaperRemover {
|
|||||||
allow_err!(Self::set_wallpaper(Some(self.old_path.clone())));
|
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,
|
display_service,
|
||||||
ipc::{connect, Data},
|
ipc::{connect, Data},
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
use hbb_common::{
|
||||||
use hbb_common::tokio;
|
anyhow::anyhow,
|
||||||
use hbb_common::{anyhow::anyhow, bail, lazy_static, ResultType};
|
bail, lazy_static,
|
||||||
|
tokio::{self, sync::oneshot},
|
||||||
|
ResultType,
|
||||||
|
};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@ -30,10 +33,10 @@ mod win_virtual_display;
|
|||||||
pub use win_virtual_display::restore_reg_connectivity;
|
pub use win_virtual_display::restore_reg_connectivity;
|
||||||
|
|
||||||
pub const INVALID_PRIVACY_MODE_CONN_ID: i32 = 0;
|
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 =
|
pub const TURN_OFF_OTHER_ID: &'static str =
|
||||||
"Failed to turn off privacy mode that belongs to someone else";
|
"Failed to turn off privacy mode that belongs to someone else.";
|
||||||
pub const NO_DISPLAYS: &'static str = "No displays";
|
pub const NO_PHYSICAL_DISPLAYS: &'static str = "no_need_privacy_mode_no_physical_displays_tip";
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub const PRIVACY_MODE_IMPL_WIN_MAG: &str = win_mag::PRIVACY_MODE_IMPL;
|
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 {
|
pub trait PrivacyMode: Sync + Send {
|
||||||
|
fn is_async_privacy_mode(&self) -> bool;
|
||||||
|
|
||||||
fn init(&self) -> ResultType<()>;
|
fn init(&self) -> ResultType<()>;
|
||||||
fn clear(&mut self);
|
fn clear(&mut self);
|
||||||
fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType<bool>;
|
fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType<bool>;
|
||||||
@ -203,7 +208,40 @@ fn get_supported_impl(impl_key: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[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
|
// Check if privacy mode is already on or occupied by another one
|
||||||
let mut privacy_mode_lock = PRIVACY_MODE.lock().unwrap();
|
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")]
|
#[cfg(feature = "virtual_display_driver")]
|
||||||
if is_installed() {
|
if is_installed() && crate::platform::windows::is_self_service_running() {
|
||||||
vec_impls.push((
|
vec_impls.push((
|
||||||
PRIVACY_MODE_IMPL_WIN_VIRTUAL_DISPLAY,
|
PRIVACY_MODE_IMPL_WIN_VIRTUAL_DISPLAY,
|
||||||
"privacy_mode_impl_virtual_display_tip",
|
"privacy_mode_impl_virtual_display_tip",
|
||||||
|
@ -72,6 +72,10 @@ pub struct PrivacyModeImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrivacyMode for PrivacyModeImpl {
|
impl PrivacyMode for PrivacyModeImpl {
|
||||||
|
fn is_async_privacy_mode(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn init(&self) -> ResultType<()> {
|
fn init(&self) -> ResultType<()> {
|
||||||
Ok(())
|
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 crate::virtual_display_manager;
|
||||||
use hbb_common::{allow_err, bail, config::Config, log, ResultType};
|
use hbb_common::{allow_err, bail, config::Config, log, ResultType};
|
||||||
use std::{
|
use std::{
|
||||||
io::Error,
|
io::Error,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
use virtual_display::MonitorMode;
|
use virtual_display::MonitorMode;
|
||||||
use winapi::{
|
use winapi::{
|
||||||
@ -27,7 +29,6 @@ use winapi::{
|
|||||||
|
|
||||||
pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_virtual_display";
|
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";
|
const CONFIG_KEY_REG_RECOVERY: &str = "reg_recovery";
|
||||||
|
|
||||||
struct Display {
|
struct Display {
|
||||||
@ -137,8 +138,9 @@ impl PrivacyModeImpl {
|
|||||||
primary,
|
primary,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(s) = std::string::String::from_utf16(&dd.DeviceString) {
|
let ds = virtual_display_manager::get_cur_device_string();
|
||||||
if &s[..IDD_DEVICE_STRING.len()] == IDD_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);
|
self.virtual_displays.push(display);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -155,7 +157,7 @@ impl PrivacyModeImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn restore_plug_out_monitor(&mut self) {
|
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();
|
self.virtual_displays_added.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,8 +306,18 @@ impl PrivacyModeImpl {
|
|||||||
if self.virtual_displays.is_empty() {
|
if self.virtual_displays.is_empty() {
|
||||||
let displays =
|
let displays =
|
||||||
virtual_display_manager::plug_in_peer_request(vec![Self::default_display_modes()])?;
|
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();
|
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(())
|
Ok(())
|
||||||
@ -348,6 +360,10 @@ impl PrivacyModeImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrivacyMode for PrivacyModeImpl {
|
impl PrivacyMode for PrivacyModeImpl {
|
||||||
|
fn is_async_privacy_mode(&self) -> bool {
|
||||||
|
virtual_display_manager::is_amyuni_idd()
|
||||||
|
}
|
||||||
|
|
||||||
fn init(&self) -> ResultType<()> {
|
fn init(&self) -> ResultType<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -367,8 +383,8 @@ impl PrivacyMode for PrivacyModeImpl {
|
|||||||
}
|
}
|
||||||
self.set_displays();
|
self.set_displays();
|
||||||
if self.displays.is_empty() {
|
if self.displays.is_empty() {
|
||||||
log::debug!("No displays");
|
log::debug!("{}", NO_PHYSICAL_DISPLAYS);
|
||||||
bail!(NO_DISPLAYS);
|
bail!(NO_PHYSICAL_DISPLAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut guard = TurnOnGuard {
|
let mut guard = TurnOnGuard {
|
||||||
@ -379,7 +395,7 @@ impl PrivacyMode for PrivacyModeImpl {
|
|||||||
guard.ensure_virtual_display()?;
|
guard.ensure_virtual_display()?;
|
||||||
if guard.virtual_displays.is_empty() {
|
if guard.virtual_displays.is_empty() {
|
||||||
log::debug!("No virtual displays");
|
log::debug!("No virtual displays");
|
||||||
bail!("No virtual displays");
|
bail!("No virtual displays.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let reg_connectivity_1 = reg_display_settings::read_reg_connectivity()?;
|
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)?;
|
self.check_off_conn_id(conn_id)?;
|
||||||
super::win_input::unhook()?;
|
super::win_input::unhook()?;
|
||||||
self.restore();
|
self.restore();
|
||||||
restore_reg_connectivity();
|
restore_reg_connectivity(false);
|
||||||
|
|
||||||
if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID {
|
if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID {
|
||||||
if let Some(state) = state {
|
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());
|
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);
|
let config_recovery_value = Config::get_option(CONFIG_KEY_REG_RECOVERY);
|
||||||
if config_recovery_value.is_empty() {
|
if config_recovery_value.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if plug_out_monitors {
|
||||||
|
let _ = virtual_display_manager::plug_out_monitor(-1);
|
||||||
|
}
|
||||||
if let Ok(reg_recovery) =
|
if let Ok(reg_recovery) =
|
||||||
serde_json::from_str::<reg_display_settings::RegRecovery>(&config_recovery_value)
|
serde_json::from_str::<reg_display_settings::RegRecovery>(&config_recovery_value)
|
||||||
{
|
{
|
||||||
|
@ -1133,10 +1133,7 @@ impl Connection {
|
|||||||
);
|
);
|
||||||
#[cfg(feature = "virtual_display_driver")]
|
#[cfg(feature = "virtual_display_driver")]
|
||||||
if crate::platform::is_installed() {
|
if crate::platform::is_installed() {
|
||||||
let virtual_displays = virtual_display_manager::get_virtual_displays();
|
platform_additions.extend(virtual_display_manager::get_platform_additions());
|
||||||
if !virtual_displays.is_empty() {
|
|
||||||
platform_additions.insert("virtual_displays".into(), json!(&virtual_displays));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
platform_additions.insert(
|
platform_additions.insert(
|
||||||
"supported_privacy_mode_impl".into(),
|
"supported_privacy_mode_impl".into(),
|
||||||
@ -2595,8 +2592,7 @@ impl Connection {
|
|||||||
self.send(make_msg("idd_not_support_under_win10_2004_tip".to_string()))
|
self.send(make_msg("idd_not_support_under_win10_2004_tip".to_string()))
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
if let Err(e) =
|
if let Err(e) = virtual_display_manager::plug_in_monitor(t.display as _, Vec::new())
|
||||||
virtual_display_manager::plug_in_index_modes(t.display as _, Vec::new())
|
|
||||||
{
|
{
|
||||||
log::error!("Failed to plug in virtual display: {}", e);
|
log::error!("Failed to plug in virtual display: {}", e);
|
||||||
self.send(make_msg(format!(
|
self.send(make_msg(format!(
|
||||||
@ -2607,13 +2603,8 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let indices = if t.display == -1 {
|
if let Err(e) = virtual_display_manager::plug_out_monitor(t.display) {
|
||||||
virtual_display_manager::get_virtual_displays()
|
log::error!("Failed to plug out virtual display {}: {}", t.display, e);
|
||||||
} 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);
|
|
||||||
self.send(make_msg(format!(
|
self.send(make_msg(format!(
|
||||||
"Failed to plug out virtual displays: {}",
|
"Failed to plug out virtual displays: {}",
|
||||||
e
|
e
|
||||||
@ -2639,7 +2630,7 @@ impl Connection {
|
|||||||
let name = display.name();
|
let name = display.name();
|
||||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||||
if let Some(_ok) =
|
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,
|
&name,
|
||||||
r.width as _,
|
r.width as _,
|
||||||
r.height as _,
|
r.height as _,
|
||||||
@ -2858,7 +2849,6 @@ impl Connection {
|
|||||||
} else {
|
} else {
|
||||||
let is_pre_privacy_on = privacy_mode::is_in_privacy_mode();
|
let is_pre_privacy_on = privacy_mode::is_in_privacy_mode();
|
||||||
let pre_impl_key = privacy_mode::get_cur_impl_key();
|
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 is_pre_privacy_on {
|
||||||
if let Some(pre_impl_key) = pre_impl_key {
|
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 {
|
match turn_on_res {
|
||||||
Some(Ok(res)) => {
|
Some(Ok(res)) => {
|
||||||
if res {
|
if res {
|
||||||
@ -2906,7 +2897,7 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
log::error!("Failed to turn on privacy mode. {}", 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(
|
let _ = Self::turn_off_privacy_to_msg(
|
||||||
privacy_mode::INVALID_PRIVACY_MODE_CONN_ID,
|
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"))]
|
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||||
if crate::platform::is_installed() {
|
if crate::platform::is_installed() {
|
||||||
let virtual_displays = crate::virtual_display_manager::get_virtual_displays();
|
let m = crate::virtual_display_manager::get_platform_additions();
|
||||||
if !virtual_displays.is_empty() {
|
pi.platform_additions = serde_json::to_string(&m).unwrap_or_default();
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// current_display should not be used in server.
|
// current_display should not be used in server.
|
||||||
@ -227,10 +220,11 @@ pub(super) fn get_original_resolution(
|
|||||||
h: usize,
|
h: usize,
|
||||||
) -> MessageField<Resolution> {
|
) -> MessageField<Resolution> {
|
||||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
#[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")))]
|
#[cfg(not(all(windows, feature = "virtual_display_driver")))]
|
||||||
let is_virtual_display = false;
|
let is_rustdesk_virtual_display = false;
|
||||||
Some(if is_virtual_display {
|
Some(if is_rustdesk_virtual_display {
|
||||||
Resolution {
|
Resolution {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@ -382,8 +376,10 @@ pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
|||||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||||
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||||
let mut displays = Display::all()?;
|
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()
|
if crate::platform::is_installed()
|
||||||
&& no_displays(&displays)
|
&& no_displays_v
|
||||||
&& virtual_display_manager::is_virtual_display_supported()
|
&& virtual_display_manager::is_virtual_display_supported()
|
||||||
{
|
{
|
||||||
log::debug!("no displays, create virtual display");
|
log::debug!("no displays, create virtual display");
|
||||||
|
@ -1,57 +1,47 @@
|
|||||||
#[cfg(target_os = "windows")]
|
use hbb_common::{bail, platform::windows::is_windows_version_or_greater, ResultType};
|
||||||
use hbb_common::platform::windows::is_windows_version_or_greater;
|
use std::sync::atomic;
|
||||||
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.
|
// This string is defined here.
|
||||||
const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0;
|
// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40
|
||||||
const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 1;
|
pub const RUSTDESK_IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0";
|
||||||
const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 5;
|
pub const AMYUNI_IDD_DEVICE_STRING: &'static str = "USB Mobile Monitor Virtual Display\0";
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
const IDD_IMPL: &str = IDD_IMPL_AMYUNI;
|
||||||
static ref VIRTUAL_DISPLAY_MANAGER: Arc<Mutex<VirtualDisplayManager>> =
|
const IDD_IMPL_RUSTDESK: &str = "rustdesk_idd";
|
||||||
Arc::new(Mutex::new(VirtualDisplayManager::default()));
|
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)]
|
// No need to consider concurrency here.
|
||||||
struct VirtualDisplayManager {
|
pub fn set_can_plug_out_all(v: bool) {
|
||||||
headless_index_name: Option<(u32, String)>,
|
if IS_CAN_PLUG_OUT_ALL.load(atomic::Ordering::Relaxed) == IS_CAN_PLUG_OUT_ALL_NOT_SET {
|
||||||
peer_index_name: HashMap<u32, String>,
|
IS_CAN_PLUG_OUT_ALL.store(
|
||||||
is_driver_installed: bool,
|
if v {
|
||||||
|
IS_CAN_PLUG_OUT_ALL_YES
|
||||||
|
} else {
|
||||||
|
IS_CAN_PLUG_OUT_ALL_NO
|
||||||
|
},
|
||||||
|
atomic::Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VirtualDisplayManager {
|
pub fn is_amyuni_idd() -> bool {
|
||||||
fn prepare_driver(&mut self) -> ResultType<()> {
|
IDD_IMPL == IDD_IMPL_AMYUNI
|
||||||
if !self.is_driver_installed {
|
}
|
||||||
self.install_update_driver()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn install_update_driver(&mut self) -> ResultType<()> {
|
pub fn get_cur_device_string() -> &'static str {
|
||||||
if let Err(e) = virtual_display::create_device() {
|
match IDD_IMPL {
|
||||||
if !e.to_string().contains("Device is already created") {
|
IDD_IMPL_RUSTDESK => RUSTDESK_IDD_DEVICE_STRING,
|
||||||
bail!("Create device failed {}", e);
|
IDD_IMPL_AMYUNI => AMYUNI_IDD_DEVICE_STRING,
|
||||||
}
|
_ => "",
|
||||||
}
|
|
||||||
// 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(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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<()> {
|
pub fn plug_in_headless() -> ResultType<()> {
|
||||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
match IDD_IMPL {
|
||||||
manager.prepare_driver()?;
|
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_in_headless(),
|
||||||
let modes = [virtual_display::MonitorMode {
|
IDD_IMPL_AMYUNI => amyuni_idd::plug_in_headless(),
|
||||||
width: 1920,
|
_ => bail!("Unsupported virtual display implementation."),
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_new_device_name(device_names: &HashSet<String>) -> String {
|
pub fn get_platform_additions() -> serde_json::Map<String, serde_json::Value> {
|
||||||
for _ in 0..3 {
|
let mut map = serde_json::Map::new();
|
||||||
let device_names_af = windows::get_device_names();
|
if !crate::platform::windows::is_self_service_running() {
|
||||||
let diff_names: Vec<_> = device_names_af.difference(&device_names).collect();
|
return map;
|
||||||
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",);
|
map.insert("idd_impl".into(), serde_json::json!(IDD_IMPL));
|
||||||
"".to_string()
|
match IDD_IMPL {
|
||||||
}
|
IDD_IMPL_RUSTDESK => {
|
||||||
|
let virtual_displays = rustdesk_idd::get_virtual_displays();
|
||||||
pub fn get_virtual_displays() -> Vec<u32> {
|
if !virtual_displays.is_empty() {
|
||||||
VIRTUAL_DISPLAY_MANAGER
|
map.insert(
|
||||||
.lock()
|
"rustdesk_virtual_displays".into(),
|
||||||
.unwrap()
|
serde_json::json!(virtual_displays),
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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<()> {
|
#[inline]
|
||||||
if is_virtual_display_supported() {
|
pub fn plug_in_monitor(idx: u32, modes: Vec<virtual_display::MonitorMode>) -> ResultType<()> {
|
||||||
return Ok(());
|
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()) {
|
pub fn plug_out_monitor(index: i32) -> ResultType<()> {
|
||||||
log::error!("Failed to plug out virtual displays: {}", e);
|
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>> {
|
pub fn plug_in_peer_request(modes: Vec<Vec<virtual_display::MonitorMode>>) -> ResultType<Vec<u32>> {
|
||||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
match IDD_IMPL {
|
||||||
manager.prepare_driver()?;
|
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();
|
pub fn plug_out_monitor_indices(indices: &[u32]) -> ResultType<()> {
|
||||||
for m in modes.iter() {
|
match IDD_IMPL {
|
||||||
for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT {
|
IDD_IMPL_RUSTDESK => rustdesk_idd::plug_out_peer_request(indices),
|
||||||
if !manager.peer_index_name.contains_key(&idx) {
|
IDD_IMPL_AMYUNI => {
|
||||||
let device_names = windows::get_device_names();
|
for _idx in indices.iter() {
|
||||||
match VirtualDisplayManager::plug_in_monitor(idx, m) {
|
amyuni_idd::plug_out_monitor(0)?;
|
||||||
Ok(_) => {
|
}
|
||||||
let device_name = get_new_device_name(&device_names);
|
Ok(())
|
||||||
manager.peer_index_name.insert(idx, device_name);
|
}
|
||||||
indices.push(idx);
|
_ => bail!("Unsupported virtual display implementation."),
|
||||||
}
|
}
|
||||||
Err(e) => {
|
}
|
||||||
log::error!("Plug in monitor failed {}", e);
|
|
||||||
}
|
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<()> {
|
for (k, v) in lock.peer_index_name.iter() {
|
||||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
if windows::is_device_name(v, name) {
|
||||||
for idx in indices.iter() {
|
return Some(change_resolution(*k, w, h));
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_resolution_if_is_virtual_display(name: &str, w: u32, h: u32) -> Option<bool> {
|
pub mod amyuni_idd {
|
||||||
let lock = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
use super::windows;
|
||||||
if let Some((index, device_name)) = &lock.headless_index_name {
|
use crate::platform::windows::get_amyuni_exe_name;
|
||||||
if windows::is_device_name(device_name, name) {
|
use hbb_common::{bail, lazy_static, log, ResultType};
|
||||||
return Some(change_resolution(*index, w, h));
|
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() {
|
fn check_install_driver() -> ResultType<()> {
|
||||||
if windows::is_device_name(v, name) {
|
let _l = LOCK.lock().unwrap();
|
||||||
return Some(change_resolution(*k, w, h));
|
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 {
|
mod windows {
|
||||||
use std::{collections::HashSet, ptr::null_mut};
|
use std::ptr::null_mut;
|
||||||
use winapi::{
|
use winapi::{
|
||||||
shared::minwindef::{DWORD, FALSE},
|
shared::{
|
||||||
|
devguid::GUID_DEVCLASS_DISPLAY,
|
||||||
|
minwindef::{DWORD, FALSE},
|
||||||
|
ntdef::ULONG,
|
||||||
|
},
|
||||||
um::{
|
um::{
|
||||||
|
cfgmgr32::{CM_Get_DevNode_Status, CR_SUCCESS},
|
||||||
|
cguid::GUID_NULL,
|
||||||
|
setupapi::{
|
||||||
|
SetupDiEnumDeviceInfo, SetupDiGetClassDevsW, SetupDiGetDeviceRegistryPropertyW,
|
||||||
|
SP_DEVINFO_DATA,
|
||||||
|
},
|
||||||
wingdi::{
|
wingdi::{
|
||||||
DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_MIRRORING_DRIVER,
|
DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ACTIVE, DISPLAY_DEVICE_MIRRORING_DRIVER,
|
||||||
},
|
},
|
||||||
|
winnt::HANDLE,
|
||||||
winuser::{EnumDisplayDevicesW, EnumDisplaySettingsExW, ENUM_CURRENT_SETTINGS},
|
winuser::{EnumDisplayDevicesW, EnumDisplaySettingsExW, ENUM_CURRENT_SETTINGS},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// This string is defined here.
|
const DIGCF_PRESENT: DWORD = 0x00000002;
|
||||||
// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40
|
const SPDRP_DEVICEDESC: DWORD = 0x00000000;
|
||||||
const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0";
|
const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn is_device_name(device_name: &str, name: &str) -> bool {
|
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> {
|
pub(super) fn get_device_names(device_string: Option<&str>) -> Vec<String> {
|
||||||
let mut device_names = HashSet::new();
|
let mut device_names = Vec::new();
|
||||||
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() };
|
let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::zeroed() };
|
||||||
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as DWORD;
|
dd.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as DWORD;
|
||||||
let mut i_dev_num = 0;
|
let mut i_dev_num = 0;
|
||||||
@ -317,15 +612,115 @@ mod windows {
|
|||||||
continue;
|
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.DeviceName),
|
||||||
String::from_utf16(&dd.DeviceString),
|
String::from_utf16(&dd.DeviceString),
|
||||||
) {
|
) {
|
||||||
if &device_string[..IDD_DEVICE_STRING.len()] == IDD_DEVICE_STRING {
|
if let Some(s) = device_string {
|
||||||
device_names.insert(device_name);
|
if ds.len() >= s.len() && &ds[..s.len()] == s {
|
||||||
|
device_names.push(device_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
device_names.push(device_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
device_names
|
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