if (is_osx) view.windowBlurbehind = #light;
stdout.println("current platform:", OS);
stdout.println("is_xfce: ", is_xfce);
// html min-width, min-height not working on mac, below works for all
view.windowMinSize = (scaleIt(500), scaleIt(300));
var app;
var tmp = handler.get_connect_status();
var connect_status = tmp[0];
var service_stopped = handler.get_option("stop-service") == "Y";
var rendezvous_service_stopped = false;
var using_public_server = handler.using_public_server();
var software_update_url = "";
var key_confirmed = tmp[1];
var system_error = "";
var svg_menu = ;
var my_id = "";
function get_id() {
my_id = handler.get_id();
return my_id;
}
class ConnectStatus: Reactor.Component {
function render() {
return
;
}
function getConnectStatusStr() {
if (service_stopped) {
return translate("Service is not running");
} else if (connect_status == -1) {
return translate('not_ready_status');
} else if (connect_status == 0) {
return translate('connecting_status');
}
if (!handler.using_public_server()) return translate('Ready');
return {translate("Ready")}, {translate("setup_server_tip")};
}
event click $(#start-service) () {
handler.set_option("stop-service", "");
}
event click $(#setup-server) () {
handler.open_url("https://rustdesk.com/blog/id-relay-set/");
}
}
function createNewConnect(id, type) {
id = id.replace(/\s/g, "");
app.remote_id.value = formatId(id);
if (!id) return;
if (id == my_id) {
msgbox("custom-error", "Error", "You cannot connect to your own computer");
return;
}
handler.set_remote_id(id);
handler.new_remote(id, type);
}
class ShareRdp: Reactor.Component {
function render() {
var rdp_shared_string = translate("Enable RDP session sharing");
var cls = handler.is_share_rdp() ? "selected" : "line-through";
return
{svg_checkmark}{rdp_shared_string}
;
}
function onClick() {
handler.set_share_rdp(!handler.is_share_rdp());
this.update();
}
}
var direct_server;
class DirectServer: Reactor.Component {
function this() {
direct_server = this;
}
function render() {
var text = translate("Enable Direct IP Access");
var enabled = handler.get_option("direct-server") == "Y";
var cls = enabled ? "selected" : "line-through";
return
{svg_checkmark}{text}{enabled && }
;
}
function onClick() {
if (is_edit_rdp_port) {
is_edit_rdp_port = false;
return;
}
handler.set_option("direct-server", handler.get_option("direct-server") == "Y" ? "" : "Y");
this.update();
}
}
var myIdMenu;
var audioInputMenu;
class AudioInputs: Reactor.Component {
function this() {
audioInputMenu = this;
}
function render() {
if (!this.show) return ;
var inputs = handler.get_sound_inputs();
if (is_win) inputs = ["System Sound"].concat(inputs);
if (!inputs.length) return ;
var me = this;
self.timer(1ms, function() { me.toggleMenuState() });
return
{translate('Audio Input')}
;
}
function get_default() {
if (is_win) return "System Sound";
return "";
}
function get_value() {
return handler.get_option("audio-input") || this.get_default();
}
function toggleMenuState() {
var el = this.$(li#enable-audio);
var enabled = handler.get_option(el.id) != "N";
el.attributes.toggleClass("selected", !enabled);
var v = this.get_value();
for (var el in this.$$(menu#audio-input>li)) {
if (el.id == 'enable-audio') continue;
var selected = el.id == v;
el.attributes.toggleClass("selected", selected);
}
}
event click $(menu#audio-input>li) (_, me) {
var v = me.id;
if (v == 'enable-audio') {
handler.set_option(v, handler.get_option(v) != 'N' ? 'N' : '');
} else {
if (v == this.get_value()) return;
if (v == this.get_default()) v = "";
handler.set_option("audio-input", v);
}
this.toggleMenuState();
}
};
class Languages: Reactor.Component {
function render() {
var langs = JSON.parse(handler.get_langs());
var me = this;
self.timer(1ms, function() { me.toggleMenuState() });
return
{translate('Language')}
;
}
function toggleMenuState() {
var cur = handler.get_local_option("lang") || "default";
for (var el in this.$$(menu#languages>li)) {
var selected = cur == el.id;
el.attributes.toggleClass("selected", selected);
}
}
event click $(menu#languages>li) (_, me) {
var v = me.id;
if (v == "default") v = "";
handler.set_local_option("lang", v);
app.update();
this.toggleMenuState();
}
}
var enhancementsMenu;
class Enhancements: Reactor.Component {
function this() {
enhancementsMenu = this;
}
function render() {
var has_hwcodec = handler.has_hwcodec();
var me = this;
self.timer(1ms, function() { me.toggleMenuState() });
return
{translate('Enhancements')}
;
}
function toggleMenuState() {
for (var el in $$(menu#enhancements-menu>li)) {
if (el.id && el.id.indexOf("enable-") == 0) {
var enabled = handler.get_option(el.id) != "N";
el.attributes.toggleClass("selected", enabled);
}
}
}
event click $(menu#enhancements-menu>li) (_, me) {
var v = me.id;
if (v.indexOf("enable-") == 0) {
handler.set_option(v, handler.get_option(v) != 'N' ? 'N' : '');
}
this.toggleMenuState();
}
}
function getUserName() {
try {
return JSON.parse(handler.get_local_option("user_info")).name;
} catch(e) {}
return '';
}
function updateTheme() {
var root_element = self;
if (handler.get_option("allow-darktheme") == "Y") {
// enable dark theme
root_element.attributes.toggleClass("darktheme", true);
} else {
// disable dark theme
root_element.attributes.toggleClass("darktheme", false);
}
}
class MyIdMenu: Reactor.Component {
function this() {
myIdMenu = this;
}
function render() {
return
{this.renderPop()}
ID{svg_menu}
;
}
function renderPop() {
var username = handler.get_local_option("access_token") ? getUserName() : '';
return
\
", function(res=null) {
if (!res) return;
var value = (res.text || "").trim();
if (value) {
var values = value.split(/[\s,;\n]+/g);
for (var ip in values) {
if (!ip.match(/^\d+\.\d+\.\d+\.\d+$/)) {
return translate("Invalid IP") + ": " + ip;
}
}
value = values.join("\n");
}
if (value == old_value) return;
stdout.println("whitelist updated");
handler.set_option("whitelist", value.replace("\n", ","));
}, 300);
} else if (me.id == "custom-server") {
var configOptions = handler.get_options();
var old_relay = configOptions["relay-server"] || "";
var old_api = configOptions["api-server"] || "";
var old_id = configOptions["custom-rendezvous-server"] || "";
var old_key = configOptions["key"] || "";
msgbox("custom-server", "ID/Relay Server", "
\
" + translate("ID Server") + ":
\
" + translate("Relay Server") + ":
\
" + translate("API Server") + ":
\
" + translate("Key") + ":
\
\
", function(res=null) {
if (!res) return;
var id = (res.id || "").trim();
var relay = (res.relay || "").trim();
var api = (res.api || "").trim().toLowerCase();
var key = (res.key || "").trim();
if (id == old_id && relay == old_relay && key == old_key && api == old_api) return;
if (id) {
var err = handler.test_if_valid_server(id);
if (err) return translate("ID Server") + ": " + err;
}
if (relay) {
var err = handler.test_if_valid_server(relay);
if (err) return translate("Relay Server") + ": " + err;
}
if (api) {
if (0 != api.indexOf("https://") && 0 != api.indexOf("http://")) {
return translate("API Server") + ": " + translate("invalid_http");
}
}
configOptions["custom-rendezvous-server"] = id;
configOptions["relay-server"] = relay;
configOptions["api-server"] = api;
configOptions["key"] = key;
handler.set_options(configOptions);
}, 260);
} else if (me.id == "socks5-server") {
var socks5 = handler.get_socks() || {};
var old_proxy = socks5[0] || "";
var old_username = socks5[1] || "";
var old_password = socks5[2] || "";
msgbox("custom-server", "Socks5 Proxy",
{translate("Hostname")}:
{translate("Username")}:
{translate("Password")}:
, function(res=null) {
if (!res) return;
var proxy = (res.proxy || "").trim();
var username = (res.username || "").trim();
var password = (res.password || "").trim();
if (proxy == old_proxy && username == old_username && password == old_password) return;
if (proxy) {
var err = handler.test_if_valid_server(proxy);
if (err) return translate("Server") + ": " + err;
}
handler.set_socks(proxy, username, password);
}, 240);
} else if (me.id == "stop-service") {
handler.set_option("stop-service", service_stopped ? "" : "Y");
} else if (me.id == "stop-rendezvous-service") {
handler.set_option("stop-rendezvous-service", rendezvous_service_stopped ? "" : "Y");
} else if (me.id == "change-id") {
msgbox("custom-id", translate("Change ID"), "
\
" + translate('id_change_tip') + "
\
ID:
\
\
", function(res=null, show_progress) {
if (!res) return;
show_progress();
var id = (res.id || "").trim();
if (!id) return;
if (id == my_id) return;
handler.change_id(id);
function check_status() {
var status = handler.get_async_job_status();
if (status == " ") self.timer(0.1s, check_status);
else {
if (status) show_progress(false, translate(status));
else show_progress(-1);
}
}
check_status();
return " ";
});
} else if (me.id == "allow-darktheme") {
updateTheme();
} else if (me.id == "about") {
this.showAbout()
}
}
}
var is_edit_direct_access_port;
class EditDirectAccessPort: Reactor.Component {
function render() {
return {svg_edit};
}
function onMouse(evt) {
if (evt.type == Event.MOUSE_DOWN) {
is_edit_direct_access_port = true;
editDirectAccessPort();
}
}
}
function editDirectAccessPort() {
var p0 = handler.get_option('direct-access-port');
var port = p0 ? :
;
msgbox("custom-direct-access-port", translate('Direct IP Access Settings'),
{translate('Port')}:{port}
, function(res=null) {
if (!res) return;
var p = (res.port || '').trim();
if (p) {
p = p.toInteger();
if (!(p > 0)) {
return translate("Invalid port");
}
p = p + '';
}
if (p != p0) handler.set_option('direct-access-port', p);
});
}
class App: Reactor.Component
{
function this() {
app = this;
}
function render() {
var is_can_screen_recording = handler.is_can_screen_recording(false);
return
;
}
event click $(button#connect) {
this.newRemote("connect");
}
event click $(button#file-transfer) {
this.newRemote("file-transfer");
}
function newRemote(type) {
createNewConnect(this.remote_id.value, type);
}
}
class InstallMe: Reactor.Component {
function render() {
return
{translate('install_tip')}
;
}
event click $(#install-me) {
handler.goto_install();
}
}
function download(from, to, args..) {
var rqp = { type:#get, url: from, toFile: to };
var fn = 0;
var on = 0;
for( var p in args ) {
if( p instanceof Function ) {
switch(++fn) {
case 1: rqp.success = p; break;
case 2: rqp.error = p; break;
case 3: rqp.progress = p; break;
}
} else if( p instanceof Object ) {
switch(++on) {
case 1: rqp.params = p; break;
case 2: rqp.headers = p; break;
}
}
}
view.request(rqp);
}
// current running version is higher than installed
class UpgradeMe: Reactor.Component {
function render() {
var update_or_download = is_osx ? "download" : "update";
return
{translate('Status')}
{translate('Your installation is lower version.')}
{translate('Click to upgrade')}
;
}
event click $(#install-me) {
handler.update_me("");
}
}
class UpdateMe: Reactor.Component {
function render() {
var update_or_download = "download"; // !is_win ? "download" : "update";
return
{translate('Status')}
There is a newer version of {handler.get_app_name()} ({handler.get_new_version()}) available.
{translate('Click to ' + update_or_download)}
;
}
event click $(#install-me) {
handler.open_url("https://rustdesk.com");
return;
if (!is_win) {
handler.open_url("https://rustdesk.com");
return;
}
var url = software_update_url + '.' + handler.get_software_ext();
var path = handler.get_software_store_path();
var onsuccess = function(md5) {
$(#download-percent).content(translate("Installing ..."));
handler.update_me(path);
};
var onerror = function(err) {
msgbox("custom-error", "Download Error", "Failed to download");
};
var onprogress = function(loaded, total) {
if (!total) total = 5 * 1024 * 1024;
var el = $(#download-percent);
el.style.set{display: "block"};
el.content("Downloading %" + (loaded * 100 / total));
};
stdout.println("Downloading " + url + " to " + path);
download(
url,
self.url(path),
onsuccess, onerror, onprogress);
}
}
class SystemError: Reactor.Component {
function render() {
return
{system_error}
;
}
}
class TrustMe: Reactor.Component {
function render() {
return
{translate('Current Wayland display server is not supported')}
{translate('Help')}
;
}
event click $(#modify-default-login) {
if (var r = handler.modify_default_login()) {
// without handler, will fail, fucking stupid sciter
handler.msgbox("custom-error", "Error", r);
}
app.update();
}
event click $(#help-me) {
handler.open_url(translate("doc_fix_wayland"));
}
}
function watch_trust() {
// not use TrustMe::update, because it is buggy
var trusted = handler.is_process_trusted(false);
var el = $(div#trust-me-box);
if (el) {
el.style.set {
display: trusted ? "none" : "block",
};
}
if (trusted) {
app.update();
return;
}
self.timer(1s, watch_trust);
}
function watch_screen_recording() {
var trusted = handler.is_can_screen_recording(false);
var el = $(div#screen-recording-box);
if (el) {
el.style.set {
display: trusted ? "none" : "block",
};
}
if (trusted) {
app.update();
return;
}
self.timer(1s, watch_screen_recording);
}
class PasswordEyeArea : Reactor.Component {
render() {
return
{svg_eye}
;
}
event mouseenter {
var me = this;
me.leaved = false;
me.timer(300ms, function() {
if (me.leaved) return;
me.input.value = handler.get_password();
});
}
event mouseleave {
this.leaved = true;
this.input.value = "******";
}
}
class Password: Reactor.Component {
function render() {
return
{svg_edit}
;
}
event click $(svg#edit) (_, me) {
var menu = $(menu#edit-password-context);
me.popup(menu);
}
event click $(li#refresh-password) {
handler.update_password("");
this.update();
}
event click $(li#set-password) {
var me = this;
msgbox("custom-password", translate("Set Password"), "
\
" + translate('Password') + ":
\
" + translate('Confirmation') + ":
\
\
", function(res=null) {
if (!res) return;
var p0 = (res.password || "").trim();
var p1 = (res.confirmation || "").trim();
if (p0.length < 6) {
return translate("Too short, at least 6 characters.");
}
if (p0 != p1) {
return translate("The confirmation is not identical.");
}
handler.update_password(p0);
me.update();
});
}
}
class ID: Reactor.Component {
function render() {
return ;
}
// https://github.com/c-smile/sciter-sdk/blob/master/doc/content/sciter/Event.htm
event change {
var fid = formatId(this.value);
var d = this.value.length - (this.old_value || "").length;
this.old_value = this.value;
var start = this.xcall(#selectionStart) || 0;
var end = this.xcall(#selectionEnd);
if (fid == this.value || d <= 0 || start != end) {
return;
}
// fix Caret position
this.value = fid;
var text_after_caret = this.old_value.substr(start);
var n = fid.length - formatId(text_after_caret).length;
this.xcall(#setSelection, n, n);
}
}
var reg = /^\d+$/;
function formatId(id) {
id = id.replace(/\s/g, "");
if (reg.test(id) && id.length > 3) {
var n = id.length;
var a = n % 3 || 3;
var new_id = id.substr(0, a);
for (var i = a; i < n; i += 3) {
new_id += " " + id.substr(i, 3);
}
return new_id;
}
return id;
}
event keydown (evt) {
if (view.focus && view.focus.id != 'remote_id') {
return;
}
if (!evt.shortcutKey) {
if (isEnterKey(evt)) {
var el = $(button#connect);
view.focus = el;
el.sendEvent("click");
// simulate button click effect, windows does not have this issue
el.attributes.toggleClass("active", true);
self.timer(0.3s, function() {
el.attributes.toggleClass("active", false);
});
}
}
}
$(body).content(
);
function self.closing() {
var (x, y, w, h) = view.box(#rectw, #border, #screen);
handler.closing(x, y, w, h);
return true;
}
function self.ready() {
var r = handler.get_size();
if (isReasonableSize(r) && r[2] > 0) {
var (sx, sy, sw, sh) = view.screenBox(#workarea, #rectw);
if (r[2] >= sw && r[3] >= sh) {
self.timer(1ms, function() { view.windowState = View.WINDOW_MAXIMIZED; });
} else {
view.move(r[0], r[1], r[2], r[3]);
}
} else {
centerize(scaleIt(800), scaleIt(600));
}
if (!handler.get_remote_id()) {
view.focus = $(#remote_id);
}
refreshCurrentUser();
updateTheme();
}
function showAbout() {
myIdMenu.showAbout();
}
function showSettings() {
if ($(#overlay).style#display == 'block') return;
myIdMenu.showSettingMenu();
}
function checkConnectStatus() {
self.timer(1s, function() {
var tmp = !!handler.get_option("stop-service");
if (tmp != service_stopped) {
service_stopped = tmp;
app.update();
}
tmp = !!handler.get_option("stop-rendezvous-service");
if (tmp != rendezvous_service_stopped) {
rendezvous_service_stopped = tmp;
myIdMenu.update();
}
tmp = handler.using_public_server();
if (tmp != using_public_server) {
using_public_server = tmp;
app.connect_status.update();
}
tmp = handler.get_connect_status();
if (tmp[0] != connect_status) {
connect_status = tmp[0];
app.connect_status.update();
}
if (tmp[1] != key_confirmed) {
key_confirmed = tmp[1];
app.update();
}
if (tmp[2] && tmp[2] != my_id) {
stdout.println("id updated");
app.update();
}
tmp = handler.get_error();
if (system_error != tmp) {
system_error = tmp;
app.update();
}
tmp = handler.get_software_update_url();
if (tmp != software_update_url) {
software_update_url = tmp;
app.update();
}
if (handler.recent_sessions_updated()) {
stdout.println("recent sessions updated");
updateAbPeer();
app.update();
}
check_if_overlay();
checkConnectStatus();
});
}
var enter = false;
function self.onMouse(evt) {
switch(evt.type) {
case Event.MOUSE_ENTER:
enter = true;
check_if_overlay();
break;
case Event.MOUSE_LEAVE:
$(#overlay).style#display = 'none';
enter = false;
break;
}
}
function check_if_overlay() {
if (!handler.get_option('allow-remote-config-modification')) {
var time0 = getTime();
handler.check_mouse_time();
self.timer(120ms, function() {
if (!enter) return;
var d = time0 - handler.get_mouse_time();
if (d < 120) $(#overlay).style#display = 'block';
});
}
}
checkConnectStatus();
function login() {
var name0 = getUserName();
var pass0 = '';
msgbox("custom-login", translate('Login'),