From 320f6ddc6e6ccb5be9a65e0e1a23a722daff9ec1 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Thu, 26 Oct 2023 12:05:59 +0200 Subject: [PATCH 01/15] Update Italian language --- src/lang/it.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 0c0989764..37b4d57b4 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -567,9 +567,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use all my displays for the remote session", "Usa tutti gli schermi per la sessione remota"), ("selinux_tip", ""), ("selinux_tip", "In questo dispositivo è abilitato SELinux, che potrebbe impedire il corretto funzionamento di RustDesk come lato controllato."), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), + ("Change view", "Modifica vista"), + ("Big tiles", "Icone grandi"), + ("Small tiles", "Icone piccole"), + ("List", "Elenco"), ].iter().cloned().collect(); } From 0fbc546696cb9dc360e4b987e575454a6d506f62 Mon Sep 17 00:00:00 2001 From: leroyloren <57643470+leroyloren@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:18:19 +0200 Subject: [PATCH 02/15] Update cs.rs --- src/lang/cs.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 3ac90b7b5..9ad3e9e84 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -565,10 +565,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Open in new window", "Otevřít v novém okně"), ("Show displays as individual windows", "Zobrazit obrazovky jako jednotlivá okna"), ("Use all my displays for the remote session", "Použít všechny mé obrazovky pro vzdálenou relaci"), - ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), + ("selinux_tip", "Na vašem zařízení je povolen SELinux, což může bránit správnému běhu RustDesku jako řízené strany."), + ("Change view", "Change view"), + ("Big tiles", "Velké dlaždice"), + ("Small tiles", "Malé dlaždice"), + ("List", "Seznam"), ].iter().cloned().collect(); } From 9d3d11755f7172ee7b68e5bd3df3a71bd27d56e0 Mon Sep 17 00:00:00 2001 From: leroyloren <57643470+leroyloren@users.noreply.github.com> Date: Thu, 26 Oct 2023 13:19:34 +0200 Subject: [PATCH 03/15] Update cs.rs --- src/lang/cs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 9ad3e9e84..a5f62db26 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -566,7 +566,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "Zobrazit obrazovky jako jednotlivá okna"), ("Use all my displays for the remote session", "Použít všechny mé obrazovky pro vzdálenou relaci"), ("selinux_tip", "Na vašem zařízení je povolen SELinux, což může bránit správnému běhu RustDesku jako řízené strany."), - ("Change view", "Change view"), + ("Change view", "Změnit pohled"), ("Big tiles", "Velké dlaždice"), ("Small tiles", "Malé dlaždice"), ("List", "Seznam"), From 35b470dbacda02dbeb7c8ca30e60830cfe8325bb Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:57:16 +0200 Subject: [PATCH 04/15] Update de.rs --- src/lang/de.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 9696b62b2..cc097b17f 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -566,9 +566,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "Jeden Bildschirm in einem eigenen Fenster anzeigen"), ("Use all my displays for the remote session", "Alle meine Bildschirme für die Fernsitzung verwenden"), ("selinux_tip", "SELinux ist auf Ihrem Gerät aktiviert, was dazu führen kann, dass RustDesk als kontrollierte Seite nicht richtig läuft."), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), + ("Change view", "Ansicht ändern"), + ("Big tiles", "Große Kacheln"), + ("Small tiles", "Kleine Kacheln"), + ("List", "Liste"), ].iter().cloned().collect(); } From b40a7b24d456360852cbfbc95f1b160325e2df25 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 27 Oct 2023 15:01:20 +0800 Subject: [PATCH 05/15] fix, flutter, infinite loop Signed-off-by: fufesou --- flutter/lib/common/widgets/peers_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index 24949b94a..ed5f95d41 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -190,14 +190,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener { child: widget.peerCardBuilder(peer), ); final windowWidth = MediaQuery.of(context).size.width; - final model = Provider.of(context); + final currentTab = gFFI.peerTabModel.currentTab; final hideAbTagsPanel = bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty; return isDesktop ? Obx( () => SizedBox( width: peerCardUiType.value != PeerUiType.list ? 220 - : model.currentTab == PeerTabIndex.group.index || (model.currentTab == PeerTabIndex.ab.index && !hideAbTagsPanel) + : currentTab == PeerTabIndex.group.index || (currentTab == PeerTabIndex.ab.index && !hideAbTagsPanel) ? windowWidth - 390 : windowWidth - 227, height: From de356304c7e45200be0d2a5adc0d83e349fdfef4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 27 Oct 2023 15:58:54 +0800 Subject: [PATCH 06/15] add comemnt Signed-off-by: fufesou --- flutter/lib/common/widgets/peers_view.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index ed5f95d41..b10fe3e7d 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -190,7 +190,13 @@ class _PeersViewState extends State<_PeersView> with WindowListener { child: widget.peerCardBuilder(peer), ); final windowWidth = MediaQuery.of(context).size.width; - final currentTab = gFFI.peerTabModel.currentTab; + // `Provider.of(context)` will causes infinete loop. + // Because `gFFI.peerTabModel.setCurrentTabCachedPeers(peers)` will trigger `notifyListeners()`. + // + // No need to listen the currentTab change event. + // Because the currentTab change event will trigger the peers change event, + // and the peers change event will trigger _buildPeersView(). + final currentTab = Provider.of(context, listen: false).currentTab; final hideAbTagsPanel = bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty; return isDesktop ? Obx( From e32748daf2176451f2daa7ed45d6b35aa5ce44b6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 27 Oct 2023 17:00:13 +0800 Subject: [PATCH 07/15] fix, autocomplete, fill id field Signed-off-by: fufesou --- flutter/lib/common/widgets/autocomplete.dart | 11 +++-------- flutter/lib/desktop/pages/connection_page.dart | 8 +++++++- flutter/lib/mobile/pages/connection_page.dart | 8 +++++++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/flutter/lib/common/widgets/autocomplete.dart b/flutter/lib/common/widgets/autocomplete.dart index 9c14eab7f..b40c4ef82 100644 --- a/flutter/lib/common/widgets/autocomplete.dart +++ b/flutter/lib/common/widgets/autocomplete.dart @@ -55,12 +55,12 @@ import 'package:flutter_hbb/common/widgets/peer_card.dart'; } class AutocompletePeerTile extends StatefulWidget { - final IDTextEditingController idController; + final VoidCallback onSelect; final Peer peer; const AutocompletePeerTile({ Key? key, - required this.idController, + required this.onSelect, required this.peer, }) : super(key: key); @@ -85,12 +85,7 @@ class _AutocompletePeerTileState extends State{ fontSize: 11, color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); final child = GestureDetector( - onTap: () { - setState(() { - widget.idController.id = widget.peer.id; - FocusScope.of(context).unfocus(); - }); - }, + onTap: () => widget.onSelect(), child: Container( height: 42, diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index a40e8abe0..bf923d388 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -277,6 +277,12 @@ class _ConnectionPageState extends State }, )); }, + onSelected: (option) { + setState(() { + _idController.id = option.id; + FocusScope.of(context).unfocus(); + }); + }, optionsViewBuilder: (BuildContext context, AutocompleteOnSelected onSelected, Iterable options) { double maxHeight = options.length * 50; maxHeight = maxHeight > 200 ? 200 : maxHeight; @@ -304,7 +310,7 @@ class _ConnectionPageState extends State : Padding( padding: const EdgeInsets.only(top: 5), child: ListView( - children: options.map((peer) => AutocompletePeerTile(idController: _idController, peer: peer)).toList(), + children: options.map((peer) => AutocompletePeerTile(onSelect: () => onSelected(peer), peer: peer)).toList(), ), ), ), diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index 1c1dec8fc..a4e3c7f4d 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -245,6 +245,12 @@ class _ConnectionPageState extends State { inputFormatters: [IDTextInputFormatter()], ); }, + onSelected: (option) { + setState(() { + _idController.id = option.id; + FocusScope.of(context).unfocus(); + }); + }, optionsViewBuilder: (BuildContext context, AutocompleteOnSelected onSelected, Iterable options) { double maxHeight = options.length * 50; maxHeight = maxHeight > 200 ? 200 : maxHeight; @@ -268,7 +274,7 @@ class _ConnectionPageState extends State { ))) : ListView( padding: EdgeInsets.only(top: 5), - children: options.map((peer) => AutocompletePeerTile(idController: _idController, peer: peer)).toList(), + children: options.map((peer) => AutocompletePeerTile(onSelect: () => onSelected(peer), peer: peer)).toList(), )))) ); }, From 725a44abd89aa485296e7a47551101f5ee6e9c6b Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 27 Oct 2023 16:19:42 +0800 Subject: [PATCH 08/15] feat, win virtual display Signed-off-by: fufesou --- flutter/lib/common/widgets/autocomplete.dart | 2 +- flutter/lib/consts.dart | 8 +++ .../lib/desktop/widgets/remote_toolbar.dart | 68 ++++++++++++++++++ flutter/lib/models/model.dart | 38 +++++++++- libs/hbb_common/protos/message.proto | 7 ++ libs/virtual_display/dylib/src/lib.rs | 32 +++++---- src/client/io_loop.rs | 1 + src/flutter.rs | 7 ++ src/flutter_ffi.rs | 6 ++ src/lang/ar.rs | 2 + src/lang/ca.rs | 2 + src/lang/cn.rs | 2 + src/lang/cs.rs | 2 + src/lang/da.rs | 2 + src/lang/de.rs | 2 + src/lang/el.rs | 2 + src/lang/eo.rs | 2 + src/lang/es.rs | 3 + src/lang/fa.rs | 2 + src/lang/fr.rs | 2 + src/lang/hu.rs | 2 + src/lang/id.rs | 2 + src/lang/it.rs | 3 +- src/lang/ja.rs | 2 + src/lang/ko.rs | 2 + src/lang/kz.rs | 2 + src/lang/lt.rs | 2 + src/lang/lv.rs | 2 + src/lang/nl.rs | 2 + src/lang/pl.rs | 2 + src/lang/pt_PT.rs | 2 + src/lang/ptbr.rs | 2 + src/lang/ro.rs | 2 + src/lang/ru.rs | 2 + src/lang/sk.rs | 2 + src/lang/sl.rs | 2 + src/lang/sq.rs | 2 + src/lang/sr.rs | 2 + src/lang/sv.rs | 2 + src/lang/template.rs | 2 + src/lang/th.rs | 2 + src/lang/tr.rs | 2 + src/lang/tw.rs | 2 + src/lang/ua.rs | 2 + src/lang/vn.rs | 2 + src/platform/windows.rs | 2 +- src/server/connection.rs | 54 +++++++++++++-- src/server/display_service.rs | 36 +++++++--- src/server/video_service.rs | 52 +------------- src/ui/remote.rs | 4 ++ src/ui_session_interface.rs | 35 ++++++++-- src/virtual_display_manager.rs | 69 ++++++++++++++++--- 52 files changed, 400 insertions(+), 95 deletions(-) diff --git a/flutter/lib/common/widgets/autocomplete.dart b/flutter/lib/common/widgets/autocomplete.dart index b40c4ef82..55e4dbcc1 100644 --- a/flutter/lib/common/widgets/autocomplete.dart +++ b/flutter/lib/common/widgets/autocomplete.dart @@ -32,7 +32,7 @@ import 'package:flutter_hbb/common/widgets/peer_card.dart'; for (var peer in peerData) { if (peer is Map && peer.containsKey("id")) { String id = peer["id"]; - if (id != null && !combinedPeers.containsKey(id)) { + if (!combinedPeers.containsKey(id)) { combinedPeers[id] = peer; } } diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index ccd8fbaac..073edbfec 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -5,6 +5,9 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:get/get.dart'; +const int kMaxVirtualDisplayCount = 4; +const int kAllVirtualDisplay = -1; + const double kDesktopRemoteTabBarHeight = 28.0; const int kInvalidWindowId = -1; const int kMainWindowId = 0; @@ -15,6 +18,11 @@ const kKeyLegacyMode = 'legacy'; const kKeyMapMode = 'map'; const kKeyTranslateMode = 'translate'; +const String kPlatformAdditionsIsWayland = "is_wayland"; +const String kPlatformAdditionsHeadless = "headless"; +const String kPlatformAdditionsIsInstalled = "is_installed"; +const String kPlatformAdditionsVirtualDisplays = "virtual_displays"; + const String kPeerPlatformWindows = "Windows"; const String kPeerPlatformLinux = "Linux"; const String kPeerPlatformMacOS = "Mac OS"; diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 1f161ec42..d8b330407 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -978,6 +978,10 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, screenAdjustor: _screenAdjustor, ), + _VirtualDisplayMenu( + id: widget.id, + ffi: widget.ffi, + ), Divider(), toggles(), widget.pluginItem, @@ -1387,6 +1391,70 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } } +class _VirtualDisplayMenu extends StatefulWidget { + final String id; + final FFI ffi; + + _VirtualDisplayMenu({ + Key? key, + required this.id, + required this.ffi, + }) : super(key: key); + + @override + State<_VirtualDisplayMenu> createState() => _VirtualDisplayMenuState(); +} + +class _VirtualDisplayMenuState extends State<_VirtualDisplayMenu> { + @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 virtualDisplays = widget.ffi.ffiModel.pi.virtualDisplays; + + final children = []; + for (var i = 0; i < kMaxVirtualDisplayCount; i++) { + children.add(CkbMenuButton( + value: virtualDisplays.contains(i + 1), + onChanged: (bool? value) async { + if (value != null) { + bind.sessionToggleVirtualDisplay( + sessionId: widget.ffi.sessionId, index: i + 1, on: value); + } + }, + child: Text('${translate('Virtual display')} ${i + 1}'), + ffi: widget.ffi, + )); + } + children.add(Divider()); + children.add(MenuButton( + onPressed: () { + bind.sessionToggleVirtualDisplay( + sessionId: widget.ffi.sessionId, + index: kAllVirtualDisplay, + on: false); + }, + ffi: widget.ffi, + child: Text(translate('Plug out all')), + )); + return _SubmenuButton( + ffi: widget.ffi, + menuChildren: children, + child: Text(translate("Virtual display")), + ); + } +} + class _KeyboardMenu extends StatelessWidget { final String id; final FFI ffi; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index c8675dfa7..0aad54b37 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -248,6 +248,8 @@ class FfiModel with ChangeNotifier { handlePeerInfo(evt, peerId, false); } else if (name == 'sync_peer_info') { handleSyncPeerInfo(evt, sessionId, peerId); + } else if (name == 'sync_platform_additions') { + handlePlatformAdditions(evt, sessionId, peerId); } else if (name == 'connection_ready') { setConnectionType( peerId, evt['secure'] == 'true', evt['direct'] == 'true'); @@ -895,6 +897,33 @@ class FfiModel with ChangeNotifier { notifyListeners(); } + handlePlatformAdditions( + Map evt, SessionID sessionId, String peerId) async { + final updateData = evt['platform_additions'] as String?; + if (updateData == null) { + return; + } + + if (updateData.isEmpty) { + _pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays); + } else { + try { + final updateJson = json.decode(updateData); + for (final key in updateJson.keys) { + _pi.platformAdditions[key] = updateJson[key]; + } + if (!updateJson.contains(kPlatformAdditionsVirtualDisplays)) { + _pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays); + } + } catch (e) { + debugPrint('Failed to decode platformAdditions $e'); + } + } + + cachedPeerData.peerInfo['platform_additions'] = + json.encode(_pi.platformAdditions); + } + // Directly switch to the new display without waiting for the response. switchToNewDisplay(int display, SessionID sessionId, String peerId) { // VideoHandler creation is upon when video frames are received, so either caching commands(don't know next width/height) or stopping recording when switching displays. @@ -2300,8 +2329,13 @@ class PeerInfo with ChangeNotifier { RxInt displaysCount = 0.obs; RxBool isSet = false.obs; - bool get isWayland => platformAdditions['is_wayland'] == true; - bool get isHeadless => platformAdditions['headless'] == true; + bool get isWayland => platformAdditions[kPlatformAdditionsIsWayland] == true; + bool get isHeadless => platformAdditions[kPlatformAdditionsHeadless] == true; + bool get isInstalled => + platform != kPeerPlatformWindows || + platformAdditions[kPlatformAdditionsIsInstalled] == true; + List get virtualDisplays => List.from( + platformAdditions[kPlatformAdditionsVirtualDisplays] ?? []); bool get isSupportMultiDisplay => isDesktop && isSupportMultiUiSession; diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index bba23ff70..454bc0d3d 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -102,6 +102,7 @@ message PeerInfo { SupportedEncoding encoding = 10; SupportedResolutions resolutions = 11; // Use JSON's key-value format which is friendly for peer to handle. + // NOTE: Only support one-level dictionaries (for peer to update), and the key is of type string. string platform_additions = 12; } @@ -498,6 +499,11 @@ message CaptureDisplays { repeated int32 set = 3; } +message ToggleVirtualDisplay { + int32 display = 1; + bool on = 2; +} + message PermissionInfo { enum Permission { Keyboard = 0; @@ -697,6 +703,7 @@ message Misc { bool client_record_status = 29; CaptureDisplays capture_displays = 30; int32 refresh_video_display = 31; + ToggleVirtualDisplay toggle_virtual_display = 32; } } diff --git a/libs/virtual_display/dylib/src/lib.rs b/libs/virtual_display/dylib/src/lib.rs index f2997da16..51a8d66ea 100644 --- a/libs/virtual_display/dylib/src/lib.rs +++ b/libs/virtual_display/dylib/src/lib.rs @@ -3,7 +3,7 @@ pub mod win10; #[cfg(windows)] use hbb_common::lazy_static; use hbb_common::{bail, ResultType}; -use std::path::Path; +use std::path::PathBuf; #[cfg(windows)] use std::sync::Mutex; @@ -33,17 +33,29 @@ pub fn download_driver() -> ResultType<()> { Ok(()) } -#[no_mangle] -pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { +fn get_driver_install_abs_path() -> ResultType { #[cfg(windows)] let install_path = win10::DRIVER_INSTALL_PATH; #[cfg(not(windows))] - let install_path = ""; + bail!("Not implemented for non-windows"); - let abs_path = Path::new(install_path).canonicalize()?; + let exe_file = std::env::current_exe()?; + let abs_path = match exe_file.parent() { + Some(cur_dir) => cur_dir.join(install_path), + None => bail!( + "Invalid exe parent for {}", + exe_file.to_string_lossy().as_ref() + ), + }; if !abs_path.exists() { bail!("{} not exists", install_path) } + Ok(abs_path) +} + +#[no_mangle] +pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { + let abs_path = get_driver_install_abs_path()?; #[cfg(windows)] unsafe { @@ -76,15 +88,7 @@ pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { #[no_mangle] pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> { - #[cfg(windows)] - let install_path = win10::DRIVER_INSTALL_PATH; - #[cfg(not(windows))] - let install_path = ""; - - let abs_path = Path::new(install_path).canonicalize()?; - if !abs_path.exists() { - bail!("{} not exists", install_path) - } + let abs_path = get_driver_install_abs_path()?; #[cfg(windows)] unsafe { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 4c830a2f9..db7a4c5b7 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1531,6 +1531,7 @@ impl Remote { } Some(message::Union::PeerInfo(pi)) => { self.handler.set_displays(&pi.displays); + self.handler.set_platform_additions(&pi.platform_additions); } _ => {} } diff --git a/src/flutter.rs b/src/flutter.rs index 9d304af8b..395056038 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -691,6 +691,13 @@ impl InvokeUiSession for FlutterHandler { ); } + fn set_platform_additions(&self, data: &str) { + self.push_event( + "sync_platform_additions", + vec![("platform_additions", &data)], + ) + } + fn on_connected(&self, _conn_type: ConnType) {} fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 1484d2440..872d782d8 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1401,6 +1401,12 @@ pub fn session_on_waiting_for_image_dialog_show(session_id: SessionID) { super::flutter::session_on_waiting_for_image_dialog_show(session_id); } +pub fn session_toggle_virtual_display(session_id: SessionID, index: i32, on: bool) { + if let Some(session) = sessions::get_session_by_session_id(&session_id) { + session.toggle_virtual_display(index, on); + } +} + pub fn main_set_home_dir(_home: String) { #[cfg(any(target_os = "android", target_os = "ios"))] { diff --git a/src/lang/ar.rs b/src/lang/ar.rs index a63e786d1..e2c4c95ea 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index ff43d0ef4..9f52439e4 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index c544d225c..4cf749ffa 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", "虚拟显示器"), + ("Plug out all", "拔出所有"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index a5f62db26..a6ce4b267 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", "Velké dlaždice"), ("Small tiles", "Malé dlaždice"), ("List", "Seznam"), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 40f1fbfb0..bb9c9f06d 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index cc097b17f..fde9d132c 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", "Große Kacheln"), ("Small tiles", "Kleine Kacheln"), ("List", "Liste"), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 72a4c492c..e6efe5677 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 1d91e7b7f..f65f42aa0 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 26bb408ac..a5ae3699e 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -570,5 +570,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("selinux_tip", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 69f883b89..129d41e44 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index e15bc38ba..91e956be0 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index f18013423..90dd4b03e 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index ce9e359b1..49bc1460a 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 37b4d57b4..87964f39a 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -565,11 +565,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Open in new window", "Apri in una nuova finestra"), ("Show displays as individual windows", "Visualizza schermi come finestre individuali"), ("Use all my displays for the remote session", "Usa tutti gli schermi per la sessione remota"), - ("selinux_tip", ""), ("selinux_tip", "In questo dispositivo è abilitato SELinux, che potrebbe impedire il corretto funzionamento di RustDesk come lato controllato."), ("Change view", "Modifica vista"), ("Big tiles", "Icone grandi"), ("Small tiles", "Icone piccole"), ("List", "Elenco"), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 9c60683f3..d5284276e 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 189fa640a..fc14bace5 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index b6475c5f9..992ba7cd5 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index ebeebaaa0..fa78425fc 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 033b45d0d..462f6e688 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 9d8445825..9f1125521 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 0c474aea0..9c4101dfa 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 77c451035..966a57c04 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 4fedb1636..5caa10268 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 700cf452f..1794ccfc0 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 3bb2e9820..f43df683c 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", "Большие значки"), ("Small tiles", "Маленькие значки"), ("List", "Список"), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index c6f6d5724..9f5b3084e 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 4e0c394d6..71300b6c6 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 2b37c35b2..52e9a2fdd 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 63d0190ef..f82b5bdec 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index c8b2f22a6..b73836e05 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index b358f3709..f14767127 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 606993498..223d14d29 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index ecab140e3..263df3741 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index d5a233f02..86116b6ce 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index f8bf4c278..3bfae65df 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 103ef2663..eacd49b85 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -570,5 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", ""), ("Small tiles", ""), ("List", ""), + ("Virtual display", ""), + ("Plug out all", ""), ].iter().cloned().collect(); } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 3b44cbc11..a8d853e23 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1838,7 +1838,7 @@ pub fn uninstall_cert() -> ResultType<()> { } mod cert { - use hbb_common::{allow_err, bail, log, ResultType}; + use hbb_common::{bail, log, ResultType}; use std::{ffi::OsStr, io::Error, os::windows::ffi::OsStrExt, path::Path, str::from_utf8}; use winapi::{ shared::{ diff --git a/src/server/connection.rs b/src/server/connection.rs index 86096288d..6c524fa49 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -53,9 +53,10 @@ use std::{ #[cfg(not(any(target_os = "android", target_os = "ios")))] use system_shutdown; +#[cfg(all(windows, feature = "virtual_display_driver"))] +use crate::virtual_display_manager; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::collections::HashSet; - pub type Sender = mpsc::UnboundedSender<(Instant, Arc)>; lazy_static::lazy_static! { @@ -1031,9 +1032,10 @@ impl Connection { pi.hostname = DEVICE_NAME.lock().unwrap().clone(); pi.platform = "Android".into(); } + #[cfg(any(target_os = "linux", target_os = "windows"))] + let mut platform_additions = serde_json::Map::new(); #[cfg(target_os = "linux")] { - let mut platform_additions = serde_json::Map::new(); if crate::platform::current_is_wayland() { platform_additions.insert("is_wayland".into(), json!(true)); } @@ -1044,12 +1046,27 @@ impl Connection { platform_additions.insert("headless".into(), json!(true)); } } - if !platform_additions.is_empty() { - pi.platform_additions = - serde_json::to_string(&platform_additions).unwrap_or("".into()); + } + #[cfg(target_os = "windows")] + { + platform_additions.insert( + "is_installed".into(), + json!(crate::platform::is_installed()), + ); + #[cfg(feature = "virtual_display_driver")] + if crate::platform::is_installed() { + let virtual_displays = virtual_display_manager::get_virtual_displays(); + if !virtual_displays.is_empty() { + platform_additions.insert("virtual_displays".into(), json!(&virtual_displays)); + } } } + #[cfg(any(target_os = "linux", target_os = "windows"))] + if !platform_additions.is_empty() { + pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); + } + pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into(); if self.port_forward_socket.is_some() { @@ -1962,6 +1979,10 @@ impl Connection { let set = displays.set.iter().map(|d| *d as usize).collect::>(); self.capture_displays(&add, &sub, &set).await; } + #[cfg(all(windows, feature = "virtual_display_driver"))] + Some(misc::Union::ToggleVirtualDisplay(t)) => { + self.toggle_virtual_display(t).await; + } Some(misc::Union::ChatMessage(c)) => { self.send_to_cm(ipc::Data::ChatMessage { text: c.text }); self.chat_unanswered = true; @@ -2214,6 +2235,25 @@ impl Connection { } } + #[cfg(all(windows, feature = "virtual_display_driver"))] + async fn toggle_virtual_display(&mut self, t: ToggleVirtualDisplay) { + if t.on { + if let Err(e) = virtual_display_manager::plug_in_index_modes(t.display as _, Vec::new()) + { + log::error!("Failed to plug in virtual display: {}", e); + } + } else { + let indices = if t.display == -1 { + virtual_display_manager::get_virtual_displays() + } else { + vec![t.display as _] + }; + if let Err(e) = virtual_display_manager::plug_out_peer_request(&indices) { + log::error!("Failed to plug out virtual display {:?}: {}", &indices, e); + } + } + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn change_resolution(&mut self, r: &Resolution) { if self.keyboard { @@ -2222,7 +2262,7 @@ impl Connection { let name = display.name(); #[cfg(all(windows, feature = "virtual_display_driver"))] if let Some(_ok) = - crate::virtual_display_manager::change_resolution_if_is_virtual_display( + virtual_display_manager::change_resolution_if_is_virtual_display( &name, r.width as _, r.height as _, @@ -2918,7 +2958,7 @@ mod raii { } #[cfg(all(windows, feature = "virtual_display_driver"))] if active_conns_lock.is_empty() { - display_service::try_plug_out_virtual_display(); + let _ = virtual_display_manager::reset_all(); } #[cfg(all(windows))] if active_conns_lock.is_empty() { diff --git a/src/server/display_service.rs b/src/server/display_service.rs index 62f5cac3d..30225e491 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -12,6 +12,8 @@ use scrap::Display; pub const NAME: &'static str = "display"; +const DUMMY_DISPLAY_SIDE_MAX_SIZE: usize = 1024; + struct ChangedResolution { original: (i32, i32), changed: (i32, i32), @@ -154,6 +156,20 @@ fn displays_to_msg(displays: Vec) -> Message { ..Default::default() }; pi.displays = displays.clone(); + + #[cfg(all(windows, feature = "virtual_display_driver"))] + if crate::platform::is_installed() { + let virtual_displays = crate::virtual_display_manager::get_virtual_displays(); + if !virtual_displays.is_empty() { + let mut platform_additions = serde_json::Map::new(); + platform_additions.insert( + "virtual_displays".into(), + serde_json::json!(&virtual_displays), + ); + pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); + } + } + // current_display should not be used in server. // It is set to 0 for compatibility with old clients. pi.current_display = 0; @@ -168,11 +184,6 @@ fn check_get_displays_changed_msg() -> Option { Some(displays_to_msg(displays)) } -#[cfg(all(windows, feature = "virtual_display_driver"))] -pub fn try_plug_out_virtual_display() { - let _res = virtual_display_manager::plug_out_headless(); -} - fn run(sp: EmptyExtraFieldService) -> ResultType<()> { while sp.ok() { sp.snapshot(|sps| { @@ -312,9 +323,18 @@ fn no_displays(displays: &Vec) -> bool { true } else if display_len == 1 { let display = &displays[0]; - let dummy_display_side_max_size = 800; - display.width() <= dummy_display_side_max_size - && display.height() <= dummy_display_side_max_size + if display.width() > DUMMY_DISPLAY_SIDE_MAX_SIZE + || display.height() > DUMMY_DISPLAY_SIDE_MAX_SIZE + { + return false; + } + let any_real = crate::platform::resolutions(&display.name()) + .iter() + .any(|r| { + (r.height as usize) > DUMMY_DISPLAY_SIDE_MAX_SIZE + || (r.width as usize) > DUMMY_DISPLAY_SIDE_MAX_SIZE + }); + !any_real } else { false } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index d5f39a2c6..b8c6343c2 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -255,7 +255,7 @@ pub fn test_create_capturer( ) -> String { let test_begin = Instant::now(); loop { - let err = match try_get_displays() { + let err = match Display::all() { Ok(mut displays) => { if displays.len() <= display_idx { anyhow!( @@ -271,7 +271,7 @@ pub fn test_create_capturer( } } } - Err(e) => e, + Err(e) => e.into(), }; if test_begin.elapsed().as_millis() >= timeout_millis as _ { return err.to_string(); @@ -332,7 +332,7 @@ fn get_capturer( } } - let mut displays = try_get_displays()?; + let mut displays = Display::all()?; let ndisplay = displays.len(); if ndisplay <= current { bail!( @@ -761,52 +761,6 @@ pub fn refresh() { Display::refresh_size(); } -#[inline] -#[cfg(not(all(windows, feature = "virtual_display_driver")))] -fn try_get_displays() -> ResultType> { - Ok(Display::all()?) -} - -#[inline] -#[cfg(all(windows, feature = "virtual_display_driver"))] -fn no_displays(displays: &Vec) -> bool { - let display_len = displays.len(); - if display_len == 0 { - true - } else if display_len == 1 { - let display = &displays[0]; - let dummy_display_side_max_size = 800; - if display.width() > dummy_display_side_max_size - || display.height() > dummy_display_side_max_size - { - return false; - } - let any_real = crate::platform::resolutions(&display.name()) - .iter() - .any(|r| { - (r.height as usize) > dummy_display_side_max_size - || (r.width as usize) > dummy_display_side_max_size - }); - !any_real - } else { - false - } -} - -#[cfg(all(windows, feature = "virtual_display_driver"))] -fn try_get_displays() -> ResultType> { - // let mut displays = Display::all()?; - // if no_displays(&displays) { - // log::debug!("no displays, create virtual display"); - // if let Err(e) = virtual_display_manager::plug_in_headless() { - // log::error!("plug in headless failed {}", e); - // } else { - // displays = Display::all()?; - // } - // } - Ok(Display::all()?) -} - #[cfg(windows)] fn start_uac_elevation_check() { static START: Once = Once::new(); diff --git a/src/ui/remote.rs b/src/ui/remote.rs index bb01ef9f4..2138fc458 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -254,6 +254,10 @@ impl InvokeUiSession for SciterHandler { ); } + fn set_platform_additions(&self, _data: &str) { + // Ignore for sciter version. + } + fn on_connected(&self, conn_type: ConnType) { match conn_type { ConnType::RDP => {} diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 991691886..b13cc01bd 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -237,11 +237,19 @@ impl Session { } pub fn get_displays_as_individual_windows(&self) -> String { - self.lc.read().unwrap().displays_as_individual_windows.clone() + self.lc + .read() + .unwrap() + .displays_as_individual_windows + .clone() } pub fn get_use_all_my_displays_for_the_remote_session(&self) -> String { - self.lc.read().unwrap().use_all_my_displays_for_the_remote_session.clone() + self.lc + .read() + .unwrap() + .use_all_my_displays_for_the_remote_session + .clone() } pub fn save_reverse_mouse_wheel(&self, value: String) { @@ -249,11 +257,17 @@ impl Session { } pub fn save_displays_as_individual_windows(&self, value: String) { - self.lc.write().unwrap().save_displays_as_individual_windows(value); + self.lc + .write() + .unwrap() + .save_displays_as_individual_windows(value); } pub fn save_use_all_my_displays_for_the_remote_session(&self, value: String) { - self.lc.write().unwrap().save_use_all_my_displays_for_the_remote_session(value); + self.lc + .write() + .unwrap() + .save_use_all_my_displays_for_the_remote_session(value); } pub fn save_view_style(&self, value: String) { @@ -310,6 +324,18 @@ impl Session { } } + pub fn toggle_virtual_display(&self, index: i32, on: bool) { + let mut misc = Misc::new(); + misc.set_toggle_virtual_display(ToggleVirtualDisplay { + display: index, + on, + ..Default::default() + }); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + self.send(Data::Message(msg_out)); + } + #[cfg(not(feature = "flutter"))] pub fn refresh_video(&self, _display: i32) { self.send(Data::Message(LoginConfigHandler::refresh())); @@ -1175,6 +1201,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn switch_display(&self, display: &SwitchDisplay); fn set_peer_info(&self, peer_info: &PeerInfo); // flutter fn set_displays(&self, displays: &Vec); + fn set_platform_additions(&self, data: &str); fn on_connected(&self, conn_type: ConnType); fn update_privacy_mode(&self); fn set_permission(&self, name: &str, value: bool); diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index 2d152b334..bc99d1c69 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -18,10 +18,18 @@ lazy_static::lazy_static! { struct VirtualDisplayManager { headless_index_name: Option<(u32, String)>, peer_index_name: HashMap, + is_driver_installed: bool, } impl VirtualDisplayManager { - fn prepare_driver() -> ResultType<()> { + 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); @@ -29,9 +37,8 @@ impl VirtualDisplayManager { } // Reboot is not required for this case. let mut _reboot_required = false; - allow_err!(virtual_display::install_update_driver( - &mut _reboot_required - )); + virtual_display::install_update_driver(&mut _reboot_required)?; + self.is_driver_installed = true; Ok(()) } @@ -48,7 +55,7 @@ impl VirtualDisplayManager { pub fn plug_in_headless() -> ResultType<()> { let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - VirtualDisplayManager::prepare_driver()?; + manager.prepare_driver()?; let modes = [virtual_display::MonitorMode { width: 1920, height: 1080, @@ -93,9 +100,55 @@ fn get_new_device_name(device_names: &HashSet) -> String { "".to_string() } +pub fn get_virtual_displays() -> Vec { + VIRTUAL_DISPLAY_MANAGER + .lock() + .unwrap() + .peer_index_name + .keys() + .cloned() + .collect() +} + +pub fn plug_in_index_modes( + idx: u32, + mut modes: Vec, +) -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + manager.prepare_driver()?; + if !manager.peer_index_name.contains_key(&idx) { + let device_names = windows::get_device_names(); + if modes.is_empty() { + modes.push(virtual_display::MonitorMode { + width: 1920, + height: 1080, + sync: 60, + }); + } + match VirtualDisplayManager::plug_in_monitor(idx, modes.as_slice()) { + Ok(_) => { + let device_name = get_new_device_name(&device_names); + manager.peer_index_name.insert(idx, device_name); + } + Err(e) => { + log::error!("Plug in monitor failed {}", e); + } + } + } + Ok(()) +} + +pub fn reset_all() -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + manager.install_update_driver()?; + manager.peer_index_name.clear(); + manager.headless_index_name = None; + Ok(()) +} + pub fn plug_in_peer_request(modes: Vec>) -> ResultType> { let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - VirtualDisplayManager::prepare_driver()?; + manager.prepare_driver()?; let mut indices: Vec = Vec::new(); for m in modes.iter() { @@ -119,9 +172,9 @@ pub fn plug_in_peer_request(modes: Vec>) -> Re Ok(indices) } -pub fn plug_out_peer_request(modes: &[u32]) -> ResultType<()> { +pub fn plug_out_peer_request(indices: &[u32]) -> ResultType<()> { let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - for idx in modes.iter() { + 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); From 5ab0f499ce96ed8f30b8cf85e66d3fd5190b62f7 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 27 Oct 2023 20:57:35 +0800 Subject: [PATCH 09/15] fix build Signed-off-by: fufesou --- libs/virtual_display/dylib/src/lib.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/libs/virtual_display/dylib/src/lib.rs b/libs/virtual_display/dylib/src/lib.rs index 51a8d66ea..573c51810 100644 --- a/libs/virtual_display/dylib/src/lib.rs +++ b/libs/virtual_display/dylib/src/lib.rs @@ -1,8 +1,9 @@ #[cfg(windows)] pub mod win10; +use hbb_common::ResultType; +#[cfg(windows)] +use hbb_common::{bail, lazy_static}; #[cfg(windows)] -use hbb_common::lazy_static; -use hbb_common::{bail, ResultType}; use std::path::PathBuf; #[cfg(windows)] @@ -33,12 +34,9 @@ pub fn download_driver() -> ResultType<()> { Ok(()) } +#[cfg(windows)] fn get_driver_install_abs_path() -> ResultType { - #[cfg(windows)] let install_path = win10::DRIVER_INSTALL_PATH; - #[cfg(not(windows))] - bail!("Not implemented for non-windows"); - let exe_file = std::env::current_exe()?; let abs_path = match exe_file.parent() { Some(cur_dir) => cur_dir.join(install_path), @@ -55,8 +53,6 @@ fn get_driver_install_abs_path() -> ResultType { #[no_mangle] pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { - let abs_path = get_driver_install_abs_path()?; - #[cfg(windows)] unsafe { { @@ -66,6 +62,7 @@ pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { bail!("{}", e); } + let abs_path = get_driver_install_abs_path()?; let full_install_path: Vec = abs_path .to_string_lossy() .as_ref() @@ -88,11 +85,10 @@ pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> { #[no_mangle] pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> { - let abs_path = get_driver_install_abs_path()?; - #[cfg(windows)] unsafe { { + let abs_path = get_driver_install_abs_path()?; let full_install_path: Vec = abs_path .to_string_lossy() .as_ref() From 4dd19884e0620907dd4ad1f05e0773953ec3606e Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Fri, 27 Oct 2023 16:06:32 +0300 Subject: [PATCH 10/15] Update lv.rs --- src/lang/lv.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 033b45d0d..a721d75b4 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -566,9 +566,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "Rādīt displejus kā atsevišķus logus"), ("Use all my displays for the remote session", "Izmantot visus manus displejus attālajai sesijai"), ("selinux_tip", "Jūsu ierīcē ir iespējots SELinux, kas var neļaut RustDesk pareizi darboties kā kontrolētajai pusei."), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), + ("Change view", "Mainīt skatu"), + ("Big tiles", "Lielas flīzes"), + ("Small tiles", "Mazas flīzes"), + ("List", "Saraksts"), ].iter().cloned().collect(); } From 2bbd759c25b23c9e1d5051b2925744ea7d599bd1 Mon Sep 17 00:00:00 2001 From: jxd1337 Date: Fri, 27 Oct 2023 19:01:30 +0200 Subject: [PATCH 11/15] Update CZ translation --- docs/README-CS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/README-CS.md b/docs/README-CS.md index e7cf998f3..65af2e8a4 100644 --- a/docs/README-CS.md +++ b/docs/README-CS.md @@ -6,10 +6,10 @@ StrukturaUkázky
[English] | [Українська] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
- Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, uživatelského rozhraní aplikace RustDesk a dokumentace k ní do vašeho jazyka + Potřebujeme Vaši pomoc s překladem tohoto README, uživatelského rozhraní aplikace RustDesk a dokumentace k ní do vašeho jazyka

-Dopisujte si s námi: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) +Popovídejte si s námi: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) @@ -44,7 +44,7 @@ Varianta pro mobilní platformy používá aplikační rámec (framework) Flutte - Připravte si vývojové prostředí pro jazyky Rust a C++ -- Nainstalujte [vcpkg](https://github.com/microsoft/vcpkg), a nastavte správně proměnnou prostsředí `VCPKG_ROOT` +- Nainstalujte [vcpkg](https://github.com/microsoft/vcpkg), a správně nastavte proměnnou prostředí `VCPKG_ROOT` - Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static - Linux/MacOS: vcpkg install libvpx libyuv opus aom @@ -127,7 +127,7 @@ Poté pokaždé, když bude třeba aplikaci sestavit, spusťte následující p docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder ``` -Všimněte si, že prvotní sestavení může trvat déle (než se do mezipaměti uloží veškeré softwarové součásti, které jsou potřeba) – následná opakování už budou rychlejší. Dále, pokud potřebujete příkazu pro sestavení zadat nějaké argumenty, je možné je zapsat na konec příkazu na pozici ``. Například, pokud byste chtěli sestavit optimalizovaně pro vydání, spustili byste výše uvedený příkaz následovaný `--release`. Výsledný spustitelný soubor se objeví v cílové složce na vašem systému a bude ho možné spustit pomocí: +Všimněte si, že prvotní sestavení může trvat déle (než se do mezipaměti uloží veškeré softwarové součásti, které jsou potřeba) – následná opakování už budou rychlejší. Pokud navíc potřebujete zadat různé argumenty příkazu pro sestavení, můžete tak učinit na konci příkazu v pozici ``. Například, pokud byste chtěli sestavit optimalizovanou verzi pro vydání, spustili byste výše uvedený příkaz následovaný `--release`. Výsledný spustitelný soubor se objeví v cílové složce na vašem systému a bude ho možné spustit pomocí: ```sh target/debug/rustdesk @@ -139,7 +139,7 @@ Nebo, pokud spouštíte variantu pro vydání: target/release/rustdesk ``` -Zajistětě, abyste tyto příkazy spouštěli z kořene repozitáře s RustDesk, jinak aplikace nemusí být schopná nalézt potřebné prostředky (resources). Také si všimněte, že ostatní dílčí príkazy nástroje cargo, jako třeba `install` nebo `run` zatím nejsou prostřednictvím této metody podporovány, protože by vedly k instalaci či spuštění program uvnitř kontejneru namísto přímo v systému. +Ujistěte se, že tyto příkazy spouštíte z kořenového adresáře RustDesk, jinak aplikace nemusí být schopná nalézt potřebné prostředky (resources). Také si všimněte, že ostatní dílčí príkazy nástroje cargo, jako třeba `install` nebo `run` zatím nejsou prostřednictvím této metody podporovány, protože by vedly k instalaci či spuštění program uvnitř kontejneru namísto přímo v systému. ## Struktura souborů From 11956d9e16a653b5f16690893939096cf668f9c5 Mon Sep 17 00:00:00 2001 From: leroyloren <57643470+leroyloren@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:26:50 +0200 Subject: [PATCH 12/15] Update cs.rs --- src/lang/cs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index a6ce4b267..6f809aa41 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -570,7 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", "Velké dlaždice"), ("Small tiles", "Malé dlaždice"), ("List", "Seznam"), - ("Virtual display", ""), - ("Plug out all", ""), + ("Virtual display", "Virtuální obrazovka"), + ("Plug out all", "Odpojit všechny"), ].iter().cloned().collect(); } From 7d1fb0a2387a8d30ef11d71cd8555dca795a9194 Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Sat, 28 Oct 2023 02:09:39 +0300 Subject: [PATCH 13/15] Update lv.rs --- src/lang/lv.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 8b9da9b56..3c6c4d19f 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -566,11 +566,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "Rādīt displejus kā atsevišķus logus"), ("Use all my displays for the remote session", "Izmantot visus manus displejus attālajai sesijai"), ("selinux_tip", "Jūsu ierīcē ir iespējots SELinux, kas var neļaut RustDesk pareizi darboties kā kontrolētajai pusei."), - ("Virtual display", ""), - ("Plug out all", ""), - (" view", "Mainīt skatu"), + ("Change view", "Mainīt skatu"), ("Big tiles", "Lielas flīzes"), ("Small tiles", "Mazas flīzes"), ("List", "Saraksts"), + ("Virtual display", "Virtuālais displejs"), + ("Plug out all", "Atvienot visu"), ].iter().cloned().collect(); } From db19528c24ae2e89d872787b0da55e4ab231a16d Mon Sep 17 00:00:00 2001 From: Ibnul Mutaki Date: Sat, 28 Oct 2023 07:46:53 +0700 Subject: [PATCH 14/15] improve ID trans --- src/lang/id.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/id.rs b/src/lang/id.rs index 49bc1460a..53114b747 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -566,11 +566,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "Tampilkan dengan jendela terpisah"), ("Use all my displays for the remote session", "Gunakan semua layar untuk sesi remote"), ("selinux_tip", ""), - ("Change view", ""), + ("Change view", "Sesuaikan tampilan"), ("Big tiles", ""), ("Small tiles", ""), ("List", ""), - ("Virtual display", ""), + ("Virtual display", "Tampilan virtual"), ("Plug out all", ""), ].iter().cloned().collect(); } From bb59778313567a55eee9dd9ee1ae72064af7e893 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 28 Oct 2023 10:42:31 +0800 Subject: [PATCH 15/15] fix, prompt, driver cert , on disconnecting Signed-off-by: fufesou --- src/virtual_display_manager.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index bc99d1c69..1c53a964f 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -140,9 +140,9 @@ pub fn plug_in_index_modes( pub fn reset_all() -> ResultType<()> { let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - manager.install_update_driver()?; - manager.peer_index_name.clear(); - manager.headless_index_name = None; + if !manager.peer_index_name.is_empty() || manager.headless_index_name.is_some() { + manager.install_update_driver()?; + } Ok(()) }