diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml
index c8865233f..70312d3e3 100644
--- a/.github/workflows/flutter-build.yml
+++ b/.github/workflows/flutter-build.yml
@@ -1573,6 +1573,7 @@ jobs:
flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak
build-rustdesk-web:
+ if: False
name: build-rustdesk-web
runs-on: ubuntu-20.04
strategy:
diff --git a/flutter/web/README.md b/flutter/web/README.md
deleted file mode 100644
index e605e72c0..000000000
--- a/flutter/web/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# RustDesk web
-
-## Functions
-
-### Current and planned
-
-- [x] Outgoing
-- [ ] Address book
-- [ ] Force relay
-
-### Unsupported
-
-1. Incoming
-2. LAN
-3. Gpu texture render. We use WebGL instead.
-
-### No plans
diff --git a/flutter/web/favicon.svg b/flutter/web/favicon.svg
deleted file mode 100644
index 0234ca69e..000000000
--- a/flutter/web/favicon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/flutter/web/icons/Icon-192.png b/flutter/web/icons/Icon-192.png
deleted file mode 100644
index 2ff3bb45c..000000000
Binary files a/flutter/web/icons/Icon-192.png and /dev/null differ
diff --git a/flutter/web/icons/Icon-512.png b/flutter/web/icons/Icon-512.png
deleted file mode 100644
index 4d94e3123..000000000
Binary files a/flutter/web/icons/Icon-512.png and /dev/null differ
diff --git a/flutter/web/icons/Icon-maskable-192.png b/flutter/web/icons/Icon-maskable-192.png
deleted file mode 100644
index 2ff3bb45c..000000000
Binary files a/flutter/web/icons/Icon-maskable-192.png and /dev/null differ
diff --git a/flutter/web/icons/Icon-maskable-512.png b/flutter/web/icons/Icon-maskable-512.png
deleted file mode 100644
index 4d94e3123..000000000
Binary files a/flutter/web/icons/Icon-maskable-512.png and /dev/null differ
diff --git a/flutter/web/js/src/consts.ts b/flutter/web/js/src/consts.ts
deleted file mode 100644
index fe7d534e5..000000000
--- a/flutter/web/js/src/consts.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-export const LOGIN_MSG_DESKTOP_SESSION_NOT_READY = 'Desktop session not ready';
-export const LOGIN_MSG_DESKTOP_XSESSION_FAILED = 'Desktop xsession failed';
-export const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER = 'Desktop session another user login';
-export const LOGIN_MSG_DESKTOP_XORG_NOT_FOUND = 'Desktop xorg not found';
-// ls /usr/share/xsessions/
-export const LOGIN_MSG_DESKTOP_NO_DESKTOP = 'Desktop none';
-export const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY =
- 'Desktop session not ready, password empty';
-export const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG =
- 'Desktop session not ready, password wrong';
-export const LOGIN_MSG_PASSWORD_EMPTY = 'Empty Password';
-export const LOGIN_MSG_PASSWORD_WRONG = 'Wrong Password';
-export const LOGIN_MSG_2FA_WRONG = 'Wrong 2FA Code';
-export const REQUIRE_2FA = '2FA Required';
-export const LOGIN_MSG_NO_PASSWORD_ACCESS = 'No Password Access';
-export const LOGIN_MSG_OFFLINE = 'Offline';
-export const LOGIN_SCREEN_WAYLAND = 'Wayland login screen is not supported';
-export const SCRAP_X11_REQUIRED = 'x11 expected';
-export const SCRAP_X11_REF_URL = 'https://rustdesk.com/docs/en/manual/linux/#x11-required';
diff --git a/flutter/web/js/src/globals.js b/flutter/web/js/src/globals.js
deleted file mode 100644
index 5c2823dbc..000000000
--- a/flutter/web/js/src/globals.js
+++ /dev/null
@@ -1,792 +0,0 @@
-import Connection from "./connection";
-import PORT from "./connection";
-import _sodium from "libsodium-wrappers";
-import { CursorData } from "./message";
-import { loadVp9 } from "./codec";
-import { checkIfRetry, version } from "./gen_js_from_hbb";
-import { initZstd, translate } from "./common";
-import PCMPlayer from "pcm-player";
-
-window.curConn = undefined;
-window.isMobile = () => {
- return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
- || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4));
-}
-
-export function isDesktop() {
- return !isMobile();
-}
-
-export function msgbox(type, title, text, link) {
- if (!type || (type == 'error' && !text)) return;
- const text2 = text.toLowerCase();
- var hasRetry = checkIfRetry(type, title, text) ? 'true' : '';
- onGlobalEvent(JSON.stringify({ name: 'msgbox', type, title, text, link: link ?? '', hasRetry }));
-}
-
-function jsonfyForDart(payload) {
- var tmp = {};
- for (const [key, value] of Object.entries(payload)) {
- if (!key) continue;
- if (value instanceof String || typeof value == 'string') {
- tmp[key] = value;
- } else if (value instanceof Uint8Array) {
- tmp[key] = '[' + value.toString() + ']';
- } else {
- tmp[key] = JSON.stringify(value);
- }
- }
- return tmp;
-}
-
-export function pushEvent(name, payload) {
- payload = jsonfyForDart(payload);
- payload.name = name;
- onGlobalEvent(JSON.stringify(payload));
-}
-
-let yuvWorker;
-let yuvCanvas;
-let gl;
-let pixels;
-let flipPixels;
-let oldSize;
-if (YUVCanvas.WebGLFrameSink.isAvailable()) {
- var canvas = document.createElement('canvas');
- yuvCanvas = YUVCanvas.attach(canvas, { webGL: true });
- gl = canvas.getContext("webgl");
-} else {
- yuvWorker = new Worker("./yuv.js");
-}
-let testSpeed = [0, 0];
-
-export function draw(display, frame) {
- if (yuvWorker) {
- // frame's (y/u/v).bytes already detached, can not transferrable any more.
- yuvWorker.postMessage({ display, frame });
- } else {
- var tm0 = new Date().getTime();
- yuvCanvas.drawFrame(frame);
- var width = canvas.width;
- var height = canvas.height;
- var size = width * height * 4;
- if (size != oldSize) {
- pixels = new Uint8Array(size);
- flipPixels = new Uint8Array(size);
- oldSize = size;
- }
- gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
- const row = width * 4;
- const end = (height - 1) * row;
- for (let i = 0; i < size; i += row) {
- flipPixels.set(pixels.subarray(i, i + row), end - i);
- }
- onRgba(display, flipPixels);
- testSpeed[1] += new Date().getTime() - tm0;
- testSpeed[0] += 1;
- if (testSpeed[0] > 30) {
- console.log('gl: ' + parseInt('' + testSpeed[1] / testSpeed[0]));
- testSpeed = [0, 0];
- }
- }
- /*
- var testCanvas = document.getElementById("test-yuv-decoder-canvas");
- if (testCanvas && currentFrame) {
- var ctx = testCanvas.getContext("2d");
- testCanvas.width = frame.format.displayWidth;
- testCanvas.height = frame.format.displayHeight;
- var img = ctx.createImageData(testCanvas.width, testCanvas.height);
- img.data.set(currentFrame);
- ctx.putImageData(img, 0, 0);
- }
- */
-}
-
-export function sendOffCanvas(c) {
- let canvas = c.transferControlToOffscreen();
- yuvWorker.postMessage({ canvas }, [canvas]);
-}
-
-export function setConn(conn) {
- window.curConn = conn;
-}
-
-export function getConn() {
- return window.curConn;
-}
-
-export async function startConn(id) {
- setByName('remote_id', id);
- await curConn.start(id);
-}
-
-export function close() {
- getConn()?.close();
- setConn(undefined);
-}
-
-export function newConn() {
- window.curConn?.close();
- const conn = new Connection();
- setConn(conn);
- return conn;
-}
-
-let sodium;
-export async function verify(signed, pk) {
- if (!sodium) {
- await _sodium.ready;
- sodium = _sodium;
- }
- if (typeof pk == 'string') {
- pk = decodeBase64(pk);
- }
- return sodium.crypto_sign_open(signed, pk);
-}
-
-export function decodeBase64(pk) {
- return sodium.from_base64(pk, sodium.base64_variants.ORIGINAL);
-}
-
-export function genBoxKeyPair() {
- const pair = sodium.crypto_box_keypair();
- const sk = pair.privateKey;
- const pk = pair.publicKey;
- return [sk, pk];
-}
-
-export function genSecretKey() {
- return sodium.crypto_secretbox_keygen();
-}
-
-export function seal(unsigned, theirPk, ourSk) {
- const nonce = Uint8Array.from(Array(24).fill(0));
- return sodium.crypto_box_easy(unsigned, nonce, theirPk, ourSk);
-}
-
-function makeOnce(value) {
- var byteArray = Array(24).fill(0);
-
- for (var index = 0; index < byteArray.length && value > 0; index++) {
- var byte = value & 0xff;
- byteArray[index] = byte;
- value = (value - byte) / 256;
- }
-
- return Uint8Array.from(byteArray);
-};
-
-export function encrypt(unsigned, nonce, key) {
- return sodium.crypto_secretbox_easy(unsigned, makeOnce(nonce), key);
-}
-
-export function decrypt(signed, nonce, key) {
- return sodium.crypto_secretbox_open_easy(signed, makeOnce(nonce), key);
-}
-
-window.setByName = (name, value) => {
- switch (name) {
- case 'remote_id':
- localStorage.setItem('remote-id', value);
- break;
- case 'connect':
- newConn();
- startConn(value);
- break;
- case 'login':
- value = JSON.parse(value);
- curConn.setRemember(value.remember);
- curConn.login({
- os_login: {
- username: value.os_username,
- password: value.os_password,
- },
- password: value.password,
- });
- break;
- case 'close':
- close();
- break;
- case 'refresh':
- curConn.refresh();
- break;
- case 'reconnect':
- curConn?.reconnect();
- break;
- case 'toggle_option':
- curConn.toggleOption(value);
- break;
- case 'toggle_privacy_mode':
- curConn.togglePrivacyMode(value);
- break;
- case 'image_quality':
- curConn.setImageQuality(value);
- break;
- case 'lock_screen':
- curConn.lockScreen();
- break;
- case 'ctrl_alt_del':
- curConn.ctrlAltDel();
- break;
- case 'switch_display':
- curConn.switchDisplay(value);
- break;
- case 'remove':
- const peers = getPeers();
- delete peers[value];
- localStorage.setItem('peers', JSON.stringify(peers));
- break;
- case 'input_key':
- value = JSON.parse(value);
- curConn.inputKey(value.name, value.down == 'true', value.press == 'true', value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true');
- break;
- case 'input_string':
- curConn.inputString(value);
- break;
- case 'send_mouse':
- if (!curConn) return;
- let mask = 0;
- value = JSON.parse(value);
- switch (value.type) {
- case 'down':
- mask = 1;
- break;
- case 'up':
- mask = 2;
- break;
- case 'wheel':
- mask = 3;
- break;
- }
- switch (value.buttons) {
- case 'left':
- mask |= 1 << 3;
- break;
- case 'right':
- mask |= 2 << 3;
- break;
- case 'wheel':
- mask |= 4 << 3;
- }
- curConn.inputMouse(mask, parseInt(value.x || '0'), parseInt(value.y || '0'), value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true');
- break;
- case 'send_2fa':
- curConn.send2fa(value);
- break;
- case 'option':
- value = JSON.parse(value);
- localStorage.setItem(value.name, value.value);
- break;
- case 'options':
- value = JSON.parse(value);
- for (const [key, value] of Object.entries(value)) {
- localStorage.setItem(key, value);
- }
- break;
- case 'option:local':
- case 'option:flutter:local':
- case 'option:flutter:peer':
- value = JSON.parse(value);
- localStorage.setItem(name + ':' + value.name, value.value);
- break;
- case 'option:user:default':
- setUserDefaultOption(value);
- break;
- case 'option:session':
- value = JSON.parse(value);
- curConn.setOption(value.name, value.value);
- break;
- case 'option:peer':
- setPeerOption(value);
- break;
- case 'option:toggle':
- return curConn.toggleOption(value);
- case 'input_os_password':
- curConn.inputOsPassword(value);
- break;
- case 'session_add_sync':
- return sessionAdd(value);
- case 'session_start':
- sessionStart(value);
- break;
- case 'session_close':
- sessionClose(value);
- break;
- case 'elevate_with_logon':
- curConn.elevateWithLogon(value);
- break;
- case 'forget':
- curConn.setRemember(false);
- break;
- case 'peer_has_password':
- const options = getPeers()[value] || {};
- return (options['password'] ?? '') !== '';
- case 'peer_exists':
- return !(!getPeers()[value]);
- case 'restart':
- curConn.restart();
- break;
- case 'fav':
- return localStorage.setItem('fav', value);
- case 'query_onlines':
- queryOnlines(value);
- break;
- case 'change_prefer_codec':
- curConn.changePreferCodec(value);
- case 'cursor':
- setCustomCursor(value);
- break;
- default:
- break;
- }
-}
-
-window.getByName = (name, arg) => {
- let v = _getByName(name, arg);
- if (typeof v == 'string' || v instanceof String) return v;
- if (v == undefined || v == null) return '';
- return JSON.stringify(v);
-}
-
-function _getByName(name, arg) {
- switch (name) {
- case 'remote_id':
- return localStorage.getItem('remote-id');
- case 'remember':
- return curConn.getRemember();
- case 'toggle_option':
- return curConn.getOption(arg) || false;
- case 'option':
- return localStorage.getItem(arg);
- case 'options':
- const keys = [
- 'custom-rendezvous-server',
- 'relay-server',
- 'api-server',
- 'key'
- ];
- const obj = {};
- keys.forEach(key => {
- const v = localStorage.getItem(key);
- if (v) obj[key] = v;
- });
- return JSON.stringify(obj);
- case 'option:local':
- case 'option:flutter:local':
- case 'option:flutter:peer':
- return localStorage.getItem(name + ':' + arg);
- case 'image_quality':
- return curConn.getImageQuality();
- case 'translate':
- arg = JSON.parse(arg);
- return translate(arg.locale, arg.text);
- case 'option:user:default':
- return getUserDefaultOption(arg);
- case 'option:session':
- if (curConn) {
- return curConn.getOption(arg);
- } else {
- return getUserDefaultOption(arg);
- }
- case 'option:peer':
- return getPeerOption(arg);
- case 'option:toggle':
- return curConn.getToggleOption(arg);
- case 'get_conn_status':
- if (curConn) {
- return curConn.getStatus();
- } else {
- return JSON.stringify({ status_num: 0 });
- }
- case 'test_if_valid_server':
- break;
- case 'version':
- return version;
- case 'load_recent_peers':
- loadRecentPeers();
- break;
- case 'load_fav_peers':
- loadFavPeers();
- break;
- case 'fav':
- return localStorage.getItem('fav') ?? '[]';
- case 'load_recent_peers_sync':
- return JSON.stringify({
- peers: JSON.stringify(getRecentPeers())
- });
- case 'api_server':
- return getApiServer();
- case 'is_using_public_server':
- return !localStorage.getItem('custom-rendezvous-server');
- case 'get_version_number':
- return getVersionNumber(arg);
- case 'audit_server':
- return getAuditServer(arg);
- case 'alternative_codecs':
- return getAlternativeCodecs();
- case 'screen_info':
- return JSON.stringify({
- frame: {
- l: window.screenX,
- t: window.screenY,
- r: window.screenX + window.innerWidth,
- b: window.screenY + window.innerHeight,
- },
- visibleFrame: {
- l: window.screen.availLeft,
- t: window.screen.availTop,
- r: window.screen.availLeft + window.screen.availWidth,
- b: window.screen.availTop + window.screen.availHeight,
- },
- scaleFactor: window.devicePixelRatio,
- });
- case 'main_display':
- return JSON.stringify({
- w: window.screen.availWidth,
- h: window.screen.availHeight,
- scaleFactor: window.devicePixelRatio,
- });
- }
- return '';
-}
-
-let opusWorker = new Worker("./libopus.js");
-let pcmPlayer;
-
-export function initAudio(channels, sampleRate) {
- pcmPlayer = newAudioPlayer(channels, sampleRate);
- opusWorker.postMessage({ channels, sampleRate });
-}
-
-export function playAudio(packet) {
- opusWorker.postMessage(packet, [packet.buffer]);
-}
-
-window.init = async () => {
- if (yuvWorker) {
- yuvWorker.onmessage = (e) => {
- onRgba(e.data.display, e.data.frame);
- }
- }
- opusWorker.onmessage = (e) => {
- pcmPlayer.feed(e.data);
- }
- loadVp9(() => { });
- await initZstd();
- console.log('init done');
-}
-
-export function getPeers() {
- return getJsonObj('peers');
-}
-
-export function getJsonObj(key) {
- try {
- return JSON.parse(localStorage.getItem(key)) || {};
- } catch (e) {
- return {};
- }
-}
-
-function newAudioPlayer(channels, sampleRate) {
- return new PCMPlayer({
- channels,
- sampleRate,
- flushingTime: 2000
- });
-}
-
-export function copyToClipboard(text) {
- if (window.clipboardData && window.clipboardData.setData) {
- // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible.
- return window.clipboardData.setData("Text", text);
- }
- else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
- var textarea = document.createElement("textarea");
- textarea.textContent = text;
- textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge.
- document.body.appendChild(textarea);
- textarea.select();
- try {
- return document.execCommand("copy"); // Security exception may be thrown by some browsers.
- }
- catch (ex) {
- console.warn("Copy to clipboard failed.", ex);
- // return prompt("Copy to clipboard: Ctrl+C, Enter", text);
- }
- finally {
- document.body.removeChild(textarea);
- }
- }
-}
-
-// Dup to the function in hbb_common, lib.rs
-// Maybe we need to move this function to js part.
-export function getVersionNumber(v) {
- try {
- let versions = v.split('-');
-
- let n = 0;
-
- // The first part is the version number.
- // 1.1.10 -> 1001100, 1.2.3 -> 1001030, multiple the last number by 10
- // to leave space for patch version.
- if (versions.length > 0) {
- let last = 0;
- for (let x of versions[0].split('.')) {
- last = parseInt(x) || 0;
- n = n * 1000 + last;
- }
- n -= last;
- n += last * 10;
- }
-
- if (versions.length > 1) {
- n += parseInt(versions[1]) || 0;
- }
-
- // Ignore the rest
-
- return n;
- }
- catch (e) {
- console.error('Failed to parse version number: "' + v + '" ' + e.message);
- return 0;
- }
-}
-
-// Set the cursor for the flutter-view element
-function setCustomCursor(value) {
- try {
- const obj = JSON.parse(value);
- // document querySelector or evaluate can not find the custom element
- var body = document.body;
- for (var i = 0; i < body.children.length; i++) {
- var child = body.children[i];
- if (child.tagName == 'FLUTTER-VIEW') {
- child.style.cursor = `url(${obj.url}) ${obj.hotx} ${obj.hoty}, auto`;
- }
- }
- } catch (e) {
- console.error('Failed to set custom cursor: ' + e.message);
- }
-}
-
-// ========================== options begin ==========================
-function setUserDefaultOption(value) {
- try {
- const ojb = JSON.parse(value);
- const userDefaultOptions = JSON.parse(localStorage.getItem('user-default-options')) || {};
- userDefaultOptions[ojb.name] = ojb.value;
- localStorage.setItem('user-default-options', JSON.stringify(userDefaultOptions));
- }
- catch (e) {
- console.error('Failed to set user default options: ' + e.message);
- }
-}
-
-export function getUserDefaultOption(value) {
- const defaultOptions = {
- 'view_style': 'original',
- 'scroll_style': 'scrollauto',
- 'image_quality': 'balanced',
- 'codec-preference': 'auto',
- 'custom_image_quality': '50',
- 'custom-fps': '30',
- };
- try {
- const userDefaultOptions = JSON.parse(localStorage.getItem('user-default-options')) || {};
- return userDefaultOptions[value] || defaultOptions[value] || '';
- }
- catch (e) {
- console.error('Failed to get user default options: ' + e.message);
- return defaultOptions[value] || '';
- }
-}
-
-function getPeerOption(value) {
- try {
- const obj = JSON.parse(value);
- const options = getPeers()[obj.id] || {};
- return options[obj.name] ?? getUserDefaultOption(obj.name);
- }
- catch (e) {
- console.error('Failed to get peer option: "' + value + '", ' + e.message);
- }
-}
-
-function setPeerOption(param) {
- try {
- const obj = JSON.parse(param);
- const id = obj.id;
- const name = obj.name;
- const value = obj.value;
- const peers = getPeers();
- const options = peers[id] || {};
-
- if (value == undefined) {
- delete options[name];
- } else {
- options[name] = value;
- }
- options["tm"] = new Date().getTime();
- peers[id] = options;
- localStorage.setItem("peers", JSON.stringify(peers));
- }
- catch (e) {
- console.error('Failed to set peer option: "' + value + '", ' + e.message);
- }
-}
-// ========================= options end ===========================
-
-// ========================== peers begin ==========================
-function getRecentPeers() {
- const peers = [];
- for (const [id, value] of Object.entries(getPeers())) {
- if (!id) continue;
- const tm = value['tm'];
- const info = value['info'];
- const cardInfo = {
- id: id,
- username: info['username'] || '',
- hostname: info['hostname'] || '',
- platform: info['platform'] || '',
- alias: value.alias || '',
- };
- if (!tm || !cardInfo) continue;
- peers.push([tm, id, cardInfo]);
- }
- return peers.sort().reverse().map(x => x[2]);
-}
-
-function loadRecentPeers() {
- const peersRecent = getRecentPeers();
- if (peersRecent) {
- onRegisteredEvent(JSON.stringify({ name: 'load_recent_peers', peers: JSON.stringify(peersRecent) }));
- }
-}
-
-function loadFavPeers() {
- try {
- const fav = localStorage.getItem('fav') ?? '[]';
- const favs = JSON.parse(fav);
- const peersFav = getRecentPeers().filter(x => favs.includes(x.id));
- if (peersFav) {
- onRegisteredEvent(JSON.stringify({ name: 'load_fav_peers', peers: JSON.stringify(peersFav) }));
- }
- } catch (e) {
- console.error('Failed to load fav peers: ' + e.message);
- }
-}
-
-export function queryOnlines(value) {
- // TODO: implement this
-}
-// ========================== peers end ===========================
-
-// ========================== session begin ==========================
-function sessionAdd(value) {
- try {
- const data = JSON.parse(value);
- window.curConn?.close();
- const conn = new Connection();
- setConn(conn);
- return '';
- } catch (e) {
- return e.message;
- }
-}
-
-function sessionStart(value) {
- try {
- const conn = getConn();
- if (!conn) {
- return;
- }
-
- const data = JSON.parse(value);
- if (data['id']) {
- startConn(data['id']);
- } else {
- msgbox('error', 'Error', 'No id found in session data ' + value, '');
- }
- } catch (e) {
- // TODO: better error handling
- msgbox('error', 'Error', e.message, '');
- }
-}
-
-function sessionClose(value) {
- close();
-}
-// ========================== session end ===========================
-
-// ========================== settings begin ==========================
-function increasePort(host, offset) {
- function isIPv6(str) {
- const ipv6Pattern = /^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/;
- return ipv6Pattern.test(str);
- }
-
- if (isIPv6(host)) {
- if (host.startsWith('[')) {
- let tmp = host.split(']:');
- if (tmp.length === 2) {
- let port = parseInt(tmp[1]) || 0;
- if (port > 0) {
- return `${tmp[0]}]:${port + offset}`;
- }
- }
- }
- } else if (host.includes(':')) {
- let tmp = host.split(':');
- if (tmp.length === 2) {
- let port = parseInt(tmp[1]) || 0;
- if (port > 0) {
- return `${tmp[0]}:${port + offset}`;
- }
- }
- }
- return host;
-}
-
-function getAlternativeCodecs() {
- return JSON.stringify({
- vp8: true,
- av1: false,
- h264: false,
- h265: false,
- });
-}
-// ========================== settings end ===========================
-
-// ========================== server begin ==========================
-function getApiServer() {
- const api_server = localStorage.getItem('api-server');
- if (api_server) {
- return api_server;
- }
-
- const custom_rendezvous_server = localStorage.getItem('custom-rendezvous-server');
- if (custom_rendezvous_server) {
- let s = increasePort(custom_rendezvous_server, -2);
- if (s == custom_rendezvous_server) {
- return `http://${s}:${PORT - 2}`;
- } else {
- return `http://${s}`;
- }
- }
- return 'https://admin.rustdesk.com';
-}
-
-function getAuditServer(typ) {
- if (!localStorage.getItem("access_token")) {
- return '';
- }
- const api_server = getApiServer();
- if (!api_server || api_server.includes('rustdesk.com')) {
- return '';
- }
- return api_server + '/api/audit/' + typ;
-}
-// ========================== server end ============================
diff --git a/flutter/web/v1/README.md b/flutter/web/v1/README.md
new file mode 100644
index 000000000..b9e2fc5c0
--- /dev/null
+++ b/flutter/web/v1/README.md
@@ -0,0 +1 @@
+v1 is not compatible with current Flutter source code.
\ No newline at end of file
diff --git a/flutter/web/index.html b/flutter/web/v1/index.html
similarity index 100%
rename from flutter/web/index.html
rename to flutter/web/v1/index.html
diff --git a/flutter/web/js/.gitattributes b/flutter/web/v1/js/.gitattributes
similarity index 100%
rename from flutter/web/js/.gitattributes
rename to flutter/web/v1/js/.gitattributes
diff --git a/flutter/web/js/.gitignore b/flutter/web/v1/js/.gitignore
similarity index 100%
rename from flutter/web/js/.gitignore
rename to flutter/web/v1/js/.gitignore
diff --git a/flutter/web/js/.yarnrc.yml b/flutter/web/v1/js/.yarnrc.yml
similarity index 100%
rename from flutter/web/js/.yarnrc.yml
rename to flutter/web/v1/js/.yarnrc.yml
diff --git a/flutter/web/js/gen_js_from_hbb.py b/flutter/web/v1/js/gen_js_from_hbb.py
similarity index 100%
rename from flutter/web/js/gen_js_from_hbb.py
rename to flutter/web/v1/js/gen_js_from_hbb.py
diff --git a/flutter/web/js/index.html b/flutter/web/v1/js/index.html
similarity index 100%
rename from flutter/web/js/index.html
rename to flutter/web/v1/js/index.html
diff --git a/flutter/web/js/package.json b/flutter/web/v1/js/package.json
similarity index 70%
rename from flutter/web/js/package.json
rename to flutter/web/v1/js/package.json
index 436806e8d..15e0e75b8 100644
--- a/flutter/web/js/package.json
+++ b/flutter/web/v1/js/package.json
@@ -3,19 +3,19 @@
"version": "1.0.0",
"scripts": {
"dev": "vite",
- "build": "python ./gen_js_from_hbb.py > src/gen_js_from_hbb.ts && python ./ts_proto.py && tsc && vite build",
+ "build": "./gen_js_from_hbb.py > src/gen_js_from_hbb.ts && ./ts_proto.py && tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"typescript": "^4.4.4",
- "vite": "2.8"
+ "vite": "^2.7.2"
},
"dependencies": {
"fast-sha256": "^1.3.0",
"libsodium": "^0.7.9",
"libsodium-wrappers": "^0.7.9",
"pcm-player": "^0.0.11",
- "ts-proto": "^1.169.1",
+ "ts-proto": "^1.101.0",
"wasm-feature-detect": "^1.2.11",
"zstddec": "^0.0.2"
}
diff --git a/flutter/web/js/src/codec.js b/flutter/web/v1/js/src/codec.js
similarity index 95%
rename from flutter/web/js/src/codec.js
rename to flutter/web/v1/js/src/codec.js
index 6b295adbd..27c9565ec 100644
--- a/flutter/web/js/src/codec.js
+++ b/flutter/web/v1/js/src/codec.js
@@ -25,7 +25,7 @@ export async function loadVp9(callback) {
// Multithreading is used only if `options.threading` is true.
// This requires browser support for the new `SharedArrayBuffer` and `Atomics` APIs,
// currently available in Firefox and Chrome with experimental flags enabled.
- // All major browsers disabled SharedArrayBuffer by default on January 5, 2018
+ // 所有主流浏览器均默认于2018年1月5日禁用SharedArrayBuffer
const isSIMD = await simd();
console.log('isSIMD: ' + isSIMD);
window.OGVLoader.loadClass(
diff --git a/flutter/web/js/src/common.ts b/flutter/web/v1/js/src/common.ts
similarity index 100%
rename from flutter/web/js/src/common.ts
rename to flutter/web/v1/js/src/common.ts
diff --git a/flutter/web/js/src/connection.ts b/flutter/web/v1/js/src/connection.ts
similarity index 64%
rename from flutter/web/js/src/connection.ts
rename to flutter/web/v1/js/src/connection.ts
index 297004bd8..b0c479c90 100644
--- a/flutter/web/js/src/connection.ts
+++ b/flutter/web/v1/js/src/connection.ts
@@ -4,10 +4,9 @@ import * as rendezvous from "./rendezvous.js";
import { loadVp9 } from "./codec";
import * as sha256 from "fast-sha256";
import * as globals from "./globals";
-import * as consts from "./consts";
import { decompress, mapKey, sleep } from "./common";
-export const PORT = 21116;
+const PORT = 21116;
const HOSTS = [
"rs-sg.rustdesk.com",
"rs-cn.rustdesk.com",
@@ -16,8 +15,8 @@ const HOSTS = [
let HOST = localStorage.getItem("rendezvous-server") || HOSTS[0];
const SCHEMA = "ws://";
-type MsgboxCallback = (type: string, title: string, text: string, link: string) => void;
-type DrawCallback = (display: number, data: Uint8Array) => void;
+type MsgboxCallback = (type: string, title: string, text: string) => void;
+type DrawCallback = (data: Uint8Array) => void;
//const cursorCanvas = document.createElement("canvas");
export default class Connection {
@@ -67,7 +66,7 @@ export default class Connection {
try {
this._password = Uint8Array.from(JSON.parse("[" + p + "]"));
} catch (e) {
- console.error('Failed to get password, ' + e);
+ console.error(e);
}
}
}
@@ -171,7 +170,7 @@ export default class Connection {
pk = undefined;
}
} catch (e) {
- console.error('Failed to verify id pk, ', e);
+ console.error(e);
pk = undefined;
}
if (!pk)
@@ -196,7 +195,7 @@ export default class Connection {
try {
signedId = await globals.verify(signedId.id, Uint8Array.from(pk!));
} catch (e) {
- console.error('Failed to verify signed id pk, ', e);
+ console.error(e);
// fall back to non-secure connection in case pk mismatch
console.error("pk mismatch, fall back to non-secure");
const public_key = message.PublicKey.fromPartial({});
@@ -243,12 +242,26 @@ export default class Connection {
this.login();
} else if (msg?.test_delay) {
const test_delay = msg?.test_delay;
- console.log('test delay: ', test_delay);
+ console.log(test_delay);
if (!test_delay.from_client) {
this._ws?.sendMessage({ test_delay });
}
} else if (msg?.login_response) {
- this.handleLoginResponse(msg?.login_response);
+ const r = msg?.login_response;
+ if (r.error) {
+ if (r.error == "Wrong Password") {
+ this._password = undefined;
+ this.msgbox(
+ "re-input-password",
+ r.error,
+ "Do you want to enter again?"
+ );
+ } else {
+ this.msgbox("error", "Login Error", r.error);
+ }
+ } else if (r.peer_info) {
+ this.handlePeerInfo(r.peer_info);
+ }
} else if (msg?.video_frame) {
this.handleVideoFrame(msg?.video_frame!);
} else if (msg?.clipboard) {
@@ -261,7 +274,7 @@ export default class Connection {
try {
globals.copyToClipboard(new TextDecoder().decode(cb.content));
} catch (e) {
- console.error('Failed to copy to clipboard, ', e);
+ console.error(e);
}
// globals.pushEvent("clipboard", cb);
} else if (msg?.cursor_data) {
@@ -305,110 +318,13 @@ export default class Connection {
}
}
- handleLoginResponse(response: message.LoginResponse) {
- const loginErrorMap: Record = {
- [consts.LOGIN_SCREEN_WAYLAND]: {
- msgtype: "error",
- title: "Login Error",
- text: "Login screen using Wayland is not supported",
- link: "https://rustdesk.com/docs/en/manual/linux/#login-screen",
- try_again: true,
- },
- [consts.LOGIN_MSG_DESKTOP_SESSION_NOT_READY]: {
- msgtype: "session-login",
- title: "",
- text: "",
- link: "",
- try_again: true,
- },
- [consts.LOGIN_MSG_DESKTOP_XSESSION_FAILED]: {
- msgtype: "session-re-login",
- title: "",
- text: "",
- link: "",
- try_again: true,
- },
- [consts.LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER]: {
- msgtype: "info-nocancel",
- title: "another_user_login_title_tip",
- text: "another_user_login_text_tip",
- link: "",
- try_again: false,
- },
- [consts.LOGIN_MSG_DESKTOP_XORG_NOT_FOUND]: {
- msgtype: "info-nocancel",
- title: "xorg_not_found_title_tip",
- text: "xorg_not_found_text_tip",
- link: "https://rustdesk.com/docs/en/manual/linux/#login-screen",
- try_again: true,
- },
- [consts.LOGIN_MSG_DESKTOP_NO_DESKTOP]: {
- msgtype: "info-nocancel",
- title: "no_desktop_title_tip",
- text: "no_desktop_text_tip",
- link: "https://rustdesk.com/docs/en/manual/linux/#login-screen",
- try_again: true,
- },
- [consts.LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY]: {
- msgtype: "session-login-password",
- title: "",
- text: "",
- link: "",
- try_again: true,
- },
- [consts.LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG]: {
- msgtype: "session-login-re-password",
- title: "",
- text: "",
- link: "",
- try_again: true,
- },
- [consts.LOGIN_MSG_NO_PASSWORD_ACCESS]: {
- msgtype: "wait-remote-accept-nook",
- title: "Prompt",
- text: "Please wait for the remote side to accept your session request...",
- link: "",
- try_again: true,
- },
- };
-
- const err = response.error;
- if (err) {
- if (err == consts.LOGIN_MSG_PASSWORD_EMPTY) {
- this._password = undefined;
- this.msgbox("input-password", "Password Required", "", "");
- }
- if (err == consts.LOGIN_MSG_PASSWORD_WRONG) {
- this._password = undefined;
- this.msgbox(
- "re-input-password",
- err,
- "Do you want to enter again?"
- );
- } else if (err == consts.LOGIN_MSG_2FA_WRONG || err == consts.REQUIRE_2FA) {
- this.msgbox("input-2fa", err, "");
- } else if (err in loginErrorMap) {
- const m = loginErrorMap[err];
- this.msgbox(m.msgtype, m.title, m.text, m.link);
- } else {
- if (err.includes(consts.SCRAP_X11_REQUIRED)) {
- this.msgbox("error", "Login Error", err, consts.SCRAP_X11_REF_URL);
- } else {
- this.msgbox("error", "Login Error", err);
- }
- }
- } else if (response.peer_info) {
- this.handlePeerInfo(response.peer_info);
- }
+ msgbox(type_: string, title: string, text: string) {
+ this._msgbox?.(type_, title, text);
}
- msgbox(type_: string, title: string, text: string, link: string = '') {
- this._msgbox?.(type_, title, text, link);
- }
-
- draw(display: number, frame: any) {
- this._draw?.(display, frame);
- globals.draw(display, frame);
+ draw(frame: any) {
+ this._draw?.(frame);
+ globals.draw(frame);
}
close() {
@@ -431,55 +347,38 @@ export default class Connection {
this._draw = callback;
}
- login(info?: {
- os_login?: message.OSLogin,
- password?: Uint8Array
- }) {
- if (info?.password) {
+ login(password: string | undefined = undefined) {
+ if (password) {
const salt = this._hash?.salt;
- let p = hash([info.password, salt!]);
+ let p = hash([password, salt!]);
this._password = p;
const challenge = this._hash?.challenge;
p = hash([p, challenge!]);
this.msgbox("connecting", "Connecting...", "Logging in...");
- this._sendLoginMessage({ os_login: info.os_login, password: p });
+ this._sendLoginMessage(p);
} else {
let p = this._password;
if (p) {
const challenge = this._hash?.challenge;
p = hash([p, challenge!]);
}
- this._sendLoginMessage({ os_login: info?.os_login, password: p });
+ this._sendLoginMessage(p);
}
}
- changePreferCodec() {
- const supported_decoding = message.SupportedDecoding.fromPartial({
- ability_vp9: 1,
- ability_h264: 1,
- });
- const option = message.OptionMessage.fromPartial({ supported_decoding });
- const misc = message.Misc.fromPartial({ option });
- this._ws?.sendMessage({ misc });
- }
-
async reconnect() {
this.close();
await this.start(this._id);
}
- _sendLoginMessage(login: {
- os_login?: message.OSLogin,
- password?: Uint8Array,
- }) {
+ _sendLoginMessage(password: Uint8Array | undefined = undefined) {
const login_request = message.LoginRequest.fromPartial({
username: this._id!,
my_id: "web", // to-do
my_name: "web", // to-do
- password: login.password,
+ password,
option: this.getOptionMessage(),
video_ack_required: true,
- os_login: login.os_login,
});
this._ws?.sendMessage({ login_request });
}
@@ -536,7 +435,7 @@ export default class Connection {
i++;
if (i == n) this.sendVideoReceived();
if (ok && dec.frameBuffer && n == i) {
- this.draw(vf.display, dec.frameBuffer);
+ this.draw(dec.frameBuffer);
const now = new Date().getTime();
var elapsed = now - tm;
this._videoTestSpeed[1] += elapsed;
@@ -544,9 +443,9 @@ export default class Connection {
if (this._videoTestSpeed[0] >= 30) {
console.log(
"video decoder: " +
- parseInt(
- "" + this._videoTestSpeed[1] / this._videoTestSpeed[0]
- )
+ parseInt(
+ "" + this._videoTestSpeed[1] / this._videoTestSpeed[0]
+ )
);
this._videoTestSpeed = [0, 0];
}
@@ -557,17 +456,8 @@ export default class Connection {
}
handlePeerInfo(pi: message.PeerInfo) {
- localStorage.setItem('last_remote_id', this._id);
this._peerInfo = pi;
- if (pi.current_display > pi.displays.length) {
- pi.current_display = 0;
- }
- if (globals.getVersionNumber(pi.version) < globals.getVersionNumber("1.1.10")) {
- this.setPermission("restart", false);
- }
if (pi.displays.length == 0) {
- this.setOption("info", pi);
- globals.pushEvent("update_privacy_mode", {});
this.msgbox("error", "Remote Error", "No Display");
return;
}
@@ -577,7 +467,6 @@ export default class Connection {
if (p) this.inputOsPassword(p);
const username = this.getOption("info")?.username;
if (username && !pi.username) pi.username = username;
- globals.pushEvent("update_privacy_mode", {});
this.setOption("info", pi);
if (this.getRemember()) {
if (this._password?.length) {
@@ -592,10 +481,6 @@ export default class Connection {
}
}
- setPermission(name: string, value: Boolean) {
- globals.pushEvent("permission", { [name]: value });
- }
-
shouldAutoLogin(): string {
const l = this.getOption("lock-after-session-end");
const a = !!this.getOption("auto-login");
@@ -631,7 +516,7 @@ export default class Connection {
default:
return;
}
- this.setPermission(name, p.enabled);
+ globals.pushEvent("permission", { [name]: p.enabled });
} else if (misc.switch_display) {
this.loadVideoDecoder();
globals.pushEvent("switch_display", misc.switch_display);
@@ -652,27 +537,7 @@ export default class Connection {
}
getOption(name: string): any {
- return this._options[name] ?? globals.getUserDefaultOption(name);
- }
-
- getToggleOption(name: string): Boolean {
- // TODO: more default settings
- const defaultToggleTrue = [
- 'show-remote-cursor',
- 'privacy-mode',
- 'enable-file-copy-paste',
- 'allow_swap_key',
- ];
- return this._options[name] || (defaultToggleTrue.includes(name) ? true : false);
- }
-
- // TODO:
- getStatus(): String {
- return JSON.stringify({ status_num: 10 });
- }
-
- // TODO:
- checkConnStatus() {
+ return this._options[name];
}
setOption(name: string, value: any) {
@@ -727,70 +592,17 @@ export default class Connection {
this._ws?.sendMessage({ key_event });
}
- restart() {
- const misc = message.Misc.fromPartial({});
- misc.restart_remote_device = true;
- this._ws?.sendMessage({ misc });
- }
-
inputString(seq: string) {
const key_event = message.KeyEvent.fromPartial({ seq });
this._ws?.sendMessage({ key_event });
}
- send2fa(code: string) {
- const auth_2fa = message.Auth2FA.fromPartial({ code });
- this._ws?.sendMessage({ auth_2fa });
- }
-
- _captureDisplays({ add, sub, set }: {
- add?: number[], sub?: number[], set?: number[]
- }) {
- const capture_displays = message.CaptureDisplays.fromPartial({ add, sub, set });
- const misc = message.Misc.fromPartial({ capture_displays });
+ switchDisplay(display: number) {
+ const switch_display = message.SwitchDisplay.fromPartial({ display });
+ const misc = message.Misc.fromPartial({ switch_display });
this._ws?.sendMessage({ misc });
}
- switchDisplay(v: string) {
- try {
- const obj = JSON.parse(v);
- const value = obj.value;
- const isDesktop = obj.isDesktop;
- if (value.length == 1) {
- const switch_display = message.SwitchDisplay.fromPartial({ display: value[0] });
- const misc = message.Misc.fromPartial({ switch_display });
- this._ws?.sendMessage({ misc });
-
- if (!isDesktop) {
- this._captureDisplays({ set: value });
- } else {
- // If support merging images, check_remove_unused_displays() in ui_session_interface.rs
- }
- } else {
- this._captureDisplays({ set: value });
- }
- }
- catch (e) {
- console.log('Failed to switch display, invalid param "' + v + '"');
- }
- }
-
- elevateWithLogon(value: string) {
- try {
- const obj = JSON.parse(value);
- const logon = message.ElevationRequestWithLogon.fromPartial({
- username: obj.username,
- password: obj.password
- });
- const elevation_request = message.ElevationRequest.fromPartial({ logon });
- const misc = message.Misc.fromPartial({ elevation_request });
- this._ws?.sendMessage({ misc });
- }
- catch (e) {
- console.log('Failed to elevate with logon, invalid param "' + value + '"');
- }
- }
-
async inputOsPassword(seq: string) {
this.inputMouse();
await sleep(50);
@@ -839,52 +651,6 @@ export default class Connection {
}
toggleOption(name: string) {
-
- // } else if name == "block-input" {
- // option.block_input = BoolOption::Yes.into();
- // } else if name == "unblock-input" {
- // option.block_input = BoolOption::No.into();
- // } else if name == "show-quality-monitor" {
- // config.show_quality_monitor.v = !config.show_quality_monitor.v;
- // } else if name == "allow_swap_key" {
- // config.allow_swap_key.v = !config.allow_swap_key.v;
- // } else if name == "view-only" {
- // config.view_only.v = !config.view_only.v;
- // let f = |b: bool| {
- // if b {
- // BoolOption::Yes.into()
- // } else {
- // BoolOption::No.into()
- // }
- // };
- // if config.view_only.v {
- // option.disable_keyboard = f(true);
- // option.disable_clipboard = f(true);
- // option.show_remote_cursor = f(true);
- // option.enable_file_transfer = f(false);
- // option.lock_after_session_end = f(false);
- // } else {
- // option.disable_keyboard = f(false);
- // option.disable_clipboard = f(self.get_toggle_option("disable-clipboard"));
- // option.show_remote_cursor = f(self.get_toggle_option("show-remote-cursor"));
- // option.enable_file_transfer = f(self.config.enable_file_transfer.v);
- // option.lock_after_session_end = f(self.config.lock_after_session_end.v);
- // }
- // } else {
- // let is_set = self
- // .options
- // .get(&name)
- // .map(|o| !o.is_empty())
- // .unwrap_or(false);
- // if is_set {
- // self.config.options.remove(&name);
- // } else {
- // self.config.options.insert(name, "Y".to_owned());
- // }
- // self.config.store(&self.id);
- // return None;
- // }
-
const v = !this._options[name];
const option = message.OptionMessage.fromPartial({});
const v2 = v
@@ -906,43 +672,13 @@ export default class Connection {
case "privacy-mode":
option.privacy_mode = v2;
break;
- case "enable-file-copy-paste":
- option.enable_file_transfer = v2;
- break;
case "block-input":
option.block_input = message.OptionMessage_BoolOption.Yes;
break;
case "unblock-input":
option.block_input = message.OptionMessage_BoolOption.No;
break;
- case "show-quality-monitor":
- case "allow-swap-key":
- break;
- case "view-only":
- if (v) {
- option.disable_keyboard = message.OptionMessage_BoolOption.Yes;
- option.disable_clipboard = message.OptionMessage_BoolOption.Yes;
- option.show_remote_cursor = message.OptionMessage_BoolOption.Yes;
- option.enable_file_transfer = message.OptionMessage_BoolOption.No;
- option.lock_after_session_end = message.OptionMessage_BoolOption.No;
- } else {
- option.disable_keyboard = message.OptionMessage_BoolOption.No;
- option.disable_clipboard = this.getToggleOption("disable-clipboard")
- ? message.OptionMessage_BoolOption.Yes
- : message.OptionMessage_BoolOption.No;
- option.show_remote_cursor = this.getToggleOption("show-remote-cursor")
- ? message.OptionMessage_BoolOption.Yes
- : message.OptionMessage_BoolOption.No;
- option.enable_file_transfer = this.getToggleOption("enable-file-copy-paste")
- ? message.OptionMessage_BoolOption.Yes
- : message.OptionMessage_BoolOption.No;
- option.lock_after_session_end = this.getToggleOption("lock-after-session-end")
- ? message.OptionMessage_BoolOption.Yes
- : message.OptionMessage_BoolOption.No;
- }
- break;
default:
- this.setOption(name, this._options[name] ? undefined : "Y");
return;
}
if (name.indexOf("block-input") < 0) this.setOption(name, v);
@@ -950,20 +686,6 @@ export default class Connection {
this._ws?.sendMessage({ misc });
}
- togglePrivacyMode(value: string) {
- try {
- const obj = JSON.parse(value);
- const toggle_privacy_mode = message.TogglePrivacyMode.fromPartial({
- impl_key: obj.impl_key,
- on: obj.on,
- });
- const misc = message.Misc.fromPartial({ toggle_privacy_mode });
- this._ws?.sendMessage({ misc });
- } catch (e) {
- console.log('Failed to toggle privacy mode, invalid param "' + value + '"')
- }
- }
-
getImageQuality() {
return this.getOption("image-quality");
}
@@ -998,7 +720,7 @@ export default class Connection {
loadVp9((decoder: any) => {
this._videoDecoder = decoder;
console.log("vp9 loaded");
- console.log('The decoder: ', decoder);
+ console.log(decoder);
});
}
}
diff --git a/flutter/web/v1/js/src/globals.js b/flutter/web/v1/js/src/globals.js
new file mode 100644
index 000000000..953add18d
--- /dev/null
+++ b/flutter/web/v1/js/src/globals.js
@@ -0,0 +1,383 @@
+import Connection from "./connection";
+import _sodium from "libsodium-wrappers";
+import { CursorData } from "./message";
+import { loadVp9 } from "./codec";
+import { checkIfRetry, version } from "./gen_js_from_hbb";
+import { initZstd, translate } from "./common";
+import PCMPlayer from "pcm-player";
+
+window.curConn = undefined;
+window.isMobile = () => {
+ return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
+ || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4));
+}
+
+export function isDesktop() {
+ return !isMobile();
+}
+
+export function msgbox(type, title, text) {
+ if (!type || (type == 'error' && !text)) return;
+ const text2 = text.toLowerCase();
+ var hasRetry = checkIfRetry(type, title, text) ? 'true' : '';
+ onGlobalEvent(JSON.stringify({ name: 'msgbox', type, title, text, hasRetry }));
+}
+
+function jsonfyForDart(payload) {
+ var tmp = {};
+ for (const [key, value] of Object.entries(payload)) {
+ if (!key) continue;
+ tmp[key] = value instanceof Uint8Array ? '[' + value.toString() + ']' : JSON.stringify(value);
+ }
+ return tmp;
+}
+
+export function pushEvent(name, payload) {
+ payload = jsonfyForDart(payload);
+ payload.name = name;
+ onGlobalEvent(JSON.stringify(payload));
+}
+
+let yuvWorker;
+let yuvCanvas;
+let gl;
+let pixels;
+let flipPixels;
+let oldSize;
+if (YUVCanvas.WebGLFrameSink.isAvailable()) {
+ var canvas = document.createElement('canvas');
+ yuvCanvas = YUVCanvas.attach(canvas, { webGL: true });
+ gl = canvas.getContext("webgl");
+} else {
+ yuvWorker = new Worker("./yuv.js");
+}
+let testSpeed = [0, 0];
+
+export function draw(frame) {
+ if (yuvWorker) {
+ // frame's (y/u/v).bytes already detached, can not transferrable any more.
+ yuvWorker.postMessage(frame);
+ } else {
+ var tm0 = new Date().getTime();
+ yuvCanvas.drawFrame(frame);
+ var width = canvas.width;
+ var height = canvas.height;
+ var size = width * height * 4;
+ if (size != oldSize) {
+ pixels = new Uint8Array(size);
+ flipPixels = new Uint8Array(size);
+ oldSize = size;
+ }
+ gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+ const row = width * 4;
+ const end = (height - 1) * row;
+ for (let i = 0; i < size; i += row) {
+ flipPixels.set(pixels.subarray(i, i + row), end - i);
+ }
+ onRgba(flipPixels);
+ testSpeed[1] += new Date().getTime() - tm0;
+ testSpeed[0] += 1;
+ if (testSpeed[0] > 30) {
+ console.log('gl: ' + parseInt('' + testSpeed[1] / testSpeed[0]));
+ testSpeed = [0, 0];
+ }
+ }
+ /*
+ var testCanvas = document.getElementById("test-yuv-decoder-canvas");
+ if (testCanvas && currentFrame) {
+ var ctx = testCanvas.getContext("2d");
+ testCanvas.width = frame.format.displayWidth;
+ testCanvas.height = frame.format.displayHeight;
+ var img = ctx.createImageData(testCanvas.width, testCanvas.height);
+ img.data.set(currentFrame);
+ ctx.putImageData(img, 0, 0);
+ }
+ */
+}
+
+export function sendOffCanvas(c) {
+ let canvas = c.transferControlToOffscreen();
+ yuvWorker.postMessage({ canvas }, [canvas]);
+}
+
+export function setConn(conn) {
+ window.curConn = conn;
+}
+
+export function getConn() {
+ return window.curConn;
+}
+
+export async function startConn(id) {
+ setByName('remote_id', id);
+ await curConn.start(id);
+}
+
+export function close() {
+ getConn()?.close();
+ setConn(undefined);
+}
+
+export function newConn() {
+ window.curConn?.close();
+ const conn = new Connection();
+ setConn(conn);
+ return conn;
+}
+
+let sodium;
+export async function verify(signed, pk) {
+ if (!sodium) {
+ await _sodium.ready;
+ sodium = _sodium;
+ }
+ if (typeof pk == 'string') {
+ pk = decodeBase64(pk);
+ }
+ return sodium.crypto_sign_open(signed, pk);
+}
+
+export function decodeBase64(pk) {
+ return sodium.from_base64(pk, sodium.base64_variants.ORIGINAL);
+}
+
+export function genBoxKeyPair() {
+ const pair = sodium.crypto_box_keypair();
+ const sk = pair.privateKey;
+ const pk = pair.publicKey;
+ return [sk, pk];
+}
+
+export function genSecretKey() {
+ return sodium.crypto_secretbox_keygen();
+}
+
+export function seal(unsigned, theirPk, ourSk) {
+ const nonce = Uint8Array.from(Array(24).fill(0));
+ return sodium.crypto_box_easy(unsigned, nonce, theirPk, ourSk);
+}
+
+function makeOnce(value) {
+ var byteArray = Array(24).fill(0);
+
+ for (var index = 0; index < byteArray.length && value > 0; index++) {
+ var byte = value & 0xff;
+ byteArray[index] = byte;
+ value = (value - byte) / 256;
+ }
+
+ return Uint8Array.from(byteArray);
+};
+
+export function encrypt(unsigned, nonce, key) {
+ return sodium.crypto_secretbox_easy(unsigned, makeOnce(nonce), key);
+}
+
+export function decrypt(signed, nonce, key) {
+ return sodium.crypto_secretbox_open_easy(signed, makeOnce(nonce), key);
+}
+
+window.setByName = (name, value) => {
+ switch (name) {
+ case 'remote_id':
+ localStorage.setItem('remote-id', value);
+ break;
+ case 'connect':
+ newConn();
+ startConn(value);
+ break;
+ case 'login':
+ value = JSON.parse(value);
+ curConn.setRemember(value.remember == 'true');
+ curConn.login(value.password);
+ break;
+ case 'close':
+ close();
+ break;
+ case 'refresh':
+ curConn.refresh();
+ break;
+ case 'reconnect':
+ curConn.reconnect();
+ break;
+ case 'toggle_option':
+ curConn.toggleOption(value);
+ break;
+ case 'image_quality':
+ curConn.setImageQuality(value);
+ break;
+ case 'lock_screen':
+ curConn.lockScreen();
+ break;
+ case 'ctrl_alt_del':
+ curConn.ctrlAltDel();
+ break;
+ case 'switch_display':
+ curConn.switchDisplay(value);
+ break;
+ case 'remove':
+ const peers = getPeers();
+ delete peers[value];
+ localStorage.setItem('peers', JSON.stringify(peers));
+ break;
+ case 'input_key':
+ value = JSON.parse(value);
+ curConn.inputKey(value.name, value.down == 'true', value.press == 'true', value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true');
+ break;
+ case 'input_string':
+ curConn.inputString(value);
+ break;
+ case 'send_mouse':
+ let mask = 0;
+ value = JSON.parse(value);
+ switch (value.type) {
+ case 'down':
+ mask = 1;
+ break;
+ case 'up':
+ mask = 2;
+ break;
+ case 'wheel':
+ mask = 3;
+ break;
+ }
+ switch (value.buttons) {
+ case 'left':
+ mask |= 1 << 3;
+ break;
+ case 'right':
+ mask |= 2 << 3;
+ break;
+ case 'wheel':
+ mask |= 4 << 3;
+ }
+ curConn.inputMouse(mask, parseInt(value.x || '0'), parseInt(value.y || '0'), value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true');
+ break;
+ case 'option':
+ value = JSON.parse(value);
+ localStorage.setItem(value.name, value.value);
+ break;
+ case 'peer_option':
+ value = JSON.parse(value);
+ curConn.setOption(value.name, value.value);
+ break;
+ case 'input_os_password':
+ curConn.inputOsPassword(value);
+ break;
+ default:
+ break;
+ }
+}
+
+window.getByName = (name, arg) => {
+ let v = _getByName(name, arg);
+ if (typeof v == 'string' || v instanceof String) return v;
+ if (v == undefined || v == null) return '';
+ return JSON.stringify(v);
+}
+
+function getPeersForDart() {
+ const peers = [];
+ for (const [id, value] of Object.entries(getPeers())) {
+ if (!id) continue;
+ const tm = value['tm'];
+ const info = value['info'];
+ if (!tm || !info) continue;
+ peers.push([tm, id, info]);
+ }
+ return peers.sort().reverse().map(x => x.slice(1));
+}
+
+function _getByName(name, arg) {
+ switch (name) {
+ case 'peers':
+ return getPeersForDart();
+ case 'remote_id':
+ return localStorage.getItem('remote-id');
+ case 'remember':
+ return curConn.getRemember();
+ case 'toggle_option':
+ return curConn.getOption(arg) || false;
+ case 'option':
+ return localStorage.getItem(arg);
+ case 'image_quality':
+ return curConn.getImageQuality();
+ case 'translate':
+ arg = JSON.parse(arg);
+ return translate(arg.locale, arg.text);
+ case 'peer_option':
+ return curConn.getOption(arg);
+ case 'test_if_valid_server':
+ break;
+ case 'version':
+ return version;
+ }
+ return '';
+}
+
+let opusWorker = new Worker("./libopus.js");
+let pcmPlayer;
+
+export function initAudio(channels, sampleRate) {
+ pcmPlayer = newAudioPlayer(channels, sampleRate);
+ opusWorker.postMessage({ channels, sampleRate });
+}
+
+export function playAudio(packet) {
+ opusWorker.postMessage(packet, [packet.buffer]);
+}
+
+window.init = async () => {
+ if (yuvWorker) {
+ yuvWorker.onmessage = (e) => {
+ onRgba(e.data);
+ }
+ }
+ opusWorker.onmessage = (e) => {
+ pcmPlayer.feed(e.data);
+ }
+ loadVp9(() => { });
+ await initZstd();
+ console.log('init done');
+}
+
+export function getPeers() {
+ try {
+ return JSON.parse(localStorage.getItem('peers')) || {};
+ } catch (e) {
+ return {};
+ }
+}
+
+function newAudioPlayer(channels, sampleRate) {
+ return new PCMPlayer({
+ channels,
+ sampleRate,
+ flushingTime: 2000
+ });
+}
+
+export function copyToClipboard(text) {
+ if (window.clipboardData && window.clipboardData.setData) {
+ // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible.
+ return window.clipboardData.setData("Text", text);
+
+ }
+ else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
+ var textarea = document.createElement("textarea");
+ textarea.textContent = text;
+ textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge.
+ document.body.appendChild(textarea);
+ textarea.select();
+ try {
+ return document.execCommand("copy"); // Security exception may be thrown by some browsers.
+ }
+ catch (ex) {
+ console.warn("Copy to clipboard failed.", ex);
+ // return prompt("Copy to clipboard: Ctrl+C, Enter", text);
+ }
+ finally {
+ document.body.removeChild(textarea);
+ }
+ }
+}
\ No newline at end of file
diff --git a/flutter/web/js/src/main.ts b/flutter/web/v1/js/src/main.ts
similarity index 100%
rename from flutter/web/js/src/main.ts
rename to flutter/web/v1/js/src/main.ts
diff --git a/flutter/web/js/src/style.css b/flutter/web/v1/js/src/style.css
similarity index 100%
rename from flutter/web/js/src/style.css
rename to flutter/web/v1/js/src/style.css
diff --git a/flutter/web/js/src/ui.js b/flutter/web/v1/js/src/ui.js
similarity index 100%
rename from flutter/web/js/src/ui.js
rename to flutter/web/v1/js/src/ui.js
diff --git a/flutter/web/js/src/vite-env.d.ts b/flutter/web/v1/js/src/vite-env.d.ts
similarity index 100%
rename from flutter/web/js/src/vite-env.d.ts
rename to flutter/web/v1/js/src/vite-env.d.ts
diff --git a/flutter/web/js/src/websock.ts b/flutter/web/v1/js/src/websock.ts
similarity index 100%
rename from flutter/web/js/src/websock.ts
rename to flutter/web/v1/js/src/websock.ts
diff --git a/flutter/web/js/ts_proto.py b/flutter/web/v1/js/ts_proto.py
similarity index 100%
rename from flutter/web/js/ts_proto.py
rename to flutter/web/v1/js/ts_proto.py
diff --git a/flutter/web/js/tsconfig.json b/flutter/web/v1/js/tsconfig.json
similarity index 100%
rename from flutter/web/js/tsconfig.json
rename to flutter/web/v1/js/tsconfig.json
diff --git a/flutter/web/js/vite.config.js b/flutter/web/v1/js/vite.config.js
similarity index 100%
rename from flutter/web/js/vite.config.js
rename to flutter/web/v1/js/vite.config.js
diff --git a/flutter/web/js/yarn.lock b/flutter/web/v1/js/yarn.lock
similarity index 100%
rename from flutter/web/js/yarn.lock
rename to flutter/web/v1/js/yarn.lock
diff --git a/flutter/web/libs/firebase-analytics.js b/flutter/web/v1/libs/firebase-analytics.js
similarity index 100%
rename from flutter/web/libs/firebase-analytics.js
rename to flutter/web/v1/libs/firebase-analytics.js
diff --git a/flutter/web/libs/firebase-app.js b/flutter/web/v1/libs/firebase-app.js
similarity index 100%
rename from flutter/web/libs/firebase-app.js
rename to flutter/web/v1/libs/firebase-app.js
diff --git a/flutter/web/manifest.json b/flutter/web/v1/manifest.json
similarity index 100%
rename from flutter/web/manifest.json
rename to flutter/web/v1/manifest.json
diff --git a/flutter/web/yarn.lock b/flutter/web/v1/yarn.lock
similarity index 100%
rename from flutter/web/yarn.lock
rename to flutter/web/v1/yarn.lock
diff --git a/flutter/web/yuv.js b/flutter/web/v1/yuv.js
similarity index 100%
rename from flutter/web/yuv.js
rename to flutter/web/v1/yuv.js
diff --git a/flutter/web/yuv.wasm b/flutter/web/v1/yuv.wasm
similarity index 100%
rename from flutter/web/yuv.wasm
rename to flutter/web/v1/yuv.wasm
diff --git a/flutter/web/v2/README.md b/flutter/web/v2/README.md
new file mode 100644
index 000000000..7c128776c
--- /dev/null
+++ b/flutter/web/v2/README.md
@@ -0,0 +1 @@
+Under dev.
\ No newline at end of file