diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index d70e508..989ec09 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -5,8 +5,6 @@ mod protocal; mod utils; use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use web_sys::{console, KeyboardEvent}; #[cfg(feature = "wee_alloc")] #[global_allocator] @@ -14,20 +12,6 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[wasm_bindgen] pub fn run_app() -> Result<(), JsValue> { - let window = web_sys::window().unwrap(); - let handler_submit = move |e: KeyboardEvent| { - e.stop_propagation(); - console::log_1(&format!("{:?}", e).into()) - }; - - let handler = Box::new(handler_submit) as Box; - - let cb = Closure::wrap(handler); - - window - .add_event_listener_with_callback("keydown", cb.as_ref().unchecked_ref()) - .unwrap(); - cb.forget(); yew::start_app::(); Ok(()) diff --git a/frontend/src/pages/page_remote.rs b/frontend/src/pages/page_remote.rs index 463b9cb..45bc9ad 100644 --- a/frontend/src/pages/page_remote.rs +++ b/frontend/src/pages/page_remote.rs @@ -1,5 +1,5 @@ use serde_json::{json, Value}; -use wasm_bindgen::{Clamped, JsValue}; +use wasm_bindgen::{prelude::Closure, Clamped, JsCast, JsValue}; use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData}; use yew::{ format::Json, @@ -121,6 +121,22 @@ impl Component for PageRemote { true } RemoteMsg::Connected => { + let window = web_sys::window().unwrap(); + let handler = self.handler.clone(); + let key_down = move |e: KeyboardEvent| { + e.stop_propagation(); + handler.key_down(e.key_code()); + }; + + let handler = Box::new(key_down) as Box; + + let cb = Closure::wrap(handler); + + window + .add_event_listener_with_callback("keypress", cb.as_ref().unchecked_ref()) + .unwrap(); + cb.forget(); + self.connected = true; true } @@ -155,7 +171,7 @@ impl Component for PageRemote { if self.interval.is_none() { let link = self.link.clone(); let tick = - Interval::new(250, move || link.send_message(RemoteMsg::RequireFrame(1))); + Interval::new(20, move || link.send_message(RemoteMsg::RequireFrame(1))); self.interval = Some(tick); } self.protocal_out_handler() diff --git a/frontend/src/protocal/common.rs b/frontend/src/protocal/common.rs index 53d3054..f8b12d6 100644 --- a/frontend/src/protocal/common.rs +++ b/frontend/src/protocal/common.rs @@ -1,3 +1,8 @@ +use std::{ + rc::Rc, + sync::{Mutex}, +}; + pub struct CanvasData { pub x: u16, pub y: u16, @@ -20,7 +25,18 @@ pub struct ProtocalHandler where T: ProtocalImpl, { - inner: T, + inner: Rc>, +} + +impl Clone for ProtocalHandler +where + T: ProtocalImpl, +{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } } impl ProtocalHandler @@ -28,27 +44,45 @@ where T: ProtocalImpl, { pub fn new() -> Self { - Self { inner: T::new() } + Self { + inner: Rc::new(Mutex::new(T::new())), + } } - pub fn do_input(&mut self, input: Vec) { - self.inner.do_input(input); + pub fn do_input(&self, input: Vec) { + self.inner.as_ref().lock().unwrap().do_input(input); } - pub fn get_output(&mut self) -> Vec { - self.inner.get_output() + pub fn get_output(&self) -> Vec { + self.inner.as_ref().lock().unwrap().get_output() } - pub fn set_credential(&mut self, username: &str, password: &str) { - self.inner.set_credential(username, password); + pub fn set_credential(&self, username: &str, password: &str) { + self.inner + .as_ref() + .lock() + .unwrap() + .set_credential(username, password); } - pub fn set_resolution(&mut self, width: u16, height: u16) { - self.inner.set_resolution(width, height); + pub fn set_resolution(&self, width: u16, height: u16) { + self.inner + .as_ref() + .lock() + .unwrap() + .set_resolution(width, height); } - pub fn require_frame(&mut self, incremental: u8) { - self.inner.require_frame(incremental); + pub fn require_frame(&self, incremental: u8) { + self.inner + .as_ref() + .lock() + .unwrap() + .require_frame(incremental); + } + + pub fn key_down(&self, key: u32) { + self.inner.as_ref().lock().unwrap().key_down(key); } } @@ -58,6 +92,7 @@ pub trait ProtocalImpl { fn get_output(&mut self) -> Vec; fn set_credential(&mut self, username: &str, password: &str); fn set_resolution(&mut self, width: u16, height: u16); + fn key_down(&mut self, key: u32); fn require_frame(&mut self, incremental: u8); } diff --git a/frontend/src/protocal/vnc.rs b/frontend/src/protocal/vnc.rs index 2fa31ef..37e07f7 100644 --- a/frontend/src/protocal/vnc.rs +++ b/frontend/src/protocal/vnc.rs @@ -8,6 +8,10 @@ const VNC_RFB38: &[u8; 12] = b"RFB 003.008\n"; const VNC_VER_UNSUPPORTED: &str = "unsupported version"; const VNC_FAILED: &str = "Connection failed with unknow reason"; +fn jskey_to_x11(key: u32) -> u32 { + key +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum SecurityType { @@ -35,6 +39,7 @@ pub enum VncEncoding { DesktopSizePseudo = -223, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum VncState { Init, Handshake, @@ -162,6 +167,16 @@ impl ProtocalImpl for VncHandler { // VNC client doen't support resolution change } + fn key_down(&mut self, key: u32) { + if self.state != VncState::Connected { + return; + } + let key = jskey_to_x11(key); + if let ServerMessage::None = self.msg_handling { + self.send_key_event(key, true); + } + } + fn require_frame(&mut self, incremental: u8) { if 0 == incremental { // first frame @@ -264,6 +279,25 @@ impl VncHandler { self.outbuf.extend_from_slice(&out); } + // +--------------+--------------+--------------+ + // | No. of bytes | Type [Value] | Description | + // +--------------+--------------+--------------+ + // | 1 | U8 [4] | message-type | + // | 1 | U8 | down-flag | + // | 2 | | padding | + // | 4 | U32 | key | + // +--------------+--------------+--------------+ + fn send_key_event(&mut self, key: u32, down: bool) { + let mut out = Vec::with_capacity(10); + let mut sw = StreamWriter::new(&mut out); + sw.write_u8(4); // message-type + sw.write_u8(if down { 1 } else { 0 }); // down + sw.write_u16(0); // padding + sw.write_u32(key); // key + ConsoleService::log(&format!("send key event {:x?} {:?}", key, down)); + self.outbuf.extend_from_slice(&out); + } + // No. of bytes Type [Value] Description // 1 CARD8 3 message-type // 1 CARD8 incremental