Merge pull request #6199 from fufesou/feat/windows_virtual_displays

feat, win virtual display
This commit is contained in:
RustDesk 2023-10-28 02:03:24 +08:00 committed by GitHub
commit cef782c388
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 403 additions and 102 deletions

View File

@ -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;
}
}

View File

@ -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";

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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()

View File

@ -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);
}
_ => {}
}

View File

@ -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) {

View File

@ -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"))]
{

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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"),

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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::{

View File

@ -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() {

View File

@ -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
}

View File

@ -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();

View File

@ -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 => {}

View File

@ -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);

View File

@ -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);