Merge pull request #24 from HsuJv/dev

Regular merge from the dev
This commit is contained in:
Jovi Hsu 2022-10-08 15:09:13 +08:00 committed by GitHub
commit e72a718b94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 303 additions and 51 deletions

View File

@ -24,6 +24,7 @@ cd ${WEBSOCKIFY} && cargo build --release && cp ./target/release/${WEBSOCKIFY} $
[tasks.install-dir]
script = '''
mkdir -p $INSTALL_PATH
cp asserts/* $INSTALL_PATH
'''
[env]

2
asserts/jquery-3.6.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -30,6 +30,7 @@ features = [
"Document",
"ErrorEvent",
"FileReader",
"HtmlButtonElement",
"HtmlCanvasElement",
"ImageData",
"Location",

View File

@ -0,0 +1,79 @@
#canvas {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.clipboardback {
position: absolute;
z-index: 2;
width: 100%;
height: 100%;
pointer-events: none;
overflow: hidden;
background: none;
transition: all 0.3s linear;
}
.clipboard {
position: relative;
float: right;
width: 320px;
right: -300px;
height: 100%;
transition: all 0.3s ease-out;
}
#clipboardbtn {
position: absolute;
width: 20px;
height: fit-content;
top: 50%;
bottom: 50%;
margin: auto;
pointer-events: visible;
border-bottom-left-radius: 25px;
border-top-left-radius: 25px;
border-style: none;
outline: none;
float: left;
background: black;
color: white;
padding-top: 4px;
transition: all 0.3s linear;
font-size: 20px;
font-weight: bold;
word-wrap: break-word;
}
#clipboardbox {
position: relative;
width: 300px;
height: 50%;
top: 50%;
transform: translateY(-50%);
float: right;
background: white;
border-bottom-left-radius: 15px;
border-top-left-radius: 15px;
}
.clipboardback-open {
background: rgba(0, 0, 0, 0.6);
}
.clipboard-open {
right: 0;
}
#clipboardtxt {
position: relative;
width: 90%;
margin: auto;
overflow: auto;
resize: none;
word-break: break-all;
}

View File

@ -5,18 +5,6 @@
<meta charset="utf-8" />
<title>Web Gateway</title>
<style type="text/css">
.navbar {
width: 100%;
height: 20px;
background-color: #66ccff;
}
.navbar .navbar-item {
margin: auto;
position: relative;
padding: 40px;
}
.horizontal-centre {
position: relative;
text-align: center;
@ -32,15 +20,11 @@
height: 100%;
margin: 0;
}
.content {
min-height: calc(100% - 58px - 40px);
}
.footer {
height: 58px;
}
</style>
<style>
@import url("clipboard.css");
</style>
<script src="jquery-3.6.1.min.js" type="text/javascript"></script>
<script type="module" defer>
import init from "/webvnc.js";
await init();
@ -48,5 +32,64 @@
</head>
<body>
<div class="horizontal-centre vertical-centre"><canvas id="vnc-canvas" tabIndex=1></canvas></div>
</body>
<div id="vnc_status" style="position: relative; height: auto;" class="horizontal-centre vertical-centre"></div>
<div id="canvas" class="horizontal-centre vertical-centre">
<canvas id="vnc-canvas" tabIndex=1></canvas>
<button type="button" id="ctrlaltdel" style="display: inline; position:absolute; right: 10px; top: 10px;">Send
CtrlAltDel</button>
</div>
<div class="clipboardback">
<div class="clipboard">
<button id="clipboardbtn">clipboard</button>
<div id="clipboardbox" class="horizontal-centre vertical-centre">
<div style="position: relative; top: 50%; transform: translateY(-50%);">
<div><textarea id="clipboardtxt" rows="30"></textarea></div>
<div><button id="clipboardsend">Send</button></div>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript" defer>
$("#clipboardbtn").attr("open1", 0);
$("#clipboardbtn").click(
function (e) {
e.stopPropagation();
if (($("#clipboardbtn")).attr("open1") == 0) {
open();
} else {
close();
}
}
);
$(".clipboardback").click(function () {
close();
})
$(".clipboard").click(function () {
event.stopPropagation();
})
function open() {
$("#clipboardbtn").attr("open1", 1);
$("#clipboardbtn").html(">")
$(".clipboard").toggleClass("clipboard-open");
$(".clipboardback").toggleClass("clipboardback-open");
$(".clipboardback").css("pointer-events", "auto");
}
function close() {
$("#clipboardbtn").attr("open1", 0);
$("#clipboardbtn").html("clipboard")
$(".clipboard").toggleClass("clipboard-open");
$(".clipboardback").toggleClass("clipboardback-open");
$(".clipboardback").css("pointer-events", "none");
}
function setClipBoard(s) {
$("#clipboardtxt").val(s);
}
function getClipBoard() {
return $("#clipboardtxt").val();
}
</script>

View File

@ -3,7 +3,9 @@ use std::rc::Rc;
use crate::vnc::{ImageData, MouseEventType, Vnc};
use wasm_bindgen::prelude::*;
use wasm_bindgen::{Clamped, JsCast};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, KeyboardEvent, MouseEvent};
use web_sys::{
CanvasRenderingContext2d, HtmlButtonElement, HtmlCanvasElement, KeyboardEvent, MouseEvent,
};
struct Canvas {
canvas: HtmlCanvasElement,
ctx: CanvasRenderingContext2d,
@ -67,6 +69,26 @@ impl Canvas {
.unwrap();
cb.forget();
let handler = vnc.clone();
let ctrl_alt_del_btn = web_sys::window()
.unwrap()
.document()
.unwrap()
.get_element_by_id("ctrlaltdel")
.unwrap()
.dyn_into::<HtmlButtonElement>()
.map_err(|_| ())
.unwrap();
let ctrl_alt_del = move || {
handler.ctrl_alt_del();
};
let handler = Box::new(ctrl_alt_del) as Box<dyn FnMut()>;
let cb = Closure::wrap(handler);
ctrl_alt_del_btn.set_onclick(Some(cb.as_ref().unchecked_ref()));
cb.forget();
// On a conventional mouse, buttons 1, 2, and 3 correspond to the left,
// middle, and right buttons on the mouse. On a wheel mouse, each step
// of the wheel upwards is represented by a press and release of button
@ -78,7 +100,7 @@ impl Canvas {
let handler = vnc.clone();
let mouse_move = move |e: MouseEvent| {
e.stop_propagation();
handler.mouse_event(e, MouseEventType::MouseMove);
handler.mouse_event(e, MouseEventType::Move);
};
let handler = Box::new(mouse_move) as Box<dyn FnMut(_)>;
@ -93,7 +115,7 @@ impl Canvas {
let handler = vnc.clone();
let mouse_down = move |e: MouseEvent| {
e.stop_propagation();
handler.mouse_event(e, MouseEventType::MouseDown);
handler.mouse_event(e, MouseEventType::Down);
};
let handler = Box::new(mouse_down) as Box<dyn FnMut(_)>;
@ -108,7 +130,7 @@ impl Canvas {
let handler = vnc.clone();
let mouse_up = move |e: MouseEvent| {
e.stop_propagation();
handler.mouse_event(e, MouseEventType::MouseUp);
handler.mouse_event(e, MouseEventType::Up);
};
let handler = Box::new(mouse_up) as Box<dyn FnMut(_)>;
@ -171,6 +193,10 @@ impl Canvas {
}
}
}
fn close(&self) {
self.ctx.fill();
}
}
pub struct CanvasUtils {
@ -203,4 +229,8 @@ impl CanvasUtils {
pub fn draw(&self, ri: &ImageData) {
self.inner.as_ref().draw(ri);
}
pub fn close(&self) {
self.inner.as_ref().close()
}
}

View File

@ -6,7 +6,7 @@ use canvas::CanvasUtils;
use vnc::Vnc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{ErrorEvent, MessageEvent, WebSocket};
use web_sys::{ErrorEvent, HtmlButtonElement, MessageEvent, WebSocket};
#[macro_export]
macro_rules! console_log {
@ -17,10 +17,45 @@ macro_rules! console_log {
extern "C" {
fn setInterval(closure: &Closure<dyn FnMut()>, millis: u32) -> f64;
// fn setTimeout(closure: &Closure<dyn FnMut()>, millis: u32) -> f64;
fn cancelInterval(token: f64);
fn clearInterval(token: f64);
#[wasm_bindgen(js_namespace = console)]
pub fn log(s: &str);
pub fn prompt(s: &str) -> String;
pub fn setClipBoard(s: String);
pub fn getClipBoard() -> String;
}
static mut REFRESHER: Option<Interval> = None;
#[wasm_bindgen]
pub struct Interval {
_closure: Closure<dyn FnMut()>,
token: f64,
}
impl Interval {
pub fn new<F: 'static>(millis: u32, f: F) -> Interval
where
F: FnMut(),
{
// Construct a new closure.
let closure = Closure::new(f);
// Pass the closure to JS, to run every n milliseconds.
let token = setInterval(&closure, millis);
Interval {
_closure: closure,
token,
}
}
}
// When the Interval is destroyed, cancel its `setInterval` timer.
impl Drop for Interval {
fn drop(&mut self) {
console_log!("interval dropped");
clearInterval(self.token);
}
}
fn vnc_out_handler(ws: &WebSocket, vnc: &Vnc, canvas: &CanvasUtils) {
@ -33,7 +68,10 @@ fn vnc_out_handler(ws: &WebSocket, vnc: &Vnc, canvas: &CanvasUtils) {
}
vnc::VncOutput::WsBuf(buf) => match ws.send_with_u8_array(buf) {
Ok(_) => {}
Err(err) => console_log!("error sending message: {:?}", err),
Err(err) => {
console_log!("error sending message: {:?}", err);
vnc_close_handle(vnc, canvas);
}
},
vnc::VncOutput::RequirePassword => {
let pwd = prompt("Please input the password");
@ -59,27 +97,36 @@ fn vnc_out_handler(ws: &WebSocket, vnc: &Vnc, canvas: &CanvasUtils) {
vnc_out_handler(&ws_cloned, &vnc_cloned, &canvas_cloned);
};
let handler = Box::new(refresh) as Box<dyn FnMut()>;
let refersher = Interval::new(20, refresh);
let cb = Closure::wrap(handler);
setInterval(&cb, 20);
cb.forget();
unsafe {
REFRESHER = Some(refersher);
}
}
vnc::VncOutput::SetClipboard(text) => {
setClipBoard(text.to_owned());
// ConsoleService::log(&self.error_msg);
}
// vnc::VncOutput::SetClipboard(text) => {
// self.clipboard
// .borrow_mut()
// .as_mut()
// .unwrap()
// .send_message(components::clipboard::ClipboardMsg::UpdateClipboard(text));
// // ConsoleService::log(&self.error_msg);
// }
_ => unimplemented!(),
}
}
}
}
fn vnc_close_handle(vnc: &Vnc, canvas: &CanvasUtils) {
vnc.close();
unsafe {
REFRESHER.take();
}
canvas.close();
let status = web_sys::window()
.unwrap()
.document()
.unwrap()
.get_element_by_id("vnc_status")
.unwrap();
status.set_text_content(Some("Disconnected"));
}
fn start_websocket() -> Result<(), JsValue> {
// connect
let url = format!(
@ -100,16 +147,35 @@ fn start_websocket() -> Result<(), JsValue> {
let canvas = CanvasUtils::new();
let vnc = Vnc::new();
let clipboard = web_sys::window()
.unwrap()
.document()
.unwrap()
.get_element_by_id("clipboardsend")
.unwrap()
.dyn_into::<HtmlButtonElement>()
.map_err(|_| ())
.unwrap();
let vnc_cloned = vnc.clone();
let onclickcb = Closure::<dyn FnMut()>::new(move || {
console_log!("Send {:?}", getClipBoard());
vnc_cloned.set_clipboard(&getClipBoard());
});
clipboard.set_onclick(Some(onclickcb.as_ref().unchecked_ref()));
onclickcb.forget();
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
// on message
let cloned_ws = ws.clone();
let vnc_cloned = vnc.clone();
let canvas_cloned = canvas.clone();
let onmessage_callback = Closure::<dyn FnMut(_)>::new(move |e: MessageEvent| {
if let Ok(abuf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
let array = js_sys::Uint8Array::new(&abuf);
// let mut canvas_ctx = None;
vnc.do_input(array.to_vec());
vnc_out_handler(&cloned_ws, &vnc, &canvas);
vnc_cloned.do_input(array.to_vec());
vnc_out_handler(&cloned_ws, &vnc_cloned, &canvas_cloned);
} else {
console_log!("message event, received Unknown: {:?}", e.data());
}
@ -134,6 +200,7 @@ fn start_websocket() -> Result<(), JsValue> {
let onclose_callback = Closure::<dyn FnMut()>::new(move || {
console_log!("socket close");
vnc_close_handle(&vnc, &canvas);
});
ws.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
onclose_callback.forget();

View File

@ -4,9 +4,9 @@ mod x11cursor;
mod x11keyboard;
pub enum MouseEventType {
MouseDown,
MouseUp,
MouseMove,
Down,
Up,
Move,
}
use std::{rc::Rc, sync::Mutex};
@ -67,6 +67,14 @@ impl Vnc {
pub fn mouse_event(&self, mouse: web_sys::MouseEvent, et: MouseEventType) {
self.inner.lock().unwrap().mouse_event(mouse, et);
}
pub fn ctrl_alt_del(&self) {
self.inner.lock().unwrap().ctrl_alt_del();
}
pub fn close(&self) {
self.inner.lock().unwrap().close();
}
}
impl Clone for Vnc {

View File

@ -166,6 +166,10 @@ impl Vnc {
}
pub fn set_clipboard(&mut self, text: &str) {
if self.state != VncState::Connected {
return;
}
self.send_client_cut_text(text);
}
@ -177,6 +181,15 @@ impl Vnc {
self.send_key_event(key, down);
}
pub fn ctrl_alt_del(&mut self) {
self.send_key_event(x11keyboard::XK_Control_L, true);
self.send_key_event(x11keyboard::XK_Alt_L, true);
self.send_key_event(x11keyboard::XK_Delete, true);
self.send_key_event(x11keyboard::XK_Control_L, false);
self.send_key_event(x11keyboard::XK_Alt_L, false);
self.send_key_event(x11keyboard::XK_Delete, false);
}
pub fn mouse_event(&mut self, mouse: web_sys::MouseEvent, et: MouseEventType) {
if self.state != VncState::Connected {
return;
@ -186,6 +199,10 @@ impl Vnc {
}
pub fn require_frame(&mut self, incremental: u8) {
if self.state != VncState::Connected {
return;
}
if 0 == incremental {
// first frame
// set the client encoding
@ -195,6 +212,10 @@ impl Vnc {
self.framebuffer_update_request(incremental)
}
}
pub fn close(&mut self) {
self.state = VncState::Disconnected;
}
}
#[allow(dead_code)]

View File

@ -22,16 +22,16 @@ impl MouseUtils {
let mask: u8 = (event.button() << 1).try_into().unwrap_or(0);
match et {
MouseEventType::MouseDown => {
MouseEventType::Down => {
self.down = true;
self.mask = self.down as u8 | mask;
}
MouseEventType::MouseUp => {
MouseEventType::Up => {
self.down = false;
self.mask = self.down as u8 & (!mask);
}
MouseEventType::MouseMove => {}
MouseEventType::Move => {}
}
(x, y, self.mask)
}