diff --git a/Makefile.toml b/Makefile.toml
index 2022076..c98ae28 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -22,7 +22,7 @@ cd ${SSH} && cargo make install-release && cd ..
[tasks.websockify]
dependencies = ["install-dir"]
script = '''
-cd ${WEBSOCKIFY} && cargo build --release && cp ./target/release/${WEBSOCKIFY} $INSTALL_PATH/
+cd ${WEBSOCKIFY} && cargo build --release --features ssl && cp ./target/release/${WEBSOCKIFY} $INSTALL_PATH/
'''
[tasks.install-dir]
@@ -36,4 +36,4 @@ INSTALL_PATH= "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/build"
WEBSOCKIFY="axum-websockify"
VNC="webvnc"
RDP="webrdp"
-SSH="webssh"
\ No newline at end of file
+SSH="webssh"
diff --git a/axum-websockify b/axum-websockify
index ed5e2b2..516a59b 160000
--- a/axum-websockify
+++ b/axum-websockify
@@ -1 +1 @@
-Subproject commit ed5e2b2f2415f4b31159ea23a1e7f1018dd7275f
+Subproject commit 516a59b315b20161522982b9efe5919a14b59649
diff --git a/webrdp/Cargo.toml b/webrdp/Cargo.toml
index 358a4be..ed54c33 100644
--- a/webrdp/Cargo.toml
+++ b/webrdp/Cargo.toml
@@ -12,6 +12,12 @@ default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.63"
+js-sys = "0.3"
+untrusted = "0.9"
+md4 = "0.10.2"
+hmac = "0.12.1"
+md-5 = "0.10.5"
+x509-parser = "0.14.0"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
@@ -19,6 +25,27 @@ wasm-bindgen = "0.2.63"
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
+[dependencies.web-sys]
+version = "0.3.22"
+features = [
+ "BinaryType",
+ "Blob",
+ "CanvasRenderingContext2d",
+ "Document",
+ "ErrorEvent",
+ "FileReader",
+ "HtmlButtonElement",
+ "HtmlCanvasElement",
+ "ImageData",
+ "Location",
+ "KeyboardEvent",
+ "MouseEvent",
+ "MessageEvent",
+ "ProgressEvent",
+ "Window",
+ "WebSocket",
+]
+
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
diff --git a/webrdp/assets/rdp.html b/webrdp/assets/rdp.html
index 8b5c28c..c96ced2 100644
--- a/webrdp/assets/rdp.html
+++ b/webrdp/assets/rdp.html
@@ -32,9 +32,9 @@
-
+
-
+
diff --git a/webrdp/src/canvas.rs b/webrdp/src/canvas.rs
new file mode 100644
index 0000000..c09acd6
--- /dev/null
+++ b/webrdp/src/canvas.rs
@@ -0,0 +1,251 @@
+use std::rc::Rc;
+
+use crate::{
+ console_log, log,
+ rdp::{ImageData, ImageType, MouseEventType, Rdp},
+};
+use wasm_bindgen::prelude::*;
+use wasm_bindgen::{Clamped, JsCast};
+use web_sys::{
+ CanvasRenderingContext2d, HtmlButtonElement, HtmlCanvasElement, KeyboardEvent, MouseEvent,
+};
+struct Canvas {
+ canvas: HtmlCanvasElement,
+ ctx: CanvasRenderingContext2d,
+}
+
+impl Canvas {
+ fn new() -> Self {
+ let document = web_sys::window().unwrap().document().unwrap();
+ let canvas = document.get_element_by_id("rdp-canvas").unwrap();
+ let canvas: HtmlCanvasElement = canvas
+ .dyn_into::()
+ .map_err(|_| ())
+ .unwrap();
+ let ctx = canvas
+ .get_context("2d")
+ .unwrap()
+ .unwrap()
+ .dyn_into::()
+ .unwrap();
+ Self { canvas, ctx }
+ }
+
+ fn set_resolution(&self, width: u32, height: u32) {
+ // set hight & width
+ self.canvas.set_height(height);
+ self.canvas.set_width(width);
+ self.ctx.rect(0_f64, 0_f64, width as f64, height as f64);
+ self.ctx.fill();
+ }
+
+ fn bind(&self, rdp: &Rdp) {
+ let handler = rdp.clone();
+ let key_down = move |e: KeyboardEvent| {
+ e.prevent_default();
+ e.stop_propagation();
+ handler.key_press(e, true);
+ };
+
+ let handler = Box::new(key_down) as Box;
+
+ let cb = Closure::wrap(handler);
+
+ self.canvas
+ .add_event_listener_with_callback("keydown", cb.as_ref().unchecked_ref())
+ .unwrap();
+ cb.forget();
+
+ let handler = rdp.clone();
+ let key_up = move |e: KeyboardEvent| {
+ e.prevent_default();
+ e.stop_propagation();
+ handler.key_press(e, false);
+ };
+
+ let handler = Box::new(key_up) as Box;
+
+ let cb = Closure::wrap(handler);
+
+ self.canvas
+ .add_event_listener_with_callback("keyup", cb.as_ref().unchecked_ref())
+ .unwrap();
+ cb.forget();
+
+ let handler = rdp.clone();
+ let ctrl_alt_del_btn = web_sys::window()
+ .unwrap()
+ .document()
+ .unwrap()
+ .get_element_by_id("ctrlaltdel")
+ .unwrap()
+ .dyn_into::()
+ .map_err(|_| ())
+ .unwrap();
+ let ctrl_alt_del = move || {
+ handler.ctrl_alt_del();
+ };
+ let handler = Box::new(ctrl_alt_del) as Box;
+
+ 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
+ // 4, and each step downwards is represented by a press and release of
+ // button 5.
+
+ // to do:
+ // calculate relation position
+ let handler = rdp.clone();
+ let mouse_move = move |e: MouseEvent| {
+ e.stop_propagation();
+ handler.mouse_event(e, MouseEventType::Move);
+ };
+
+ let handler = Box::new(mouse_move) as Box;
+
+ let cb = Closure::wrap(handler);
+
+ self.canvas
+ .add_event_listener_with_callback("mousemove", cb.as_ref().unchecked_ref())
+ .unwrap();
+ cb.forget();
+
+ let handler = rdp.clone();
+ let mouse_down = move |e: MouseEvent| {
+ e.stop_propagation();
+ handler.mouse_event(e, MouseEventType::Down);
+ };
+
+ let handler = Box::new(mouse_down) as Box;
+
+ let cb = Closure::wrap(handler);
+
+ self.canvas
+ .add_event_listener_with_callback("mousedown", cb.as_ref().unchecked_ref())
+ .unwrap();
+ cb.forget();
+
+ let handler = rdp.clone();
+ let mouse_up = move |e: MouseEvent| {
+ e.stop_propagation();
+ handler.mouse_event(e, MouseEventType::Up);
+ };
+
+ let handler = Box::new(mouse_up) as Box;
+
+ let cb = Closure::wrap(handler);
+
+ self.canvas
+ .add_event_listener_with_callback("mouseup", cb.as_ref().unchecked_ref())
+ .unwrap();
+ cb.forget();
+
+ let get_context_menu = move |e: MouseEvent| {
+ e.prevent_default();
+ e.stop_propagation();
+ };
+
+ let handler = Box::new(get_context_menu) as Box;
+
+ let cb = Closure::wrap(handler);
+
+ self.canvas
+ .add_event_listener_with_callback("contextmenu", cb.as_ref().unchecked_ref())
+ .unwrap();
+ cb.forget();
+ }
+
+ fn draw(&self, ri: &ImageData) {
+ match ri.type_ {
+ ImageType::Copy => {
+ //copy
+ let sx = (ri.data[0] as u16) << 8 | ri.data[1] as u16;
+ let sy = (ri.data[2] as u16) << 8 | ri.data[3] as u16;
+
+ let _ = self
+ .ctx
+ .draw_image_with_html_canvas_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
+ &self.canvas,
+ sx as f64,
+ sy as f64,
+ ri.width as f64,
+ ri.height as f64,
+ ri.x as f64,
+ ri.y as f64,
+ ri.width as f64,
+ ri.height as f64,
+ );
+ }
+ ImageType::Fill => {
+ // fill
+ let (r, g, b) = (ri.data[2], ri.data[1], ri.data[0]);
+ let style = format!("rgb({},{},{})", r, g, b);
+ self.ctx.set_fill_style(&JsValue::from_str(&style));
+ }
+ ImageType::Raw => {
+ let data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(
+ Clamped(&ri.data),
+ ri.width as u32,
+ ri.height as u32,
+ );
+ if data.is_err() {
+ console_log!(
+ "renderring failed at ({}, {}), width {}, height {}, len {}",
+ ri.x,
+ ri.y,
+ ri.width,
+ ri.height,
+ ri.data.len(),
+ );
+ }
+ let data = data.unwrap();
+ let _ = self.ctx.put_image_data(&data, ri.x as f64, ri.y as f64);
+ }
+ }
+ }
+
+ fn close(&self) {
+ self.ctx.fill();
+ }
+}
+
+pub struct CanvasUtils {
+ inner: Rc