2021-03-29 15:59:14 +08:00
|
|
|
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 = <svg #menu viewBox="0 0 512 512">
|
|
|
|
<circle cx="256" cy="256" r="64"/>
|
|
|
|
<circle cx="256" cy="448" r="64"/>
|
|
|
|
<circle cx="256" cy="64" r="64"/>
|
|
|
|
</svg>;
|
|
|
|
|
|
|
|
class ConnectStatus: Reactor.Component {
|
|
|
|
function render() {
|
|
|
|
return
|
|
|
|
<div .connect-status>
|
|
|
|
<span class={"connect-status-icon connect-status" + (service_stopped ? 0 : connect_status)} />
|
|
|
|
{this.getConnectStatusStr()}
|
|
|
|
{service_stopped ? <span class="link">Start Service</span> : ""}
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 <span />;
|
|
|
|
sessions = sessions.map(this.getSession);
|
|
|
|
return <div style="width: *">
|
|
|
|
<div .recent-sessions-title>RECENT SESSIONS</div>
|
|
|
|
<div .recent-sessions-content key={sessions.length}>
|
|
|
|
{sessions}
|
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSession(s) {
|
|
|
|
var id = s[0];
|
|
|
|
var username = s[1];
|
|
|
|
var hostname = s[2];
|
|
|
|
var platform = s[3];
|
2021-06-14 18:27:09 +08:00
|
|
|
var alias = s[4];
|
2021-03-29 15:59:14 +08:00
|
|
|
return <div .remote-session id={id} platform={platform} style={"background:"+string2RGB(id+platform, 0.5)}>
|
|
|
|
<div .platform>
|
|
|
|
{platformSvg(platform, "white")}
|
|
|
|
<div .username>{username}@{hostname}</div>
|
|
|
|
</div>
|
|
|
|
<div .text>
|
2021-06-14 18:27:09 +08:00
|
|
|
<div #alias>{alias ? alias : formatId(id)}</div>
|
2021-03-29 15:59:14 +08:00
|
|
|
{svg_menu}
|
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2021-06-14 17:51:45 +08:00
|
|
|
} else if (action == "shortcut") {
|
|
|
|
handler.create_shortcut(id);
|
2021-03-29 15:59:14 +08:00
|
|
|
} else if (action == "rdp") {
|
|
|
|
createNewConnect(id, "rdp");
|
|
|
|
} else if (action == "tunnel") {
|
|
|
|
createNewConnect(id, "port-forward");
|
2021-06-14 18:27:09 +08:00
|
|
|
} else if (action == "rename") {
|
|
|
|
var old_name = handler.get_peer_option(id, "alias");
|
|
|
|
handler.msgbox("custom-rename", "Rename", "<div .form> \
|
|
|
|
<div><input name='name' style='width: *; height: 23px', value='" + old_name + "' /></div> \
|
|
|
|
</div> \
|
|
|
|
", 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;
|
|
|
|
});
|
2021-03-29 15:59:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function createNewConnect(id, type) {
|
|
|
|
id = id.replace(/\s/g, "");
|
|
|
|
app.remote_id.value = formatId(id);
|
|
|
|
if (!id) return;
|
|
|
|
if (id == handler.get_id()) {
|
2021-06-05 07:27:53 +08:00
|
|
|
handler.msgbox("custom-error", "Error", "You cannot connect to your own computer");
|
2021-03-29 15:59:14 +08:00
|
|
|
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 <li />;
|
|
|
|
var inputs = handler.get_sound_inputs();
|
|
|
|
if (is_win) inputs = ["System Sound"].concat(inputs);
|
|
|
|
if (!inputs.length) return <li style="display:hidden" />;
|
|
|
|
inputs = ["Mute"].concat(inputs);
|
|
|
|
var me = this;
|
|
|
|
self.timer(1ms, function() { me.toggleMenuState() });
|
|
|
|
return <li>Audio Input
|
|
|
|
<menu #audio-input key={inputs.length}>
|
|
|
|
{inputs.map(function(name) {
|
|
|
|
return <li id={name}><span>{svg_checkmark}</span>{name}</li>;
|
|
|
|
})}
|
|
|
|
</menu>
|
|
|
|
</li>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 <div #myid>
|
|
|
|
{this.renderPop()}
|
|
|
|
ID{svg_menu}
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
function renderPop() {
|
|
|
|
return <popup>
|
|
|
|
<menu.context #config-options>
|
|
|
|
<li #enable-keyboard><span>{svg_checkmark}</span>Enable Keyboard/Mouse</li>
|
|
|
|
<li #enable-clipboard><span>{svg_checkmark}</span>Enable Clipboard</li>
|
|
|
|
<li #enable-file-transfer><span>{svg_checkmark}</span>Enable File Transfer</li>
|
|
|
|
<li #enable-tunnel><span>{svg_checkmark}</span>Enable TCP Tunneling</li>
|
|
|
|
<AudioInputs />
|
|
|
|
<div .separator />
|
|
|
|
<li #whitelist title="Only whitelisted IP can access me">IP Whitelisting</li>
|
|
|
|
<li #custom-server>ID/Relay Server</li>
|
|
|
|
<div .separator />
|
|
|
|
<li #stop-service>{service_stopped ? "Start service" : "Stop service"}</li>
|
|
|
|
<div .separator />
|
|
|
|
<li #forum>Forum</li>
|
|
|
|
<li #about>About {handler.get_app_name()}</li>
|
|
|
|
</menu>
|
|
|
|
</popup>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
this.toggleMenuState();
|
|
|
|
}
|
|
|
|
if (me.id == "whitelist") {
|
|
|
|
var old_value = (configOptions["whitelist"] || "").split(",").join("\n");
|
|
|
|
handler.msgbox("custom-whitelist", "IP Whitelisting", "<div .form> \
|
|
|
|
<textarea spellcheck=\"false\" name=\"text\" novalue=\"0.0.0.0\" style=\"overflow: scroll-indicator; height: 160px; font-size: 1.2em; padding: 0.5em;\">" + old_value + "</textarea>\
|
|
|
|
</div> \
|
|
|
|
", 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", ",");
|
2021-04-24 13:47:05 +08:00
|
|
|
stdout.println("whitelist updated");
|
2021-03-29 15:59:14 +08:00
|
|
|
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", "<div .form> \
|
|
|
|
<div><span style='width: 100px; display:inline-block'>ID Server: </span><input style='width: 250px' name='id' value='" + old_id + "' /></div> \
|
|
|
|
<div><span style='width: 100px; display:inline-block'>Relay Server: </span><input style='width: 250px' name='relay' value='" + old_relay + "' /></div> \
|
|
|
|
</div> \
|
|
|
|
", 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, "<div style='line-height: 2em'> \
|
|
|
|
<div>Version: " + handler.get_version() + " \
|
|
|
|
<div .link .custom-event url='http://rustdesk.com/privacy'>Privacy Statement</div> \
|
|
|
|
<div .link .custom-event url='http://forum.rustdesk.com'>Forum</div> \
|
|
|
|
<div style='background: #2c8cff; color: white; padding: 1em; margin-top: 1em;'>Copyright © 2020 CarrieZ Studio \
|
|
|
|
<br /> Author: Carrie \
|
|
|
|
<p style='font-weight: bold'>Made with heart in this chaotic world!</p>\
|
|
|
|
</div>\
|
|
|
|
</div>", function(el) {
|
|
|
|
if (el && el.attributes) {
|
|
|
|
handler.open_url(el.attributes['url']);
|
|
|
|
};
|
|
|
|
}, 400);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class App: Reactor.Component
|
|
|
|
{
|
|
|
|
function this() {
|
|
|
|
app = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
function render() {
|
|
|
|
var is_can_screen_recording = handler.is_can_screen_recording(false);
|
|
|
|
return
|
|
|
|
<div .app>
|
2021-06-14 17:51:45 +08:00
|
|
|
<popup>
|
|
|
|
<menu.context #remote-context>
|
|
|
|
<li #connect>Connect</li>
|
|
|
|
<li #transfer>Transfer File</li>
|
|
|
|
<li #tunnel>TCP Tunneling</li>
|
|
|
|
<li #rdp>RDP</li>
|
2021-06-14 18:27:09 +08:00
|
|
|
<li #rename>Rename</li>
|
2021-06-14 17:51:45 +08:00
|
|
|
<li #remove>Remove</li>
|
|
|
|
{is_win && <li #shortcut>Create Desktop Shortcut</li>}
|
|
|
|
</menu>
|
|
|
|
</popup>
|
|
|
|
<popup>
|
|
|
|
<menu.context #edit-password-context>
|
|
|
|
<li #refresh-password>Refresh random password</li>
|
|
|
|
<li #set-password>Set your own password</li>
|
|
|
|
</menu>
|
|
|
|
</popup>
|
2021-03-29 15:59:14 +08:00
|
|
|
<div .left-pane>
|
|
|
|
<div>
|
|
|
|
<div .title>Your Desktop</div>
|
|
|
|
<div .lighter-text>Your desktop can be accessed with this ID and password.</div>
|
|
|
|
<div .your-desktop>
|
|
|
|
<MyIdMenu />
|
|
|
|
{key_confirmed ? <input type="text" readonly value={formatId(handler.get_id())}/> : "Generating ..."}
|
|
|
|
</div>
|
|
|
|
<div .your-desktop>
|
|
|
|
<div>Password</div>
|
|
|
|
<Password />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{handler.is_installed() ? "": <InstalllMe />}
|
|
|
|
{handler.is_installed() && software_update_url ? <UpdateMe /> : ""}
|
|
|
|
{handler.is_installed() && !software_update_url && handler.is_installed_lower_version() ? <UpgradeMe /> : ""}
|
|
|
|
{is_can_screen_recording ? "": <CanScreenRecording />}
|
|
|
|
{is_can_screen_recording && !handler.is_process_trusted(false) ? <TrustMe /> : ""}
|
|
|
|
{system_error ? <SystemError /> : ""}
|
|
|
|
{!system_error && handler.is_login_wayland() ? <FixWayland /> : ""}
|
|
|
|
</div>
|
|
|
|
<div .right-pane>
|
|
|
|
<div .right-content>
|
|
|
|
<div .card-connect>
|
|
|
|
<div .title>Control Remote Desktop</div>
|
|
|
|
<ID @{this.remote_id} />
|
|
|
|
<div .right-buttons>
|
|
|
|
<button .button .outline #file-transfer>Transfer File</button>
|
|
|
|
<button .button #connect>Connect</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<RecentSessions @{this.recent_sessions} />
|
|
|
|
</div>
|
|
|
|
<ConnectStatus @{this.connect_status} />
|
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 <div .install-me>
|
|
|
|
<div>Install RustDesk</div>
|
|
|
|
<div #install-me .link>Install RustDesk on this computer ...</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 <div .install-me>
|
|
|
|
<div>{handler.get_app_name()} Status</div>
|
2021-06-05 07:27:53 +08:00
|
|
|
<div>An update is available for RustDesk.</div>
|
2021-03-29 15:59:14 +08:00
|
|
|
<div #install-me .link style="padding-top: 1em">Click to upgrade</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
event click $(#install-me) {
|
|
|
|
handler.update_me("");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class UpdateMe: Reactor.Component {
|
|
|
|
function render() {
|
|
|
|
var update_or_download = is_osx ? "download" : "update";
|
|
|
|
return <div .install-me>
|
|
|
|
<div>{handler.get_app_name()} Status</div>
|
|
|
|
<div>There is a newer version of {handler.get_app_name()} ({handler.get_new_version()}) available.</div>
|
|
|
|
<div #install-me .link style="padding-top: 1em">Click to {update_or_download}</div>
|
|
|
|
<div #download-percent style="display:hidden; padding-top: 1em;" />
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
event click $(#install-me) {
|
|
|
|
if (is_osx) {
|
2021-06-05 07:27:53 +08:00
|
|
|
handler.open_url("https://rustdesk.com");
|
2021-03-29 15:59:14 +08:00
|
|
|
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 <div .install-me>
|
|
|
|
<div>{system_error}</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TrustMe: Reactor.Component {
|
|
|
|
function render() {
|
|
|
|
return <div .trust-me>
|
|
|
|
<div>Configuration Permissions</div>
|
|
|
|
<div>In order to control your Desktop remotely, you need to grant RustDesk "Accessibility" permissions</div>
|
|
|
|
<div #trust-me .link>Configure</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
event click $(#trust-me) {
|
|
|
|
handler.is_process_trusted(true);
|
|
|
|
watch_trust();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class CanScreenRecording: Reactor.Component {
|
|
|
|
function render() {
|
|
|
|
return <div .trust-me>
|
|
|
|
<div>Configuration Permissions</div>
|
|
|
|
<div>In order to access your Desktop remotely, you need to grant RustDesk "Screen Recording" permissions</div>
|
|
|
|
<div #screen-recording .link>Configure</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
event click $(#screen-recording) {
|
|
|
|
handler.is_can_screen_recording(true);
|
|
|
|
watch_trust();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class FixWayland: Reactor.Component {
|
|
|
|
function render() {
|
|
|
|
return <div .trust-me>
|
|
|
|
<div>Warning</div>
|
|
|
|
<div>Login screen using Wayland is not supported</div>
|
|
|
|
<div #fix-wayland .link>Fix it</div>
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
event click $(#fix-wayland) {
|
|
|
|
handler.fix_login_wayland();
|
|
|
|
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
|
|
|
|
<div .eye-area style="width: *">
|
|
|
|
<input|text @{this.input} readonly value="******" />
|
|
|
|
{svg_eye}
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 <div .password style="flow:horizontal">
|
|
|
|
<PasswordEyeArea />
|
|
|
|
{svg_edit}
|
|
|
|
</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
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", "<div .form .set-password> \
|
|
|
|
<div><span>Password:</span><input|password(password) /></div> \
|
|
|
|
<div><span>Confirmation:</span><input|password(confirmation) /></div> \
|
|
|
|
</div> \
|
|
|
|
", 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 <input type="text" #remote_id .outline-focus novalue="Enter Remote ID" maxlength="13"
|
|
|
|
value={formatId(handler.get_remote_id())} />;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 ||
|
2021-07-27 20:12:53 +08:00
|
|
|
(is_osx && evt.keyCode == 0x4C) ||
|
|
|
|
(is_linux && evt.keyCode == 65421)) {
|
2021-03-29 15:59:14 +08:00
|
|
|
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(<App />);
|
|
|
|
|
|
|
|
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();
|