Merge pull request #15 from HsuJv/dev

Add a simple clipboard
This commit is contained in:
Jovi Hsu 2021-11-27 16:19:24 +08:00 committed by GitHub
commit 4d63056d07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 154 additions and 34 deletions

View File

@ -0,0 +1,69 @@
use yew::prelude::*;
use crate::utils::WeakComponentLink;
pub enum ClipboardMsg {
UpdateClipboard(String),
SendClipboard,
}
pub struct Clipboard {
link: ComponentLink<Self>,
onsubmit: Callback<String>,
text: String,
}
// Props
#[derive(Clone, PartialEq, Properties)]
pub struct ClipboardProps {
#[prop_or_default]
pub weak_link: WeakComponentLink<Clipboard>,
pub onsubmit: Callback<String>,
}
impl Component for Clipboard {
type Message = ClipboardMsg;
type Properties = ClipboardProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
props.weak_link.borrow_mut().replace(link.clone());
Clipboard {
link,
onsubmit: props.onsubmit,
text: String::new(),
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
ClipboardMsg::UpdateClipboard(text) => {
self.text = text;
}
ClipboardMsg::SendClipboard => {
self.onsubmit.emit(self.text.clone());
self.text.clear();
}
}
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
false
}
fn view(&self) -> Html {
let update_clipboard = self.link.callback(|e: ChangeData| match e {
ChangeData::Value(v) => ClipboardMsg::UpdateClipboard(v),
_ => panic!("unexpected message"),
});
let set_clipboard = self.link.callback(|_| ClipboardMsg::SendClipboard);
html! {
<>
<textarea rows="5" cols="60" id="clipboard" onchange=update_clipboard value=self.text.clone()/>
<br/>
<button id="clipboard-send" onclick=set_clipboard> {"Send to peer"} </button>
<br/>
</>
}
}
}

View File

@ -1,4 +1,5 @@
pub mod auth; pub mod auth;
pub mod clipboard;
pub mod host; pub mod host;
pub mod input; pub mod input;
pub mod ws; pub mod ws;

View File

@ -26,7 +26,7 @@ pub struct PageRemote {
fetch_task: Option<FetchTask>, fetch_task: Option<FetchTask>,
connected: bool, connected: bool,
handler: ProtocalHandler<VncHandler>, handler: ProtocalHandler<VncHandler>,
ws_link: WeakComponentLink<components::ws::WebsocketCtx>, websocket: WeakComponentLink<components::ws::WebsocketCtx>,
request_username: bool, request_username: bool,
request_password: bool, request_password: bool,
username: String, username: String,
@ -34,6 +34,7 @@ pub struct PageRemote {
canvas: NodeRef, canvas: NodeRef,
canvas_ctx: Option<CanvasRenderingContext2d>, canvas_ctx: Option<CanvasRenderingContext2d>,
interval: Option<Interval>, interval: Option<Interval>,
clipboard: WeakComponentLink<components::clipboard::Clipboard>,
} }
#[derive(Clone, PartialEq, Properties)] #[derive(Clone, PartialEq, Properties)]
@ -47,6 +48,7 @@ pub enum RemoteMsg {
Send(Vec<u8>), Send(Vec<u8>),
UpdateUsername(String), UpdateUsername(String),
UpdatePassword(String), UpdatePassword(String),
UpdateClipboard(String),
SendCredential, SendCredential,
RequireFrame(u8), RequireFrame(u8),
} }
@ -63,7 +65,7 @@ impl Component for PageRemote {
fetch_task: None, fetch_task: None,
connected: false, connected: false,
handler: ProtocalHandler::new(), handler: ProtocalHandler::new(),
ws_link: WeakComponentLink::default(), websocket: WeakComponentLink::default(),
request_username: false, request_username: false,
request_password: false, request_password: false,
username: String::from(""), username: String::from(""),
@ -71,6 +73,7 @@ impl Component for PageRemote {
canvas: NodeRef::default(), canvas: NodeRef::default(),
canvas_ctx: None, canvas_ctx: None,
interval: None, interval: None,
clipboard: WeakComponentLink::default(),
} }
} }
@ -129,7 +132,7 @@ impl Component for PageRemote {
self.protocal_out_handler() self.protocal_out_handler()
} }
RemoteMsg::Send(v) => { RemoteMsg::Send(v) => {
self.ws_link self.websocket
.borrow() .borrow()
.as_ref() .as_ref()
.unwrap() .unwrap()
@ -160,6 +163,14 @@ impl Component for PageRemote {
} }
self.protocal_out_handler() self.protocal_out_handler()
} }
RemoteMsg::UpdateClipboard(clipboard) => {
if clipboard.len() > 0 {
self.handler.set_clipboard(&clipboard);
self.protocal_out_handler()
} else {
false
}
}
} }
} }
@ -178,7 +189,9 @@ impl Component for PageRemote {
} }
} else { } else {
let recv_msg = self.link.callback(RemoteMsg::Recv); let recv_msg = self.link.callback(RemoteMsg::Recv);
let ws_link = &self.ws_link; let clipboard_update = self.link.callback(RemoteMsg::UpdateClipboard);
let websocket = &self.websocket;
let clipboard = &self.clipboard;
html! { html! {
<> <>
<div class="horizontal-centre vertical-centre"> <div class="horizontal-centre vertical-centre">
@ -186,8 +199,10 @@ impl Component for PageRemote {
{self.password_view()} {self.password_view()}
{self.button_connect_view()} {self.button_connect_view()}
<components::ws::WebsocketCtx <components::ws::WebsocketCtx
weak_link=ws_link onrecv=recv_msg/> weak_link=websocket onrecv=recv_msg/>
<canvas id="remote-canvas" ref=self.canvas.clone()></canvas> <canvas id="remote-canvas" ref=self.canvas.clone()></canvas>
<components::clipboard::Clipboard
weak_link=clipboard onsubmit=clipboard_update/>
{self.error_msg.clone()} {self.error_msg.clone()}
</div> </div>
</> </>
@ -212,7 +227,7 @@ impl PageRemote {
match o { match o {
ProtocalHandlerOutput::Err(err) => { ProtocalHandlerOutput::Err(err) => {
self.error_msg = err.clone(); self.error_msg = err.clone();
self.ws_link self.websocket
.borrow_mut() .borrow_mut()
.as_mut() .as_mut()
.unwrap() .unwrap()
@ -220,8 +235,10 @@ impl PageRemote {
should_render = true; should_render = true;
} }
ProtocalHandlerOutput::WsBuf(out) => { ProtocalHandlerOutput::WsBuf(out) => {
if out.len() > 0 {
self.link.send_message(RemoteMsg::Send(out)); self.link.send_message(RemoteMsg::Send(out));
} }
}
ProtocalHandlerOutput::RequirePassword => { ProtocalHandlerOutput::RequirePassword => {
self.request_password = true; self.request_password = true;
should_render = true; should_render = true;
@ -273,9 +290,11 @@ impl PageRemote {
should_render = true; should_render = true;
} }
ProtocalHandlerOutput::SetClipboard(text) => { ProtocalHandlerOutput::SetClipboard(text) => {
self.error_msg = format!("Clipboard get {}", text); self.clipboard.borrow_mut().as_mut().unwrap().send_message(
ConsoleService::log(&self.error_msg); components::clipboard::ClipboardMsg::UpdateClipboard(text),
should_render = true; );
// ConsoleService::log(&self.error_msg);
should_render = false;
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
@ -330,6 +349,7 @@ impl PageRemote {
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let handler = self.handler.clone(); let handler = self.handler.clone();
let key_down = move |e: KeyboardEvent| { let key_down = move |e: KeyboardEvent| {
e.prevent_default();
e.stop_propagation(); e.stop_propagation();
handler.key_press(e, true); handler.key_press(e, true);
}; };
@ -345,6 +365,7 @@ impl PageRemote {
let handler = self.handler.clone(); let handler = self.handler.clone();
let key_up = move |e: KeyboardEvent| { let key_up = move |e: KeyboardEvent| {
e.prevent_default();
e.stop_propagation(); e.stop_propagation();
handler.key_press(e, false); handler.key_press(e, false);
}; };

View File

@ -68,6 +68,10 @@ where
.set_credential(username, password); .set_credential(username, password);
} }
pub fn set_clipboard(&mut self, text: &str) {
self.inner.as_ref().lock().unwrap().set_clipboard(text);
}
pub fn set_resolution(&self, width: u16, height: u16) { pub fn set_resolution(&self, width: u16, height: u16) {
self.inner self.inner
.as_ref() .as_ref()
@ -100,6 +104,7 @@ pub trait ProtocalImpl {
fn do_input(&mut self, input: Vec<u8>); fn do_input(&mut self, input: Vec<u8>);
fn get_output(&mut self) -> Vec<ProtocalHandlerOutput>; fn get_output(&mut self) -> Vec<ProtocalHandlerOutput>;
fn set_credential(&mut self, username: &str, password: &str); fn set_credential(&mut self, username: &str, password: &str);
fn set_clipboard(&mut self, text: &str);
fn set_resolution(&mut self, width: u16, height: u16); fn set_resolution(&mut self, width: u16, height: u16);
fn key_press(&mut self, key: web_sys::KeyboardEvent, down: bool); fn key_press(&mut self, key: web_sys::KeyboardEvent, down: bool);
fn mouse_event(&mut self, mouse: web_sys::MouseEvent, et: MouseEventType); fn mouse_event(&mut self, mouse: web_sys::MouseEvent, et: MouseEventType);
@ -233,18 +238,18 @@ impl<'a> StreamWriter<'a> {
self.write_u32(b as u32); self.write_u32(b as u32);
} }
pub fn write_string_with_len(&mut self, s: &str) { pub fn write_string(&mut self, s: &str) {
self.inner.extend_from_slice(s.as_bytes()); self.inner.extend_from_slice(s.as_bytes());
} }
pub fn write_string_l16(&mut self, s: &str) { pub fn write_string_l16(&mut self, s: &str) {
self.write_u16(s.len() as u16); self.write_u16(s.len() as u16);
self.write_string_with_len(s); self.write_string(s);
} }
pub fn write_string_l32(&mut self, s: &str) { pub fn write_string_l32(&mut self, s: &str) {
self.write_u32(s.len() as u32); self.write_u32(s.len() as u32);
self.write_string_with_len(s); self.write_string(s);
} }
pub fn write_slice(&mut self, s: &[u8]) { pub fn write_slice(&mut self, s: &[u8]) {

View File

@ -123,6 +123,7 @@ impl ProtocalImpl for VncHandler {
} }
fn get_output(&mut self) -> Vec<ProtocalHandlerOutput> { fn get_output(&mut self) -> Vec<ProtocalHandlerOutput> {
if let ServerMessage::None = self.msg_handling {
let mut out = Vec::with_capacity(self.outs.len()); let mut out = Vec::with_capacity(self.outs.len());
// ConsoleService::log(&format!("Get {} output", self.outs.len())); // ConsoleService::log(&format!("Get {} output", self.outs.len()));
for o in self.outs.drain(..) { for o in self.outs.drain(..) {
@ -132,7 +133,10 @@ impl ProtocalImpl for VncHandler {
out.push(ProtocalHandlerOutput::WsBuf(self.outbuf.clone())); out.push(ProtocalHandlerOutput::WsBuf(self.outbuf.clone()));
self.outbuf.clear(); self.outbuf.clear();
} }
out return out;
} else {
return Vec::new();
}
} }
fn set_credential(&mut self, _username: &str, password: &str) { fn set_credential(&mut self, _username: &str, password: &str) {
@ -162,6 +166,10 @@ impl ProtocalImpl for VncHandler {
self.require = 4; // the auth result message length self.require = 4; // the auth result message length
} }
fn set_clipboard(&mut self, text: &str) {
self.send_client_cut_text(text);
}
fn set_resolution(&mut self, _width: u16, _height: u16) { fn set_resolution(&mut self, _width: u16, _height: u16) {
// VNC client doen't support resolution change // VNC client doen't support resolution change
} }
@ -171,20 +179,16 @@ impl ProtocalImpl for VncHandler {
return; return;
} }
let key = x11keyboard::KeyboardUtils::get_keysym(key); let key = x11keyboard::KeyboardUtils::get_keysym(key);
if let ServerMessage::None = self.msg_handling {
self.send_key_event(key, down); self.send_key_event(key, down);
} }
}
fn mouse_event(&mut self, mouse: web_sys::MouseEvent, et: MouseEventType) { fn mouse_event(&mut self, mouse: web_sys::MouseEvent, et: MouseEventType) {
if self.state != VncState::Connected { if self.state != VncState::Connected {
return; return;
} }
let (x, y, mask) = self.mouse.get_mouse_sym(mouse, et); let (x, y, mask) = self.mouse.get_mouse_sym(mouse, et);
if let ServerMessage::None = self.msg_handling {
self.send_pointer_event(x, y, mask); self.send_pointer_event(x, y, mask);
} }
}
fn require_frame(&mut self, incremental: u8) { fn require_frame(&mut self, incremental: u8) {
if 0 == incremental { if 0 == incremental {
@ -324,7 +328,29 @@ impl VncHandler {
sw.write_u16(x); // x sw.write_u16(x); // x
sw.write_u16(y); // y sw.write_u16(y); // y
ConsoleService::log(&format!("send mouse event {:x?} {:x?} {:#08b}", x, y, mask)); // ConsoleService::log(&format!("send mouse event {:x?} {:x?} {:#08b}", x, y, mask));
self.outbuf.extend_from_slice(&out);
}
// +--------------+--------------+--------------+
// | No. of bytes | Type [Value] | Description |
// +--------------+--------------+--------------+
// | 1 | U8 [6] | message-type |
// | 3 | | padding |
// | 4 | U32 | length |
// | length | U8 array | text |
// +--------------+--------------+--------------+
fn send_client_cut_text(&mut self, text: &str) {
let mut out = Vec::with_capacity(10);
let mut sw = StreamWriter::new(&mut out);
let len: u32 = text.len().try_into().unwrap_or(0);
sw.write_u8(6); // message-type
sw.write_u8(0); // padding
sw.write_u16(0); // padding
sw.write_u32(len); // length
sw.write_string(text); // text
// ConsoleService::log(&format!("send client cut text {:?}", len));
self.outbuf.extend_from_slice(&out); self.outbuf.extend_from_slice(&out);
} }
@ -546,8 +572,6 @@ impl VncHandler {
self.require = 12; // the length of the next rectangle hdr self.require = 12; // the length of the next rectangle hdr
} }
} }
// ConsoleService::log(&format!("{} rects left", self.num_rects_left));
} }
// Currently there is little or no support for colour maps. Some preliminary work was done // Currently there is little or no support for colour maps. Some preliminary work was done

View File

@ -3,7 +3,7 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(non_upper_case_globals)] #![allow(non_upper_case_globals)]
use yew::services::ConsoleService;
// referring: // referring:
// https://github.com/AltF02/x11-rs/blob/master/src/keysym.rs // https://github.com/AltF02/x11-rs/blob/master/src/keysym.rs
@ -1347,7 +1347,7 @@ impl KeyboardUtils {
let capslock = event.get_modifier_state("CapsLock"); let capslock = event.get_modifier_state("CapsLock");
let upper = capslock ^ shift; let upper = capslock ^ shift;
let which = event.which(); let which = event.which();
ConsoleService::log(&format!("which {}, shift {}", which, shift)); // ConsoleService::log(&format!("which {}, shift {}", which, shift));
match which { match which {
8_u32 => { 8_u32 => {
// Backspace // Backspace
@ -1359,7 +1359,7 @@ impl KeyboardUtils {
} }
13_u32 => { 13_u32 => {
// Enter // Enter
XK_Linefeed XK_Return
} }
16_u32 => { 16_u32 => {
// ShiftLeft // ShiftLeft