This commit is contained in:
Asura
2022-07-20 19:51:09 -07:00
110 changed files with 4942 additions and 1487 deletions

View File

@@ -8,6 +8,7 @@ use crate::video_service;
use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel};
use crate::{ipc, VERSION};
use hbb_common::fs::can_enable_overwrite_detection;
use hbb_common::password_security::password;
use hbb_common::{
config::Config,
fs,
@@ -35,6 +36,7 @@ pub type Sender = mpsc::UnboundedSender<(Instant, Arc<Message>)>;
lazy_static::lazy_static! {
static ref LOGIN_FAILURES: Arc::<Mutex<HashMap<String, (i32, i32, i32)>>> = Default::default();
static ref SESSIONS: Arc::<Mutex<HashMap<String, Session>>> = Default::default();
}
pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0);
pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0);
@@ -53,6 +55,14 @@ enum MessageInput {
BlockOff,
}
#[derive(Clone, Debug)]
struct Session {
name: String,
session_id: u64,
last_recv_time: Arc<Mutex<Instant>>,
random_password: String,
}
pub struct Connection {
inner: ConnInner,
stream: super::Stream,
@@ -80,6 +90,8 @@ pub struct Connection {
video_ack_required: bool,
peer_info: (String, String),
api_server: String,
lr: LoginRequest,
last_recv_time: Arc<Mutex<Instant>>,
}
impl Subscriber for ConnInner {
@@ -91,7 +103,7 @@ impl Subscriber for ConnInner {
#[inline]
fn send(&mut self, msg: Arc<Message>) {
match &msg.union {
Some(message::Union::video_frame(_)) => {
Some(message::Union::VideoFrame(_)) => {
self.tx_video.as_mut().map(|tx| {
allow_err!(tx.send((Instant::now(), msg)));
});
@@ -111,6 +123,7 @@ const H1: Duration = Duration::from_secs(3600);
const MILLI1: Duration = Duration::from_millis(1);
const SEND_TIMEOUT_VIDEO: u64 = 12_000;
const SEND_TIMEOUT_OTHER: u64 = SEND_TIMEOUT_VIDEO * 10;
const SESSION_TIMEOUT: Duration = Duration::from_secs(30);
impl Connection {
pub async fn start(
@@ -164,6 +177,8 @@ impl Connection {
video_ack_required: false,
peer_info: Default::default(),
api_server: "".to_owned(),
lr: Default::default(),
last_recv_time: Arc::new(Mutex::new(Instant::now())),
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
tokio::spawn(async move {
@@ -222,7 +237,8 @@ impl Connection {
let mut msg_out = Message::new();
msg_out.set_misc(misc);
conn.send(msg_out).await;
conn.on_close("Close requested from connection manager", false);
conn.on_close("Close requested from connection manager", false).await;
SESSIONS.lock().unwrap().remove(&conn.lr.my_id);
break;
}
ipc::Data::ChatMessage{text} => {
@@ -311,11 +327,12 @@ impl Connection {
if let Some(res) = res {
match res {
Err(err) => {
conn.on_close(&err.to_string(), true);
conn.on_close(&err.to_string(), true).await;
break;
},
Ok(bytes) => {
last_recv_time = Instant::now();
*conn.last_recv_time.lock().unwrap() = Instant::now();
if let Ok(msg_in) = Message::parse_from_bytes(&bytes) {
if !conn.on_message(msg_in).await {
break;
@@ -324,14 +341,14 @@ impl Connection {
}
}
} else {
conn.on_close("Reset by the peer", true);
conn.on_close("Reset by the peer", true).await;
break;
}
},
_ = conn.timer.tick() => {
if !conn.read_jobs.is_empty() {
if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut conn.stream).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
break;
}
} else {
@@ -344,7 +361,7 @@ impl Connection {
video_service::notify_video_frame_feched(id, Some(instant.into()));
}
if let Err(err) = conn.stream.send(&value as &Message).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
break;
}
},
@@ -354,7 +371,7 @@ impl Connection {
if latency > 1000 {
match &msg.union {
Some(message::Union::audio_frame(_)) => {
Some(message::Union::AudioFrame(_)) => {
// log::info!("audio frame latency {}", instant.elapsed().as_secs_f32());
continue;
}
@@ -362,13 +379,13 @@ impl Connection {
}
}
if let Err(err) = conn.stream.send(msg).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
break;
}
},
_ = test_delay_timer.tick() => {
if last_recv_time.elapsed() >= SEC30 {
conn.on_close("Timeout", true);
conn.on_close("Timeout", true).await;
break;
}
let time = crate::get_time();
@@ -398,8 +415,9 @@ impl Connection {
video_service::notify_video_frame_feched(id, None);
scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove);
video_service::VIDEO_QOS.lock().unwrap().reset();
password::after_session(conn.authorized);
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
}
conn.post_audit(json!({
@@ -493,7 +511,7 @@ impl Connection {
res = self.stream.next() => {
if let Some(res) = res {
last_recv_time = Instant::now();
timeout(SEND_TIMEOUT_OTHER, forward.send(res?.into())).await??;
timeout(SEND_TIMEOUT_OTHER, forward.send(res?)).await??;
} else {
bail!("Stream reset by the peer");
}
@@ -572,7 +590,7 @@ impl Connection {
let url = self.api_server.clone();
let mut v = v;
v["id"] = json!(Config::get_id());
v["uuid"] = json!(base64::encode(crate::get_uuid()));
v["uuid"] = json!(base64::encode(hbb_common::get_uuid()));
v["Id"] = json!(self.inner.id);
tokio::spawn(async move {
allow_err!(Self::post_audit_async(url, v).await);
@@ -629,9 +647,9 @@ impl Connection {
#[cfg(target_os = "linux")]
if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() {
let dtype = crate::platform::linux::get_display_server();
if dtype != "x11" {
if dtype != "x11" && dtype != "wayland" {
res.set_error(format!(
"Unsupported display server type {}, x11 expected",
"Unsupported display server type {}, x11 or wayland expected",
dtype
));
let mut msg_out = Message::new();
@@ -667,7 +685,7 @@ impl Connection {
res.set_peer_info(pi);
} else {
try_activate_screen();
match video_service::get_displays() {
match super::video_service::get_displays().await {
Err(err) => {
res.set_error(format!("X11 error: {}", err));
}
@@ -779,8 +797,77 @@ impl Connection {
self.tx_input.send(MessageInput::Key((msg, press))).ok();
}
fn validate_one_password(&self, password: String) -> bool {
if password.len() == 0 {
return false;
}
let mut hasher = Sha256::new();
hasher.update(password);
hasher.update(&self.hash.salt);
let mut hasher2 = Sha256::new();
hasher2.update(&hasher.finalize()[..]);
hasher2.update(&self.hash.challenge);
hasher2.finalize()[..] == self.lr.password[..]
}
fn validate_password(&mut self) -> bool {
if password::security_enabled() {
if self.validate_one_password(Config::get_security_password()) {
return true;
}
}
if password::random_password_valid() {
let password = password::random_password();
if self.validate_one_password(password.clone()) {
if password::onetime_password_activated() {
password::set_onetime_password_activated(false);
}
SESSIONS.lock().unwrap().insert(
self.lr.my_id.clone(),
Session {
name: self.lr.my_name.clone(),
session_id: self.lr.session_id,
last_recv_time: self.last_recv_time.clone(),
random_password: password,
},
);
return true;
}
}
false
}
fn is_of_recent_session(&mut self) -> bool {
let session = SESSIONS
.lock()
.unwrap()
.get(&self.lr.my_id)
.map(|s| s.to_owned());
if let Some(session) = session {
if session.name == self.lr.my_name
&& session.session_id == self.lr.session_id
&& !self.lr.password.is_empty()
&& self.validate_one_password(session.random_password.clone())
&& session.last_recv_time.lock().unwrap().elapsed() < SESSION_TIMEOUT
{
SESSIONS.lock().unwrap().insert(
self.lr.my_id.clone(),
Session {
name: self.lr.my_name.clone(),
session_id: self.lr.session_id,
last_recv_time: self.last_recv_time.clone(),
random_password: session.random_password,
},
);
return true;
}
}
false
}
async fn on_message(&mut self, msg: Message) -> bool {
if let Some(message::Union::login_request(lr)) = msg.union {
if let Some(message::Union::LoginRequest(lr)) = msg.union {
self.lr = lr.clone();
if let Some(o) = lr.option.as_ref() {
self.update_option(o).await;
if let Some(q) = o.video_codec_state.clone().take() {
@@ -805,7 +892,7 @@ impl Connection {
return true;
}
match lr.union {
Some(login_request::Union::file_transfer(ft)) => {
Some(login_request::Union::FileTransfer(ft)) => {
if !Config::get_option("enable-file-transfer").is_empty() {
self.send_login_error("No permission of file transfer")
.await;
@@ -814,7 +901,7 @@ impl Connection {
}
self.file_transfer = Some((ft.dir, ft.show_hidden));
}
Some(login_request::Union::port_forward(mut pf)) => {
Some(login_request::Union::PortForward(mut pf)) => {
if !Config::get_option("enable-tunnel").is_empty() {
self.send_login_error("No permission of IP tunneling").await;
sleep(1.).await;
@@ -851,15 +938,19 @@ impl Connection {
}
if !crate::is_ip(&lr.username) && lr.username != Config::get_id() {
self.send_login_error("Offline").await;
} else if self.is_of_recent_session() {
self.try_start_cm(lr.my_id, lr.my_name, true);
self.send_logon_response().await;
if self.port_forward_socket.is_some() {
return false;
}
} else if lr.password.is_empty() {
self.try_start_cm(lr.my_id, lr.my_name, false);
} else {
let mut hasher = Sha256::new();
hasher.update(&Config::get_password());
hasher.update(&self.hash.salt);
let mut hasher2 = Sha256::new();
hasher2.update(&hasher.finalize()[..]);
hasher2.update(&self.hash.challenge);
if password::passwords().len() == 0 {
self.send_login_error("Connection not allowed").await;
return false;
}
let mut failure = LOGIN_FAILURES
.lock()
.unwrap()
@@ -872,7 +963,7 @@ impl Connection {
.await;
} else if time == failure.0 && failure.1 > 6 {
self.send_login_error("Please try 1 minute later").await;
} else if hasher2.finalize()[..] != lr.password[..] {
} else if !self.validate_password() {
if failure.0 == time {
failure.1 += 1;
failure.2 += 1;
@@ -898,7 +989,7 @@ impl Connection {
}
}
}
} else if let Some(message::Union::test_delay(t)) = msg.union {
} else if let Some(message::Union::TestDelay(t)) = msg.union {
if t.from_client {
let mut msg_out = Message::new();
msg_out.set_test_delay(t);
@@ -913,7 +1004,7 @@ impl Connection {
}
} else if self.authorized {
match msg.union {
Some(message::Union::mouse_event(me)) => {
Some(message::Union::MouseEvent(me)) => {
#[cfg(any(target_os = "android", target_os = "ios"))]
if let Err(e) = call_main_service_mouse_input(me.mask, me.x, me.y) {
log::debug!("call_main_service_mouse_input fail:{}", e);
@@ -928,7 +1019,7 @@ impl Connection {
self.input_mouse(me, self.inner.id());
}
}
Some(message::Union::key_event(me)) => {
Some(message::Union::KeyEvent(me)) => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.keyboard {
if is_enter(&me) {
@@ -944,8 +1035,8 @@ impl Connection {
};
if is_press {
match me.union {
Some(key_event::Union::unicode(_))
| Some(key_event::Union::seq(_)) => {
Some(key_event::Union::Unicode(_))
| Some(key_event::Union::Seq(_)) => {
self.input_key(me, false);
}
_ => {
@@ -957,14 +1048,14 @@ impl Connection {
}
}
}
Some(message::Union::clipboard(cb)) =>
Some(message::Union::Clipboard(cb)) =>
{
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.clipboard {
update_clipboard(cb, None);
}
}
Some(message::Union::cliprdr(_clip)) => {
Some(message::Union::Cliprdr(_clip)) => {
if self.file_transfer_enabled() {
#[cfg(windows)]
if let Some(clip) = msg_2_clip(_clip) {
@@ -972,13 +1063,13 @@ impl Connection {
}
}
}
Some(message::Union::file_action(fa)) => {
Some(message::Union::FileAction(fa)) => {
if self.file_transfer.is_some() {
match fa.union {
Some(file_action::Union::read_dir(rd)) => {
Some(file_action::Union::ReadDir(rd)) => {
self.read_dir(&rd.path, rd.include_hidden);
}
Some(file_action::Union::all_files(f)) => {
Some(file_action::Union::AllFiles(f)) => {
match fs::get_recursive_files(&f.path, f.include_hidden) {
Err(err) => {
self.send(fs::new_error(f.id, err, -1)).await;
@@ -988,7 +1079,7 @@ impl Connection {
}
}
}
Some(file_action::Union::send(s)) => {
Some(file_action::Union::Send(s)) => {
let id = s.id;
let od =
can_enable_overwrite_detection(get_version_number(VERSION));
@@ -1013,7 +1104,7 @@ impl Connection {
}
}
}
Some(file_action::Union::receive(r)) => {
Some(file_action::Union::Receive(r)) => {
self.send_fs(ipc::FS::NewWrite {
path: r.path,
id: r.id,
@@ -1026,31 +1117,31 @@ impl Connection {
.collect(),
});
}
Some(file_action::Union::remove_dir(d)) => {
Some(file_action::Union::RemoveDir(d)) => {
self.send_fs(ipc::FS::RemoveDir {
path: d.path,
id: d.id,
recursive: d.recursive,
});
}
Some(file_action::Union::remove_file(f)) => {
Some(file_action::Union::RemoveFile(f)) => {
self.send_fs(ipc::FS::RemoveFile {
path: f.path,
id: f.id,
file_num: f.file_num,
});
}
Some(file_action::Union::create(c)) => {
Some(file_action::Union::Create(c)) => {
self.send_fs(ipc::FS::CreateDir {
path: c.path,
id: c.id,
});
}
Some(file_action::Union::cancel(c)) => {
Some(file_action::Union::Cancel(c)) => {
self.send_fs(ipc::FS::CancelWrite { id: c.id });
fs::remove_job(c.id, &mut self.read_jobs);
}
Some(file_action::Union::send_confirm(r)) => {
Some(file_action::Union::SendConfirm(r)) => {
if let Some(job) = fs::get_job(r.id, &mut self.read_jobs) {
job.confirm(&r);
}
@@ -1059,8 +1150,8 @@ impl Connection {
}
}
}
Some(message::Union::file_response(fr)) => match fr.union {
Some(file_response::Union::block(block)) => {
Some(message::Union::FileResponse(fr)) => match fr.union {
Some(file_response::Union::Block(block)) => {
self.send_fs(ipc::FS::WriteBlock {
id: block.id,
file_num: block.file_num,
@@ -1068,13 +1159,13 @@ impl Connection {
compressed: block.compressed,
});
}
Some(file_response::Union::done(d)) => {
Some(file_response::Union::Done(d)) => {
self.send_fs(ipc::FS::WriteDone {
id: d.id,
file_num: d.file_num,
});
}
Some(file_response::Union::digest(d)) => self.send_fs(ipc::FS::CheckDigest {
Some(file_response::Union::Digest(d)) => self.send_fs(ipc::FS::CheckDigest {
id: d.id,
file_num: d.file_num,
file_size: d.file_size,
@@ -1083,27 +1174,32 @@ impl Connection {
}),
_ => {}
},
Some(message::Union::misc(misc)) => match misc.union {
Some(misc::Union::switch_display(s)) => {
video_service::switch_display(s.display);
Some(message::Union::Misc(misc)) => match misc.union {
Some(misc::Union::SwitchDisplay(s)) => {
video_service::switch_display(s.display).await;
}
Some(misc::Union::chat_message(c)) => {
Some(misc::Union::ChatMessage(c)) => {
self.send_to_cm(ipc::Data::ChatMessage { text: c.text });
}
Some(misc::Union::option(o)) => {
Some(misc::Union::Option(o)) => {
self.update_option(&o).await;
}
Some(misc::Union::refresh_video(r)) => {
Some(misc::Union::RefreshVideo(r)) => {
if r {
video_service::refresh();
super::video_service::refresh();
}
}
Some(misc::Union::video_received(_)) => {
Some(misc::Union::VideoReceived(_)) => {
video_service::notify_video_frame_feched(
self.inner.id,
Some(Instant::now().into()),
);
}
Some(misc::Union::CloseReason(_)) => {
self.on_close("Peer close", true).await;
SESSIONS.lock().unwrap().remove(&self.lr.my_id);
return false;
}
_ => {}
},
_ => {}
@@ -1258,14 +1354,14 @@ impl Connection {
}
}
fn on_close(&mut self, reason: &str, lock: bool) {
async fn on_close(&mut self, reason: &str, lock: bool) {
if let Some(s) = self.server.upgrade() {
s.write().unwrap().remove_connection(&self.inner);
}
log::info!("#{} Connection closed: {}", self.inner.id(), reason);
if lock && self.lock_after_session_end && self.keyboard {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lock_screen();
lock_screen().await;
}
self.tx_to_cm.send(ipc::Data::Close).ok();
self.port_forward_socket.take();

View File

@@ -2,7 +2,7 @@ use super::*;
#[cfg(target_os = "macos")]
use dispatch::Queue;
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
use hbb_common::{config::COMPRESS_LEVEL, protobuf::ProtobufEnumOrUnknown};
use hbb_common::{config::COMPRESS_LEVEL, protobuf::ProtobufEnumOrUnknown, protobuf::EnumOrUnknown};
use rdev::{simulate, EventType, Key as RdevKey};
use std::{
convert::TryFrom,
@@ -69,7 +69,7 @@ impl Subscriber for MouseCursorSub {
#[inline]
fn send(&mut self, msg: Arc<Message>) {
if let Some(message::Union::cursor_data(cd)) = &msg.union {
if let Some(message::Union::CursorData(cd)) = &msg.union {
if let Some(msg) = self.cached.get(&cd.id) {
self.inner.send(msg.clone());
} else {
@@ -188,6 +188,26 @@ lazy_static::lazy_static! {
static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned());
}
#[cfg(target_os = "linux")]
pub async fn set_uinput() -> ResultType<()> {
// Keyboard and mouse both open /dev/uinput
// TODO: Make sure there's no race
let keyboard = super::uinput::client::UInputKeyboard::new().await?;
log::info!("UInput keyboard created");
let mouse = super::uinput::client::UInputMouse::new().await?;
log::info!("UInput mouse created");
let mut en = ENIGO.lock().unwrap();
en.set_uinput_keyboard(Some(Box::new(keyboard)));
en.set_uinput_mouse(Some(Box::new(mouse)));
Ok(())
}
#[cfg(target_os = "linux")]
pub async fn set_uinput_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
super::uinput::client::set_resolution(minx, maxx, miny, maxy).await
}
pub fn is_left_up(evt: &MouseEvent) -> bool {
let buttons = evt.mask >> 3;
let evt_type = evt.mask & 0x7;
@@ -300,12 +320,12 @@ fn fix_key_down_timeout(force: bool) {
// e.g. current state of ctrl is down, but ctrl not in modifier, we should change ctrl to up, to make modifier state sync between remote and local
#[inline]
fn fix_modifier(
modifiers: &[ProtobufEnumOrUnknown<ControlKey>],
modifiers: &[EnumOrUnknown<ControlKey>],
key0: ControlKey,
key1: Key,
en: &mut Enigo,
) {
if get_modifier_state(key1, en) && !modifiers.contains(&ProtobufEnumOrUnknown::new(key0)) {
if get_modifier_state(key1, en) && !modifiers.contains(&EnumOrUnknown::new(key0)) {
#[cfg(windows)]
if key0 == ControlKey::Control && get_modifier_state(Key::Alt, en) {
// AltGr case
@@ -316,7 +336,7 @@ fn fix_modifier(
}
}
fn fix_modifiers(modifiers: &[ProtobufEnumOrUnknown<ControlKey>], en: &mut Enigo, ck: i32) {
fn fix_modifiers(modifiers: &[EnumOrUnknown<ControlKey>], en: &mut Enigo, ck: i32) {
if ck != ControlKey::Shift.value() {
fix_modifier(modifiers, ControlKey::Shift, Key::Shift, en);
}
@@ -431,7 +451,7 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) {
}
pub fn is_enter(evt: &KeyEvent) -> bool {
if let Some(key_event::Union::control_key(ck)) = evt.union {
if let Some(key_event::Union::ControlKey(ck)) = evt.union {
if ck.value() == ControlKey::Return.value() || ck.value() == ControlKey::NumpadEnter.value()
{
return true;
@@ -440,7 +460,7 @@ pub fn is_enter(evt: &KeyEvent) -> bool {
return false;
}
pub fn lock_screen() {
pub async fn lock_screen() {
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
// xdg_screensaver lock not work on Linux from our service somehow
@@ -477,7 +497,7 @@ pub fn lock_screen() {
crate::platform::lock_screen();
}
}
super::video_service::switch_to_primary();
super::video_service::switch_to_primary().await;
}
lazy_static::lazy_static! {
@@ -556,7 +576,6 @@ lazy_static::lazy_static! {
(ControlKey::Equals, Key::Equals),
(ControlKey::NumpadEnter, Key::NumpadEnter),
(ControlKey::RAlt, Key::RightAlt),
(ControlKey::RWin, Key::RWin),
(ControlKey::RControl, Key::RightControl),
(ControlKey::RShift, Key::RightShift),
].iter().map(|(a, b)| (a.value(), b.clone())).collect();
@@ -656,7 +675,7 @@ fn legacy_keyboard_map(evt: &KeyEvent) {
#[cfg(windows)]
let mut has_numlock = false;
if evt.down {
let ck = if let Some(key_event::Union::control_key(ck)) = evt.union {
let ck = if let Some(key_event::Union::ControlKey(ck)) = evt.union {
ck.value()
} else {
-1
@@ -711,7 +730,7 @@ fn legacy_keyboard_map(evt: &KeyEvent) {
}
}
match evt.union {
Some(key_event::Union::control_key(ck)) => {
Some(key_event::Union::ControlKey(ck)) => {
if let Some(key) = KEY_MAP.get(&ck.value()) {
#[cfg(windows)]
if let Some(_) = NUMPAD_KEY_MAP.get(&ck.value()) {
@@ -737,10 +756,10 @@ fn legacy_keyboard_map(evt: &KeyEvent) {
allow_err!(send_sas());
});
} else if ck.value() == ControlKey::LockScreen.value() {
lock_screen();
lock_screen_2();
}
}
Some(key_event::Union::chr(chr)) => {
Some(key_event::Union::Chr(chr)) => {
if evt.down {
if en.key_down(get_layout(chr)).is_ok() {
KEYS_DOWN
@@ -766,12 +785,12 @@ fn legacy_keyboard_map(evt: &KeyEvent) {
.remove(&(chr as u64 + KEY_CHAR_START));
}
}
Some(key_event::Union::unicode(chr)) => {
Some(key_event::Union::Unicode(chr)) => {
if let Ok(chr) = char::try_from(chr) {
en.key_sequence(&chr.to_string());
}
}
Some(key_event::Union::seq(ref seq)) => {
Some(key_event::Union::Seq(ref seq)) => {
en.key_sequence(&seq);
}
_ => {}
@@ -805,6 +824,11 @@ fn handle_key_(evt: &KeyEvent) {
}
}
#[tokio::main(flavor = "current_thread")]
async fn lock_screen_2() {
lock_screen().await;
}
#[tokio::main(flavor = "current_thread")]
async fn send_sas() -> ResultType<()> {
let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?;

651
src/server/uinput.rs Normal file
View File

@@ -0,0 +1,651 @@
use crate::ipc::{self, new_listener, Connection, Data, DataKeyboard, DataMouse};
use enigo::{Key, KeyboardControllable, MouseButton, MouseControllable};
use evdev::{
uinput::{VirtualDevice, VirtualDeviceBuilder},
AttributeSet, EventType, InputEvent,
};
use hbb_common::{allow_err, bail, log, tokio, ResultType};
static IPC_CONN_TIMEOUT: u64 = 1000;
static IPC_REQUEST_TIMEOUT: u64 = 1000;
static IPC_POSTFIX_KEYBOARD: &str = "_uinput_keyboard";
static IPC_POSTFIX_MOUSE: &str = "_uinput_mouse";
static IPC_POSTFIX_CONTROL: &str = "_uinput_control";
pub mod client {
use super::*;
pub struct UInputKeyboard {
conn: Connection,
}
impl UInputKeyboard {
pub async fn new() -> ResultType<Self> {
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?;
Ok(Self { conn })
}
#[tokio::main(flavor = "current_thread")]
async fn send(&mut self, data: Data) -> ResultType<()> {
self.conn.send(&data).await
}
#[tokio::main(flavor = "current_thread")]
async fn send_get_key_state(&mut self, data: Data) -> ResultType<bool> {
self.conn.send(&data).await?;
match self.conn.next_timeout(IPC_REQUEST_TIMEOUT).await {
Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(state)))) => {
Ok(state)
}
Ok(Some(resp)) => {
// FATAL error!!!
bail!(
"FATAL error, wait keyboard result other response: {:?}",
&resp
);
}
Ok(None) => {
// FATAL error!!!
// Maybe wait later
bail!("FATAL error, wait keyboard result, receive None",);
}
Err(e) => {
// FATAL error!!!
bail!(
"FATAL error, wait keyboard result timeout {}, {}",
&e,
IPC_REQUEST_TIMEOUT
);
}
}
}
}
impl KeyboardControllable for UInputKeyboard {
fn get_key_state(&mut self, key: Key) -> bool {
match self.send_get_key_state(Data::Keyboard(DataKeyboard::GetKeyState(key))) {
Ok(state) => state,
Err(e) => {
// unreachable!()
log::error!("Failed to get key state {}", &e);
false
}
}
}
fn key_sequence(&mut self, sequence: &str) {
allow_err!(self.send(Data::Keyboard(DataKeyboard::Sequence(sequence.to_string()))));
}
// TODO: handle error???
fn key_down(&mut self, key: Key) -> enigo::ResultType {
allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyDown(key))));
Ok(())
}
fn key_up(&mut self, key: Key) {
allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyUp(key))));
}
fn key_click(&mut self, key: Key) {
allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyClick(key))));
}
}
pub struct UInputMouse {
conn: Connection,
}
impl UInputMouse {
pub async fn new() -> ResultType<Self> {
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?;
Ok(Self { conn })
}
#[tokio::main(flavor = "current_thread")]
async fn send(&mut self, data: Data) -> ResultType<()> {
self.conn.send(&data).await
}
}
impl MouseControllable for UInputMouse {
fn mouse_move_to(&mut self, x: i32, y: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::MoveTo(x, y))));
}
fn mouse_move_relative(&mut self, x: i32, y: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::MoveRelative(x, y))));
}
// TODO: handle error???
fn mouse_down(&mut self, button: MouseButton) -> enigo::ResultType {
allow_err!(self.send(Data::Mouse(DataMouse::Down(button))));
Ok(())
}
fn mouse_up(&mut self, button: MouseButton) {
allow_err!(self.send(Data::Mouse(DataMouse::Up(button))));
}
fn mouse_click(&mut self, button: MouseButton) {
allow_err!(self.send(Data::Mouse(DataMouse::Click(button))));
}
fn mouse_scroll_x(&mut self, length: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::ScrollX(length))));
}
fn mouse_scroll_y(&mut self, length: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::ScrollY(length))));
}
}
pub async fn set_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
let mut conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_CONTROL).await?;
conn.send(&Data::Control(ipc::DataControl::Resolution {
minx,
maxx,
miny,
maxy,
}))
.await?;
let _ = conn.next().await?;
Ok(())
}
}
pub mod service {
use super::*;
use hbb_common::lazy_static;
use mouce::MouseActions;
use std::{collections::HashMap, sync::Mutex};
lazy_static::lazy_static! {
static ref KEY_MAP: HashMap<enigo::Key, evdev::Key> = HashMap::from(
[
(enigo::Key::Alt, evdev::Key::KEY_LEFTALT),
(enigo::Key::Backspace, evdev::Key::KEY_BACKSPACE),
(enigo::Key::CapsLock, evdev::Key::KEY_CAPSLOCK),
(enigo::Key::Control, evdev::Key::KEY_LEFTCTRL),
(enigo::Key::Delete, evdev::Key::KEY_DELETE),
(enigo::Key::DownArrow, evdev::Key::KEY_DOWN),
(enigo::Key::End, evdev::Key::KEY_END),
(enigo::Key::Escape, evdev::Key::KEY_ESC),
(enigo::Key::F1, evdev::Key::KEY_F1),
(enigo::Key::F10, evdev::Key::KEY_F10),
(enigo::Key::F11, evdev::Key::KEY_F11),
(enigo::Key::F12, evdev::Key::KEY_F12),
(enigo::Key::F2, evdev::Key::KEY_F2),
(enigo::Key::F3, evdev::Key::KEY_F3),
(enigo::Key::F4, evdev::Key::KEY_F4),
(enigo::Key::F5, evdev::Key::KEY_F5),
(enigo::Key::F6, evdev::Key::KEY_F6),
(enigo::Key::F7, evdev::Key::KEY_F7),
(enigo::Key::F8, evdev::Key::KEY_F8),
(enigo::Key::F9, evdev::Key::KEY_F9),
(enigo::Key::Home, evdev::Key::KEY_HOME),
(enigo::Key::LeftArrow, evdev::Key::KEY_LEFT),
(enigo::Key::Meta, evdev::Key::KEY_LEFTMETA),
(enigo::Key::Option, evdev::Key::KEY_OPTION),
(enigo::Key::PageDown, evdev::Key::KEY_PAGEDOWN),
(enigo::Key::PageUp, evdev::Key::KEY_PAGEUP),
(enigo::Key::Return, evdev::Key::KEY_ENTER),
(enigo::Key::RightArrow, evdev::Key::KEY_RIGHT),
(enigo::Key::Shift, evdev::Key::KEY_LEFTSHIFT),
(enigo::Key::Space, evdev::Key::KEY_SPACE),
(enigo::Key::Tab, evdev::Key::KEY_TAB),
(enigo::Key::UpArrow, evdev::Key::KEY_UP),
(enigo::Key::Numpad0, evdev::Key::KEY_KP0), // check if correct?
(enigo::Key::Numpad1, evdev::Key::KEY_KP1),
(enigo::Key::Numpad2, evdev::Key::KEY_KP2),
(enigo::Key::Numpad3, evdev::Key::KEY_KP3),
(enigo::Key::Numpad4, evdev::Key::KEY_KP4),
(enigo::Key::Numpad5, evdev::Key::KEY_KP5),
(enigo::Key::Numpad6, evdev::Key::KEY_KP6),
(enigo::Key::Numpad7, evdev::Key::KEY_KP7),
(enigo::Key::Numpad8, evdev::Key::KEY_KP8),
(enigo::Key::Numpad9, evdev::Key::KEY_KP9),
(enigo::Key::Cancel, evdev::Key::KEY_CANCEL),
(enigo::Key::Clear, evdev::Key::KEY_CLEAR),
(enigo::Key::Alt, evdev::Key::KEY_LEFTALT),
(enigo::Key::Pause, evdev::Key::KEY_PAUSE),
(enigo::Key::Kana, evdev::Key::KEY_KATAKANA), // check if correct?
(enigo::Key::Hangul, evdev::Key::KEY_HANGEUL), // check if correct?
// (enigo::Key::Junja, evdev::Key::KEY_JUNJA), // map?
// (enigo::Key::Final, evdev::Key::KEY_FINAL), // map?
(enigo::Key::Hanja, evdev::Key::KEY_HANJA),
// (enigo::Key::Kanji, evdev::Key::KEY_KANJI), // map?
// (enigo::Key::Convert, evdev::Key::KEY_CONVERT),
(enigo::Key::Select, evdev::Key::KEY_SELECT),
(enigo::Key::Print, evdev::Key::KEY_PRINT),
// (enigo::Key::Execute, evdev::Key::KEY_EXECUTE),
// (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT),
(enigo::Key::Insert, evdev::Key::KEY_INSERT),
(enigo::Key::Help, evdev::Key::KEY_HELP),
(enigo::Key::Sleep, evdev::Key::KEY_SLEEP),
// (enigo::Key::Separator, evdev::Key::KEY_SEPARATOR),
(enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK),
(enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK),
(enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA),
(enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU),
(enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK),
(enigo::Key::Add, evdev::Key::KEY_KPPLUS),
(enigo::Key::Subtract, evdev::Key::KEY_KPMINUS),
(enigo::Key::Decimal, evdev::Key::KEY_KPCOMMA), // KEY_KPDOT and KEY_KPCOMMA are exchanged?
(enigo::Key::Divide, evdev::Key::KEY_KPSLASH),
(enigo::Key::Equals, evdev::Key::KEY_KPEQUAL),
(enigo::Key::NumpadEnter, evdev::Key::KEY_KPENTER),
(enigo::Key::RightAlt, evdev::Key::KEY_RIGHTALT),
(enigo::Key::RightControl, evdev::Key::KEY_RIGHTCTRL),
(enigo::Key::RightShift, evdev::Key::KEY_RIGHTSHIFT),
]);
static ref KEY_MAP_LAYOUT: HashMap<char, evdev::Key> = HashMap::from(
[
('a', evdev::Key::KEY_A),
('b', evdev::Key::KEY_B),
('c', evdev::Key::KEY_C),
('d', evdev::Key::KEY_D),
('e', evdev::Key::KEY_E),
('f', evdev::Key::KEY_F),
('g', evdev::Key::KEY_G),
('h', evdev::Key::KEY_H),
('i', evdev::Key::KEY_I),
('j', evdev::Key::KEY_J),
('k', evdev::Key::KEY_K),
('l', evdev::Key::KEY_L),
('m', evdev::Key::KEY_M),
('n', evdev::Key::KEY_N),
('o', evdev::Key::KEY_O),
('p', evdev::Key::KEY_P),
('q', evdev::Key::KEY_Q),
('r', evdev::Key::KEY_R),
('s', evdev::Key::KEY_S),
('t', evdev::Key::KEY_T),
('u', evdev::Key::KEY_U),
('v', evdev::Key::KEY_V),
('w', evdev::Key::KEY_W),
('x', evdev::Key::KEY_X),
('y', evdev::Key::KEY_Y),
('z', evdev::Key::KEY_Z),
('0', evdev::Key::KEY_0),
('1', evdev::Key::KEY_1),
('2', evdev::Key::KEY_2),
('3', evdev::Key::KEY_3),
('4', evdev::Key::KEY_4),
('5', evdev::Key::KEY_5),
('6', evdev::Key::KEY_6),
('7', evdev::Key::KEY_7),
('8', evdev::Key::KEY_8),
('9', evdev::Key::KEY_9),
('`', evdev::Key::KEY_GRAVE),
('-', evdev::Key::KEY_MINUS),
('=', evdev::Key::KEY_EQUAL),
('[', evdev::Key::KEY_LEFTBRACE),
(']', evdev::Key::KEY_RIGHTBRACE),
('\\', evdev::Key::KEY_BACKSLASH),
(',', evdev::Key::KEY_COMMA),
('.', evdev::Key::KEY_DOT),
('/', evdev::Key::KEY_SLASH),
(';', evdev::Key::KEY_SEMICOLON),
('\'', evdev::Key::KEY_APOSTROPHE),
]);
// ((minx, maxx), (miny, maxy))
static ref RESOLUTION: Mutex<((i32, i32), (i32, i32))> = Mutex::new(((0, 0), (0, 0)));
}
fn create_uinput_keyboard() -> ResultType<VirtualDevice> {
// TODO: ensure keys here
let mut keys = AttributeSet::<evdev::Key>::new();
for i in evdev::Key::KEY_ESC.code()..(evdev::Key::BTN_TRIGGER_HAPPY40.code() + 1) {
let key = evdev::Key::new(i);
if !format!("{:?}", &key).contains("unknown key") {
keys.insert(key);
}
}
let mut leds = AttributeSet::<evdev::LedType>::new();
leds.insert(evdev::LedType::LED_NUML);
leds.insert(evdev::LedType::LED_CAPSL);
leds.insert(evdev::LedType::LED_SCROLLL);
let mut miscs = AttributeSet::<evdev::MiscType>::new();
miscs.insert(evdev::MiscType::MSC_SCAN);
let keyboard = VirtualDeviceBuilder::new()?
.name("RustDesk UInput Keyboard")
.with_keys(&keys)?
.with_leds(&leds)?
.with_miscs(&miscs)?
.build()?;
Ok(keyboard)
}
fn map_key(key: &enigo::Key) -> ResultType<evdev::Key> {
if let Some(k) = KEY_MAP.get(&key) {
log::trace!("mapkey {:?}, get {:?}", &key, &k);
return Ok(k.clone());
} else {
match key {
enigo::Key::Layout(c) => {
if let Some(k) = KEY_MAP_LAYOUT.get(&c) {
log::trace!("mapkey {:?}, get {:?}", &key, k);
return Ok(k.clone());
}
}
// enigo::Key::Raw(c) => {
// let k = evdev::Key::new(c);
// if !format!("{:?}", &k).contains("unknown key") {
// return Ok(k.clone());
// }
// }
_ => {}
}
}
bail!("Failed to map key {:?}", &key);
}
async fn ipc_send_data(stream: &mut Connection, data: &Data) {
allow_err!(stream.send(data).await);
}
async fn handle_keyboard(
stream: &mut Connection,
keyboard: &mut VirtualDevice,
data: &DataKeyboard,
) {
log::trace!("handle_keyboard {:?}", &data);
match data {
DataKeyboard::Sequence(_seq) => {
// ignore
}
DataKeyboard::KeyDown(key) => {
if let Ok(k) = map_key(key) {
let down_event = InputEvent::new(EventType::KEY, k.code(), 1);
allow_err!(keyboard.emit(&[down_event]));
}
}
DataKeyboard::KeyUp(key) => {
if let Ok(k) = map_key(key) {
let up_event = InputEvent::new(EventType::KEY, k.code(), 0);
allow_err!(keyboard.emit(&[up_event]));
}
}
DataKeyboard::KeyClick(key) => {
if let Ok(k) = map_key(key) {
let down_event = InputEvent::new(EventType::KEY, k.code(), 1);
let up_event = InputEvent::new(EventType::KEY, k.code(), 0);
allow_err!(keyboard.emit(&[down_event, up_event]));
}
}
DataKeyboard::GetKeyState(key) => {
let key_state = if enigo::Key::CapsLock == *key {
match keyboard.get_led_state() {
Ok(leds) => leds.contains(evdev::LedType::LED_CAPSL),
Err(_e) => {
// log::debug!("Failed to get led state {}", &_e);
false
}
}
} else {
match keyboard.get_key_state() {
Ok(keys) => match key {
enigo::Key::Shift => {
keys.contains(evdev::Key::KEY_LEFTSHIFT)
|| keys.contains(evdev::Key::KEY_RIGHTSHIFT)
}
enigo::Key::Control => {
keys.contains(evdev::Key::KEY_LEFTCTRL)
|| keys.contains(evdev::Key::KEY_RIGHTCTRL)
}
enigo::Key::Alt => {
keys.contains(evdev::Key::KEY_LEFTALT)
|| keys.contains(evdev::Key::KEY_RIGHTALT)
}
enigo::Key::NumLock => keys.contains(evdev::Key::KEY_NUMLOCK),
enigo::Key::Meta => {
keys.contains(evdev::Key::KEY_LEFTMETA)
|| keys.contains(evdev::Key::KEY_RIGHTMETA)
}
_ => false,
},
Err(_e) => {
// log::debug!("Failed to get key state: {}", &_e);
false
}
}
};
ipc_send_data(
stream,
&Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(key_state)),
)
.await;
}
}
}
fn handle_mouse(mouse: &mut mouce::nix::UInputMouseManager, data: &DataMouse) {
log::trace!("handle_mouse {:?}", &data);
match data {
DataMouse::MoveTo(x, y) => {
allow_err!(mouse.move_to(*x as _, *y as _))
}
DataMouse::MoveRelative(x, y) => {
allow_err!(mouse.move_relative(*x, *y))
}
DataMouse::Down(button) => {
let btn = match button {
enigo::MouseButton::Left => mouce::common::MouseButton::Left,
enigo::MouseButton::Middle => mouce::common::MouseButton::Middle,
enigo::MouseButton::Right => mouce::common::MouseButton::Right,
_ => {
return;
}
};
allow_err!(mouse.press_button(&btn))
}
DataMouse::Up(button) => {
let btn = match button {
enigo::MouseButton::Left => mouce::common::MouseButton::Left,
enigo::MouseButton::Middle => mouce::common::MouseButton::Middle,
enigo::MouseButton::Right => mouce::common::MouseButton::Right,
_ => {
return;
}
};
allow_err!(mouse.release_button(&btn))
}
DataMouse::Click(button) => {
let btn = match button {
enigo::MouseButton::Left => mouce::common::MouseButton::Left,
enigo::MouseButton::Middle => mouce::common::MouseButton::Middle,
enigo::MouseButton::Right => mouce::common::MouseButton::Right,
_ => {
return;
}
};
allow_err!(mouse.click_button(&btn))
}
DataMouse::ScrollX(_length) => {
// TODO: not supported for now
}
DataMouse::ScrollY(length) => {
let mut length = *length;
let scroll = if length < 0 {
mouce::common::ScrollDirection::Up
} else {
mouce::common::ScrollDirection::Down
};
if length < 0 {
length = -length;
}
for _ in 0..length {
allow_err!(mouse.scroll_wheel(&scroll))
}
}
}
}
fn spawn_keyboard_handler(mut stream: Connection) {
tokio::spawn(async move {
let mut keyboard = match create_uinput_keyboard() {
Ok(keyboard) => keyboard,
Err(e) => {
log::error!("Failed to create keyboard {}", e);
return;
}
};
loop {
tokio::select! {
res = stream.next() => {
match res {
Err(err) => {
log::info!("UInput keyboard ipc connection closed: {}", err);
break;
}
Ok(Some(data)) => {
match data {
Data::Keyboard(data) => {
handle_keyboard(&mut stream, &mut keyboard, &data).await;
}
_ => {
}
}
}
_ => {}
}
}
}
}
});
}
fn spawn_mouse_handler(mut stream: ipc::Connection) {
let resolution = RESOLUTION.lock().unwrap();
if resolution.0 .0 == resolution.0 .1 || resolution.1 .0 == resolution.1 .1 {
return;
}
let rng_x = resolution.0.clone();
let rng_y = resolution.1.clone();
tokio::spawn(async move {
log::info!(
"Create uinput mouce with rng_x: ({}, {}), rng_y: ({}, {})",
rng_x.0,
rng_x.1,
rng_y.0,
rng_y.1
);
let mut mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) {
Ok(mouse) => mouse,
Err(e) => {
log::error!("Failed to create mouse, {}", e);
return;
}
};
loop {
tokio::select! {
res = stream.next() => {
match res {
Err(err) => {
log::info!("UInput mouse ipc connection closed: {}", err);
break;
}
Ok(Some(data)) => {
match data {
Data::Mouse(data) => {
handle_mouse(&mut mouse, &data);
}
_ => {
}
}
}
_ => {}
}
}
}
}
});
}
fn spawn_controller_handler(mut stream: ipc::Connection) {
tokio::spawn(async move {
loop {
tokio::select! {
res = stream.next() => {
match res {
Err(_err) => {
// log::info!("UInput controller ipc connection closed: {}", err);
break;
}
Ok(Some(data)) => {
match data {
Data::Control(data) => match data {
ipc::DataControl::Resolution{
minx,
maxx,
miny,
maxy,
} => {
*RESOLUTION.lock().unwrap() = ((minx, maxx), (miny, maxy));
allow_err!(stream.send(&Data::Empty).await);
}
}
_ => {
}
}
}
_ => {}
}
}
}
}
});
}
/// Start uinput service.
async fn start_service<F: FnOnce(ipc::Connection) + Copy>(postfix: &str, handler: F) {
match new_listener(postfix).await {
Ok(mut incoming) => {
while let Some(result) = incoming.next().await {
match result {
Ok(stream) => {
log::debug!("Got new connection of uinput ipc {}", postfix);
handler(Connection::new(stream));
}
Err(err) => {
log::error!("Couldn't get uinput mouse client: {:?}", err);
}
}
}
}
Err(err) => {
log::error!("Failed to start uinput mouse ipc service: {}", err);
}
}
}
/// Start uinput keyboard service.
#[tokio::main(flavor = "current_thread")]
pub async fn start_service_keyboard() {
log::info!("start uinput keyboard service");
start_service(IPC_POSTFIX_KEYBOARD, spawn_keyboard_handler).await;
}
/// Start uinput mouse service.
#[tokio::main(flavor = "current_thread")]
pub async fn start_service_mouse() {
log::info!("start uinput mouse service");
start_service(IPC_POSTFIX_MOUSE, spawn_mouse_handler).await;
}
/// Start uinput mouse service.
#[tokio::main(flavor = "current_thread")]
pub async fn start_service_control() {
log::info!("start uinput control service");
start_service(IPC_POSTFIX_CONTROL, spawn_controller_handler).await;
}
pub fn stop_service_keyboard() {
log::info!("stop uinput keyboard service");
}
pub fn stop_service_mouse() {
log::info!("stop uinput mouse service");
}
pub fn stop_service_control() {
log::info!("stop uinput control service");
}
}

View File

@@ -147,7 +147,6 @@ impl VideoQoS {
// handle image_quality change from peer
pub fn update_image_quality(&mut self, image_quality: i32) {
let image_quality = Self::convert_quality(image_quality) as _;
log::debug!("VideoQoS update_image_quality: {}", image_quality);
if self.current_image_quality != image_quality {
self.current_image_quality = image_quality;
let _ = self.generate_bitrate().ok();
@@ -171,7 +170,7 @@ impl VideoQoS {
#[cfg(target_os = "android")]
{
// fix when andorid screen shrinks
let fix = Display::fix_quality() as u32;
let fix = scrap::Display::fix_quality() as u32;
log::debug!("Android screen, fix quality:{}", fix);
let base_bitrate = base_bitrate * fix;
self.target_bitrate = base_bitrate * self.current_image_quality / 100;

View File

@@ -31,6 +31,7 @@ use scrap::{
use std::{
collections::HashSet,
io::{ErrorKind::WouldBlock, Result},
ops::{Deref, DerefMut},
time::{self, Duration, Instant},
};
#[cfg(windows)]
@@ -127,9 +128,11 @@ impl VideoFrameController {
}
}
trait TraitCapturer {
pub(super) trait TraitCapturer {
fn frame<'a>(&'a mut self, timeout: Duration) -> Result<Frame<'a>>;
fn set_use_yuv(&mut self, use_yuv: bool);
#[cfg(windows)]
fn is_gdi(&self) -> bool;
#[cfg(windows)]
@@ -141,6 +144,10 @@ impl TraitCapturer for Capturer {
self.frame(timeout)
}
fn set_use_yuv(&mut self, use_yuv: bool) {
self.set_use_yuv(use_yuv);
}
#[cfg(windows)]
fn is_gdi(&self) -> bool {
self.is_gdi()
@@ -158,6 +165,10 @@ impl TraitCapturer for scrap::CapturerMag {
self.frame(_timeout_ms)
}
fn set_use_yuv(&mut self, use_yuv: bool) {
self.set_use_yuv(use_yuv);
}
fn is_gdi(&self) -> bool {
false
}
@@ -179,6 +190,14 @@ fn check_display_changed(
last_width: usize,
last_hegiht: usize,
) -> bool {
#[cfg(target_os = "linux")]
{
// wayland do not support changing display for now
if !scrap::is_x11() {
return false;
}
}
let displays = match try_get_displays() {
Ok(d) => d,
_ => return false,
@@ -293,6 +312,7 @@ fn ensure_close_virtual_device() -> ResultType<()> {
Ok(())
}
// This function works on privacy mode. Windows only for now.
pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool {
let test_begin = Instant::now();
while test_begin.elapsed().as_millis() < timeout_millis as _ {
@@ -321,9 +341,38 @@ fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> Res
Ok(())
}
fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)]
ensure_close_virtual_device()?;
pub(super) struct CapturerInfo {
pub origin: (i32, i32),
pub width: usize,
pub height: usize,
pub ndisplay: usize,
pub current: usize,
pub privacy_mode_id: i32,
pub _captuerer_privacy_mode_id: i32,
pub capturer: Box<dyn TraitCapturer>,
}
impl Deref for CapturerInfo {
type Target = Box<dyn TraitCapturer>;
fn deref(&self) -> &Self::Target {
&self.capturer
}
}
impl DerefMut for CapturerInfo {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.capturer
}
}
fn get_capturer(use_yuv: bool) -> ResultType<CapturerInfo> {
#[cfg(target_os = "linux")]
{
if !scrap::is_x11() {
return super::wayland::get_capturer();
}
}
let (ndisplay, current, display) = get_current_display()?;
let (origin, width, height) = (display.origin(), display.width(), display.height());
@@ -338,38 +387,6 @@ fn run(sp: GenericService) -> ResultType<()> {
num_cpus::get(),
);
let mut video_qos = VIDEO_QOS.lock().unwrap();
video_qos.set_size(width as _, height as _);
let mut spf = video_qos.spf();
let bitrate = video_qos.generate_bitrate()?;
let abr = video_qos.check_abr_config();
drop(video_qos);
log::info!("init bitrate={}, abr enabled:{}", bitrate, abr);
let encoder_cfg = match Encoder::current_hw_encoder_name() {
Some(codec_name) => EncoderCfg::HW(HwEncoderConfig {
codec_name,
width,
height,
bitrate: bitrate as _,
}),
None => EncoderCfg::VPX(VpxEncoderConfig {
width: width as _,
height: height as _,
timebase: [1, 1000], // Output timestamp precision
bitrate,
codec: VpxVideoCodecId::VP9,
num_threads: (num_cpus::get() / 2) as _,
}),
};
let mut encoder;
match Encoder::new(encoder_cfg) {
Ok(x) => encoder = x,
Err(err) => bail!("Failed to create encoder: {}", err),
}
let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap();
#[cfg(not(windows))]
let captuerer_privacy_mode_id = privacy_mode_id;
@@ -389,17 +406,67 @@ fn run(sp: GenericService) -> ResultType<()> {
} else {
log::info!("In privacy mode, the peer side cannot watch the screen");
}
let mut c = create_capturer(captuerer_privacy_mode_id, display, encoder.use_yuv())?;
let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv)?;
Ok(CapturerInfo {
origin,
width,
height,
ndisplay,
current,
privacy_mode_id,
_captuerer_privacy_mode_id: captuerer_privacy_mode_id,
capturer,
})
}
fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)]
ensure_close_virtual_device()?;
let mut c = get_capturer(true)?;
let mut video_qos = VIDEO_QOS.lock().unwrap();
video_qos.set_size(c.width as _, c.height as _);
let mut spf = video_qos.spf();
let bitrate = video_qos.generate_bitrate()?;
let abr = video_qos.check_abr_config();
drop(video_qos);
log::info!("init bitrate={}, abr enabled:{}", bitrate, abr);
let encoder_cfg = match Encoder::current_hw_encoder_name() {
Some(codec_name) => EncoderCfg::HW(HwEncoderConfig {
codec_name,
width: c.width,
height: c.height,
bitrate: bitrate as _,
}),
None => EncoderCfg::VPX(VpxEncoderConfig {
width: c.width as _,
height: c.height as _,
timebase: [1, 1000], // Output timestamp precision
bitrate,
codec: VpxVideoCodecId::VP9,
num_threads: (num_cpus::get() / 2) as _,
}),
};
let mut encoder;
match Encoder::new(encoder_cfg) {
Ok(x) => encoder = x,
Err(err) => bail!("Failed to create encoder: {}", err),
}
c.set_use_yuv(encoder.use_yuv());
if *SWITCH.lock().unwrap() {
log::debug!("Broadcasting display switch");
let mut misc = Misc::new();
misc.set_switch_display(SwitchDisplay {
display: current as _,
x: origin.0 as _,
y: origin.1 as _,
width: width as _,
height: height as _,
display: c.current as _,
x: c.origin.0 as _,
y: c.origin.1 as _,
width: c.width as _,
height: c.height as _,
..Default::default()
});
let mut msg_out = Message::new();
@@ -419,7 +486,7 @@ fn run(sp: GenericService) -> ResultType<()> {
while sp.ok() {
#[cfg(windows)]
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?;
{
let mut video_qos = VIDEO_QOS.lock().unwrap();
@@ -437,11 +504,11 @@ fn run(sp: GenericService) -> ResultType<()> {
if *SWITCH.lock().unwrap() {
bail!("SWITCH");
}
if current != *CURRENT_DISPLAY.lock().unwrap() {
if c.current != *CURRENT_DISPLAY.lock().unwrap() {
*SWITCH.lock().unwrap() = true;
bail!("SWITCH");
}
check_privacy_mode_changed(&sp, privacy_mode_id)?;
check_privacy_mode_changed(&sp, c.privacy_mode_id)?;
#[cfg(windows)]
{
if crate::platform::windows::desktop_changed() {
@@ -451,7 +518,7 @@ fn run(sp: GenericService) -> ResultType<()> {
let now = time::Instant::now();
if last_check_displays.elapsed().as_millis() > 1000 {
last_check_displays = now;
if ndisplay != get_display_num() {
if c.ndisplay != get_display_num() {
log::info!("Displays changed");
*SWITCH.lock().unwrap() = true;
bail!("SWITCH");
@@ -515,7 +582,7 @@ fn run(sp: GenericService) -> ResultType<()> {
}
}
Err(err) => {
if check_display_changed(ndisplay, current, width, height) {
if check_display_changed(c.ndisplay, c.current, c.width, c.height) {
log::info!("Displays changed");
*SWITCH.lock().unwrap() = true;
bail!("SWITCH");
@@ -537,9 +604,9 @@ fn run(sp: GenericService) -> ResultType<()> {
let timeout_millis = 3_000u64;
let wait_begin = Instant::now();
while wait_begin.elapsed().as_millis() < timeout_millis as _ {
check_privacy_mode_changed(&sp, privacy_mode_id)?;
check_privacy_mode_changed(&sp, c.privacy_mode_id)?;
#[cfg(windows)]
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?;
frame_controller.try_wait_next(&mut fetched_conn_ids, 300);
// break if all connections have received current frame
if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() {
@@ -633,6 +700,17 @@ pub fn handle_one_frame_encoded(
}
fn get_display_num() -> usize {
#[cfg(target_os = "linux")]
{
if !scrap::is_x11() {
return if let Ok(n) = super::wayland::get_display_num() {
n
} else {
0
};
}
}
if let Ok(d) = try_get_displays() {
d.len()
} else {
@@ -640,14 +718,10 @@ fn get_display_num() -> usize {
}
}
pub fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
// switch to primary display if long time (30 seconds) no users
if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 {
*CURRENT_DISPLAY.lock().unwrap() = usize::MAX;
}
pub(super) fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) {
let mut displays = Vec::new();
let mut primary = 0;
for (i, d) in try_get_displays()?.iter().enumerate() {
for (i, d) in all.iter().enumerate() {
if d.is_primary() {
primary = i;
}
@@ -665,12 +739,26 @@ pub fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
if *lock >= displays.len() {
*lock = primary
}
Ok((*lock, displays))
(*lock, displays)
}
pub fn switch_display(i: i32) {
pub async fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
#[cfg(target_os = "linux")]
{
if !scrap::is_x11() {
return super::wayland::get_displays().await;
}
}
// switch to primary display if long time (30 seconds) no users
if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 {
*CURRENT_DISPLAY.lock().unwrap() = usize::MAX;
}
Ok(get_displays_2(&try_get_displays()?))
}
pub async fn switch_display(i: i32) {
let i = i as usize;
if let Ok((_, displays)) = get_displays() {
if let Ok((_, displays)) = get_displays().await {
if i < displays.len() {
*CURRENT_DISPLAY.lock().unwrap() = i;
}
@@ -684,6 +772,16 @@ pub fn refresh() {
}
fn get_primary() -> usize {
#[cfg(target_os = "linux")]
{
if !scrap::is_x11() {
return match super::wayland::get_primary() {
Ok(n) => n,
Err(_) => 0,
};
}
}
if let Ok(all) = try_get_displays() {
for (i, d) in all.iter().enumerate() {
if d.is_primary() {
@@ -694,8 +792,8 @@ fn get_primary() -> usize {
0
}
pub fn switch_to_primary() {
switch_display(get_primary() as _);
pub async fn switch_to_primary() {
switch_display(get_primary() as _).await;
}
#[cfg(not(windows))]
@@ -733,16 +831,15 @@ fn try_get_displays() -> ResultType<Vec<Display>> {
Ok(displays)
}
fn get_current_display() -> ResultType<(usize, usize, Display)> {
pub(super) fn get_current_display_2(mut all: Vec<Display>) -> ResultType<(usize, usize, Display)> {
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
let mut displays = try_get_displays()?;
if displays.len() == 0 {
if all.len() == 0 {
bail!("No displays");
}
let n = displays.len();
let n = all.len();
if current >= n {
current = 0;
for (i, d) in displays.iter().enumerate() {
for (i, d) in all.iter().enumerate() {
if d.is_primary() {
current = i;
break;
@@ -750,5 +847,9 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> {
}
*CURRENT_DISPLAY.lock().unwrap() = current;
}
return Ok((n, current, displays.remove(current)));
return Ok((n, current, all.remove(current)));
}
fn get_current_display() -> ResultType<(usize, usize, Display)> {
get_current_display_2(try_get_displays()?)
}

179
src/server/wayland.rs Normal file
View File

@@ -0,0 +1,179 @@
use super::*;
use hbb_common::allow_err;
use scrap::{Capturer, Display, Frame};
use std::{io::Result, time::Duration};
lazy_static::lazy_static! {
static ref CAP_DISPLAY_INFO: RwLock<u64> = RwLock::new(0);
}
struct CapDisplayInfo {
rects: Vec<((i32, i32), usize, usize)>,
displays: Vec<DisplayInfo>,
num: usize,
primary: usize,
current: usize,
capturer: *mut Capturer,
}
impl super::video_service::TraitCapturer for *mut Capturer {
fn frame<'a>(&'a mut self, timeout: Duration) -> Result<Frame<'a>> {
unsafe { (**self).frame(timeout) }
}
fn set_use_yuv(&mut self, use_yuv: bool) {
unsafe {
(**self).set_use_yuv(use_yuv);
}
}
}
async fn check_init() -> ResultType<()> {
if !scrap::is_x11() {
let mut minx = 0;
let mut maxx = 0;
let mut miny = 0;
let mut maxy = 0;
if *CAP_DISPLAY_INFO.read().unwrap() == 0 {
let mut lock = CAP_DISPLAY_INFO.write().unwrap();
if *lock == 0 {
let all = Display::all()?;
let num = all.len();
let (primary, displays) = super::video_service::get_displays_2(&all);
let mut rects: Vec<((i32, i32), usize, usize)> = Vec::new();
for d in &all {
rects.push((d.origin(), d.width(), d.height()));
}
let (ndisplay, current, display) =
super::video_service::get_current_display_2(all)?;
let (origin, width, height) = (display.origin(), display.width(), display.height());
log::debug!(
"#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}",
ndisplay,
current,
&origin,
width,
height,
num_cpus::get_physical(),
num_cpus::get(),
);
minx = origin.0;
maxx = origin.0 + width as i32;
miny = origin.1;
maxy = origin.1 + height as i32;
let capturer = Box::into_raw(Box::new(
Capturer::new(display, true).with_context(|| "Failed to create capturer")?,
));
let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo {
rects,
displays,
num,
primary,
current,
capturer,
}));
*lock = cap_display_info as _;
}
}
if minx != maxx && miny != maxy {
log::info!(
"send uinput resolution: ({}, {}), ({}, {})",
minx,
maxx,
miny,
maxy
);
allow_err!(input_service::set_uinput_resolution(minx, maxx, miny, maxy).await);
allow_err!(input_service::set_uinput().await);
}
}
Ok(())
}
pub fn clear() {
if scrap::is_x11() {
return;
}
let mut lock = CAP_DISPLAY_INFO.write().unwrap();
if *lock != 0 {
unsafe {
let cap_display_info = Box::from_raw(*lock as *mut CapDisplayInfo);
let _ = Box::from_raw(cap_display_info.capturer);
}
*lock = 0;
}
}
pub(super) async fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
check_init().await?;
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
let primary = cap_display_info.primary;
let displays = cap_display_info.displays.clone();
Ok((primary, displays))
}
} else {
bail!("Failed to get capturer display info");
}
}
pub(super) fn get_primary() -> ResultType<usize> {
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
Ok(cap_display_info.primary)
}
} else {
bail!("Failed to get capturer display info");
}
}
pub(super) fn get_display_num() -> ResultType<usize> {
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
Ok(cap_display_info.num)
}
} else {
bail!("Failed to get capturer display info");
}
}
pub(super) fn get_capturer() -> ResultType<super::video_service::CapturerInfo> {
if scrap::is_x11() {
bail!("Do not call this function if not wayland");
}
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
let rect = cap_display_info.rects[cap_display_info.current];
Ok(super::video_service::CapturerInfo {
origin: rect.0,
width: rect.1,
height: rect.2,
ndisplay: cap_display_info.num,
current: cap_display_info.current,
privacy_mode_id: 0,
_captuerer_privacy_mode_id: 0,
capturer: Box::new(cap_display_info.capturer),
})
}
} else {
bail!("Failed to get capturer display info");
}
}