100% open source

This commit is contained in:
rustdesk
2022-05-12 17:35:25 +08:00
parent 9098619162
commit c1bad84a86
58 changed files with 8397 additions and 3292 deletions

1
libs/enigo/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=auto

View File

@@ -1,7 +1,7 @@
[package]
name = "hbb_common"
version = "0.1.0"
authors = ["rustdesk<info@rustdesk.com>"]
authors = ["open-trade <info@opentradesolutions.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -35,7 +35,7 @@ tokio-socks = { git = "https://github.com/open-trade/tokio-socks" }
mac_address = "1.1"
[features]
quic = ["quinn"]
quic = []
[build-dependencies]
protobuf-codegen-pure = "3.0.0-alpha.2"

View File

@@ -72,6 +72,7 @@ message PeerInfo {
int32 current_display = 5;
bool sas_enabled = 6;
string version = 7;
int32 conn_id = 8;
}
message LoginResponse {

View File

@@ -18,7 +18,9 @@ message RegisterPeerResponse { bool request_pk = 2; }
message PunchHoleRequest {
string id = 1;
NatType nat_type = 2;
string licence_key = 3;
ConnType conn_type = 4;
string token = 5;
}
message PunchHole {
@@ -55,6 +57,7 @@ message RegisterPk {
string id = 1;
bytes uuid = 2;
bytes pk = 3;
string old_id = 4;
}
message RegisterPkResponse {
@@ -99,7 +102,9 @@ message RequestRelay {
bytes socket_addr = 3;
string relay_server = 4;
bool secure = 5;
string licence_key = 6;
ConnType conn_type = 7;
string token = 8;
}
message RelayResponse {

View File

@@ -12,7 +12,6 @@ use std::{
time::SystemTime,
};
pub const APP_NAME: &str = "RustDesk";
pub const RENDEZVOUS_TIMEOUT: u64 = 12_000;
pub const CONNECT_TIMEOUT: u64 = 18_000;
pub const REG_INTERVAL: i64 = 12_000;
@@ -26,7 +25,9 @@ pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACA
pub const ICON: &str = "data:image/png;base64,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
";
#[cfg(target_os = "macos")]
pub const ORG: &str = "com.carriez";
lazy_static::lazy_static! {
pub static ref ORG: Arc<RwLock<String>> = Arc::new(RwLock::new("com.carriez".to_owned()));
}
type Size = (i32, i32, i32, i32);
@@ -35,10 +36,13 @@ lazy_static::lazy_static! {
static ref CONFIG2: Arc<RwLock<Config2>> = Arc::new(RwLock::new(Config2::load()));
static ref LOCAL_CONFIG: Arc<RwLock<LocalConfig>> = Arc::new(RwLock::new(LocalConfig::load()));
pub static ref ONLINE: Arc<Mutex<HashMap<String, i64>>> = Default::default();
pub static ref PROD_RENDEZVOUS_SERVER: Arc<RwLock<String>> = Default::default();
pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned()));
}
#[cfg(any(target_os = "android", target_os = "ios"))]
lazy_static::lazy_static! {
pub static ref APP_DIR: Arc<RwLock<String>> = Default::default();
pub static ref APP_HOME_DIR: Arc<RwLock<String>> = Default::default();
}
const CHARS: &'static [char] = &[
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
@@ -256,13 +260,13 @@ impl Config {
}
fn file_(suffix: &str) -> PathBuf {
let name = format!("{}{}", APP_NAME, suffix);
let name = format!("{}{}", *APP_NAME.read().unwrap(), suffix);
Self::path(name).with_extension("toml")
}
pub fn get_home() -> PathBuf {
#[cfg(any(target_os = "android", target_os = "ios"))]
return Self::path("");
return Self::path(APP_HOME_DIR.read().unwrap().as_str());
if let Some(path) = dirs_next::home_dir() {
patch(path)
} else if let Ok(path) = std::env::current_dir() {
@@ -282,9 +286,9 @@ impl Config {
#[cfg(not(target_os = "macos"))]
let org = "";
#[cfg(target_os = "macos")]
let org = ORG;
let org = ORG.read().unwrap().clone();
// /var/root for root
if let Some(project) = ProjectDirs::from("", org, APP_NAME) {
if let Some(project) = ProjectDirs::from("", &org, &*APP_NAME.read().unwrap()) {
let mut path = patch(project.config_dir().to_path_buf());
path.push(p);
return path;
@@ -297,14 +301,14 @@ impl Config {
#[cfg(target_os = "macos")]
{
if let Some(path) = dirs_next::home_dir().as_mut() {
path.push(format!("Library/Logs/{}", APP_NAME));
path.push(format!("Library/Logs/{}", *APP_NAME.read().unwrap()));
return path.clone();
}
}
#[cfg(target_os = "linux")]
{
let mut path = Self::get_home();
path.push(format!(".local/share/logs/{}", APP_NAME));
path.push(format!(".local/share/logs/{}", *APP_NAME.read().unwrap()));
std::fs::create_dir_all(&path).ok();
return path;
}
@@ -322,12 +326,20 @@ impl Config {
// \\ServerName\pipe\PipeName
// where ServerName is either the name of a remote computer or a period, to specify the local computer.
// https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names
format!("\\\\.\\pipe\\{}\\query{}", APP_NAME, postfix)
format!(
"\\\\.\\pipe\\{}\\query{}",
*APP_NAME.read().unwrap(),
postfix
)
}
#[cfg(not(windows))]
{
use std::os::unix::fs::PermissionsExt;
let mut path: PathBuf = format!("/tmp/{}", APP_NAME).into();
#[cfg(target_os = "android")]
let mut path: PathBuf =
format!("{}/{}", *APP_DIR.read().unwrap(), *APP_NAME.read().unwrap()).into();
#[cfg(not(target_os = "android"))]
let mut path: PathBuf = format!("/tmp/{}", *APP_NAME.read().unwrap()).into();
fs::create_dir(&path).ok();
fs::set_permissions(&path, fs::Permissions::from_mode(0o0777)).ok();
path.push(format!("ipc{}", postfix));
@@ -351,7 +363,10 @@ impl Config {
pub fn get_rendezvous_server() -> String {
let mut rendezvous_server = Self::get_option("custom-rendezvous-server");
if rendezvous_server.is_empty() {
rendezvous_server = CONFIG2.write().unwrap().rendezvous_server.clone();
rendezvous_server = PROD_RENDEZVOUS_SERVER.read().unwrap().clone();
}
if rendezvous_server.is_empty() {
rendezvous_server = CONFIG2.read().unwrap().rendezvous_server.clone();
}
if rendezvous_server.is_empty() {
rendezvous_server = Self::get_rendezvous_servers()
@@ -370,6 +385,10 @@ impl Config {
if !s.is_empty() {
return vec![s];
}
let s = PROD_RENDEZVOUS_SERVER.read().unwrap().clone();
if !s.is_empty() {
return vec![s];
}
let serial_obsolute = CONFIG2.read().unwrap().serial > SERIAL;
if serial_obsolute {
let ss: Vec<String> = Self::get_option("rendezvous-servers")
@@ -446,7 +465,13 @@ impl Config {
fn get_auto_id() -> Option<String> {
#[cfg(any(target_os = "android", target_os = "ios"))]
return None;
{
return Some(
rand::thread_rng()
.gen_range(1_000_000_000..2_000_000_000)
.to_string(),
);
}
let mut id = 0u32;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if let Ok(Some(ma)) = mac_address::get_mac_address() {
@@ -531,6 +556,15 @@ impl Config {
id
}
pub fn get_id_or(b: String) -> String {
let a = CONFIG.read().unwrap().id.clone();
if a.is_empty() {
b
} else {
a
}
}
pub fn get_options() -> HashMap<String, String> {
CONFIG2.read().unwrap().options.clone()
}

View File

@@ -430,10 +430,11 @@ pub fn new_error<T: std::string::ToString>(id: i32, err: T, file_num: i32) -> Me
}
#[inline]
pub fn new_dir(id: i32, files: Vec<FileEntry>) -> Message {
pub fn new_dir(id: i32, path: String, files: Vec<FileEntry>) -> Message {
let mut resp = FileResponse::new();
resp.set_dir(FileDirectory {
id,
path,
entries: files.into(),
..Default::default()
});

View File

@@ -27,7 +27,7 @@ pub use anyhow::{self, bail};
pub use futures_util;
pub mod config;
pub mod fs;
pub use lazy_static;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub use mac_address;
pub use rand;
pub use regex;
@@ -35,6 +35,7 @@ pub use sodiumoxide;
pub use tokio_socks;
pub use tokio_socks::IntoTargetAddr;
pub use tokio_socks::TargetAddr;
pub use lazy_static;
#[cfg(feature = "quic")]
pub type Stream = quic::Connection;
@@ -179,6 +180,12 @@ where
Ok(io::BufReader::new(file).lines())
}
pub fn is_valid_custom_id(id: &str) -> bool {
regex::Regex::new(r"^[a-zA-Z]\w{5,15}$")
.unwrap()
.is_match(id)
}
pub fn get_version_number(v: &str) -> i64 {
let mut n = 0;
for x in v.split(".") {

View File

@@ -14,7 +14,7 @@ pub enum FramedSocket {
ProxySocks(Socks5UdpFramed),
}
fn new_socket(addr: SocketAddr, reuse: bool) -> Result<Socket, std::io::Error> {
fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result<Socket, std::io::Error> {
let socket = match addr {
SocketAddr::V4(..) => Socket::new(Domain::ipv4(), Type::dgram(), None),
SocketAddr::V6(..) => Socket::new(Domain::ipv6(), Type::dgram(), None),
@@ -27,6 +27,14 @@ fn new_socket(addr: SocketAddr, reuse: bool) -> Result<Socket, std::io::Error> {
socket.set_reuse_port(true)?;
socket.set_reuse_address(true)?;
}
if buf_size > 0 {
socket.set_recv_buffer_size(buf_size).ok();
}
log::info!(
"Receive buf size of udp {}: {:?}",
addr,
socket.recv_buffer_size()
);
socket.bind(&addr.into())?;
Ok(socket)
}
@@ -40,7 +48,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()? {
let socket = new_socket(addr, true)?.into_udp_socket();
let socket = new_socket(addr, true, 0)?.into_udp_socket();
return Ok(Self::Direct(UdpFramed::new(
UdpSocket::from_std(socket)?,
BytesCodec::new(),
@@ -49,6 +57,19 @@ impl FramedSocket {
bail!("could not resolve to any address");
}
pub async fn new_with_buf_size<T: std::net::ToSocketAddrs>(
addr: T,
buf_size: usize,
) -> ResultType<Self> {
for addr in addr.to_socket_addrs()? {
return Ok(Self::Direct(UdpFramed::new(
UdpSocket::from_std(new_socket(addr, false, buf_size)?.into_udp_socket())?,
BytesCodec::new(),
)));
}
bail!("could not resolve to any address");
}
pub async fn new_proxy<'a, 't, P: ToProxyAddrs, T: ToSocketAddrs>(
proxy: P,
local: T,

View File

@@ -23,7 +23,13 @@ version = "0.3"
default-features = true
features = ["dxgi", "dxgi1_2", "dxgi1_5", "d3d11", "winuser"]
[dev-dependencies]
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.10"
jni = "0.19"
lazy_static = "1.4"
log = "0.4"
[target.'cfg(not(target_os = "android"))'.dev-dependencies]
repng = "0.2"
docopt = "1.1"
webm = "1.0"
@@ -33,7 +39,6 @@ quest = "0.3"
[build-dependencies]
target_build_utils = "0.3"
bindgen = "0.59"
vcpkg = "0.2"
[target.'cfg(target_os = "linux")'.dependencies]
dbus = { version = "0.9", optional = true }

View File

@@ -4,35 +4,40 @@ use std::{
};
fn find_package(name: &str) -> Vec<PathBuf> {
let library = vcpkg::find_package(name).expect("Failed to find package");
println!("cargo:info={}", library.vcpkg_triplet); //TODO
let lib_name = name.trim_start_matches("lib").to_string();
println!("{}", format!("cargo:rustc-link-lib=static={}", lib_name));
match (
library.link_paths.as_slice(),
library.include_paths.as_slice(),
) {
([link_search, ..], [include, ..]) => {
println!(
"{}",
format!("cargo:rustc-link-search={}", link_search.display())
);
println!("{}", format!("cargo:include={}", include.display()));
}
_ => {
panic!(
"{}",
if library.link_paths.is_empty() {
"link path not found"
} else {
"include path not found"
}
)
}
let vcpkg_root = std::env::var("VCPKG_ROOT").unwrap();
let mut path: PathBuf = vcpkg_root.into();
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
let mut target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
if target_arch == "x86_64" {
target_arch = "x64".to_owned();
} else if target_arch == "aarch64" {
target_arch = "arm64".to_owned();
}
library.include_paths
let mut target = if target_os == "macos" {
"x64-osx".to_owned()
} else if target_os == "windows" {
"x64-windows-static".to_owned()
} else {
format!("{}-{}", target_arch, target_os)
};
if target_arch == "x86" {
target = target.replace("x64", "x86");
}
println!("cargo:info={}", target);
path.push("installed");
path.push(target);
let lib = name.trim_start_matches("lib").to_string();
println!("{}", format!("cargo:rustc-link-lib=static={}", lib));
println!(
"{}",
format!(
"cargo:rustc-link-search={}",
path.join("lib").to_str().unwrap()
)
);
let include = path.join("include");
println!("{}", format!("cargo:include={}", include.to_str().unwrap()));
vec![include]
}
fn generate_bindings(
@@ -94,8 +99,10 @@ fn main() {
// there is problem with cfg(target_os) in build.rs, so use our workaround
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
if target_os == "android" || target_os == "ios" {
if target_os == "ios" {
// nothing
} else if target_os == "android" {
println!("cargo:rustc-cfg=android");
} else if cfg!(windows) {
// The first choice is Windows because DXGI is amazing.
println!("cargo:rustc-cfg=dxgi");

View File

@@ -0,0 +1,235 @@
use jni::objects::JByteBuffer;
use jni::objects::JString;
use jni::objects::JValue;
use jni::sys::jboolean;
use jni::JNIEnv;
use jni::{
objects::{GlobalRef, JClass, JObject},
JavaVM,
};
use jni::errors::{Error as JniError, Result as JniResult};
use lazy_static::lazy_static;
use std::ops::Not;
use std::sync::atomic::{AtomicPtr, Ordering::SeqCst};
use std::sync::{Mutex, RwLock};
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));
}
const MAX_VIDEO_FRAME_TIMEOUT: Duration = Duration::from_millis(100);
const MAX_AUDIO_FRAME_TIMEOUT: Duration = Duration::from_millis(1000);
struct FrameRaw {
name: &'static str,
ptr: AtomicPtr<u8>,
len: usize,
last_update: Instant,
timeout: Duration,
enable: bool,
}
impl FrameRaw {
fn new(name: &'static str, timeout: Duration) -> Self {
FrameRaw {
name,
ptr: AtomicPtr::default(),
len: 0,
last_update: Instant::now(),
timeout,
enable: false,
}
}
fn set_enable(&mut self, value: bool) {
self.enable = value;
}
fn update(&mut self, data: &mut [u8]) {
if self.enable.not() {
return;
}
self.len = data.len();
self.ptr.store(data.as_mut_ptr(), SeqCst);
self.last_update = Instant::now();
}
// take inner data as slice
// release when success
fn take<'a>(&mut self) -> Option<&'a [u8]> {
if self.enable.not() {
return None;
}
let ptr = self.ptr.load(SeqCst);
if ptr.is_null() || self.len == 0 {
None
} else {
if self.last_update.elapsed() > self.timeout {
log::trace!("Failed to take {} raw,timeout!", self.name);
return None;
}
let slice = unsafe { std::slice::from_raw_parts(ptr, self.len) };
self.release();
Some(slice)
}
}
fn release(&mut self) {
self.len = 0;
self.ptr.store(std::ptr::null_mut(), SeqCst);
}
}
pub fn get_video_raw<'a>() -> Option<&'a [u8]> {
VIDEO_RAW.lock().ok()?.take()
}
pub fn get_audio_raw<'a>() -> Option<&'a [u8]> {
AUDIO_RAW.lock().ok()?.take()
}
#[no_mangle]
pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_onVideoFrameUpdate(
env: JNIEnv,
_class: JClass,
buffer: JObject,
) {
let jb = JByteBuffer::from(buffer);
let slice = env.get_direct_buffer_address(jb).unwrap();
VIDEO_RAW.lock().unwrap().update(slice);
}
#[no_mangle]
pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_onAudioFrameUpdate(
env: JNIEnv,
_class: JClass,
buffer: JObject,
) {
let jb = JByteBuffer::from(buffer);
let slice = env.get_direct_buffer_address(jb).unwrap();
AUDIO_RAW.lock().unwrap().update(slice);
}
#[no_mangle]
pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_setFrameRawEnable(
env: JNIEnv,
_class: JClass,
name: JString,
value: jboolean,
) {
if let Ok(name) = env.get_string(name) {
let name: String = name.into();
let value = value.eq(&1);
if name.eq("video") {
VIDEO_RAW.lock().unwrap().set_enable(value);
} else if name.eq("audio") {
AUDIO_RAW.lock().unwrap().set_enable(value);
}
};
}
#[no_mangle]
pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init(
env: JNIEnv,
_class: JClass,
ctx: JObject,
) {
log::debug!("MainService init from java");
let jvm = env.get_java_vm().unwrap();
*JVM.write().unwrap() = Some(jvm);
let context = env.new_global_ref(ctx).unwrap();
*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<()> {
if let (Some(jvm), Some(ctx)) = (
JVM.read().unwrap().as_ref(),
INPUT_CTX.read().unwrap().as_ref(),
) {
let env = jvm.attach_current_thread_as_daemon()?;
env.call_method(
ctx,
"rustMouseInput",
"(III)V",
&[JValue::Int(mask), JValue::Int(x), JValue::Int(y)],
)?;
return Ok(());
} else {
return Err(JniError::ThrowFailed(-1));
}
}
pub fn call_main_service_get_by_name(name: &str) -> JniResult<String> {
if let (Some(jvm), Some(ctx)) = (
JVM.read().unwrap().as_ref(),
MAIN_SERVICE_CTX.read().unwrap().as_ref(),
) {
let env = jvm.attach_current_thread_as_daemon()?;
let name = env.new_string(name)?;
let res = env
.call_method(
ctx,
"rustGetByName",
"(Ljava/lang/String;)Ljava/lang/String;",
&[JValue::Object(name.into())],
)?
.l()?;
let res = env.get_string(res.into())?;
let res = res.to_string_lossy().to_string();
return Ok(res);
} else {
return Err(JniError::ThrowFailed(-1));
}
}
pub fn call_main_service_set_by_name(
name: &str,
arg1: Option<&str>,
arg2: Option<&str>,
) -> JniResult<()> {
if let (Some(jvm), Some(ctx)) = (
JVM.read().unwrap().as_ref(),
MAIN_SERVICE_CTX.read().unwrap().as_ref(),
) {
let env = jvm.attach_current_thread_as_daemon()?;
let name = env.new_string(name)?;
let arg1 = env.new_string(arg1.unwrap_or(""))?;
let arg2 = env.new_string(arg2.unwrap_or(""))?;
env.call_method(
ctx,
"rustSetByName",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
&[
JValue::Object(name.into()),
JValue::Object(arg1.into()),
JValue::Object(arg2.into()),
],
)?;
return Ok(());
} else {
return Err(JniError::ThrowFailed(-1));
}
}

View File

@@ -0,0 +1,6 @@
pub mod ffi;
use std::sync::RwLock;
pub use ffi::*;
use lazy_static::lazy_static;

View File

@@ -0,0 +1,129 @@
use crate::android::ffi::*;
use crate::rgba_to_i420;
use lazy_static::lazy_static;
use std::io;
use std::sync::Mutex;
lazy_static! {
static ref SCREEN_SIZE: Mutex<(u16, u16)> = Mutex::new((0, 0));
}
pub struct Capturer {
display: Display,
bgra: Vec<u8>,
saved_raw_data: Vec<u128>, // for faster compare and copy
}
impl Capturer {
pub fn new(display: Display, _yuv: bool) -> io::Result<Capturer> {
Ok(Capturer {
display,
bgra: Vec::new(),
saved_raw_data: Vec::new(),
})
}
pub fn width(&self) -> usize {
self.display.width() as usize
}
pub fn height(&self) -> usize {
self.display.height() as usize
}
pub fn frame<'a>(&'a mut self, _timeout_ms: u32) -> io::Result<Frame<'a>> {
if let Some(buf) = get_video_raw() {
crate::would_block_if_equal(&mut self.saved_raw_data, buf)?;
rgba_to_i420(self.width(), self.height(), buf, &mut self.bgra);
Ok(Frame::RAW(&self.bgra))
} else {
return Err(io::ErrorKind::WouldBlock.into());
}
}
}
pub enum Frame<'a> {
RAW(&'a [u8]),
VP9(&'a [u8]),
Empty,
}
pub struct Display {
default: bool,
rect: Rect,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
struct Rect {
pub x: i16,
pub y: i16,
pub w: u16,
pub h: u16,
}
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;
}
Ok(Display {
default: true,
rect: Rect {
x: 0,
y: 0,
w: size.0,
h: size.1,
},
})
}
pub fn all() -> io::Result<Vec<Display>> {
Ok(vec![Display::primary()?])
}
pub fn width(&self) -> usize {
self.rect.w as usize
}
pub fn height(&self) -> usize {
self.rect.h as usize
}
pub fn origin(&self) -> (i32, i32) {
let r = self.rect;
(r.x as _, r.y as _)
}
pub fn is_online(&self) -> bool {
true
}
pub fn is_primary(&self) -> bool {
self.default
}
pub fn name(&self) -> String {
"Android".into()
}
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;
}
}
fn get_size() -> Option<(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));
}
None
}

View File

@@ -19,7 +19,10 @@ cfg_if! {
} else if #[cfg(dxgi)] {
mod dxgi;
pub use self::dxgi::*;
} else {
} else if #[cfg(android)] {
mod android;
pub use self::android::*;
}else {
//TODO: Fallback implementation.
}
}
@@ -42,4 +45,4 @@ pub fn would_block_if_equal(old: &mut Vec<u128>, b: &[u8]) -> std::io::Result<()
old.resize(b.len(), 0);
old.copy_from_slice(b);
Ok(())
}
}

View File

@@ -20,4 +20,7 @@ pub mod wayland;
#[cfg(dxgi)]
pub mod dxgi;
#[cfg(android)]
pub mod android;
mod common;