internationalization

This commit is contained in:
rustdesk
2021-12-25 16:45:22 +08:00
parent b3e3f6151d
commit af218dbc83
19 changed files with 774 additions and 243 deletions

View File

@@ -300,6 +300,10 @@ impl ConnectionManager {
fn exit(&self) {
std::process::exit(0);
}
fn t(&self, name: String) -> String {
crate::client::translate(name)
}
}
impl sciter::EventHandler for ConnectionManager {
@@ -308,6 +312,7 @@ impl sciter::EventHandler for ConnectionManager {
}
sciter::dispatch_script_call! {
fn t(String);
fn get_icon();
fn close(i32);
fn authorize(i32);

View File

@@ -33,22 +33,22 @@ class Body: Reactor.Component
<div>
<div .id style="font-weight: bold; font-size: 1.2em;">{c.name}</div>
<div .id>({c.peer_id})</div>
<div style="margin-top: 1.2em">Connected <span #time>{getElaspsed(c.time)}</span></div>
<div style="margin-top: 1.2em">{translate('Connected')} {" "} <span #time>{getElaspsed(c.time)}</span></div>
</div>
</div>
<div />
{c.is_file_transfer || c.port_forward ? "" : <div>Permissions</div>}
{c.is_file_transfer || c.port_forward ? "" : <div>{translate('Permissions')}</div>}
{c.is_file_transfer || c.port_forward ? "" : <div .permissions>
<div class={!c.keyboard ? "disabled" : ""} title="Allow using keyboard and mouse"><icon .keyboard /></div>
<div class={!c.clipboard ? "disabled" : ""} title="Allow using clipboard"><icon .clipboard /></div>
<div class={!c.audio ? "disabled" : ""} title="Allow hearing sound"><icon .audio /></div>
<div class={!c.keyboard ? "disabled" : ""} title={translate('Allow using keyboard and mouse')}><icon .keyboard /></div>
<div class={!c.clipboard ? "disabled" : ""} title={translate('Allow using clipboard')}><icon .clipboard /></div>
<div class={!c.audio ? "disabled" : ""} title={translate('Allow hearing sound')}><icon .audio /></div>
</div>}
{c.port_forward ? <div>Port Forwarding: {c.port_forward}</div> : ""}
<div style="size:*"/>
<div .buttons>
{auth ? "" : <button .button tabindex="-1" #accept>Accept</button>}
{auth ? "" : <button .button tabindex="-1" .outline #dismiss>Dismiss</button>}
{auth ? <button .button tabindex="-1" #disconnect>Disconnect</button> : ""}
{auth ? "" : <button .button tabindex="-1" #accept>{translate('Accept')}</button>}
{auth ? "" : <button .button tabindex="-1" .outline #dismiss>{translate('Dismiss')}</button>}
{auth ? <button .button tabindex="-1" #disconnect>{translate('Disconnect')}</button> : ""}
</div>
{c.is_file_transfer || c.port_forward ? "" : <div .chaticon>{svg_chat}</div>}
</div>
@@ -101,6 +101,7 @@ class Body: Reactor.Component
connection.authorized = true;
body.update();
handler.authorize(cid);
view.windowState = View.WINDOW_MINIMIZED;
});
}

View File

@@ -10,6 +10,15 @@ var is_file_transfer;
var is_xfce = false;
try { is_xfce = handler.is_xfce(); } catch(e) {}
function translate(name) {
try {
return handler.t(name);
} catch(_) {
return name;
}
}
function hashCode(str) {
var hash = 160 << 16 + 114 << 8 + 91;
for (var i = 0; i < str.length; i += 1) {
@@ -207,7 +216,7 @@ function getMsgboxParams() {
return msgbox_params;
}
function msgbox(type, title, text, callback, height, width, retry=0) {
function msgbox(type, title, text, callback, height, width, retry=0, contentStyle="") {
var has_msgbox = msgbox_params != null;
if (!has_msgbox && !type) return;
var remember = false;
@@ -217,7 +226,8 @@ function msgbox(type, title, text, callback, height, width, retry=0) {
msgbox_params = {
remember: remember, type: type, text: text, title: title,
getParams: getMsgboxParams,
callback: callback, retry: retry,
callback: callback, translate: translate,
retry: retry, contentStyle: contentStyle,
};
if (has_msgbox) return;
var dialog = {
@@ -239,7 +249,7 @@ function msgbox(type, title, text, callback, height, width, retry=0) {
} else if (res == "!alive") {
// do nothing
} else if (res.type == "input-password") {
if (!is_port_forward) connecting();
if (!is_port_forward) handler.msgbox("connecting", "Connecting...", "Logging in...");
handler.login(res.password, res.remember);
} else if (res.reconnect) {
if (!is_port_forward) connecting();
@@ -251,10 +261,10 @@ function connecting() {
handler.msgbox("connecting", "Connecting...", "Connection in progress. Please wait.");
}
handler.msgbox = function(type, title, text, callback=null, height=180, width=500, retry=0) {
handler.msgbox = function(type, title, text, callback=null, height=180, width=500, retry=0, contentStyle="") {
// directly call view.Dialog from native may crash, add timer here, seem safe
// too short time, msgbox won't get focus, per my test, 150 is almost minimun
self.timer(150ms, function() { msgbox(type, title, text, callback, height, width, retry); });
self.timer(150ms, function() { msgbox(type, title, text, callback, height, width, retry, contentStyle); });
}
handler.block_msgbox = function(type, title, text, callback=null, height=180, width=500, retry=0) {
@@ -312,6 +322,47 @@ function Progress()
this.value = "";
}
var svg_eye_cross = <svg viewBox="0 -21 511.96 511">
<path d="m506.68 261.88c7.043-16.984 7.043-36.461 0-53.461-41.621-100.4-140.03-165.27-250.71-165.27-46.484 0-90.797 11.453-129.64 32.191l-68.605-68.609c-8.3438-8.3398-21.824-8.3398-30.168 0-8.3398 8.3398-8.3398 21.824 0 30.164l271.49 271.49 86.484 86.488 68.676 68.672c4.1797 4.1797 9.6406 6.2695 15.102 6.2695 5.4609 0 10.922-2.0898 15.082-6.25 8.3438-8.3398 8.3438-21.824 0-30.164l-62.145-62.145c36.633-27.883 66.094-65.109 84.438-109.38zm-293.91-100.1c12.648-7.5742 27.391-11.969 43.199-11.969 47.062 0 85.332 38.273 85.332 85.336 0 15.805-4.3945 30.547-11.969 43.199z"/>
<path d="m255.97 320.48c-47.062 0-85.336-38.273-85.336-85.332 0-3.0938 0.59766-6.0195 0.91797-9.0039l-106.15-106.16c-25.344 24.707-46.059 54.465-60.117 88.43-7.043 16.98-7.043 36.457 0 53.461 41.598 100.39 140.01 165.27 250.69 165.27 34.496 0 67.797-6.3164 98.559-18.027l-89.559-89.559c-2.9844 0.32031-5.9062 0.91797-9 0.91797z"/>
</svg>;
class PasswordComponent: Reactor.Component {
this var visible = false;
this var value = '';
this var name = 'password';
function this(params) {
if (params && params.value) {
this.value = params.value;
}
if (params && params.name) {
this.name = params.name;
}
}
function render() {
return <div .password>
<input name={this.name} value={this.value} type={this.visible ? "text" : "password"} .outline-focus />
{this.visible ? svg_eye_cross : svg_eye}
</div>;
}
event click $(svg) {
var el = this.$(input);
var value = el.value;
var start = el.xcall(#selectionStart) || 0;
var end = el.xcall(#selectionEnd);
this.update({ visible: !this.visible });
self.timer(30ms, function() {
var el = this.$(input);
view.focus = el;
el.value = value;
el.xcall(#setSelection, start, end);
});
}
}
function isReasonableSize(r) {
var x = r[0];
var y = r[1];

View File

@@ -141,11 +141,11 @@ class JobTable: Reactor.Component {
}
function getStatus(job) {
if (!job.entries) return "Waiting";
if (!job.entries) return translate("Waiting");
var i = job.file_num + 1;
var n = job.num_entries || job.entries.length;
if (i > n) i = n;
var res = i + ' / ' + n + " files";
var res = i + ' / ' + n + " " + translate("files");
if (job.total_size > 0) {
var s = getSize(0, job.finished_size);
if (s) s += " / ";
@@ -155,7 +155,7 @@ class JobTable: Reactor.Component {
var percent = job.total_size == 0 ? 100 : (100. * job.finished_size / job.total_size).toInteger(); // (100. * i / (n || 1)).toInteger();
if (job.finished) percent = '100';
if (percent) res += ", " + percent + "%";
if (job.finished) res = "Finished " + res;
if (job.finished) res = translate("Finished") + " " + res;
if (job.speed) res += ", " + getSize(0, job.speed) + "/s";
return res;
}
@@ -250,7 +250,7 @@ class FolderView : Reactor.Component {
return <div .title>
{svg_computer}
<div .platform>{platformSvg(handler.get_platform(this.is_remote), "white")}</div>
<div><span>{this.is_remote ? "Remote Computer" : "Local Computer"}</span></div>
<div><span>{translate(this.is_remote ? "Remote Computer" : "Local Computer")}</span></div>
</div>
}
@@ -273,7 +273,7 @@ class FolderView : Reactor.Component {
function renderOpBar() {
if (this.is_remote) {
return <div .toolbar .remote>
<div .send .button>{svg_send}<span>Receive</span></div>
<div .send .button>{svg_send}<span>{translate('Receive')}</span></div>
<div .spacer></div>
<div .add-folder .button>{svg_add_folder}</div>
<div .trash .button>{svg_trash}</div>
@@ -283,7 +283,7 @@ class FolderView : Reactor.Component {
<div .add-folder .button>{svg_add_folder}</div>
<div .trash .button>{svg_trash}</div>
<div .spacer></div>
<div .send .button><span>Send</span>{svg_send}</div>
<div .send .button><span>{translate('Send')}</span>{svg_send}</div>
</div>;
}
@@ -308,14 +308,14 @@ class FolderView : Reactor.Component {
var id = (this.is_remote ? "remote" : "local") + "-folder-view";
return <table @{this.table} .folder-view .has_current id={id}>
<thead>
<tr><th></th><th .sortable>Name</th><th .sortable>Modified</th><th .sortable>Size</th></tr>
<tr><th></th><th .sortable>{translate('Name')}</th><th .sortable>{translate('Modified')}</th><th .sortable>{translate('Size')}</th></tr>
</thead>
<tbody>
{rows}
</tbody>
<popup>
<menu.context id={id}>
<li #switch-hidden class={this.show_hidden ? "selected" : ""}><span>{svg_checkmark}</span>Show Hidden Files</li>
<li #switch-hidden class={this.show_hidden ? "selected" : ""}><span>{svg_checkmark}</span>{translate('Show Hidden Files')}</li>
</menu>
</popup>
</table>;
@@ -431,8 +431,8 @@ class FolderView : Reactor.Component {
event click $(.add-folder) () {
var me = this;
handler.msgbox("custom", "Create Folder", "<div .form> \
<div>Please enter the folder name:</div> \
handler.msgbox("custom", translate("Create Folder"), "<div .form> \
<div>" + translate("Please enter the folder name") + ":</div> \
<div><input|text(name) .outline-focus /></div> \
</div>", function(res=null) {
if (!res) return;
@@ -523,7 +523,7 @@ class FolderView : Reactor.Component {
var file_transfer;
class FileTransfer: Reactor.Component {
function this(params) {
function this() {
file_transfer = this;
}
@@ -600,7 +600,7 @@ var create_dir_jobs = {}
function confirmDelete(path, is_remote) {
handler.block_msgbox("custom-skip", "Confirm Delete", "<div .form> \
<div>Are you sure you want to delete this file?</div> \
<div>" + translate('Are you sure you want to delete this file?') + "</div> \
<div.ellipsis style=\"font-weight: bold;\">" + path + "</div> \
</div>", function(res=null) {
if (res) {
@@ -620,10 +620,10 @@ handler.confirmDeleteFiles = function(id, i, name) {
var file_path = job.path;
if (name) file_path += handler.get_path_sep(job.is_remote) + name;
handler.block_msgbox("custom-skip", "Confirm Delete", "<div .form> \
<div>Deleting #" + (i + 1) + " of " + n + " files.</div> \
<div>Are you sure you want to delete this file?</div> \
<div>" + translate('Deleting') + " #" + (i + 1) + " / " + n + " " + translate('files') + ".</div> \
<div>" + translate('Are you sure you want to delete this file?') + "</div> \
<div.ellipsis style=\"font-weight: bold;\" .text>" + name + "</div> \
<div><button|checkbox(remember) {ts}>Do this for all conflicts</button></div> \
<div><button|checkbox(remember) {ts}>" + translate('Do this for all conflicts') + "</button></div> \
</div>", function(res=null) {
if (!res) {
jt.updateJobStatus(id, i - 1, "cancel");

View File

@@ -31,6 +31,10 @@ if (is_linux) {
}
}
function get_id() {
return handler.get_option('alias') || handler.get_id()
}
function stateChanged() {
stdout.println('state changed from ' + cur_window_state + ' -> ' + view.windowState);
cur_window_state = view.windowState;
@@ -58,7 +62,7 @@ var old_window_state = View.WINDOW_SHOWN;
var input_blocked;
class Header: Reactor.Component {
function this(params) {
function this() {
header = this;
}
@@ -67,18 +71,18 @@ class Header: Reactor.Component {
var title_conn;
if (this.secure_connection && this.direct_connection) {
icon_conn = svg_secure;
title_conn = "Direct and secure connection";
title_conn = translate("Direct and encrypted connection");
} else if (this.secure_connection && !this.direct_connection) {
icon_conn = svg_secure_relay;
title_conn = "Relayed and secure connection";
title_conn = translate("Relayed and encrypted connection");
} else if (!this.secure_connection && this.direct_connection) {
icon_conn = svg_insecure;
title_conn = "Direct and insecure connection";
title_conn = translate("Direct and unencrypted connection");
} else {
icon_conn = svg_insecure_relay;
title_conn = "Relayed and insecure connection";
title_conn = translate("Relayed and unencrypted connection");
}
var title = handler.get_id();
var title = get_id();
if (pi.hostname) title += "(" + pi.username + "@" + pi.hostname + ")";
if ((pi.displays || []).length == 0) {
return <div .ellipsis style="size:*;text-align:center;margin:*;">{title}</div>;
@@ -89,14 +93,14 @@ class Header: Reactor.Component {
</div>;
});
updateWindowToolbarPosition();
var style = "flow: horizontal;";
if (is_osx) style += "margin: *";
var style = "flow:horizontal;";
if (is_osx) style += "margin:*";
self.timer(1ms, toggleMenuState);
return <div style={style}>
{is_osx || is_xfce ? "" : <span #fullscreen>{svg_fullscreen}</span>}
<div #screens>
<span #secure title={title_conn}>{icon_conn}</span>
<div .remote-id>{handler.get_id()}</div>
<div .remote-id>{get_id()}</div>
<div style="flow:horizontal;border-spacing: 0.5em;">{screens}</div>
{this.renderGlobalScreens()}
</div>
@@ -111,22 +115,22 @@ class Header: Reactor.Component {
function renderDisplayPop() {
return <popup>
<menu.context #display-options>
<li #adjust-window style="display:none">Adjust Window</li>
<li #adjust-window style="display:none">{translate('Adjust Window')}</li>
<div #adjust-window .separator style="display:none"/>
<li #original type="view-style"><span>{svg_checkmark}</span>Original</li>
<li #shrink type="view-style"><span>{svg_checkmark}</span>Shrink</li>
<li #stretch type="view-style"><span>{svg_checkmark}</span>Stretch</li>
<li #original type="view-style"><span>{svg_checkmark}</span>{translate('Original')}</li>
<li #shrink type="view-style"><span>{svg_checkmark}</span>{translate('Shrink')}</li>
<li #stretch type="view-style"><span>{svg_checkmark}</span>{translate('Stretch')}</li>
<div .separator />
<li #best type="image-quality"><span>{svg_checkmark}</span>Good image quality</li>
<li #balanced type="image-quality"><span>{svg_checkmark}</span>Balanced</li>
<li #low type="image-quality"><span>{svg_checkmark}</span>Optimize reaction time</li>
<li #custom type="image-quality"><span>{svg_checkmark}</span>Custom</li>
<li #best type="image-quality"><span>{svg_checkmark}</span>{translate('Good image quality')}</li>
<li #balanced type="image-quality"><span>{svg_checkmark}</span>{translate('Balanced')}</li>
<li #low type="image-quality"><span>{svg_checkmark}</span>{translate('Optimize reaction time')}</li>
<li #custom type="image-quality"><span>{svg_checkmark}</span>{translate('Custom')}</li>
<div .separator />
<li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>Show remote cursor</li>
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>Mute</li> : ""}
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>Disable clipboard</li> : ""}
{keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>Lock after session end</li> : ""}
{false && pi.platform == "Windows" ? <li #privacy-mode .toggle-option><span>{svg_checkmark}</span>Privacy mode</li> : ""}
<li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li>
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""}
{keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""}
{false && pi.platform == "Windows" ? <li #privacy-mode .toggle-option><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
</menu>
</popup>;
}
@@ -134,23 +138,20 @@ class Header: Reactor.Component {
function renderActionPop() {
return <popup>
<menu.context #action-options>
<li #transfer-file>Transfer File</li>
<li #tunnel>TCP Tunneling</li>
<li #transfer-file>{translate('Transfer File')}</li>
<li #tunnel>{translate('TCP Tunneling')}</li>
<div .separator />
{keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ? <li #ctrl-alt-del>Insert Ctrl + Alt + Del</li> : ""}
<li #ctrl-space>Insert Ctrl + Space</li>
<li #alt-tab>Insert Alt + Tab</li>
{false && <li #super-x>Insert Win/Super + ...</li>}
{keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ? <li #ctrl-alt-del>{translate('Insert')} Ctrl + Alt + Del</li> : ""}
<div .separator />
{keyboard_enabled ? <li #lock-screen>Insert Lock</li> : ""}
{keyboard_enabled ? <li #lock-screen>{translate('Insert Lock')}</li> : ""}
{false && pi.platform == "Windows" ? <li #block-input>Block user input </li> : ""}
{handler.support_refresh() ? <li #refresh>Refresh</li> : ""}
{handler.support_refresh() ? <li #refresh>{translate('Refresh')}</li> : ""}
</menu>
</popup>;
}
function renderGlobalScreens() {
if (pi.displays.length < 2) return "";
if (pi.displays.length < 3) return "";
var x0 = 9999999;
var y0 = 9999999;
var x = -9999999;
@@ -232,18 +233,6 @@ class Header: Reactor.Component {
event click $(#ctrl-alt-del) {
handler.ctrl_alt_del();
}
event click $(#alt-tab) {
handler.alt_tab();
}
event click $(#ctrl-space) {
handler.ctrl_space();
}
event click $(#super-x) {
handler.super_x();
}
event click $(#lock-screen) {
handler.lock_screen();
@@ -289,8 +278,8 @@ function handle_custom_image_quality() {
var bitrate0 = tmp[0] || 50;
var quantizer0 = tmp.length > 1 ? tmp[1] : 100;
handler.msgbox("custom", "Custom Image Quality", "<div .form> \
<div><input type=\"hslider\" style=\"width: 66%\" name=\"bitrate\" max=\"100\" min=\"10\" value=\"" + bitrate0 + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% bitrate</div> \
<div><input type=\"hslider\" style=\"width: 66%\" name=\"quantizer\" max=\"100\" min=\"0\" value=\"" + quantizer0 + "\"/ buddy=\"quantizer-buddy\"><b #quantizer-buddy>x</b>% quantizer</div> \
<div><input type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"100\" min=\"10\" value=\"" + bitrate0 + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% bitrate</div> \
<div><input type=\"hslider\" style=\"width: 50%\" name=\"quantizer\" max=\"100\" min=\"0\" value=\"" + quantizer0 + "\"/ buddy=\"quantizer-buddy\"><b #quantizer-buddy>x</b>% quantizer</div> \
</div>", function(res=null) {
if (!res) return;
if (!res.bitrate) return;

View File

@@ -18,29 +18,35 @@ var svg_menu = <svg #menu viewBox="0 0 512 512">
<circle cx="256" cy="64" r="64"/>
</svg>;
var my_id = "";
function get_id() {
my_id = handler.get_id();
return my_id;
}
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> : ""}
{service_stopped ? <span .link #start-service>{translate('Start Service')}</span> : ""}
</div>;
}
function getConnectStatusStr() {
if (service_stopped) {
return "Service is not running";
return translate("Service is not running");
} else if (connect_status == -1) {
return "Not ready. Please check your connection";
return translate('not_ready_status');
} else if (connect_status == 0) {
return "Connecting to the RustDesk network...";
return translate('connecting_status');
}
return "Ready";
return translate("Ready");
}
event click $(.connect-status .link) () {
handler.set_option("stop-service", "");
event click $(#start-service) () {
handler.set_option("stop-service", "");
}
}
@@ -50,7 +56,7 @@ class RecentSessions: Reactor.Component {
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-title>{translate("Recent Sessions")}</div>
<div .recent-sessions-content key={sessions.length}>
{sessions}
</div>
@@ -126,7 +132,7 @@ function createNewConnect(id, type) {
id = id.replace(/\s/g, "");
app.remote_id.value = formatId(id);
if (!id) return;
if (id == handler.get_id()) {
if (id == my_id) {
handler.msgbox("custom-error", "Error", "You cannot connect to your own computer");
return;
}
@@ -149,10 +155,10 @@ class AudioInputs: Reactor.Component {
inputs = ["Mute"].concat(inputs);
var me = this;
self.timer(1ms, function() { me.toggleMenuState() });
return <li>Audio Input
return <li>{translate('Audio Input')}
<menu #audio-input key={inputs.length}>
{inputs.map(function(name) {
return <li id={name}><span>{svg_checkmark}</span>{name}</li>;
return <li id={name}><span>{svg_checkmark}</span>{translate(name)}</li>;
})}
</menu>
</li>;
@@ -190,7 +196,6 @@ class MyIdMenu: Reactor.Component {
}
function render() {
var me = this;
return <div #myid>
{this.renderPop()}
ID{svg_menu}
@@ -200,19 +205,18 @@ class MyIdMenu: Reactor.Component {
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>
<li #enable-keyboard><span>{svg_checkmark}</span>{translate('Enable Keyboard/Mouse')}</li>
<li #enable-clipboard><span>{svg_checkmark}</span>{translate('Enable Clipboard')}</li>
<li #enable-file-transfer><span>{svg_checkmark}</span>{translate('Enable File Transfer')}</li>
<li #enable-tunnel><span>{svg_checkmark}</span>{translate('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>
<li #whitelist title={translate('whitelist_tip')}>{translate('IP Whitelisting')}</li>
<li #custom-server>{translate('ID/Relay Server')}</li>
<div .separator />
<li #stop-service class={service_stopped ? "line-through" : "selected"}><span>{svg_checkmark}</span>Enable Service</li>
<li #stop-service class={service_stopped ? "line-through" : "selected"}><span>{svg_checkmark}</span>{translate("Enable Service")}</li>
<div .separator />
<li #forum>Forum</li>
<li #about>About {handler.get_app_name()}</li>
<li #about>{translate('About')} {" "} {handler.get_app_name()}</li>
</menu>
</popup>;
}
@@ -240,8 +244,9 @@ class MyIdMenu: Reactor.Component {
}
if (me.id == "whitelist") {
var old_value = handler.get_option("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>\
handler.msgbox("custom-whitelist", translate("IP Whitelisting"), "<div .form> \
<div>" + translate("whitelist_sep") + "</div> \
<textarea spellcheck=\"false\" name=\"text\" novalue=\"0.0.0.0\" style=\"overflow: scroll-indicator; width:*; height: 160px; font-size: 1.2em; padding: 0.5em;\">" + old_value + "</textarea>\
</div> \
", function(res=null) {
if (!res) return;
@@ -250,7 +255,7 @@ class MyIdMenu: Reactor.Component {
var values = value.split(/[\s,;\n]+/g);
for (var ip in values) {
if (!ip.match(/^\d+\.\d+\.\d+\.\d+$/)) {
return "Invalid ip: " + ip;
return translate("Invalid IP") + ": " + ip;
}
}
value = values.join("\n");
@@ -260,12 +265,12 @@ class MyIdMenu: Reactor.Component {
handler.set_option("whitelist", value.replace("\n", ","));
}, 300);
} else if (me.id == "custom-server") {
var configOptions = handler.get_options();
var configOptions = handler.get_options();
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><span style='width: 100px; display:inline-block'>" + translate("ID Server") + ": </span><input .outline-focus style='width: 250px' name='id' value='" + old_id + "' /></div> \
<div><span style='width: 100px; display:inline-block'>" + translate("Relay Server") + ": </span><input style='width: 250px' name='relay' value='" + old_relay + "' /></div> \
</div> \
", function(res=null) {
if (!res) return;
@@ -274,18 +279,16 @@ class MyIdMenu: Reactor.Component {
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 (err) return translate("ID Server") + ": " + err;
}
if (relay) {
var err = handler.test_if_valid_server(relay);
if (err) return "Relay Server: " + err;
if (err) return translate("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");
}, 240);
} else if (me.id == "stop-service") {
handler.set_option("stop-service", service_stopped ? "" : "Y");
} else if (me.id == "about") {
@@ -293,7 +296,7 @@ class MyIdMenu: Reactor.Component {
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 .link .custom-event url='http://rustdesk.com'>Website</div> \
<div style='background: #2c8cff; color: white; padding: 1em; margin-top: 1em;'>Copyright &copy; 2020 CarrieZ Studio \
<br /> Author: Carrie \
<p style='font-weight: bold'>Made with heart in this chaotic world!</p>\
@@ -319,13 +322,13 @@ class App: Reactor.Component
<div .app>
<popup>
<menu.context #remote-context>
<li #connect>Connect</li>
<li #transfer>Transfer File</li>
<li #tunnel>TCP Tunneling</li>
<li #rdp>RDP</li>
<li #rename>Rename</li>
<li #remove>Remove</li>
{is_win && <li #shortcut>Create Desktop Shortcut</li>}
<li #connect>{translate('Connect')}</li>
<li #transfer>{translate('Transfer File')}</li>
<li #tunnel>{translate('TCP Tunneling')}</li>
<li #rdp>RDP</li>
<li #rename>{translate('Rename')}</li>
<li #remove>{translate('Remove')}</li>
{is_win && <li #shortcut>{translate('Create Desktop Shortcut')}</li>}
</menu>
</popup>
<popup>
@@ -336,18 +339,18 @@ class App: Reactor.Component
</popup>
<div .left-pane>
<div>
<div .title>Your Desktop</div>
<div .lighter-text>Your desktop can be accessed with this ID and password.</div>
<div .title>{translate('Your Desktop')}</div>
<div .lighter-text>{translate('desk_tip')}</div>
<div .your-desktop>
<MyIdMenu />
{key_confirmed ? <input type="text" readonly value={formatId(handler.get_id())}/> : "Generating ..."}
<MyIdMenu />
{key_confirmed ? <input type="text" readonly value={formatId(get_id())}/> : translate("Generating ...")}
</div>
<div .your-desktop>
<div>Password</div>
<div>{translate('Password')}</div>
<Password />
</div>
</div>
{handler.is_installed() ? "": <InstalllMe />}
{handler.is_installed() ? "": <InstallMe />}
{handler.is_installed() && software_update_url ? <UpdateMe /> : ""}
{handler.is_installed() && !software_update_url && handler.is_installed_lower_version() ? <UpgradeMe /> : ""}
{is_can_screen_recording ? "": <CanScreenRecording />}
@@ -359,11 +362,11 @@ class App: Reactor.Component
<div .right-pane>
<div .right-content>
<div .card-connect>
<div .title>Control Remote Desktop</div>
<div .title>{translate('Control Remote Desktop')}</div>
<ID @{this.remote_id} />
<div .right-buttons>
<button .button .outline #file-transfer>Transfer File</button>
<button .button #connect>Connect</button>
<button .button .outline #file-transfer>{translate('Transfer File')}</button>
<button .button #connect>{translate('Connect')}</button>
</div>
</div>
<RecentSessions @{this.recent_sessions} />
@@ -386,11 +389,12 @@ class App: Reactor.Component
}
}
class InstalllMe: Reactor.Component {
class InstallMe: Reactor.Component {
function render() {
return <div .install-me>
<div>Install RustDesk</div>
<div #install-me .link>Install RustDesk on this computer ...</div>
<span />
<div>{translate('install_tip')}</div>
<button #install-me .button>{translate('Install')}</button>
</div>;
}
@@ -445,9 +449,9 @@ 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>
<div>An update is available for RustDesk.</div>
<div #install-me .link style="padding-top: 1em">Click to upgrade</div>
<div>{translate('Status')}</div>
<div>{translate('Your installation is lower version.')}</div>
<div #install-me .link style="padding-top: 1em">{translate('Click to upgrade')}</div>
</div>;
}
@@ -458,9 +462,9 @@ class UpgradeMe: Reactor.Component {
class UpdateMe: Reactor.Component {
function render() {
var update_or_download = is_osx ? "download" : "update";
var update_or_download = "download"; // !is_win ? "download" : "update";
return <div .install-me>
<div>{handler.get_app_name()} Status</div>
<div>{translate('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;" />
@@ -468,14 +472,16 @@ class UpdateMe: Reactor.Component {
}
event click $(#install-me) {
if (is_osx) {
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("Installing ...");
$(#download-percent).content(translate("Installing ..."));
handler.update_me(path);
};
var onerror = function(err) {
@@ -506,9 +512,9 @@ class SystemError: Reactor.Component {
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>{translate('Configuration Permissions')}</div>
<div>{translate('config_acc')}</div>
<div #trust-me .link>{translate('Configure')}</div>
</div>;
}
@@ -521,9 +527,9 @@ class TrustMe: Reactor.Component {
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>{translate('Configuration Permissions')}</div>
<div>{translate('config_screen')}</div>
<div #screen-recording .link>{translate('Configure')}</div>
</div>;
}
@@ -536,9 +542,10 @@ class CanScreenRecording: Reactor.Component {
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>{translate('Warning')}</div>
<div>{translate('Login screen using Wayland is not supported')}</div>
<div #fix-wayland .link>{translate('Fix it')}</div>
<div style="text-align: center">({translate('Reboot required')})</div>
</div>;
}
@@ -551,9 +558,10 @@ class FixWayland: Reactor.Component {
class ModifyDefaultLogin: Reactor.Component {
function render() {
return <div .trust-me>
<div>Warning</div>
<div>Current Wayland display server is not supported</div>
<div #modify-default-login .link>Fix it(re-login required)</div>
<div>{translate('Warning')}</div>
<div>{translate('Current Wayland display server is not supported')}</div>
<div #modify-default-login .link>{translate('Fix it')}</div>
<div style="text-align: center">({translate('Reboot required')})</div>
</div>;
}
@@ -622,19 +630,19 @@ class Password: Reactor.Component {
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> \
handler.msgbox("custom-password", translate("Set Password"), "<div .form .set-password> \
<div><span>" + translate('Password') + ":</span><input|password(password) .outline-focus /></div> \
<div><span>" + translate('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.";
return translate("Too short, at least 6 characters.");
}
if (p0 != p1) {
return "The confirmation is not identical.";
return translate("The confirmation is not identical.");
}
handler.update_password(p0);
me.update();
@@ -644,7 +652,7 @@ class Password: Reactor.Component {
class ID: Reactor.Component {
function render() {
return <input type="text" #remote_id .outline-focus novalue="Enter Remote ID" maxlength="13"
return <input type="text" #remote_id .outline-focus novalue={translate("Enter Remote ID")} maxlength="15"
value={formatId(handler.get_remote_id())} />;
}
@@ -735,6 +743,10 @@ function checkConnectStatus() {
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;

View File

@@ -5,17 +5,17 @@ function self.ready() {
class Install: Reactor.Component {
function render() {
return <div .content>
<div style="font-size: 2em;">Installation</div>
<div style="margin: 2em 0;">Installation Path: <input|text disabled value={view.install_path()} /></div>
<div><button|checkbox #startmenu checked>Create start menu shortcuts</button></div>
<div><button|checkbox #desktopicon checked>Create desktop icon</button></div>
<div #aggrement .link style="margin-top: 2em;">End-user license agreement</div>
<div>By starting the installation, you accept the license agreement.</div>
<div style="font-size: 2em;">{translate('Installation')}</div>
<div style="margin: 2em 0;">{translate('Installation Path')} {": "}<input|text disabled value={view.install_path()} /></div>
<div><button|checkbox #startmenu checked>{translate('Create start menu shortcuts')}</button></div>
<div><button|checkbox #desktopicon checked>{translate('Create desktop icon')}</button></div>
<div #aggrement .link style="margin-top: 2em;">{translate('End-user license agreement')}</div>
<div>{translate('agreement_tip')}</div>
<div style="height: 1px; background: gray; margin-top: 1em" />
<div style="text-align: right;">
<progress style={"color:" + color} style="display: none" />
<button .button id="cancel" .outline style="margin-right: 2em;">Cancel</button>
<button .button id="submit">Accept and Install</button>
<button .button id="cancel" .outline style="margin-right: 2em;">{translate('Cancel')}</button>
<button .button id="submit">{translate('Accept and Install')}</button>
</div>
</div>;
}
@@ -42,4 +42,4 @@ class Install: Reactor.Component {
}
}
$(body).content(<Install />);
$(body).content(<Install />);

View File

@@ -1,4 +1,5 @@
var type, title, text, getParams, remember, retry, callback;
var type, title, text, getParams, remember, retry, callback, contentStyle;
var my_translate;
function updateParams(params) {
type = params.type;
@@ -7,7 +8,10 @@ function updateParams(params) {
getParams = params.getParams;
remember = params.remember;
callback = params.callback;
my_translate = params.translate;
retry = params.retry;
contentStyle = params.contentStyle;
try { text = translate_text(text); } catch (e) {}
if (retry > 0) {
self.timer(retry * 1000, function() {
view.close({ reconnect: true });
@@ -15,39 +19,20 @@ function updateParams(params) {
}
}
function translate_text(text) {
if (text.indexOf('Failed') == 0 && text.indexOf(': ') > 0) {
var fds = text.split(': ');
for (var i = 0; i < fds.length; ++i) {
fds[i] = my_translate(fds[i]);
}
text = fds.join(': ');
}
return text;
}
var params = view.parameters;
updateParams(params);
var svg_eye_cross = <svg viewBox="0 -21 511.96 511">
<path d="m506.68 261.88c7.043-16.984 7.043-36.461 0-53.461-41.621-100.4-140.03-165.27-250.71-165.27-46.484 0-90.797 11.453-129.64 32.191l-68.605-68.609c-8.3438-8.3398-21.824-8.3398-30.168 0-8.3398 8.3398-8.3398 21.824 0 30.164l271.49 271.49 86.484 86.488 68.676 68.672c4.1797 4.1797 9.6406 6.2695 15.102 6.2695 5.4609 0 10.922-2.0898 15.082-6.25 8.3438-8.3398 8.3438-21.824 0-30.164l-62.145-62.145c36.633-27.883 66.094-65.109 84.438-109.38zm-293.91-100.1c12.648-7.5742 27.391-11.969 43.199-11.969 47.062 0 85.332 38.273 85.332 85.336 0 15.805-4.3945 30.547-11.969 43.199z"/>
<path d="m255.97 320.48c-47.062 0-85.336-38.273-85.336-85.332 0-3.0938 0.59766-6.0195 0.91797-9.0039l-106.15-106.16c-25.344 24.707-46.059 54.465-60.117 88.43-7.043 16.98-7.043 36.457 0 53.461 41.598 100.39 140.01 165.27 250.69 165.27 34.496 0 67.797-6.3164 98.559-18.027l-89.559-89.559c-2.9844 0.32031-5.9062 0.91797-9 0.91797z"/>
</svg>;
class Password: Reactor.Component {
this var visible = false;
function render() {
return <div .password>
<input name="password" type={this.visible ? "text" : "password"} .outline-focus />
{this.visible ? svg_eye_cross : svg_eye}
</div>;
}
event click $(svg) {
var el = this.$(input);
var value = el.value;
var start = el.xcall(#selectionStart) || 0;
var end = el.xcall(#selectionEnd);
this.update({ visible: !this.visible });
self.timer(30ms, function() {
var el = this.$(input);
view.focus = el;
el.value = value;
el.xcall(#setSelection, start, end);
});
}
}
var body;
class Body: Reactor.Component {
@@ -74,9 +59,9 @@ class Body: Reactor.Component {
function getInputPasswordContent() {
var ts = remember ? { checked: true } : {};
return <div .form>
<div>Please enter your password</div>
<Password />
<div><button|checkbox(remember) {ts}>Remember password</button></div>
<div>{my_translate('Please enter your password')}</div>
<PasswordComponent />
<div><button|checkbox(remember) {ts}>{my_translate('Remember password')}</button></div>
</div>;
}
@@ -116,27 +101,27 @@ class Body: Reactor.Component {
var me = this;
self.timer(1ms, function() {
if (typeof content == "string")
me.$(#content).html = content;
me.$(#content).html = my_translate(content);
else
me.$(#content).content(content);
});
return (
<div style="size: *">
<header style={"height: 2em; background: " + color}>
<caption role="window-caption">{title}</caption>
<caption role="window-caption">{my_translate(title)}</caption>
</header>
<div style="padding: 1em 2em; size: *;">
<div style="height: *; flow: horizontal">
{icon && <div style="height: *; margin: * 0; padding-right: 2em;">{icon}</div>}
<div style="size: *; margin: * 0;" #content />
<div style={contentStyle || "size: *; margin: * 0;"} #content />
</div>
<div style="text-align: right;">
<span #error />
{show_progress ? <progress style={"color:" + color} /> : ""}
{hasCancel || hasRetry ? <button .button #cancel .outline>{hasRetry ? "OK" : "Cancel"}</button> : ""}
{this.hasSkip() ? <button .button #skip .outline>Skip</button> : ""}
{hasOk || hasRetry ? <button .button #submit>{hasRetry ? "Retry" : "OK"}</button> : ""}
{hasClose ? <button .button #cancel .outline>Close</button> : ""}
<span style="display:inline-block; max-width: 260px; font-size:12px;" #error />
<progress #progress style={"color:" + color + "; display: " + (show_progress ? "inline-block" : "none")} />
{hasCancel || hasRetry ? <button .button #cancel .outline>{my_translate(hasRetry ? "OK" : "Cancel")}</button> : ""}
{this.hasSkip() ? <button .button #skip .outline>{my_translate('Skip')}</button> : ""}
{hasOk || hasRetry ? <button .button #submit>{my_translate(hasRetry ? "Retry" : "OK")}</button> : ""}
{hasClose ? <button .button #cancel .outline>{my_translate('Close')}</button> : ""}
</div>
</div>
</div>);
@@ -149,6 +134,17 @@ class Body: Reactor.Component {
$(body).content(<Body />);
function show_progress(show=1, err="") {
if (show == -1) {
view.close()
return;
}
$(#progress).style.set {
display: show ? "inline-block" : "none"
};
$(#error).text = err;
}
function submit() {
if ($(button#submit)) {
$(button#submit).sendEvent("click");
@@ -211,9 +207,12 @@ event click $(button#submit) {
}
var values = getValues();
if (callback) {
var err = callback(values);
var err = callback(values, show_progress);
if (err && !err.trim()) {
return;
}
if (err) {
$(#error).text = err;
show_progress(false, err);
return;
}
}
@@ -235,7 +234,7 @@ event keydown (evt) {
function set_outline_focus() {
self.timer(30ms, function() {
var el = $(input.outline-focus);
var el = $(.outline-focus);
if (el) view.focus = el;
else {
el = $(#submit);

View File

@@ -21,17 +21,17 @@ class PortForward: Reactor.Component {
});
return <div #file-transfer><section>
{pfs.length ? <div style="background: green; color: white; text-align: center; padding: 0.5em;">
<span style="font-size: 1.2em">Listening ...</span><br/>
<span style="font-size: 0.8em; color: #ddd">Don't close this window while you are using the tunnel</span>
<span style="font-size: 1.2em">{translate('Listening ...')}</span><br/>
<span style="font-size: 0.8em; color: #ddd">{translate('not_close_tcp_tip')}</span>
</div> : ""}
<table #port-forward>
<thead>
<tr>
<th>Local Port</th>
<th>{translate('Local Port')}</th>
<th style="width: 1em" />
<th>Remote Host</th>
<th>Remote Port</th>
{args.length ? "" : <th style="width: 6em">Action</th>}
<th>{translate('Remote Host')}</th>
<th>{translate('Remote Port')}</th>
{args.length ? "" : <th style="width: 6em">{translate('Action')}</th>}
</tr>
</thead>
<tbody key={pfs.length}>
@@ -41,7 +41,7 @@ class PortForward: Reactor.Component {
<td .right-arrow style="text-align: center">{svg_arrow}</td>
<td><input|text #remote-host novalue="localhost" /></td>
<td><input|number #remote-port /></td>
<td style="margin:0;"><button .button #add>Add</button></td>
<td style="margin:0;"><button .button #add>{translate('Add')}</button></td>
</tr>
}
{pfs}

View File

@@ -54,7 +54,6 @@ pub struct HandlerInner {
sender: Option<mpsc::UnboundedSender<Data>>,
thread: Option<std::thread::JoinHandle<()>>,
close_state: HashMap<String, String>,
last_down_key: Option<(String, i32, bool)>,
}
#[derive(Clone, Default)]
@@ -64,7 +63,6 @@ pub struct Handler {
id: String,
args: Vec<String>,
lc: Arc<RwLock<LoginConfigHandler>>,
super_on: bool,
}
impl Deref for Handler {
@@ -146,6 +144,8 @@ impl sciter::EventHandler for Handler {
fn get_id();
fn get_default_pi();
fn get_option(String);
fn t(String);
fn set_option(String, String);
fn save_close_state(String, String);
fn is_file_transfer();
fn is_port_forward();
@@ -155,9 +155,6 @@ impl sciter::EventHandler for Handler {
fn send_mouse(i32, i32, i32, bool, bool, bool, bool);
fn key_down_or_up(bool, String, i32, bool, bool, bool, bool, bool);
fn ctrl_alt_del();
fn ctrl_space();
fn alt_tab();
fn super_x();
fn transfer_file();
fn tunnel();
fn lock_screen();
@@ -286,6 +283,10 @@ impl Handler {
self.lc.read().unwrap().remember
}
fn t(&self, name: String) -> String {
crate::client::translate(name)
}
fn is_xfce(&self) -> bool {
crate::platform::is_xfce()
}
@@ -408,6 +409,10 @@ impl Handler {
self.lc.read().unwrap().get_option(&k)
}
fn set_option(&self, k: String, v: String) {
self.lc.write().unwrap().set_option(k, v);
}
fn save_close_state(&self, k: String, v: String) {
self.write().unwrap().close_state.insert(k, v);
}
@@ -534,7 +539,6 @@ impl Handler {
fn reconnect(&mut self) {
let cloned = self.clone();
let mut lock = self.write().unwrap();
lock.last_down_key.take();
lock.thread.take().map(|t| t.join());
lock.thread = Some(std::thread::spawn(move || {
io_loop(cloned);
@@ -667,7 +671,6 @@ impl Handler {
let evt_type = mask & 0x7;
if buttons == 1 && evt_type == 1 && ctrl && self.peer_platform() != "Mac OS" {
self.send_mouse((1 << 3 | 2) as _, x, y, alt, ctrl, shift, command);
return;
}
}
}
@@ -820,20 +823,6 @@ impl Handler {
}
}
fn super_x(&mut self) {
self.super_on = true;
}
fn ctrl_space(&mut self) {
let key = "VK_SPACE".to_owned();
self.key_down_or_up(3, key, 0, false, true, false, false, false);
}
fn alt_tab(&mut self) {
let key = "VK_TAB".to_owned();
self.key_down_or_up(3, key, 0, true, false, false, false, false);
}
fn lock_screen(&mut self) {
let lock = "LOCK_SCREEN".to_owned();
self.key_down_or_up(1, lock, 0, false, false, false, false, false);
@@ -866,6 +855,18 @@ impl Handler {
command: bool,
extended: bool,
) {
if self.peer_platform() == "Windows" {
if ctrl && alt && name == "VK_DELETE" {
self.ctrl_alt_del();
return;
}
if command && name == "VK_L" {
self.lock_screen();
return;
}
}
// extended: e.g. ctrl key on right side, https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-keybd_event
// not found api of osx and xdo
log::debug!(
@@ -881,15 +882,6 @@ impl Handler {
extended,
);
let mut command = command;
if self.super_on {
command = true;
}
if down_or_up == 0 {
self.super_on = false;
}
let mut name = name;
#[cfg(target_os = "linux")]
if code == 65383 {