Merge remote-tracking branch 'rustdesk/master' into flutter_desktop

# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	build.rs
#	flutter/.gitignore
#	flutter/lib/common.dart
#	flutter/lib/mobile/pages/remote_page.dart
#	flutter/lib/models/model.dart
#	flutter/lib/models/native_model.dart
#	flutter/lib/models/server_model.dart
#	flutter/pubspec.lock
#	flutter/pubspec.yaml
#	src/client.rs
#	src/client/file_trait.rs
#	src/flutter.rs
#	src/mobile_ffi.rs
#	src/ui.rs
This commit is contained in:
Kingtous
2022-06-27 11:18:53 +08:00
138 changed files with 5534 additions and 798 deletions

View File

@@ -10,4 +10,8 @@ fn main() {
enigo.key_down(Key::Layout('.')).ok();
enigo.key_up(Key::Layout('.'));
enigo.key_up(Key::Shift);
enigo.key_down(Key::Shift).ok();
enigo.key_down(Key::Layout('-')).ok();
enigo.key_up(Key::Layout('-'));
enigo.key_up(Key::Shift);
}

View File

@@ -63,6 +63,8 @@ extern crate objc;
mod win;
#[cfg(target_os = "windows")]
pub use win::Enigo;
#[cfg(target_os = "windows")]
pub use win::ENIGO_INPUT_EXTRA_VALUE;
#[cfg(target_os = "macos")]
mod macos;

View File

@@ -491,7 +491,11 @@ fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) {
if !std::path::Path::new(&py).exists() {
py = "/usr/lib/rustdesk/pynput_service.py".to_owned();
if !std::path::Path::new(&py).exists() {
log::error!("{} not exits", py);
// enigo libs, not rustdesk root project, so skip using appimage features
py = std::env::var("APPDIR").unwrap_or("".to_string()) + "/usr/lib/rustdesk/pynput_service.py";
if !std::path::Path::new(&py).exists() {
log::error!("{} not exists", py);
}
}
}
}

View File

@@ -338,7 +338,7 @@ impl KeyboardControllable for Enigo {
fn key_click(&mut self, key: Key) {
let keycode = self.key_to_keycode(key);
if keycode == 0 {
if keycode == u16::MAX {
return;
}
@@ -355,7 +355,7 @@ impl KeyboardControllable for Enigo {
fn key_down(&mut self, key: Key) -> crate::ResultType {
let code = self.key_to_keycode(key);
if code == 0 {
if code == u16::MAX {
return Err("".into());
}
if let Some(src) = self.event_source.as_ref() {
@@ -489,13 +489,18 @@ impl Enigo {
Key::Layout(c) => self.map_key_board(c),
Key::Super | Key::Command | Key::Windows | Key::Meta => kVK_Command,
_ => 0,
_ => u16::MAX,
}
}
#[inline]
fn map_key_board(&mut self, ch: char) -> CGKeyCode {
let mut code = 0;
// no idea why below char not working with shift, https://github.com/rustdesk/rustdesk/issues/406#issuecomment-1145157327
// seems related to numpad char
if ch == '-' || ch == '=' || ch == '.' || ch == '/' || (ch >= '0' && ch <= '9') {
return self.map_key_board_en(ch);
}
let mut code = u16::MAX;
unsafe {
let (keyboard, layout) = get_layout();
if !keyboard.is_null() && !layout.is_null() {
@@ -504,10 +509,10 @@ impl Enigo {
let name = get_string(name_ref as _);
if let Some(name) = name {
if let Some(m) = self.char_to_vkey_map.get(&name) {
code = *m.get(&ch).unwrap_or(&0);
code = *m.get(&ch).unwrap_or(&u16::MAX);
} else {
let m = get_map(&name, layout);
code = *m.get(&ch).unwrap_or(&0);
code = *m.get(&ch).unwrap_or(&u16::MAX);
self.char_to_vkey_map.insert(name.clone(), m);
}
}
@@ -517,9 +522,14 @@ impl Enigo {
CFRelease(keyboard);
}
}
if code > 0 {
if code != u16::MAX {
return code;
}
self.map_key_board_en(ch)
}
#[inline]
fn map_key_board_en(&mut self, ch: char) -> CGKeyCode {
match ch {
'a' => kVK_ANSI_A,
'b' => kVK_ANSI_B,
@@ -568,7 +578,7 @@ impl Enigo {
'.' => kVK_ANSI_Period,
'/' => kVK_ANSI_Slash,
'`' => kVK_ANSI_Grave,
_ => 0,
_ => u16::MAX,
}
}
}

View File

@@ -79,3 +79,4 @@ pub const EVK_MULTIPLY: u16 = 0x6A;
pub const EVK_SUBTRACT: u16 = 0x6D;
pub const EVK_DECIMAL: u16 = 0x6E;
pub const EVK_DIVIDE: u16 = 0x6F;
pub const EVK_PERIOD: u16 = 0xBE;

View File

@@ -2,3 +2,4 @@ mod win_impl;
pub mod keycodes;
pub use self::win_impl::Enigo;
pub use self::win_impl::ENIGO_INPUT_EXTRA_VALUE;

View File

@@ -1,7 +1,7 @@
use winapi;
use self::winapi::ctypes::c_int;
use self::winapi::shared::{minwindef::*, windef::*};
use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
use self::winapi::um::winbase::*;
use self::winapi::um::winuser::*;
@@ -18,6 +18,9 @@ extern "system" {
pub struct Enigo;
static mut LAYOUT: HKL = std::ptr::null_mut();
/// The dwExtraInfo value in keyboard and mouse structure that used in SendInput()
pub const ENIGO_INPUT_EXTRA_VALUE: ULONG_PTR = 100;
fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
let mut input = INPUT {
type_: INPUT_MOUSE,
@@ -28,7 +31,7 @@ fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
mouseData: data,
dwFlags: flags,
time: 0,
dwExtraInfo: 0,
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
})
},
};
@@ -56,7 +59,7 @@ fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
wScan: scan,
dwFlags: flags,
time: 0,
dwExtraInfo: 0,
dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
})
},
};
@@ -376,7 +379,7 @@ impl Enigo {
let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) };
if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' {
// a workaround of italian keyboard shift + '.' issue
unsafe { VkKeyScanW(chr as _) as _ }
EVK_PERIOD as _
} else {
keycode_and_shiftstate as _
}

View File

@@ -65,6 +65,10 @@ message LoginRequest {
message ChatMessage { string text = 1; }
message Features {
bool privacy_mode = 1;
}
message PeerInfo {
string username = 1;
string hostname = 2;
@@ -74,6 +78,7 @@ message PeerInfo {
bool sas_enabled = 6;
string version = 7;
int32 conn_id = 8;
Features features = 9;
}
message LoginResponse {
@@ -442,11 +447,6 @@ message OptionMessage {
BoolOption enable_file_transfer = 9;
}
message OptionResponse {
OptionMessage opt = 1;
string error = 2;
}
message TestDelay {
int64 time = 1;
bool from_client = 2;
@@ -469,6 +469,44 @@ message AudioFrame {
int64 timestamp = 2;
}
message BackNotification {
// no need to consider block input by someone else
enum BlockInputState {
StateUnknown = 1;
OnSucceeded = 2;
OnFailed = 3;
OffSucceeded = 4;
OffFailed = 5;
}
enum PrivacyModeState {
StateUnknown = 1;
// Privacy mode on by someone else
OnByOther = 2;
// Privacy mode is not supported on the remote side
NotSupported = 3;
// Privacy mode on by self
OnSucceeded = 4;
// Privacy mode on by self, but denied
OnFailedDenied = 5;
// Some plugins are not found
OnFailedPlugin = 6;
// Privacy mode on by self, but failed
OnFailed = 7;
// Privacy mode off by self
OffSucceeded = 8;
// Ctrl + P
OffByPeer = 9;
// Privacy mode off by self, but failed
OffFailed = 10;
OffUnknown = 11;
}
oneof union {
PrivacyModeState privacy_mode_state = 1;
BlockInputState block_input_state = 2;
}
}
message Misc {
oneof union {
ChatMessage chat_message = 4;
@@ -478,8 +516,8 @@ message Misc {
AudioFormat audio_format = 8;
string close_reason = 9;
bool refresh_video = 10;
OptionResponse option_response = 11;
bool video_received = 12;
BackNotification back_notification = 13;
}
}

View File

@@ -16,7 +16,7 @@ pub const RENDEZVOUS_TIMEOUT: u64 = 12_000;
pub const CONNECT_TIMEOUT: u64 = 18_000;
pub const REG_INTERVAL: i64 = 12_000;
pub const COMPRESS_LEVEL: i32 = 3;
const SERIAL: i32 = 1;
const SERIAL: i32 = 3;
// 128x128
#[cfg(target_os = "macos")] // 128x128 on 160x160 canvas, then shrink to 128, mac looks better with padding
pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAyVBMVEUAAAAAcf8Acf8Acf8Acv8Acf8Acf8Acf8Acf8AcP8Acf8Ab/8AcP8Acf////8AaP/z+f/o8v/k7v/5/v/T5f8AYP/u9v/X6f+hx/+Kuv95pP8Aef/B1/+TwP9xoP8BdP/g6P+Irv9ZmP8Bgf/E3f98q/9sn/+01f+Es/9nm/9Jif8hhv8off/M4P+syP+avP86iP/c7f+xy/9yqf9Om/9hk/9Rjv+60P99tv9fpf88lv8yjf8Tgf8deP+kvP8BiP8NeP8hkP80gP8oj2VLAAAADXRSTlMA7o7qLvnaxZ1FOxYPjH9HWgAABHJJREFUeNrtm+tW4jAQgBfwuu7MtIUWsOUiCCioIIgLiqvr+z/UHq/LJKVkmwTcc/r9E2nzlU4mSTP9lpGRkZGR8VX5cZjfL+yCEXYL+/nDH//U/Pd8DgyTy39Xbv7oIAcWyB0cqbW/sweW2NtRaj8H1sgpGOwUIAH7Bkd7YJW9dXFwAJY5WNP/cmCZQnJvzIN18on5LwfWySXlxEPYAIcad8D6PdiHDbCfIFCADVBIENiFDbCbIACKPPXrZ+cP8E6/0znvP4EymgIEravIRcTxu8HxNSJ60a8W0AYECKrlAN+YwAthCd9wm1Ug6wKzIn5SgRduXfwkqDasCjx0XFzi9PV6zwNcIuhcWBOg+ikySq8C9UD4dEKWBCoOcspvAuLHTo9sCDQiFPHotRM48j8G5gVur1FdAN2uaYEuiz7xFsgEJ2RUoMUakXuBTHHoGxQYOBhHjeUBAefEnMAowFhaLBOKuOemBBbxLRQrH2PBCgMvNCPQGMeevTb9zLrPxz2Mo+QbEaijzPUcOOHMQZkKGRAIPem39+bypREMPTkQW/oCfk866zAkiIFG4yIKRE/aAnfiSd0WrORY6pFdXQEqi9mvAQm0RIOSnoCcZ8vJoz3diCnjRk+g8VP4/fuQDJ2Lxr6WwG0gXs9aTpDzW0vgDBlVUpixR8gYk44AD8FrUKHr8JQJGgIDnoDqoALxmWPQSi9AVVzm8gKUuEPGr/QCvptwJkbSYT/TC4S8C96DGjTj86aHtAI0x2WaBIq0eSYYpRa4EsdWVVwWu9O0Aj6f6dyBMnwEraeOgSYu0wZlauzA47QCbT7DgAQSE+hZWoEBF/BBmWOewNMK3BsSqKUW4MGcWqCSVmDkbvkXGKQOwg6PAUO9oL3xXhA20yaiCjuwYygRVQlUOTWTCf2SuNJTxeFjgaHByGuAIvd8ItdPLTDhS7IuqEE1YSKVOgbayLhSFQhMzYh8hwfBs1r7c505YVIQYEdNoKwxK06MJiyrpUFHiF0NAfCQUVHoiRclIXJIR6C2fqG37pBHvcWpgwzvAtYwkR5UGV2e42UISdBJETl3mg8ouo54Rcnti1/vaT+iuUQBt500Cgo4U10BeHSkk57FB0JjWkKRMWgLUA0lLodtImAQdaMiiri3+gIAPZQoutHNsgKF1aaDMhMyIdBf8Th+Bh8MTjGWCpl5Wv43tDmnF+IUVMrcZgRoiAxhtrloYizNkZaAnF5leglbNhj0wYCAbCDvGb0mP4nib7O7ZlcYQ2m1gPtIZgVgGNNMeaVAaWR+57TrqgtUnm3sHQ+kYeE6fufUubG1ez50FXbPnWgBlgSABmN3TTcsRl2yWkHRrwbiunvk/W2+Mg1hPZplPDeXRbZzStFH15s1QIVd3UImP5z/bHpeeQLvRJ7XLFUffQIlCvqlXETQbgN9/rlYABGosv+Vi9m2Xs639YLGrZd0br+odetlvdsvbN56abfd4vbCzv9Q3v/ygoOV21A4OPpfXvH4Ai+5ZGRkZGRkbJA/t/I0QMzoMiEAAAAASUVORK5CYII=
@@ -271,7 +271,7 @@ impl Config {
fn file_(suffix: &str) -> PathBuf {
let name = format!("{}{}", *APP_NAME.read().unwrap(), suffix);
Self::path(name).with_extension("toml")
Config::with_extension(Self::path(name))
}
pub fn get_home() -> PathBuf {
@@ -687,6 +687,16 @@ impl Config {
lock.store();
true
}
fn with_extension(path: PathBuf) -> PathBuf {
let ext = path.extension();
if let Some(ext) = ext {
let ext = format!("{}.toml", ext.to_string_lossy());
path.with_extension(&ext)
} else {
path.with_extension("toml")
}
}
}
const PEERS: &str = "peers";
@@ -716,7 +726,7 @@ impl PeerConfig {
fn path(id: &str) -> PathBuf {
let path: PathBuf = [PEERS, id].iter().collect();
Config::path(path).with_extension("toml")
Config::with_extension(Config::path(path))
}
pub fn peers() -> Vec<(String, SystemTime, PeerConfig)> {

View File

@@ -195,7 +195,7 @@ pub fn is_file_exists(file_path: &str) -> bool {
#[inline]
pub fn can_enable_overwrite_detection(version: i64) -> bool {
version >= get_version_number("1.2.0")
version >= get_version_number("1.1.10")
}
#[derive(Default)]

View File

@@ -11,7 +11,10 @@ use tokio_socks::{IntoTargetAddr, TargetAddr};
fn to_socket_addr(host: &str) -> ResultType<SocketAddr> {
use std::net::ToSocketAddrs;
host.to_socket_addrs()?.next().context("Failed to solve")
host.to_socket_addrs()?
.filter(|x| x.is_ipv4())
.next()
.context("Failed to solve")
}
pub fn get_target_addr(host: &str) -> ResultType<TargetAddr<'static>> {
@@ -60,8 +63,9 @@ pub async fn connect_tcp<'t, T: IntoTargetAddr<'t>>(
.await
} else {
let addr = std::net::ToSocketAddrs::to_socket_addrs(&target_addr)?
.filter(|x| x.is_ipv4())
.next()
.context("Invalid target addr")?;
.context("Invalid target addr, no valid ipv4 address can be resolved.")?;
Ok(FramedStream::new(addr, local, ms_timeout).await?)
}
}

View File

@@ -47,7 +47,7 @@ impl FramedSocket {
#[allow(clippy::never_loop)]
pub async fn new_reuse<T: std::net::ToSocketAddrs>(addr: T) -> ResultType<Self> {
for addr in addr.to_socket_addrs()? {
for addr in addr.to_socket_addrs()?.filter(|x| x.is_ipv4()) {
let socket = new_socket(addr, true, 0)?.into_udp_socket();
return Ok(Self::Direct(UdpFramed::new(
UdpSocket::from_std(socket)?,
@@ -61,7 +61,7 @@ impl FramedSocket {
addr: T,
buf_size: usize,
) -> ResultType<Self> {
for addr in addr.to_socket_addrs()? {
for addr in addr.to_socket_addrs()?.filter(|x| x.is_ipv4()) {
return Ok(Self::Direct(UdpFramed::new(
UdpSocket::from_std(new_socket(addr, false, buf_size)?.into_udp_socket())?,
BytesCodec::new(),

View File

@@ -17,17 +17,19 @@ block = "0.1"
cfg-if = "1.0"
libc = "0.2"
num_cpus = "1.13"
lazy_static = "1.4"
[dependencies.winapi]
version = "0.3"
default-features = true
features = ["dxgi", "dxgi1_2", "dxgi1_5", "d3d11", "winuser"]
features = ["dxgi", "dxgi1_2", "dxgi1_5", "d3d11", "winuser", "winerror", "errhandlingapi", "libloaderapi"]
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.10"
jni = "0.19"
lazy_static = "1.4"
log = "0.4"
serde_json = "1.0"
[target.'cfg(not(target_os = "android"))'.dev-dependencies]
repng = "0.2"

View File

@@ -0,0 +1,105 @@
extern crate repng;
extern crate scrap;
use std::fs::File;
#[cfg(windows)]
use scrap::CapturerMag;
use scrap::{i420_to_rgb, Display};
fn main() {
let n = Display::all().unwrap().len();
for i in 0..n {
#[cfg(windows)]
record(i);
}
}
fn get_display(i: usize) -> Display {
Display::all().unwrap().remove(i)
}
#[cfg(windows)]
fn record(i: usize) {
for d in Display::all().unwrap() {
println!("{:?} {} {}", d.origin(), d.width(), d.height());
}
let display = get_display(i);
let (w, h) = (display.width(), display.height());
{
let mut capture_mag =
CapturerMag::new(display.origin(), display.width(), display.height(), false)
.expect("Couldn't begin capture.");
let wnd_cls = "";
let wnd_name = "RustDeskPrivacyWindow";
if false == capture_mag.exclude(wnd_cls, wnd_name).unwrap() {
println!("No window found for cls {} name {}", wnd_cls, wnd_name);
} else {
println!("Filter window for cls {} name {}", wnd_cls, wnd_name);
}
let frame = capture_mag.frame(0).unwrap();
println!("Capture data len: {}, Saving...", frame.len());
let mut bitflipped = Vec::with_capacity(w * h * 4);
let stride = frame.len() / h;
for y in 0..h {
for x in 0..w {
let i = stride * y + 4 * x;
bitflipped.extend_from_slice(&[frame[i + 2], frame[i + 1], frame[i], 255]);
}
}
// Save the image.
let name = format!("capture_mag_{}_1.png", i);
repng::encode(
File::create(name.clone()).unwrap(),
w as u32,
h as u32,
&bitflipped,
)
.unwrap();
println!("Image saved to `{}`.", name);
}
{
let mut capture_mag =
CapturerMag::new(display.origin(), display.width(), display.height(), true)
.expect("Couldn't begin capture.");
let wnd_cls = "";
let wnd_title = "RustDeskPrivacyWindow";
if false == capture_mag.exclude(wnd_cls, wnd_title).unwrap() {
println!("No window found for cls {} title {}", wnd_cls, wnd_title);
} else {
println!("Filter window for cls {} title {}", wnd_cls, wnd_title);
}
let buffer = capture_mag.frame(0).unwrap();
println!("Capture data len: {}, Saving...", buffer.len());
let mut frame = Default::default();
i420_to_rgb(w, h, &buffer, &mut frame);
let mut bitflipped = Vec::with_capacity(w * h * 4);
let stride = frame.len() / h;
for y in 0..h {
for x in 0..w {
let i = stride * y + 3 * x;
bitflipped.extend_from_slice(&[frame[i], frame[i + 1], frame[i + 2], 255]);
}
}
let name = format!("capture_mag_{}_2.png", i);
repng::encode(
File::create(name.clone()).unwrap(),
w as u32,
h as u32,
&bitflipped,
)
.unwrap();
println!("Image saved to `{}`.", name);
}
}

View File

@@ -46,8 +46,7 @@ fn record(i: usize) {
}
}
};
println!("Captured! Saving...");
println!("Captured data len: {}, Saving...", buffer.len());
// Flip the BGRA image into a RGBA image.
@@ -96,8 +95,7 @@ fn record(i: usize) {
}
}
};
println!("Captured! Saving...");
println!("Captured data len: {}, Saving...", buffer.len());
let mut frame = Default::default();
i420_to_rgb(w, h, &buffer, &mut frame);

View File

@@ -17,7 +17,6 @@ use std::time::{Duration, Instant};
lazy_static! {
static ref JVM: RwLock<Option<JavaVM>> = RwLock::new(None);
static ref MAIN_SERVICE_CTX: RwLock<Option<GlobalRef>> = RwLock::new(None); // MainService -> video service / audio service / info
static ref INPUT_CTX: RwLock<Option<GlobalRef>> = RwLock::new(None);
static ref VIDEO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("video", MAX_VIDEO_FRAME_TIMEOUT));
static ref AUDIO_RAW: Mutex<FrameRaw> = Mutex::new(FrameRaw::new("audio", MAX_AUDIO_FRAME_TIMEOUT));
}
@@ -148,25 +147,10 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init(
*MAIN_SERVICE_CTX.write().unwrap() = Some(context);
}
#[no_mangle]
pub extern "system" fn Java_com_carriez_flutter_1hbb_InputService_init(
env: JNIEnv,
_class: JClass,
ctx: JObject,
) {
log::debug!("InputService init from java");
let jvm = env.get_java_vm().unwrap();
*JVM.write().unwrap() = Some(jvm);
let context = env.new_global_ref(ctx).unwrap();
*INPUT_CTX.write().unwrap() = Some(context);
}
pub fn call_input_service_mouse_input(mask: i32, x: i32, y: i32) -> JniResult<()> {
pub fn call_main_service_mouse_input(mask: i32, x: i32, y: i32) -> JniResult<()> {
if let (Some(jvm), Some(ctx)) = (
JVM.read().unwrap().as_ref(),
INPUT_CTX.read().unwrap().as_ref(),
MAIN_SERVICE_CTX.read().unwrap().as_ref(),
) {
let env = jvm.attach_current_thread_as_daemon()?;
env.call_method(

View File

@@ -1,11 +1,13 @@
use crate::android::ffi::*;
use crate::rgba_to_i420;
use lazy_static::lazy_static;
use serde_json::Value;
use std::collections::HashMap;
use std::io;
use std::sync::Mutex;
lazy_static! {
static ref SCREEN_SIZE: Mutex<(u16, u16)> = Mutex::new((0, 0));
static ref SCREEN_SIZE: Mutex<(u16, u16, u16)> = Mutex::new((0, 0, 0)); // (width, height, scale)
}
pub struct Capturer {
@@ -65,9 +67,7 @@ impl Display {
pub fn primary() -> io::Result<Display> {
let mut size = SCREEN_SIZE.lock().unwrap();
if size.0 == 0 || size.1 == 0 {
let (w, h) = get_size().unwrap_or((0, 0));
size.0 = w;
size.1 = h;
*size = get_size().unwrap_or_default();
}
Ok(Display {
default: true,
@@ -111,19 +111,33 @@ impl Display {
pub fn refresh_size() {
let mut size = SCREEN_SIZE.lock().unwrap();
let (w, h) = get_size().unwrap_or((0, 0));
size.0 = w;
size.1 = h;
*size = get_size().unwrap_or_default();
}
// Big android screen size will be shrinked, to improve performance when screen-capturing and encoding
// e.g 2280x1080 size will be set to 1140x540, and `scale` is 2
// need to multiply by `4` (2*2) when compute the bitrate
pub fn fix_quality() -> u16 {
let scale = SCREEN_SIZE.lock().unwrap().2;
if scale <= 0 {
1
} else {
scale * scale
}
}
}
fn get_size() -> Option<(u16, u16)> {
fn get_size() -> Option<(u16, u16, u16)> {
let res = call_main_service_get_by_name("screen_size").ok()?;
if res.len() > 0 {
let mut sp = res.split(":");
let w = sp.next()?.parse::<u16>().ok()?;
let h = sp.next()?.parse::<u16>().ok()?;
return Some((w, h));
if let Ok(json) = serde_json::from_str::<HashMap<String, Value>>(&res) {
if let (Some(Value::Number(w)), Some(Value::Number(h)), Some(Value::Number(scale))) =
(json.get("width"), json.get("height"), json.get("scale"))
{
let w = w.as_i64()? as _;
let h = h.as_i64()? as _;
let scale = scale.as_i64()? as _;
return Some((w, h, scale));
}
}
None
}

View File

@@ -111,3 +111,32 @@ impl Display {
self.origin() == (0, 0)
}
}
pub struct CapturerMag {
inner: dxgi::mag::CapturerMag,
data: Vec<u8>,
}
impl CapturerMag {
pub fn is_supported() -> bool {
dxgi::mag::CapturerMag::is_supported()
}
pub fn new(origin: (i32, i32), width: usize, height: usize, use_yuv: bool) -> io::Result<Self> {
Ok(CapturerMag {
inner: dxgi::mag::CapturerMag::new(origin, width, height, use_yuv)?,
data: Vec::new(),
})
}
pub fn exclude(&mut self, cls: &str, name: &str) -> io::Result<bool> {
self.inner.exclude(cls, name)
}
// ((x, y), w, h)
pub fn get_rect(&self) -> ((i32, i32), usize, usize) {
self.inner.get_rect()
}
pub fn frame<'a>(&'a mut self, _timeout_ms: u32) -> io::Result<Frame<'a>> {
self.inner.frame(&mut self.data)?;
Ok(Frame(&self.data))
}
}

View File

@@ -19,7 +19,7 @@ cfg_if! {
} else if #[cfg(dxgi)] {
mod dxgi;
pub use self::dxgi::*;
} else if #[cfg(android)] {
} else if #[cfg(target_os = "android")] {
mod android;
pub use self::android::*;
}else {
@@ -36,13 +36,11 @@ mod vpx;
#[inline]
pub fn would_block_if_equal(old: &mut Vec<u128>, b: &[u8]) -> std::io::Result<()> {
let b = unsafe {
std::slice::from_raw_parts::<u128>(b.as_ptr() as _, b.len() / 16)
};
let b = unsafe { std::slice::from_raw_parts::<u128>(b.as_ptr() as _, b.len() / 16) };
if b == &old[..] {
return Err(std::io::ErrorKind::WouldBlock.into());
}
old.resize(b.len(), 0);
old.copy_from_slice(b);
Ok(())
}
}

662
libs/scrap/src/dxgi/mag.rs Normal file
View File

@@ -0,0 +1,662 @@
// logic from webrtc -- https://github.com/shiguredo/libwebrtc/blob/main/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
use lazy_static;
use std::{
ffi::CString,
io::{Error, ErrorKind, Result},
mem::size_of,
sync::Mutex,
};
use winapi::{
shared::{
basetsd::SIZE_T,
guiddef::{IsEqualGUID, GUID},
minwindef::{BOOL, DWORD, FALSE, FARPROC, HINSTANCE, HMODULE, HRGN, TRUE, UINT},
ntdef::{LONG, NULL},
windef::{HWND, RECT},
winerror::ERROR_CLASS_ALREADY_EXISTS,
},
um::{
errhandlingapi::GetLastError,
libloaderapi::{FreeLibrary, GetModuleHandleExA, GetProcAddress, LoadLibraryExA},
winuser::*,
},
};
pub const MW_FILTERMODE_EXCLUDE: u32 = 0;
pub const MW_FILTERMODE_INCLUDE: u32 = 1;
pub const GET_MODULE_HANDLE_EX_FLAG_PIN: u32 = 1;
pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2;
pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 4;
pub const LOAD_LIBRARY_AS_DATAFILE: u32 = 2;
pub const LOAD_WITH_ALTERED_SEARCH_PATH: u32 = 8;
pub const LOAD_IGNORE_CODE_AUTHZ_LEVEL: u32 = 16;
pub const LOAD_LIBRARY_AS_IMAGE_RESOURCE: u32 = 32;
pub const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: u32 = 64;
pub const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: u32 = 128;
pub const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: u32 = 256;
pub const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: u32 = 512;
pub const LOAD_LIBRARY_SEARCH_USER_DIRS: u32 = 1024;
pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 2048;
pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: u32 = 4096;
pub const LOAD_LIBRARY_SAFE_CURRENT_DIRS: u32 = 8192;
pub const LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER: u32 = 16384;
pub const LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY: u32 = 32768;
extern "C" {
pub static GUID_WICPixelFormat32bppRGBA: GUID;
}
lazy_static::lazy_static! {
static ref MAG_BUFFER: Mutex<(bool, Vec<u8>)> = Default::default();
}
pub type REFWICPixelFormatGUID = *const GUID;
pub type WICPixelFormatGUID = GUID;
#[allow(non_snake_case)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct tagMAGIMAGEHEADER {
pub width: UINT,
pub height: UINT,
pub format: WICPixelFormatGUID,
pub stride: UINT,
pub offset: UINT,
pub cbSize: SIZE_T,
}
pub type MAGIMAGEHEADER = tagMAGIMAGEHEADER;
pub type PMAGIMAGEHEADER = *mut tagMAGIMAGEHEADER;
// Function types
pub type MagImageScalingCallback = ::std::option::Option<
unsafe extern "C" fn(
hwnd: HWND,
srcdata: *mut ::std::os::raw::c_void,
srcheader: MAGIMAGEHEADER,
destdata: *mut ::std::os::raw::c_void,
destheader: MAGIMAGEHEADER,
unclipped: RECT,
clipped: RECT,
dirty: HRGN,
) -> BOOL,
>;
extern "C" {
pub fn MagShowSystemCursor(fShowCursor: BOOL) -> BOOL;
}
pub type MagInitializeFunc = ::std::option::Option<unsafe extern "C" fn() -> BOOL>;
pub type MagUninitializeFunc = ::std::option::Option<unsafe extern "C" fn() -> BOOL>;
pub type MagSetWindowSourceFunc =
::std::option::Option<unsafe extern "C" fn(hwnd: HWND, rect: RECT) -> BOOL>;
pub type MagSetWindowFilterListFunc = ::std::option::Option<
unsafe extern "C" fn(
hwnd: HWND,
dwFilterMode: DWORD,
count: ::std::os::raw::c_int,
pHWND: *mut HWND,
) -> BOOL,
>;
pub type MagSetImageScalingCallbackFunc = ::std::option::Option<
unsafe extern "C" fn(hwnd: HWND, callback: MagImageScalingCallback) -> BOOL,
>;
#[repr(C)]
#[derive(Debug, Clone)]
struct MagInterface {
init_succeeded: bool,
lib_handle: HINSTANCE,
pub mag_initialize_func: MagInitializeFunc,
pub mag_uninitialize_func: MagUninitializeFunc,
pub set_window_source_func: MagSetWindowSourceFunc,
pub set_window_filter_list_func: MagSetWindowFilterListFunc,
pub set_image_scaling_callback_func: MagSetImageScalingCallbackFunc,
}
// NOTE: MagInitialize and MagUninitialize should not be called in global init and uninit.
// If so, strange errors occur.
impl MagInterface {
fn new() -> Result<Self> {
let mut s = MagInterface {
init_succeeded: false,
lib_handle: NULL as _,
mag_initialize_func: None,
mag_uninitialize_func: None,
set_window_source_func: None,
set_window_filter_list_func: None,
set_image_scaling_callback_func: None,
};
s.init_succeeded = false;
unsafe {
if GetSystemMetrics(SM_CMONITORS) != 1 {
// Do not try to use the magnifier in multi-screen setup (where the API
// crashes sometimes).
return Err(Error::new(
ErrorKind::Other,
"Magnifier capturer cannot work on multi-screen system.",
));
}
// load lib
let lib_file_name = "Magnification.dll";
let lib_file_name_c = CString::new(lib_file_name).unwrap();
s.lib_handle = LoadLibraryExA(
lib_file_name_c.as_ptr() as _,
NULL,
LOAD_WITH_ALTERED_SEARCH_PATH,
);
if s.lib_handle.is_null() {
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed to LoadLibraryExA {}, error: {}",
lib_file_name,
GetLastError()
),
));
};
// load functions
s.mag_initialize_func = Some(std::mem::transmute(Self::load_func(
s.lib_handle,
"MagInitialize",
)?));
s.mag_uninitialize_func = Some(std::mem::transmute(Self::load_func(
s.lib_handle,
"MagUninitialize",
)?));
s.set_window_source_func = Some(std::mem::transmute(Self::load_func(
s.lib_handle,
"MagSetWindowSource",
)?));
s.set_window_filter_list_func = Some(std::mem::transmute(Self::load_func(
s.lib_handle,
"MagSetWindowFilterList",
)?));
s.set_image_scaling_callback_func = Some(std::mem::transmute(Self::load_func(
s.lib_handle,
"MagSetImageScalingCallback",
)?));
// MagInitialize
if let Some(init_func) = s.mag_initialize_func {
if FALSE == init_func() {
return Err(Error::new(
ErrorKind::Other,
format!("Failed to MagInitialize, error: {}", GetLastError()),
));
} else {
s.init_succeeded = true;
}
} else {
return Err(Error::new(
ErrorKind::Other,
"Unreachable, mag_initialize_func should not be none",
));
}
}
Ok(s)
}
unsafe fn load_func(lib_module: HMODULE, func_name: &str) -> Result<FARPROC> {
let func_name_c = CString::new(func_name).unwrap();
let func = GetProcAddress(lib_module, func_name_c.as_ptr() as _);
if func.is_null() {
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed to GetProcAddress {}, error: {}",
func_name,
GetLastError()
),
));
}
Ok(func)
}
pub(super) fn uninit(&mut self) {
if self.init_succeeded {
if let Some(uninit_func) = self.mag_uninitialize_func {
unsafe {
if FALSE == uninit_func() {
println!("Failed MagUninitialize {}", GetLastError())
}
}
}
if !self.lib_handle.is_null() {
unsafe {
if FALSE == FreeLibrary(self.lib_handle) {
println!("Failed FreeLibrary {}", GetLastError())
}
}
self.lib_handle = NULL as _;
}
}
self.init_succeeded = false;
}
}
impl Drop for MagInterface {
fn drop(&mut self) {
self.uninit();
}
}
pub struct CapturerMag {
mag_interface: MagInterface,
host_window: HWND,
magnifier_window: HWND,
magnifier_host_class: CString,
host_window_name: CString,
magnifier_window_class: CString,
magnifier_window_name: CString,
rect: RECT,
width: usize,
height: usize,
use_yuv: bool,
data: Vec<u8>,
}
impl Drop for CapturerMag {
fn drop(&mut self) {
self.destroy_windows();
self.mag_interface.uninit();
}
}
impl CapturerMag {
pub(crate) fn is_supported() -> bool {
MagInterface::new().is_ok()
}
pub(crate) fn new(
origin: (i32, i32),
width: usize,
height: usize,
use_yuv: bool,
) -> Result<Self> {
unsafe {
let x = GetSystemMetrics(SM_XVIRTUALSCREEN);
let y = GetSystemMetrics(SM_YVIRTUALSCREEN);
let w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
let h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if !(origin.0 == x as _ && origin.1 == y as _ && width == w as _ && height == h as _) {
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed Check screen rect ({}, {}, {} , {}) to ({}, {}, {}, {})",
origin.0,
origin.1,
origin.0 + width as i32,
origin.1 + height as i32,
x,
y,
x + w,
y + h
),
));
}
}
let mut s = Self {
mag_interface: MagInterface::new()?,
host_window: 0 as _,
magnifier_window: 0 as _,
magnifier_host_class: CString::new("ScreenCapturerWinMagnifierHost")?,
host_window_name: CString::new("MagnifierHost")?,
magnifier_window_class: CString::new("Magnifier")?,
magnifier_window_name: CString::new("MagnifierWindow")?,
rect: RECT {
left: origin.0 as _,
top: origin.1 as _,
right: origin.0 + width as LONG,
bottom: origin.1 + height as LONG,
},
width,
height,
use_yuv,
data: Vec::new(),
};
unsafe {
let mut instance = 0 as HMODULE;
if 0 == GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
DefWindowProcA as _,
&mut instance as _,
) {
return Err(Error::new(
ErrorKind::Other,
format!("Failed to GetModuleHandleExA, error: {}", GetLastError()),
));
}
// Register the host window class. See the MSDN documentation of the
// Magnification API for more infomation.
let wcex = WNDCLASSEXA {
cbSize: size_of::<WNDCLASSEXA>() as _,
style: 0,
lpfnWndProc: Some(DefWindowProcA),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: instance,
hIcon: 0 as _,
hCursor: LoadCursorA(NULL as _, IDC_ARROW as _),
hbrBackground: 0 as _,
lpszClassName: s.magnifier_host_class.as_ptr() as _,
lpszMenuName: 0 as _,
hIconSm: 0 as _,
};
// Ignore the error which may happen when the class is already registered.
if 0 == RegisterClassExA(&wcex) {
let code = GetLastError();
if code != ERROR_CLASS_ALREADY_EXISTS {
return Err(Error::new(
ErrorKind::Other,
format!("Failed to RegisterClassExA, error: {}", code),
));
}
}
// Create the host window.
s.host_window = CreateWindowExA(
WS_EX_LAYERED,
s.magnifier_host_class.as_ptr(),
s.host_window_name.as_ptr(),
WS_POPUP,
0,
0,
0,
0,
NULL as _,
NULL as _,
instance,
NULL,
);
if s.host_window.is_null() {
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed to CreateWindowExA host_window, error: {}",
GetLastError()
),
));
}
// Create the magnifier control.
s.magnifier_window = CreateWindowExA(
0,
s.magnifier_window_class.as_ptr(),
s.magnifier_window_name.as_ptr(),
WS_CHILD | WS_VISIBLE,
0,
0,
0,
0,
s.host_window,
NULL as _,
instance,
NULL,
);
if s.magnifier_window.is_null() {
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed CreateWindowA magnifier_window, error: {}",
GetLastError()
),
));
}
// Hide the host window.
let _ = ShowWindow(s.host_window, SW_HIDE);
// Set the scaling callback to receive captured image.
if let Some(set_callback_func) = s.mag_interface.set_image_scaling_callback_func {
if FALSE
== set_callback_func(
s.magnifier_window,
Some(Self::on_gag_image_scaling_callback),
)
{
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed to MagSetImageScalingCallback, error: {}",
GetLastError()
),
));
}
} else {
return Err(Error::new(
ErrorKind::Other,
"Unreachable, set_image_scaling_callback_func should not be none",
));
}
}
Ok(s)
}
pub(crate) fn exclude(&mut self, cls: &str, name: &str) -> Result<bool> {
let name_c = CString::new(name).unwrap();
unsafe {
let mut hwnd = if cls.len() == 0 {
FindWindowExA(NULL as _, NULL as _, NULL as _, name_c.as_ptr())
} else {
let cls_c = CString::new(cls).unwrap();
FindWindowExA(NULL as _, NULL as _, cls_c.as_ptr(), name_c.as_ptr())
};
if hwnd.is_null() {
return Ok(false);
}
if let Some(set_window_filter_list_func) =
self.mag_interface.set_window_filter_list_func
{
if FALSE
== set_window_filter_list_func(
self.magnifier_window,
MW_FILTERMODE_EXCLUDE,
1,
&mut hwnd,
)
{
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed MagSetWindowFilterList for cls {} name {}, err: {}",
cls,
name,
GetLastError()
),
));
}
} else {
return Err(Error::new(
ErrorKind::Other,
"Unreachable, MagSetWindowFilterList should not be none",
));
}
}
Ok(true)
}
pub(crate) fn get_rect(&self) -> ((i32, i32), usize, usize) {
(
(self.rect.left as _, self.rect.top as _),
self.width as _,
self.height as _,
)
}
fn clear_data() {
let mut lock = MAG_BUFFER.lock().unwrap();
lock.0 = false;
lock.1.clear();
}
pub(crate) fn frame(&mut self, data: &mut Vec<u8>) -> Result<()> {
Self::clear_data();
unsafe {
let x = GetSystemMetrics(SM_XVIRTUALSCREEN);
let y = GetSystemMetrics(SM_YVIRTUALSCREEN);
let w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
let h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
if !(self.rect.left == x as _
&& self.rect.top == y as _
&& self.rect.right == (x + w) as _
&& self.rect.bottom == (y + h) as _)
{
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed Check screen rect ({}, {}, {} , {}) to ({}, {}, {}, {})",
self.rect.left,
self.rect.top,
self.rect.right,
self.rect.bottom,
x,
y,
x + w,
y + h
),
));
}
if FALSE
== SetWindowPos(
self.magnifier_window,
HWND_TOP,
self.rect.left,
self.rect.top,
self.rect.right,
self.rect.bottom,
0,
)
{
return Err(Error::new(
ErrorKind::Other,
format!(
"Failed SetWindowPos (x, y, w , h) - ({}, {}, {}, {}), error {}",
self.rect.left,
self.rect.top,
self.rect.right,
self.rect.bottom,
GetLastError()
),
));
}
// on_gag_image_scaling_callback will be called and fill in the
// frame before set_window_source_func_ returns.
if let Some(set_window_source_func) = self.mag_interface.set_window_source_func {
if FALSE == set_window_source_func(self.magnifier_window, self.rect) {
return Err(Error::new(
ErrorKind::Other,
format!("Failed to MagSetWindowSource, error: {}", GetLastError()),
));
}
} else {
return Err(Error::new(
ErrorKind::Other,
"Unreachable, set_window_source_func should not be none",
));
}
}
let mut lock = MAG_BUFFER.lock().unwrap();
if !lock.0 {
return Err(Error::new(
ErrorKind::Other,
"No data captured by magnifier",
));
}
if self.use_yuv {
self.data.resize(lock.1.len(), 0);
unsafe {
std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut self.data[0], self.data.len());
}
crate::common::bgra_to_i420(
self.width as usize,
self.height as usize,
&self.data,
data,
);
} else {
data.resize(lock.1.len(), 0);
unsafe {
std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut data[0], data.len());
}
}
Ok(())
}
fn destroy_windows(&mut self) {
if !self.magnifier_window.is_null() {
unsafe {
if FALSE == DestroyWindow(self.magnifier_window) {
//
println!("Failed DestroyWindow magnifier window {}", GetLastError())
}
}
}
self.magnifier_window = NULL as _;
if !self.host_window.is_null() {
unsafe {
if FALSE == DestroyWindow(self.host_window) {
//
println!("Failed DestroyWindow host window {}", GetLastError())
}
}
}
self.host_window = NULL as _;
}
unsafe extern "C" fn on_gag_image_scaling_callback(
_hwnd: HWND,
srcdata: *mut ::std::os::raw::c_void,
srcheader: MAGIMAGEHEADER,
_destdata: *mut ::std::os::raw::c_void,
_destheader: MAGIMAGEHEADER,
_unclipped: RECT,
_clipped: RECT,
_dirty: HRGN,
) -> BOOL {
Self::clear_data();
if !IsEqualGUID(&srcheader.format, &GUID_WICPixelFormat32bppRGBA) {
// log warning?
return FALSE;
}
let mut lock = MAG_BUFFER.lock().unwrap();
lock.1.resize(srcheader.cbSize, 0);
std::ptr::copy_nonoverlapping(srcdata as _, &mut lock.1[0], srcheader.cbSize);
lock.0 = true;
TRUE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let mut capture_mag = CapturerMag::new((0, 0), 1920, 1080, false).unwrap();
capture_mag.exclude("", "RustDeskPrivacyWindow").unwrap();
std::thread::sleep(std::time::Duration::from_millis(1000 * 10));
let mut data = Vec::new();
capture_mag.frame(&mut data).unwrap();
println!("capture data len: {}", data.len());
}
}

View File

@@ -1,6 +1,7 @@
use std::{io, mem, ptr, slice};
pub mod gdi;
pub use gdi::CapturerGDI;
pub mod mag;
use winapi::{
shared::{

View File

@@ -14,13 +14,13 @@ pub mod quartz;
#[cfg(x11)]
pub mod x11;
#[cfg(all(x11, feature="wayland"))]
#[cfg(all(x11, feature = "wayland"))]
pub mod wayland;
#[cfg(dxgi)]
pub mod dxgi;
#[cfg(android)]
#[cfg(target_os = "android")]
pub mod android;
mod common;

13
libs/simple_rc/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "simple_rc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde_derive = "1.0"
serde = "1.0"
walkdir = "2"
confy = { git = "https://github.com/open-trade/confy" }
hbb_common = { path = "../hbb_common" }

View File

@@ -0,0 +1,23 @@
extern crate simple_rc;
use simple_rc::*;
fn main() {
{
const CONF_FILE: &str = "simple_rc.toml";
generate(CONF_FILE).unwrap();
}
{
generate_with_conf(&Config {
outfile: "src/rc.rs".to_owned(),
confs: vec![ConfigItem {
inc: "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx".to_owned(),
// exc: vec!["*.dll".to_owned(), "*.exe".to_owned()],
exc: vec![],
suppressed_front: "D:/projects/windows".to_owned(),
}],
})
.unwrap();
}
}

View File

@@ -0,0 +1,12 @@
# The output source file
outfile = "src/rc.rs"
# The resource config list.
[[confs]]
# The file or director to integrate.
inc = "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx"
# The exclusions.
exc = ["*.dll", "*.exe"]
# The front path that will ignore for extracting.
# The following config will make base output path to be "RustDeskTempTopMostWindow/x64/Release/xxx".
suppressed_front = "D:/projects/windows"

208
libs/simple_rc/src/lib.rs Normal file
View File

@@ -0,0 +1,208 @@
use hbb_common::{bail, ResultType};
use serde_derive::{Deserialize, Serialize};
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path};
use walkdir::WalkDir;
//mod rc;
#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
pub struct ConfigItem {
// include directory or file
pub inc: String,
// exclude files
pub exc: Vec<String>,
// out_path = origin_path - suppressed_front
pub suppressed_front: String,
}
#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
pub struct Config {
// output source file
pub outfile: String,
// config items
pub confs: Vec<ConfigItem>,
}
pub fn get_outin_files<'a>(item: &'a ConfigItem) -> ResultType<HashMap<String, String>> {
let mut outin_filemap = HashMap::new();
for entry in WalkDir::new(&item.inc).follow_links(true) {
let path = entry?.into_path();
if path.is_file() {
let mut exclude = false;
for excfile in item.exc.iter() {
if excfile.starts_with("*.") {
if let Some(ext) = path.extension().and_then(|x| x.to_str()) {
if excfile.ends_with(&format!(".{}", ext)) {
exclude = true;
break;
}
}
} else {
if path.ends_with(Path::new(excfile)) {
exclude = true;
break;
}
}
}
if exclude {
continue;
}
let mut suppressed_front = item.suppressed_front.clone();
if !suppressed_front.is_empty() && suppressed_front.ends_with('/') {
suppressed_front.push('/');
}
let outpath = path.strip_prefix(Path::new(&suppressed_front))?;
let outfile = if outpath.is_absolute() {
match outpath
.file_name()
.and_then(|f| f.to_str())
.map(|f| f.to_string())
{
None => {
bail!("Failed to get filename of {}", outpath.display());
}
Some(s) => s,
}
} else {
match outpath.to_str() {
None => {
bail!("Failed to convert {} to string", outpath.display());
}
// Simple replace \ to / here.
// A better way is to use lib [path-slash](https://github.com/rhysd/path-slash)
Some(s) => s.to_string().replace("\\", "/"),
}
};
let infile = match path.canonicalize()?.to_str() {
None => {
bail!("Failed to get file path of {}", path.display());
}
Some(s) => s.to_string(),
};
if let Some(_) = outin_filemap.insert(outfile.clone(), infile) {
bail!("outfile {} is set before", outfile);
}
}
}
Ok(outin_filemap)
}
pub fn generate(conf_file: &str) -> ResultType<()> {
let conf = confy::load_path(conf_file)?;
generate_with_conf(&conf)?;
Ok(())
}
pub fn generate_with_conf<'a>(conf: &'a Config) -> ResultType<()> {
let mut outfile = File::create(&conf.outfile)?;
outfile.write(
br##"use hbb_common::{bail, ResultType};
use std::{
fs::{self, File},
io::prelude::*,
path::Path,
};
"##,
)?;
outfile.write(b"#[allow(dead_code)]\n")?;
outfile.write(b"pub fn extract_resources(root_path: &str) -> ResultType<()> {\n")?;
outfile.write(b" let mut resources: Vec<(&str, &[u8])> = Vec::new();\n")?;
let mut outin_files = HashMap::new();
for item in conf.confs.iter() {
for (o, i) in get_outin_files(item)?.into_iter() {
if let Some(_) = outin_files.insert(o.clone(), i) {
bail!("outfile {} is set before", o);
}
}
}
let mut count = 1;
for (o, i) in outin_files.iter() {
let mut infile = File::open(&i)?;
let mut buffer = Vec::<u8>::new();
infile.read_to_end(&mut buffer)?;
let var_outfile = format!("outfile_{}", count);
let var_outdata = format!("outdata_{}", count);
write!(outfile, " let {} = \"{}\";\n", var_outfile, o)?;
write!(outfile, " let {}: &[u8] = &[\n ", var_outdata)?;
let mut line_num = 20;
for v in buffer {
if line_num == 0 {
write!(outfile, "\n ")?;
line_num = 20;
}
write!(outfile, "{:#04x}, ", v)?;
line_num -= 1;
}
write!(outfile, "\n ];\n")?;
write!(
outfile,
" resources.push(({}, &{}));\n",
var_outfile, var_outdata
)?;
count += 1;
}
outfile.write(b" do_extract(root_path, resources)?;\n")?;
outfile.write(b" Ok(())\n")?;
outfile.write(b"}\n")?;
outfile.write(
br##"
#[allow(dead_code)]
fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> {
let mut root_path = root_path.replace("\\", "/");
if !root_path.ends_with('/') {
root_path.push('/');
}
let root_path = Path::new(&root_path);
for (outfile, data) in resources {
let outfile_path = root_path.join(outfile);
match outfile_path.parent().and_then(|p| p.to_str()) {
None => {
bail!("Failed to get parent of {}", outfile_path.display());
}
Some(p) => {
fs::create_dir_all(p)?;
let mut of = File::create(outfile_path)?;
of.write_all(data)?;
of.flush()?;
}
}
}
Ok(())
}
"##,
)?;
outfile.flush()?;
Ok(())
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
// #[test]
// fn test_extract() {
// use super::*;
// rc::extract_resources("D:").unwrap();
// }
}

View File

@@ -1,12 +1,14 @@
#[cfg(windows)]
use virtual_display::win10::{idd, DRIVER_INSTALL_PATH};
#[cfg(windows)]
use std::{
ffi::{CStr, CString},
ffi::CStr,
io::{self, Read},
path::Path,
};
#[cfg(windows)]
fn prompt_input() -> u8 {
println!("Press key execute:");
println!(" 1. 'x' 1. exit");
@@ -24,6 +26,7 @@ fn prompt_input() -> u8 {
.unwrap_or(0)
}
#[cfg(windows)]
unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) {
println!("Plug in monitor begin");
if idd::FALSE == idd::MonitorPlugIn(index, edid, 25) {
@@ -48,6 +51,7 @@ unsafe fn plug_in(index: idd::UINT, edid: idd::UINT) {
}
}
#[cfg(windows)]
unsafe fn plug_out(index: idd::UINT) {
println!("Plug out monitor begin");
if idd::FALSE == idd::MonitorPlugOut(index) {
@@ -58,81 +62,91 @@ unsafe fn plug_out(index: idd::UINT) {
}
fn main() {
let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap();
let full_inf_path = abs_path.to_str().unwrap();
#[cfg(windows)]
{
let abs_path = Path::new(DRIVER_INSTALL_PATH).canonicalize().unwrap();
unsafe {
let invalid_device = 0 as idd::HSWDEVICE;
let mut h_sw_device = invalid_device;
let full_inf_path = CString::new(full_inf_path).unwrap().into_raw();
loop {
match prompt_input() as char {
'x' => break,
'i' => {
println!("Install or update driver begin");
let mut reboot_required = idd::FALSE;
if idd::InstallUpdate(full_inf_path, &mut reboot_required) == idd::FALSE {
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
} else {
println!(
"Install or update driver done, reboot is {} required",
if reboot_required == idd::FALSE {
"not"
} else {
""
}
);
unsafe {
let invalid_device = 0 as idd::HSWDEVICE;
let mut h_sw_device = invalid_device;
let full_inf_path: Vec<u16> = abs_path
.to_string_lossy()
.as_ref()
.encode_utf16()
.chain(Some(0).into_iter())
.collect();
loop {
match prompt_input() as char {
'x' => break,
'i' => {
println!("Install or update driver begin, {}", abs_path.display());
let mut reboot_required = idd::FALSE;
if idd::InstallUpdate(full_inf_path.as_ptr() as _, &mut reboot_required)
== idd::FALSE
{
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
} else {
println!(
"Install or update driver done, reboot is {} required",
if reboot_required == idd::FALSE {
"not"
} else {
""
}
);
}
}
}
'u' => {
println!("Uninstall driver begin");
let mut reboot_required = idd::FALSE;
if idd::Uninstall(full_inf_path, &mut reboot_required) == idd::FALSE {
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
} else {
println!(
"Uninstall driver done, reboot is {} required",
if reboot_required == idd::FALSE {
"not"
} else {
""
}
);
'u' => {
println!("Uninstall driver begin {}", abs_path.display());
let mut reboot_required = idd::FALSE;
if idd::Uninstall(full_inf_path.as_ptr() as _, &mut reboot_required)
== idd::FALSE
{
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
} else {
println!(
"Uninstall driver done, reboot is {} required",
if reboot_required == idd::FALSE {
"not"
} else {
""
}
);
}
}
}
'c' => {
println!("Create device begin");
if h_sw_device != invalid_device {
println!("Device created before");
continue;
'c' => {
println!("Create device begin");
if h_sw_device != invalid_device {
println!("Device created before");
continue;
}
if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) {
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
idd::DeviceClose(h_sw_device);
h_sw_device = invalid_device;
} else {
println!("Create device done");
}
}
if idd::FALSE == idd::DeviceCreate(&mut h_sw_device) {
println!("{}", CStr::from_ptr(idd::GetLastMsg()).to_str().unwrap());
'd' => {
println!("Close device begin");
idd::DeviceClose(h_sw_device);
h_sw_device = invalid_device;
} else {
println!("Create device done");
println!("Close device done");
}
'1' => plug_in(0, 0),
'2' => plug_in(1, 0),
'3' => plug_in(2, 0),
'4' => plug_out(0),
'5' => plug_out(1),
'6' => plug_out(2),
_ => {}
}
'd' => {
println!("Close device begin");
idd::DeviceClose(h_sw_device);
h_sw_device = invalid_device;
println!("Close device done");
}
'1' => plug_in(0, 0),
'2' => plug_in(1, 0),
'3' => plug_in(2, 0),
'4' => plug_out(0),
'5' => plug_out(1),
'6' => plug_out(2),
_ => {}
}
}
if !full_inf_path.is_null() {
let _ = CString::from_raw(full_inf_path);
}
idd::DeviceClose(h_sw_device);
idd::DeviceClose(h_sw_device);
}
}
}

View File

@@ -2,7 +2,7 @@
pub mod win10;
use hbb_common::{bail, lazy_static, ResultType};
use std::{ffi::CString, path::Path, sync::Mutex};
use std::{path::Path, sync::Mutex};
lazy_static::lazy_static! {
// If device is uninstalled though "Device Manager" Window.
@@ -33,16 +33,24 @@ pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
bail!("{} not exists", install_path)
}
let _full_install_path = match abs_path.to_str() {
Some(p) => CString::new(p)?.into_raw(),
None => bail!("{} not exists", install_path),
};
#[cfg(windows)]
unsafe {
{
// Device must be created before install driver.
// https://github.com/fufesou/RustDeskIddDriver/issues/1
if let Err(e) = create_device() {
bail!("{}", e);
}
let full_install_path: Vec<u16> = abs_path
.to_string_lossy()
.as_ref()
.encode_utf16()
.chain(Some(0).into_iter())
.collect();
let mut reboot_required_tmp = win10::idd::FALSE;
if win10::idd::InstallUpdate(_full_install_path, &mut reboot_required_tmp)
if win10::idd::InstallUpdate(full_install_path.as_ptr() as _, &mut reboot_required_tmp)
== win10::idd::FALSE
{
bail!("{}", win10::get_last_msg()?);
@@ -65,16 +73,18 @@ pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> {
bail!("{} not exists", install_path)
}
let _full_install_path = match abs_path.to_str() {
Some(p) => CString::new(p)?.into_raw(),
None => bail!("{} not exists", install_path),
};
#[cfg(windows)]
unsafe {
{
let full_install_path: Vec<u16> = abs_path
.to_string_lossy()
.as_ref()
.encode_utf16()
.chain(Some(0).into_iter())
.collect();
let mut reboot_required_tmp = win10::idd::FALSE;
if win10::idd::Uninstall(_full_install_path, &mut reboot_required_tmp)
if win10::idd::Uninstall(full_install_path.as_ptr() as _, &mut reboot_required_tmp)
== win10::idd::FALSE
{
bail!("{}", win10::get_last_msg()?);

View File

@@ -64,14 +64,14 @@ const char* GetLastMsg()
return g_lastMsg;
}
BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired)
BOOL InstallUpdate(LPCWSTR fullInfPath, PBOOL rebootRequired)
{
SetLastMsg("Sucess");
// UpdateDriverForPlugAndPlayDevices may return FALSE while driver was successfully installed...
if (FALSE == UpdateDriverForPlugAndPlayDevices(
// UpdateDriverForPlugAndPlayDevicesW may return FALSE while driver was successfully installed...
if (FALSE == UpdateDriverForPlugAndPlayDevicesW(
NULL,
_T("RustDeskIddDriver"), // match hardware id in the inf file
L"RustDeskIddDriver", // match hardware id in the inf file
fullInfPath,
INSTALLFLAG_FORCE
// | INSTALLFLAG_NONINTERACTIVE // INSTALLFLAG_NONINTERACTIVE may cause error 0xe0000247
@@ -82,7 +82,7 @@ BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired)
DWORD error = GetLastError();
if (error != 0)
{
SetLastMsg("UpdateDriverForPlugAndPlayDevices failed, last error 0x%x\n", error);
SetLastMsg("UpdateDriverForPlugAndPlayDevicesW failed, last error 0x%x\n", error);
if (g_printMsg)
{
printf(g_lastMsg);
@@ -94,11 +94,11 @@ BOOL InstallUpdate(LPCTSTR fullInfPath, PBOOL rebootRequired)
return TRUE;
}
BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired)
BOOL Uninstall(LPCWSTR fullInfPath, PBOOL rebootRequired)
{
SetLastMsg("Sucess");
if (FALSE == DiUninstallDriver(
if (FALSE == DiUninstallDriverW(
NULL,
fullInfPath,
0,
@@ -108,7 +108,7 @@ BOOL Uninstall(LPCTSTR fullInfPath, PBOOL rebootRequired)
DWORD error = GetLastError();
if (error != 0)
{
SetLastMsg("DiUninstallDriver failed, last error 0x%x\n", error);
SetLastMsg("DiUninstallDriverW failed, last error 0x%x\n", error);
if (g_printMsg)
{
printf(g_lastMsg);