mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-24 04:12:20 +08:00
Merge remote-tracking branch 'rd/master' into feat/x11/clipboard-file/init
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
commit
251245d315
@ -6,10 +6,10 @@
|
||||
<a href="#file-structure">Struktura</a> •
|
||||
<a href="#snapshot">Ukázky</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
|
||||
<b>Potřebujeme Vaši pomoc s překladem tohoto README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
|
||||
</p>
|
||||
|
||||
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 `<OPTIONAL-ARGS>`. 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 `<OPTIONAL-ARGS>`. 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ů
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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<AutocompletePeerTile>{
|
||||
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,
|
||||
|
@ -190,14 +190,20 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
child: widget.peerCardBuilder(peer),
|
||||
);
|
||||
final windowWidth = MediaQuery.of(context).size.width;
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
// `Provider.of<PeerTabModel>(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<PeerTabModel>(context, listen: false).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:
|
||||
|
@ -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";
|
||||
|
@ -277,6 +277,12 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
},
|
||||
));
|
||||
},
|
||||
onSelected: (option) {
|
||||
setState(() {
|
||||
_idController.id = option.id;
|
||||
FocusScope.of(context).unfocus();
|
||||
});
|
||||
},
|
||||
optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<Peer> onSelected, Iterable<Peer> options) {
|
||||
double maxHeight = options.length * 50;
|
||||
maxHeight = maxHeight > 200 ? 200 : maxHeight;
|
||||
@ -304,7 +310,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
: 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(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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;
|
||||
|
@ -245,6 +245,12 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
inputFormatters: [IDTextInputFormatter()],
|
||||
);
|
||||
},
|
||||
onSelected: (option) {
|
||||
setState(() {
|
||||
_idController.id = option.id;
|
||||
FocusScope.of(context).unfocus();
|
||||
});
|
||||
},
|
||||
optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<Peer> onSelected, Iterable<Peer> options) {
|
||||
double maxHeight = options.length * 50;
|
||||
maxHeight = maxHeight > 200 ? 200 : maxHeight;
|
||||
@ -268,7 +274,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
||||
)))
|
||||
: 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(),
|
||||
))))
|
||||
);
|
||||
},
|
||||
|
@ -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()
|
||||
|
@ -1534,6 +1534,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();
|
||||
}
|
||||
|
@ -565,10 +565,12 @@ 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", "Změnit pohled"),
|
||||
("Big tiles", "Velké dlaždice"),
|
||||
("Small tiles", "Malé dlaždice"),
|
||||
("List", "Seznam"),
|
||||
("Virtual display", "Virtuální obrazovka"),
|
||||
("Plug out all", "Odpojit všechny"),
|
||||
].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,9 +566,11 @@ 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"),
|
||||
("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();
|
||||
}
|
||||
|
@ -566,9 +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", "Tampilan virtual"),
|
||||
("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", ""),
|
||||
("Big tiles", ""),
|
||||
("Small tiles", ""),
|
||||
("List", ""),
|
||||
("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,9 +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."),
|
||||
("Change view", ""),
|
||||
("Big tiles", ""),
|
||||
("Small tiles", ""),
|
||||
("List", ""),
|
||||
("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();
|
||||
}
|
||||
|
@ -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() {
|
||||
@ -1963,6 +1980,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;
|
||||
@ -2215,6 +2236,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 {
|
||||
@ -2223,7 +2263,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 _,
|
||||
@ -2919,7 +2959,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();
|
||||
if !manager.peer_index_name.is_empty() || manager.headless_index_name.is_some() {
|
||||
manager.install_update_driver()?;
|
||||
}
|
||||
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