if (is_osx) view.windowBlurbehind = #light;
stdout.println("current platform:", OS);
// html min-width, min-height not working on mac, below works for all
view.windowMinSize = (500, 300);
var app;
var tmp = handler.get_connect_status();
var connect_status = tmp[0];
var service_stopped = false;
var software_update_url = "";
var key_confirmed = tmp[1];
var system_error = "";
var svg_menu = ;
class ConnectStatus: Reactor.Component {
function render() {
return
{this.getConnectStatusStr()}
{service_stopped ? Start Service : ""}
;
}
function getConnectStatusStr() {
if (service_stopped) {
return "Service is not running";
} else if (connect_status == -1) {
return "Not ready. Please check your connection";
} else if (connect_status == 0) {
return "Connecting to the RustDesk network...";
}
return "Ready";
}
event click $(.connect-status .link) () {
var options = handler.get_options();
options["stop-service"] = "";
handler.set_options(options);
}
}
class RecentSessions: Reactor.Component {
function render() {
var sessions = handler.get_recent_sessions();
if (sessions.length == 0) return ;
sessions = sessions.map(this.getSession);
return
RECENT SESSIONS
{sessions}
;
}
function getSession(s) {
var id = s[0];
var username = s[1];
var hostname = s[2];
var platform = s[3];
var alias = s[4];
return
{platformSvg(platform, "white")}
{username}@{hostname}
{alias ? alias : formatId(id)}
{svg_menu}
;
}
event dblclick $(div.remote-session) (evt, me) {
createNewConnect(me.id, "connect");
}
event click $(#menu) (_, me) {
var id = me.parent.parent.id;
var platform = me.parent.parent.attributes["platform"];
$(#rdp).style.set{
display: (platform == "Windows" && is_win) ? "block" : "none",
};
// https://sciter.com/forums/topic/replacecustomize-context-menu/
var menu = $(menu#remote-context);
menu.attributes["remote-id"] = id;
me.popup(menu);
}
}
event click $(menu#remote-context li) (evt, me) {
var action = me.id;
var id = me.parent.attributes["remote-id"];
if (action == "connect") {
createNewConnect(id, "connect");
} else if (action == "transfer") {
createNewConnect(id, "file-transfer");
} else if (action == "remove") {
handler.remove_peer(id);
app.recent_sessions.update();
} else if (action == "shortcut") {
handler.create_shortcut(id);
} else if (action == "rdp") {
createNewConnect(id, "rdp");
} else if (action == "tunnel") {
createNewConnect(id, "port-forward");
} else if (action == "rename") {
var old_name = handler.get_peer_option(id, "alias");
handler.msgbox("custom-rename", "Rename", "
\
\
\
", function(res=null) {
if (!res) return;
var name = (res.name || "").trim();
if (name != old_name) handler.set_peer_option(id, "alias", name);
self.select('#' + id).select('#alias').text = name || id;
});
}
}
function createNewConnect(id, type) {
id = id.replace(/\s/g, "");
app.remote_id.value = formatId(id);
if (!id) return;
if (id == handler.get_id()) {
handler.msgbox("custom-error", "Error", "You cannot connect to your own computer");
return;
}
handler.set_remote_id(id);
handler.new_remote(id, type);
}
var myIdMenu;
var audioInputMenu;
var configOptions = {};
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 ;
inputs = ["Mute"].concat(inputs);
var me = this;
self.timer(1ms, function() { me.toggleMenuState() });
return
Audio Input
;
}
function get_default() {
if (is_win) return "System Sound";
return "";
}
function get_value() {
return configOptions["audio-input"] || this.get_default();
}
function toggleMenuState() {
var v = this.get_value();
for (var el in $$(menu#audio-input>li)) {
var selected = el.id == v;
el.attributes.toggleClass("selected", selected);
}
}
event click $(menu#audio-input>li) (_, me) {
var v = me.id;
if (v == this.get_value()) return;
if (v == this.get_default()) v = "";
configOptions["audio-input"] = v;
handler.set_options(configOptions);
this.toggleMenuState();
}
}
class MyIdMenu: Reactor.Component {
function this() {
myIdMenu = this;
}
function render() {
var me = this;
return
{this.renderPop()}
ID{svg_menu}
;
}
function renderPop() {
return
{svg_checkmark}Enable Keyboard/Mouse
{svg_checkmark}Enable Clipboard
{svg_checkmark}Enable File Transfer
{svg_checkmark}Enable TCP Tunneling
IP Whitelisting
ID/Relay Server
{svg_checkmark}Enable service
Forum
About {handler.get_app_name()}
;
}
event click $(svg#menu) (_, me) {
audioInputMenu.update({ show: true });
configOptions = handler.get_options();
this.toggleMenuState();
var menu = $(menu#config-options);
me.popup(menu);
}
function toggleMenuState() {
for (var el in $$(menu#config-options>li)) {
if (el.id && el.id.indexOf("enable-") == 0) {
var enabled = configOptions[el.id] != "N";
el.attributes.toggleClass("selected", enabled);
el.attributes.toggleClass("line-through", !enabled);
}
}
}
event click $(menu#config-options>li) (_, me) {
if (me.id && me.id.indexOf("enable-") == 0) {
configOptions[me.id] = configOptions[me.id] == "N" ? "" : "N";
handler.set_options(configOptions);
}
if (me.id == "whitelist") {
var old_value = (configOptions["whitelist"] || "").split(",").join("\n");
handler.msgbox("custom-whitelist", "IP Whitelisting", "
\
\
\
", function(res=null) {
if (!res) return;
var value = (res.text || "").trim();
if (value) {
var values = value.split(/[\s,;]+/g);
for (var ip in values) {
if (!ip.match(/^\d+\.\d+\.\d+\.\d+$/)) {
return "Invalid ip: " + ip;
}
}
value = values.join("\n");
}
if (value == old_value) return;
configOptions["whitelist"] = value.replace("\n", ",");
stdout.println("whitelist updated");
handler.set_options(configOptions);
}, 300);
} else if (me.id == "custom-server") {
var old_relay = configOptions["relay-server"] || "";
var old_id = configOptions["custom-rendezvous-server"] || "";
handler.msgbox("custom-server", "ID/Relay Server", "
\
ID Server:
\
Relay Server:
\
\
", function(res=null) {
if (!res) return;
var id = (res.id || "").trim();
var relay = (res.relay || "").trim();
if (id == old_id && relay == old_relay) return;
if (id) {
var err = handler.test_if_valid_server(id);
if (err) return "ID Server: " + err;
}
if (relay) {
var err = handler.test_if_valid_server(relay);
if (err) return "Relay Server: " + err;
}
configOptions["custom-rendezvous-server"] = id;
configOptions["relay-server"] = relay;
handler.set_options(configOptions);
});
} else if (me.id == "forum") {
handler.open_url("https:://forum.rustdesk.com");
} else if (me.id == "stop-service") {
configOptions["stop-service"] = service_stopped ? "" : "Y";
handler.set_options(configOptions);
} else if (me.id == "about") {
var name = handler.get_app_name();
handler.msgbox("custom-nocancel-nook-hasclose", "About " + name, "
;
}
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 InstalllMe: Reactor.Component {
function render() {
return
Install RustDesk
Install RustDesk on this computer ...
;
}
event click $(#install-me) {
handler.goto_install();
}
}
const http = function() {
function makeRequest(httpverb) {
return function( params ) {
params.type = httpverb;
view.request(params);
};
}
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);
}
return {
get: makeRequest(#get),
post: makeRequest(#post),
put: makeRequest(#put),
del: makeRequest(#delete),
download: download
};
}();
class UpgradeMe: Reactor.Component {
function render() {
var update_or_download = is_osx ? "download" : "update";
return
{handler.get_app_name()} Status
An update is available for RustDesk.
Click to upgrade
;
}
event click $(#install-me) {
handler.update_me("");
}
}
class UpdateMe: Reactor.Component {
function render() {
var update_or_download = is_osx ? "download" : "update";
return
{handler.get_app_name()} Status
There is a newer version of {handler.get_app_name()} ({handler.get_new_version()}) available.
Click to {update_or_download}
;
}
event click $(#install-me) {
if (is_osx) {
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("Installing ...");
handler.update_me(path);
};
var onerror = function(err) {
handler.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);
http.download(
url,
self.url(path),
onsuccess, onerror, onprogress);
}
}
class SystemError: Reactor.Component {
function render() {
return
{system_error}
;
}
}
class TrustMe: Reactor.Component {
function render() {
return
Configuration Permissions
In order to control your Desktop remotely, you need to grant RustDesk "Accessibility" permissions
Configure
;
}
event click $(#trust-me) {
handler.is_process_trusted(true);
watch_trust();
}
}
class CanScreenRecording: Reactor.Component {
function render() {
return
Configuration Permissions
In order to access your Desktop remotely, you need to grant RustDesk "Screen Recording" permissions
Configure
;
}
event click $(#screen-recording) {
handler.is_can_screen_recording(true);
watch_trust();
}
}
class FixWayland: Reactor.Component {
function render() {
return
Warning
Login screen using Wayland is not supported
Fix it
;
}
event click $(#fix-wayland) {
handler.fix_login_wayland();
app.update();
}
}
class ModifyDefaultLogin: Reactor.Component {
function render() {
return
Warning
Current Wayland display server is not supported
Fix it(re-login required)
;
}
event click $(#modify-default-login) {
if (var r = handler.modify_default_login()) {
handler.msgbox("custom-error", "Error", r);
}
app.update();
}
}
function watch_trust() {
// not use TrustMe::update, because it is buggy
var trusted = handler.is_process_trusted(false);
var el = $(div.trust-me);
if (el) {
el.style.set {
display: trusted ? "none" : "block",
};
}
// if (trusted) return;
self.timer(1s, watch_trust);
}
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;
handler.msgbox("custom-password", "Set Password", "
\
Password:
\
Confirmation:
\
\
", function(res=null) {
if (!res) return;
var p0 = (res.password || "").trim();
var p1 = (res.confirmation || "").trim();
if (p0.length < 6) {
return "Too short, at least 6 characters.";
}
if (p0 != p1) {
return "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 (!evt.shortcutKey) {
if (evt.keyCode == Event.VK_ENTER ||
(is_osx && evt.keyCode == 0x4C) ||
(is_linux && evt.keyCode == 65421)) {
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() {
// return false; // can prevent window close
var (x, y, w, h) = view.box(#rectw, #border, #screen);
handler.save_size(x, y, w, h);
}
function self.ready() {
var r = handler.get_size();
if (r[2] == 0) {
centerize(800, 600);
} else {
view.move(r[0], r[1], r[2], r[3]);
}
if (!handler.get_remote_id()) {
view.focus = $(#remote_id);
}
}
function checkConnectStatus() {
self.timer(1s, function() {
var tmp = !!handler.get_option("stop-service");
if (tmp != service_stopped) {
service_stopped = tmp;
app.connect_status.update();
myIdMenu.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();
}
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");
app.recent_sessions.update();
}
checkConnectStatus();
});
}
checkConnectStatus();