mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-27 23:19:02 +08:00
Merge pull request #6199 from fufesou/feat/windows_virtual_displays
feat, win virtual display
This commit is contained in:
commit
cef782c388
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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 = <Widget>[];
|
||||
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;
|
||||
|
@ -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<String, dynamic> 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<int> get virtualDisplays => List<int>.from(
|
||||
platformAdditions[kPlatformAdditionsVirtualDisplays] ?? []);
|
||||
|
||||
bool get isSupportMultiDisplay => isDesktop && isSupportMultiUiSession;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
#[cfg(windows)]
|
||||
pub mod win10;
|
||||
use hbb_common::ResultType;
|
||||
#[cfg(windows)]
|
||||
use hbb_common::lazy_static;
|
||||
use hbb_common::{bail, ResultType};
|
||||
use std::path::Path;
|
||||
use hbb_common::{bail, lazy_static};
|
||||
#[cfg(windows)]
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::sync::Mutex;
|
||||
@ -33,18 +34,25 @@ pub fn download_driver() -> ResultType<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
#[cfg(windows)]
|
||||
fn get_driver_install_abs_path() -> ResultType<PathBuf> {
|
||||
let install_path = win10::DRIVER_INSTALL_PATH;
|
||||
#[cfg(not(windows))]
|
||||
let install_path = "";
|
||||
|
||||
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<()> {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
{
|
||||
@ -54,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<u16> = abs_path
|
||||
.to_string_lossy()
|
||||
.as_ref()
|
||||
@ -76,19 +85,10 @@ 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)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
{
|
||||
let abs_path = get_driver_install_abs_path()?;
|
||||
let full_install_path: Vec<u16> = abs_path
|
||||
.to_string_lossy()
|
||||
.as_ref()
|
||||
|
@ -1531,6 +1531,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
Some(message::Union::PeerInfo(pi)) => {
|
||||
self.handler.set_displays(&pi.displays);
|
||||
self.handler.set_platform_additions(&pi.platform_additions);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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"))]
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -566,7 +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", "Mainīt skatu"),
|
||||
("Virtual display", ""),
|
||||
("Plug out all", ""),
|
||||
(" view", "Mainīt skatu"),
|
||||
("Big tiles", "Lielas flīzes"),
|
||||
("Small tiles", "Mazas flīzes"),
|
||||
("List", "Saraksts"),
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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::{
|
||||
|
@ -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<Message>)>;
|
||||
|
||||
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::<Vec<_>>();
|
||||
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() {
|
||||
|
@ -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<DisplayInfo>) -> 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<Message> {
|
||||
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<Display>) -> 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
|
||||
}
|
||||
|
@ -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<Vec<Display>> {
|
||||
Ok(Display::all()?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
fn no_displays(displays: &Vec<Display>) -> 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<Vec<Display>> {
|
||||
// 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();
|
||||
|
@ -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 => {}
|
||||
|
@ -237,11 +237,19 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
}
|
||||
|
||||
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<T: InvokeUiSession> Session<T> {
|
||||
}
|
||||
|
||||
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<T: InvokeUiSession> Session<T> {
|
||||
}
|
||||
}
|
||||
|
||||
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<DisplayInfo>);
|
||||
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);
|
||||
|
@ -18,10 +18,18 @@ lazy_static::lazy_static! {
|
||||
struct VirtualDisplayManager {
|
||||
headless_index_name: Option<(u32, String)>,
|
||||
peer_index_name: HashMap<u32, String>,
|
||||
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>) -> String {
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
pub fn get_virtual_displays() -> Vec<u32> {
|
||||
VIRTUAL_DISPLAY_MANAGER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.peer_index_name
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn plug_in_index_modes(
|
||||
idx: u32,
|
||||
mut modes: Vec<virtual_display::MonitorMode>,
|
||||
) -> ResultType<()> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
manager.prepare_driver()?;
|
||||
if !manager.peer_index_name.contains_key(&idx) {
|
||||
let device_names = windows::get_device_names();
|
||||
if modes.is_empty() {
|
||||
modes.push(virtual_display::MonitorMode {
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
sync: 60,
|
||||
});
|
||||
}
|
||||
match VirtualDisplayManager::plug_in_monitor(idx, modes.as_slice()) {
|
||||
Ok(_) => {
|
||||
let device_name = get_new_device_name(&device_names);
|
||||
manager.peer_index_name.insert(idx, device_name);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Plug in monitor failed {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
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<Vec<virtual_display::MonitorMode>>) -> ResultType<Vec<u32>> {
|
||||
let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap();
|
||||
VirtualDisplayManager::prepare_driver()?;
|
||||
manager.prepare_driver()?;
|
||||
|
||||
let mut indices: Vec<u32> = Vec::new();
|
||||
for m in modes.iter() {
|
||||
@ -119,9 +172,9 @@ pub fn plug_in_peer_request(modes: Vec<Vec<virtual_display::MonitorMode>>) -> 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);
|
||||
|
Loading…
Reference in New Issue
Block a user