Add a simple clipboard
This commit is contained in:
parent
ac33b294c2
commit
6ac6a0a522
69
frontend/src/components/clipboard.rs
Normal file
69
frontend/src/components/clipboard.rs
Normal 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/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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]) {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user