mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge translate mode
This commit is contained in:
@@ -104,6 +104,10 @@ icon.file {
|
||||
background:url('data: image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAH+CAESEN8jyZkcIb5N/ONy3vmHhmiGjUm7UwS+YAAAHZSURBVGje7dnbboMwDIBhBwgQoFAO7Ta//4NOqCAXYZQstatq4r+r5ubrgQSpg8iyC4ZURa+PlIpQYGiwrzyeHtYZjAL8T05O4H8BbbKvFgRa4NoBU8pXeYEkDDgaaLQBcwJrmeErJQB/7wes3QBWGnCIX0+AQycL1PO6BMwPa0nA4ZxbgTvOjUYMGPHRnZkQAY4mxPZBjmy53E7ukSkFKYB/D4XsWZQx64sCeYebOogGsoOBYvv6/UCb8F0IOBZ0TlP6lEYdANY350AJqB9/qPVuOI5evw4A1hgLigAlepnyxW80bcCcwN++A2s82Vcu02ta+ceq9BoL5KGTTRwQPlpqA3gCnwWU2kCDgeWRQPj2jAPCDxgCMjhI6uZnToDpvd/BJeFrJQB/fsAa02gCt3mi1wNuy8GgBNDZlysBNNSrADVSjcJl6vCpUn6jOdx0kz0q6PMhQRa4465SFKhx35cgUCBTwj2/NHwZAb71qR8GEP2H1XcmAtBPTEO67GP6FUUAIKGABbDLQ0EArhN2sAIGesRO+iyy+RMAjckVTlMCKFVAbh/4Af9OPgG61SkDVco3BQGT3GXaDAnTIAcYZDuBTwGsAGDxuBFeAQqIqwoFMlAVLrHr/wId5MPt0nilGgAAAABJRU5ErkJggg==');
|
||||
}
|
||||
|
||||
icon.restart {
|
||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAB7BAAAewQHDaVRTAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAbhJREFUWIXVlrFqFGEUhb+7UYxaWCQKlrKKxaZSQVGDJih2tj6MD2DnMwiWvoAIRnENIpZiYxEro6IooiS7SPwsMgNLkk3mjmYmnmb45/73nMNwz/x/qH3gMu2gH6rAU+Blw+Lngau4jpmGxVF7qp1iPWjaQKnZ2WnXbuP/NqAeUPc3ZkA9XDwvqc+BVWCgPlJ7tRwUKThZce819b46VH+pfXVRXVO/q2cSul3VOgZUl0ejq86r39TXI8mqZKDuDEwCw3IREQvAbWAGmMsQZQ0sAl3gHPB1Q+0e8BuYzRDuy2yOiFVgaUxtRf0ETGc4syk4rc6PqU0Cx9j8Zf6dAeAK8Fi9sUXtFjABvEgxJlNwRP2svlNPjbw/q35U36oTFbnyMSwabxb/gB/qA3VBHagrauV7RW0DRfP1IvMlXqkXkhz1DYyQTKtHa/Z2VVMx3IiI+PI3/bCHjuOpFrSnAMpL6QfgTcMGesDx0kBr2BMzsNyi/vtQu8CJlgwsRbZDnWP90NkKaxHxJMOXMqAeAn5u0ydwMCKGY+qbkB3C2W3EKWoXk5zVoHbUZ+6Mh7tl4G4F8RJ3qvL+AfV3r5Vdpj70AAAAAElFTkSuQmCC');
|
||||
}
|
||||
|
||||
div.buttons {
|
||||
width: *;
|
||||
border-spacing: 0.5em;
|
||||
|
||||
14
src/ui/cm.rs
14
src/ui/cm.rs
@@ -14,7 +14,7 @@ use hbb_common::{
|
||||
fs, get_version_number, log,
|
||||
message_proto::*,
|
||||
protobuf::Message as _,
|
||||
tokio::{self, sync::mpsc, task::spawn_blocking}
|
||||
tokio::{self, sync::mpsc, task::spawn_blocking},
|
||||
};
|
||||
use sciter::{make_args, Element, Value, HELEMENT};
|
||||
use std::{
|
||||
@@ -90,6 +90,7 @@ impl ConnectionManager {
|
||||
clipboard: bool,
|
||||
audio: bool,
|
||||
file: bool,
|
||||
restart: bool,
|
||||
tx: mpsc::UnboundedSender<Data>,
|
||||
) {
|
||||
self.call(
|
||||
@@ -104,7 +105,8 @@ impl ConnectionManager {
|
||||
keyboard,
|
||||
clipboard,
|
||||
audio,
|
||||
file
|
||||
file,
|
||||
restart
|
||||
),
|
||||
);
|
||||
self.write().unwrap().senders.insert(id, tx);
|
||||
@@ -158,8 +160,8 @@ impl ConnectionManager {
|
||||
id,
|
||||
file_num,
|
||||
mut files,
|
||||
overwrite_detection
|
||||
} => {
|
||||
let od = can_enable_overwrite_detection(get_version_number(VERSION));
|
||||
// cm has no show_hidden context
|
||||
// dummy remote, show_hidden, is_remote
|
||||
write_jobs.push(fs::TransferJob::new_write(
|
||||
@@ -177,7 +179,7 @@ impl ConnectionManager {
|
||||
..Default::default()
|
||||
})
|
||||
.collect(),
|
||||
od,
|
||||
overwrite_detection,
|
||||
));
|
||||
}
|
||||
ipc::FS::CancelWrite { id } => {
|
||||
@@ -489,11 +491,11 @@ async fn start_ipc(cm: ConnectionManager) {
|
||||
}
|
||||
Ok(Some(data)) => {
|
||||
match data {
|
||||
Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled} => {
|
||||
Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled, restart} => {
|
||||
log::debug!("conn_id: {}", id);
|
||||
conn_id = id;
|
||||
tx_file.send(ClipboardFileData::Enable((id, file_transfer_enabled))).ok();
|
||||
cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, tx.clone());
|
||||
cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, tx.clone());
|
||||
}
|
||||
Data::Close => {
|
||||
tx_file.send(ClipboardFileData::Enable((conn_id, false))).ok();
|
||||
|
||||
@@ -46,6 +46,7 @@ class Body: Reactor.Component
|
||||
<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 class={!c.file ? "disabled" : ""} title={translate('Allow file copy and paste')}><icon .file /></div>
|
||||
<div class={!c.restart ? "disabled" : ""} title={translate('Allow remote restart')}><icon .restart /></div>
|
||||
</div>}
|
||||
{c.port_forward ? <div>Port Forwarding: {c.port_forward}</div> : ""}
|
||||
<div style="size:*"/>
|
||||
@@ -108,6 +109,15 @@ class Body: Reactor.Component
|
||||
});
|
||||
}
|
||||
|
||||
event click $(icon.restart) {
|
||||
var { cid, connection } = this;
|
||||
checkClickTime(function() {
|
||||
connection.restart = !connection.restart;
|
||||
body.update();
|
||||
handler.switch_permission(cid, "restart", connection.restart);
|
||||
});
|
||||
}
|
||||
|
||||
event click $(button#accept) {
|
||||
var { cid, connection } = this;
|
||||
checkClickTime(function() {
|
||||
@@ -266,7 +276,7 @@ function bring_to_top(idx=-1) {
|
||||
}
|
||||
}
|
||||
|
||||
handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file) {
|
||||
handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart) {
|
||||
stdout.println("new connection #" + id + ": " + peer_id);
|
||||
var conn;
|
||||
connections.map(function(c) {
|
||||
@@ -283,7 +293,7 @@ handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, na
|
||||
port_forward: port_forward,
|
||||
name: name, authorized: authorized, time: new Date(),
|
||||
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
|
||||
audio: audio, file: file
|
||||
audio: audio, file: file, restart: restart
|
||||
});
|
||||
body.cur = connections.length - 1;
|
||||
bring_to_top();
|
||||
|
||||
@@ -199,6 +199,7 @@ class Header: Reactor.Component {
|
||||
{handler.get_audit_server() && <li #note>{translate('Note')}</li>}
|
||||
<div .separator />
|
||||
{keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ? <li #ctrl-alt-del>{translate('Insert')} Ctrl + Alt + Del</li> : ""}
|
||||
{restart_enabled && (pi.platform == "Linux" || pi.platform == "Windows" || pi.platform == "Mac OS") ? <li #restart_remote_device>{translate('Restart Remote Device')}</li> : ""}
|
||||
{keyboard_enabled ? <li #lock-screen>{translate('Insert Lock')}</li> : ""}
|
||||
{keyboard_enabled && pi.platform == "Windows" && pi.sas_enabled ? <li #block-input>{translate("Block user input")}</li> : ""}
|
||||
<li #refresh>{translate('Refresh')}</li>
|
||||
@@ -317,6 +318,12 @@ class Header: Reactor.Component {
|
||||
handler.ctrl_alt_del();
|
||||
}
|
||||
|
||||
event click $(#restart_remote_device) {
|
||||
msgbox("restart-confirmation", translate("Restart Remote Device"), translate("Are you sure you want to restart") + " " + pi.username + "@" + pi.hostname + "(" + get_id() + ") ?", function(res=null) {
|
||||
if (res != null) handler.restart_remote_device();
|
||||
});
|
||||
}
|
||||
|
||||
event click $(#lock-screen) {
|
||||
handler.lock_screen();
|
||||
}
|
||||
|
||||
190
src/ui/index.tis
190
src/ui/index.tis
@@ -3,7 +3,7 @@ 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));
|
||||
view.windowMinSize = (scaleIt(560), scaleIt(300));
|
||||
|
||||
var app;
|
||||
var tmp = handler.get_connect_status();
|
||||
@@ -212,8 +212,8 @@ class Enhancements: Reactor.Component {
|
||||
self.timer(1ms, function() { me.toggleMenuState() });
|
||||
return <li>{translate('Enhancements')}
|
||||
<menu #enhancements-menu>
|
||||
{has_hwcodec ? <li #enable-hwcodec><span>{svg_checkmark}</span>{translate("Hardware Codec")}{"(beta)"}</li> : ""}
|
||||
<li #enable-abr><span>{svg_checkmark}</span>{translate("Adaptive Bitrate")}{"(beta)"}</li>
|
||||
{has_hwcodec ? <li #enable-hwcodec><span>{svg_checkmark}</span>{translate("Hardware Codec")} (beta)</li> : ""}
|
||||
<li #enable-abr><span>{svg_checkmark}</span>{translate("Adaptive Bitrate")} (beta)</li>
|
||||
</menu>
|
||||
</li>;
|
||||
}
|
||||
@@ -274,6 +274,7 @@ class MyIdMenu: Reactor.Component {
|
||||
<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-remote-restart><span>{svg_checkmark}</span>{translate('Enable Remote Restart')}</li>
|
||||
<li #enable-tunnel><span>{svg_checkmark}</span>{translate('Enable TCP Tunneling')}</li>
|
||||
<AudioInputs />
|
||||
<Enhancements />
|
||||
@@ -529,9 +530,7 @@ class App: Reactor.Component
|
||||
<MyIdMenu />
|
||||
{key_confirmed ? <input type="text" readonly value={formatId(get_id())}/> : translate("Generating ...")}
|
||||
</div>
|
||||
<div .your-desktop>
|
||||
<PasswordArea />
|
||||
</div>
|
||||
<PasswordArea />
|
||||
</div>
|
||||
{!is_win || handler.is_installed() ? "": <InstallMe />}
|
||||
{software_update_url ? <UpdateMe /> : ""}
|
||||
@@ -802,8 +801,8 @@ function watch_screen_recording() {
|
||||
|
||||
class PasswordEyeArea : Reactor.Component {
|
||||
render() {
|
||||
var show = handler.is_random_password_valid();
|
||||
var value = show ? handler.get_random_password() : "-";
|
||||
var method = handler.get_option('verification-method');
|
||||
var value = method != 'use-permanent-password' ? password_cache[0] : "-";
|
||||
return
|
||||
<div .eye-area style="width: *">
|
||||
<input|text @{this.input} readonly value={value} />
|
||||
@@ -812,95 +811,49 @@ class PasswordEyeArea : Reactor.Component {
|
||||
}
|
||||
|
||||
event click $(svg#refresh-password) (_, me) {
|
||||
if (handler.is_random_password_valid()) handler.update_random_password();
|
||||
handler.update_temporary_password();
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
var verificationMethodMenu;
|
||||
class VerificationMethodMenu: Reactor.Component {
|
||||
var temporaryPasswordLengthMenu;
|
||||
class TemporaryPasswordLengthMenu: Reactor.Component {
|
||||
function this() {
|
||||
verificationMethodMenu = this;
|
||||
temporaryPasswordLengthMenu = this;
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!this.show) return <li />;
|
||||
var me = this;
|
||||
var method = handler.get_option('verification-method');
|
||||
self.timer(1ms, function() { me.toggleMenuState() });
|
||||
return <li>{translate('Verification Method')}
|
||||
<menu #verification-method>
|
||||
<li #verification-method-security><span>{svg_checkmark}</span>{translate('Enable security password')}</li>
|
||||
<li #verification-method-random><span>{svg_checkmark}</span>{translate('Enable random password')}</li>
|
||||
return <li disabled={ method == 'use-permanent-password' ? "true" : "false" }>{translate("Set temporary password length")}
|
||||
<menu #temporary-password-length>
|
||||
<li #temporary-password-length-6><span>{svg_checkmark}</span>6</li>
|
||||
<li #temporary-password-length-8><span>{svg_checkmark}</span>8</li>
|
||||
<li #temporary-password-length-10><span>{svg_checkmark}</span>10</li>
|
||||
</menu>
|
||||
</li>;
|
||||
}
|
||||
|
||||
function toggleMenuState() {
|
||||
var security_enabled = handler.is_security_password_enabled();
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
for (var (index, el) in this.$$(menu#verification-method>li)) {
|
||||
if (index == 0) el.attributes.toggleClass("selected", security_enabled);
|
||||
if (index == 1) el.attributes.toggleClass("selected", random_enabled);
|
||||
var length = handler.get_option("temporary-password-length");
|
||||
var index = ['6', '8', '10'].indexOf(length);
|
||||
if (index < 0) index = 0;
|
||||
for (var (i, el) in this.$$(menu#temporary-password-length>li)) {
|
||||
el.attributes.toggleClass("selected", i == index);
|
||||
}
|
||||
}
|
||||
|
||||
event click $(menu#verification-method>li) (_, me) {
|
||||
switch (me.id.substring('verification-method-'.length)) {
|
||||
case 'security':
|
||||
{
|
||||
var security_enabled = handler.is_security_password_enabled();
|
||||
handler.set_security_password_enabled(!security_enabled);
|
||||
}
|
||||
break;
|
||||
case 'random':
|
||||
{
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
handler.set_random_password_enabled(!random_enabled);
|
||||
}
|
||||
break;
|
||||
event click $(menu#temporary-password-length>li) (_, me) {
|
||||
var length = me.id.substring('temporary-password-length-'.length);
|
||||
var old_length = handler.get_option('temporary-password-length');
|
||||
if (length != old_length) {
|
||||
handler.set_option('temporary-password-length', length);
|
||||
handler.update_temporary_password();
|
||||
this.toggleMenuState();
|
||||
passwordArea.update();
|
||||
}
|
||||
|
||||
this.toggleMenuState();
|
||||
passwordArea.update();
|
||||
}
|
||||
}
|
||||
|
||||
var randomPasswordUpdateMethodMenu;
|
||||
class RandomPasswordUpdateMethodMenu: Reactor.Component {
|
||||
function this() {
|
||||
randomPasswordUpdateMethodMenu = this;
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!this.show) return <li />;
|
||||
var me = this;
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
self.timer(1ms, function() { me.toggleMenuState() });
|
||||
return <li disabled={ random_enabled ? "false" : "true" }>{translate('Random Password After Session')}
|
||||
<menu #random-password-update-method>
|
||||
<li #random-password-update-method-keep><span>{svg_checkmark}</span>{translate('Keep')}</li>
|
||||
<li #random-password-update-method-update><span>{svg_checkmark}</span>{translate('Update')}</li>
|
||||
<li #random-password-update-method-disable><span>{svg_checkmark}</span>{translate('Disable')}</li>
|
||||
</menu>
|
||||
</li>;
|
||||
}
|
||||
|
||||
function toggleMenuState() {
|
||||
var method = handler.random_password_update_method();
|
||||
for (var (index, el) in this.$$(menu#random-password-update-method>li)) {
|
||||
if (index == 0) el.attributes.toggleClass("selected", method == "KEEP");
|
||||
if (index == 1) el.attributes.toggleClass("selected", method == "UPDATE");
|
||||
if (index == 2) el.attributes.toggleClass("selected", method == "DISABLE");
|
||||
}
|
||||
}
|
||||
|
||||
event click $(menu#random-password-update-method>li) (_, me) {
|
||||
if (me.id === 'random-password-update-method-keep') handler.set_random_password_update_method("KEEP");
|
||||
if (me.id === 'random-password-update-method-update') handler.set_random_password_update_method("UPDATE");
|
||||
if (me.id === 'random-password-update-method-disable') handler.set_random_password_update_method("DISABLE");
|
||||
this.toggleMenuState();
|
||||
passwordArea.update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,11 +864,11 @@ class PasswordArea: Reactor.Component {
|
||||
}
|
||||
|
||||
function render() {
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
|
||||
var me = this;
|
||||
self.timer(1ms, function() { me.toggleMenuState() });
|
||||
return
|
||||
<div>
|
||||
<div>{translate(onetime_enabled ? 'Onetime Password' : 'Password')}</div>
|
||||
<div .your-desktop>
|
||||
<div>{translate('Password')}</div>
|
||||
<div .password style="flow:horizontal">
|
||||
{this.renderPop()}
|
||||
<PasswordEyeArea />
|
||||
@@ -925,35 +878,39 @@ class PasswordArea: Reactor.Component {
|
||||
}
|
||||
|
||||
function renderPop() {
|
||||
var security_enabled = handler.is_security_password_enabled();
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
var onetime_activated = handler.is_onetime_password_activated();
|
||||
|
||||
var method = handler.get_option('verification-method');
|
||||
return <popup><menu.context #edit-password-context>
|
||||
<li #enable-onetime-password disabled={ random_enabled ? "false" : "true" }>{translate(onetime_enabled ? "Disable onetime password" : "Enable onetime password")}</li>
|
||||
<li #activate-onetime-password disabled={ !random_enabled || !onetime_enabled || onetime_activated ? "true" : "false" }>{translate('Activate onetime password')}</li>
|
||||
<li #use-temporary-password><span>{svg_checkmark}</span>{translate('Use temporary password')}</li>
|
||||
<li #use-permanent-password><span>{svg_checkmark}</span>{translate('Use permanent password')}</li>
|
||||
<li #use-both-passwords><span>{svg_checkmark}</span>{translate('Use both passwords')}</li>
|
||||
<div .separator />
|
||||
<VerificationMethodMenu />
|
||||
<div .separator />
|
||||
<li #set-password disabled={ security_enabled ? "false" : "true" }>{translate('Set security password')}</li>
|
||||
<div .separator />
|
||||
<RandomPasswordUpdateMethodMenu />
|
||||
<li #set-password disabled={ method == 'use-temporary-password' ? "true" : "false" }>{translate('Set permanent password')}</li>
|
||||
<TemporaryPasswordLengthMenu />
|
||||
</menu></popup>;
|
||||
}
|
||||
|
||||
function toggleMenuState() {
|
||||
var id = handler.get_option('verification-method');
|
||||
if (id != 'use-temporary-password' && id != 'use-permanent-password')
|
||||
id = 'use-both-passwords';
|
||||
for (var el in [this.$(li#use-temporary-password), this.$(li#use-permanent-password), this.$(li#use-both-passwords)]) {
|
||||
el.attributes.toggleClass("selected", el.id == id);
|
||||
}
|
||||
}
|
||||
|
||||
event click $(svg#edit) (_, me) {
|
||||
randomPasswordUpdateMethodMenu.update({show: true });
|
||||
verificationMethodMenu.update({show: true });
|
||||
temporaryPasswordLengthMenu.update({show: true });
|
||||
var menu = $(menu#edit-password-context);
|
||||
me.popup(menu);
|
||||
}
|
||||
|
||||
event click $(li#set-password) {
|
||||
var me = this;
|
||||
var password = handler.permanent_password();
|
||||
var value_field = password.length == 0 ? "" : "value=" + password;
|
||||
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><span>" + translate('Password') + ":</span><input|password(password) .outline-focus " + value_field + " /></div> \
|
||||
<div><span>" + translate('Confirmation') + ":</span><input|password(confirmation) " + value_field + " /></div> \
|
||||
</div> \
|
||||
", function(res=null) {
|
||||
if (!res) return;
|
||||
@@ -965,31 +922,40 @@ class PasswordArea: Reactor.Component {
|
||||
if (p0 != p1) {
|
||||
return translate("The confirmation is not identical.");
|
||||
}
|
||||
handler.set_security_password(p0);
|
||||
handler.set_permanent_password(p0);
|
||||
me.update();
|
||||
});
|
||||
}
|
||||
|
||||
event click $(li#enable-onetime-password) {
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
handler.set_onetime_password_enabled(!onetime_enabled);
|
||||
passwordArea.update();
|
||||
}
|
||||
|
||||
event click $(li#activate-onetime-password) {
|
||||
handler.set_onetime_password_activated(true);
|
||||
passwordArea.update();
|
||||
event click $(menu#edit-password-context>li) (_, me) {
|
||||
if (me.id.indexOf('use-') == 0) {
|
||||
handler.set_option('verification-method', me.id);
|
||||
this.toggleMenuState();
|
||||
passwordArea.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var last_password_description = "";
|
||||
var password_cache = ["","",""];
|
||||
function updatePasswordArea() {
|
||||
self.timer(1s, function() {
|
||||
var description = handler.password_description();
|
||||
if (last_password_description != description) {
|
||||
last_password_description = description
|
||||
passwordArea.update();
|
||||
var temporary_password = handler.temporary_password();
|
||||
var verification_method = handler.get_option('verification-method');
|
||||
var temporary_password_length = handler.get_option('temporary-password-length');
|
||||
var update = false;
|
||||
if (password_cache[0] != temporary_password) {
|
||||
password_cache[0] = temporary_password;
|
||||
update = true;
|
||||
}
|
||||
if (password_cache[1] != verification_method) {
|
||||
password_cache[1] = verification_method;
|
||||
update = true;
|
||||
}
|
||||
if (password_cache[2] != temporary_password_length) {
|
||||
password_cache[2] = temporary_password_length;
|
||||
update = true;
|
||||
}
|
||||
if (update) passwordArea.update();
|
||||
updatePasswordArea();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ pub fn make_menubar(host: Rc<Host>, is_index: bool) {
|
||||
app_menu.addItem_(new_item);
|
||||
} else {
|
||||
// When app launched without argument, is the main panel.
|
||||
let about_item = make_menu_item("About", "a", SHOW_ABOUT_TAG);
|
||||
let about_item = make_menu_item("About", "", SHOW_ABOUT_TAG);
|
||||
app_menu.addItem_(about_item);
|
||||
let separator = NSMenuItem::separatorItem(nil).autorelease();
|
||||
app_menu.addItem_(separator);
|
||||
|
||||
@@ -91,7 +91,7 @@ class MsgboxComponent: Reactor.Component {
|
||||
var color = this.getColor();
|
||||
var icon = this.getIcon(color);
|
||||
var content = this.getContent();
|
||||
var hasCancel = this.type.indexOf("error") < 0 && this.type.indexOf("nocancel") < 0;
|
||||
var hasCancel = this.type.indexOf("error") < 0 && this.type.indexOf("nocancel") < 0 && this.type != "restarting";
|
||||
var hasOk = this.type != "connecting" && this.type != "success" && this.type.indexOf("nook") < 0;
|
||||
var hasClose = this.type.indexOf("hasclose") >= 0;
|
||||
var show_progress = this.type == "connecting";
|
||||
|
||||
115
src/ui/remote.rs
115
src/ui/remote.rs
@@ -23,10 +23,6 @@ use clipboard::{
|
||||
get_rx_clip_client, server_clip_file,
|
||||
};
|
||||
use enigo::{self, Enigo, KeyboardControllable};
|
||||
use hbb_common::fs::{
|
||||
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
|
||||
RemoveJobMeta,
|
||||
};
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
config::{Config, LocalConfig, PeerConfig},
|
||||
@@ -44,6 +40,13 @@ use hbb_common::{
|
||||
};
|
||||
use hbb_common::{config::TransferSerde, fs::TransferJobMeta};
|
||||
use rdev::{Event, EventType::*, Key as RdevKey};
|
||||
use hbb_common::{
|
||||
fs::{
|
||||
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
|
||||
RemoveJobMeta,
|
||||
},
|
||||
get_version_number,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
use crate::clipboard_file::*;
|
||||
@@ -89,6 +92,7 @@ pub struct Handler {
|
||||
inner: Arc<RwLock<HandlerInner>>,
|
||||
cmd: String,
|
||||
id: String,
|
||||
password: String,
|
||||
args: Vec<String>,
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
}
|
||||
@@ -238,23 +242,16 @@ impl sciter::EventHandler for Handler {
|
||||
fn has_hwcodec();
|
||||
fn supported_hwcodec();
|
||||
fn change_prefer_codec();
|
||||
fn restart_remote_device();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct QualityStatus {
|
||||
speed: Option<String>,
|
||||
fps: Option<i32>,
|
||||
delay: Option<i32>,
|
||||
target_bitrate: Option<i32>,
|
||||
codec_format: Option<CodecFormat>,
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn new(cmd: String, id: String, args: Vec<String>) -> Self {
|
||||
pub fn new(cmd: String, id: String, password: String, args: Vec<String>) -> Self {
|
||||
let me = Self {
|
||||
cmd,
|
||||
id: id.clone(),
|
||||
password: password.clone(),
|
||||
args,
|
||||
..Default::default()
|
||||
};
|
||||
@@ -480,6 +477,17 @@ impl Handler {
|
||||
self.send(Data::Message(msg));
|
||||
}
|
||||
|
||||
fn restart_remote_device(&mut self) {
|
||||
let mut lc = self.lc.write().unwrap();
|
||||
lc.restarting_remote_device = true;
|
||||
let msg = lc.restart_remote_device();
|
||||
self.send(Data::Message(msg));
|
||||
}
|
||||
|
||||
pub fn is_restarting_remote_device(&self) -> bool {
|
||||
self.lc.read().unwrap().restarting_remote_device
|
||||
}
|
||||
|
||||
fn t(&self, name: String) -> String {
|
||||
crate::client::translate(name)
|
||||
}
|
||||
@@ -993,7 +1001,7 @@ impl Handler {
|
||||
|
||||
fn transfer_file(&mut self) {
|
||||
let id = self.get_id();
|
||||
let args = vec!["--file-transfer", &id];
|
||||
let args = vec!["--file-transfer", &id, &self.password];
|
||||
if let Err(err) = crate::run_me(args) {
|
||||
log::error!("Failed to spawn file transfer: {}", err);
|
||||
}
|
||||
@@ -1001,7 +1009,7 @@ impl Handler {
|
||||
|
||||
fn tunnel(&mut self) {
|
||||
let id = self.get_id();
|
||||
let args = vec!["--port-forward", &id];
|
||||
let args = vec!["--port-forward", &id, &self.password];
|
||||
if let Err(err) = crate::run_me(args) {
|
||||
log::error!("Failed to spawn IP tunneling: {}", err);
|
||||
}
|
||||
@@ -1390,6 +1398,7 @@ async fn start_one_port_forward(
|
||||
handler.lc.write().unwrap().port_forward = (remote_host, remote_port);
|
||||
if let Err(err) = crate::port_forward::listen(
|
||||
handler.id.clone(),
|
||||
handler.password.clone(),
|
||||
port,
|
||||
handler.clone(),
|
||||
receiver,
|
||||
@@ -1626,8 +1635,13 @@ impl Remote {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::info!("Reset by the peer");
|
||||
self.handler.msgbox("error", "Connection Error", "Reset by the peer");
|
||||
if self.handler.is_restarting_remote_device() {
|
||||
log::info!("Restart remote device");
|
||||
self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip");
|
||||
} else {
|
||||
log::info!("Reset by the peer");
|
||||
self.handler.msgbox("error", "Connection Error", "Reset by the peer");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1808,7 +1822,6 @@ impl Remote {
|
||||
}
|
||||
|
||||
async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool {
|
||||
// log::info!("new msg from ui, {}",data);
|
||||
match data {
|
||||
Data::Close => {
|
||||
let mut misc = Misc::new();
|
||||
@@ -2197,6 +2210,22 @@ impl Remote {
|
||||
true
|
||||
}
|
||||
|
||||
async fn send_opts_after_login(&self, peer: &mut Stream) {
|
||||
if let Some(opts) = self
|
||||
.handler
|
||||
.lc
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_option_message_after_login()
|
||||
{
|
||||
let mut misc = Misc::new();
|
||||
misc.set_option(opts);
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(misc);
|
||||
allow_err!(peer.send(&msg_out).await);
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
||||
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
||||
match msg_in.union {
|
||||
@@ -2205,6 +2234,7 @@ impl Remote {
|
||||
self.first_frame = true;
|
||||
self.handler.call2("closeSuccess", &make_args!());
|
||||
self.handler.call("adaptSize", &make_args!());
|
||||
self.send_opts_after_login(peer).await;
|
||||
}
|
||||
let incomming_format = CodecFormat::from(&vf);
|
||||
if self.video_format != incomming_format {
|
||||
@@ -2217,7 +2247,9 @@ impl Remote {
|
||||
self.video_sender.send(MediaData::VideoFrame(vf)).ok();
|
||||
}
|
||||
Some(message::Union::Hash(hash)) => {
|
||||
self.handler.handle_hash(hash, peer).await;
|
||||
self.handler
|
||||
.handle_hash(&self.handler.password.clone(), hash, peer)
|
||||
.await;
|
||||
}
|
||||
Some(message::Union::LoginResponse(lr)) => match lr.union {
|
||||
Some(login_response::Union::Error(err)) => {
|
||||
@@ -2458,6 +2490,10 @@ impl Remote {
|
||||
self.handler
|
||||
.call2("setPermission", &make_args!("file", p.enabled));
|
||||
}
|
||||
Permission::Restart => {
|
||||
self.handler
|
||||
.call2("setPermission", &make_args!("restart", p.enabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(misc::Union::SwitchDisplay(s)) => {
|
||||
@@ -2513,14 +2549,14 @@ impl Remote {
|
||||
match notification.union {
|
||||
Some(back_notification::Union::BlockInputState(state)) => {
|
||||
self.handle_back_msg_block_input(
|
||||
state.enum_value_or(back_notification::BlockInputState::StateUnknown),
|
||||
state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Some(back_notification::Union::PrivacyModeState(state)) => {
|
||||
if !self
|
||||
.handle_back_msg_privacy_mode(
|
||||
state.enum_value_or(back_notification::PrivacyModeState::StateUnknown),
|
||||
state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -2539,18 +2575,18 @@ impl Remote {
|
||||
|
||||
async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) {
|
||||
match state {
|
||||
back_notification::BlockInputState::OnSucceeded => {
|
||||
back_notification::BlockInputState::BlkOnSucceeded => {
|
||||
self.update_block_input_state(true);
|
||||
}
|
||||
back_notification::BlockInputState::OnFailed => {
|
||||
back_notification::BlockInputState::BlkOnFailed => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Block user input", "Failed");
|
||||
self.update_block_input_state(false);
|
||||
}
|
||||
back_notification::BlockInputState::OffSucceeded => {
|
||||
back_notification::BlockInputState::BlkOffSucceeded => {
|
||||
self.update_block_input_state(false);
|
||||
}
|
||||
back_notification::BlockInputState::OffFailed => {
|
||||
back_notification::BlockInputState::BlkOffFailed => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Unblock user input", "Failed");
|
||||
}
|
||||
@@ -2572,7 +2608,7 @@ impl Remote {
|
||||
state: back_notification::PrivacyModeState,
|
||||
) -> bool {
|
||||
match state {
|
||||
back_notification::PrivacyModeState::OnByOther => {
|
||||
back_notification::PrivacyModeState::PrvOnByOther => {
|
||||
self.handler.msgbox(
|
||||
"error",
|
||||
"Connecting...",
|
||||
@@ -2580,46 +2616,46 @@ impl Remote {
|
||||
);
|
||||
return false;
|
||||
}
|
||||
back_notification::PrivacyModeState::NotSupported => {
|
||||
back_notification::PrivacyModeState::PrvNotSupported => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Privacy mode", "Unsupported");
|
||||
self.update_privacy_mode(false);
|
||||
}
|
||||
back_notification::PrivacyModeState::OnSucceeded => {
|
||||
back_notification::PrivacyModeState::PrvOnSucceeded => {
|
||||
self.handler
|
||||
.msgbox("custom-nocancel", "Privacy mode", "In privacy mode");
|
||||
self.update_privacy_mode(true);
|
||||
}
|
||||
back_notification::PrivacyModeState::OnFailedDenied => {
|
||||
back_notification::PrivacyModeState::PrvOnFailedDenied => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Privacy mode", "Peer denied");
|
||||
self.update_privacy_mode(false);
|
||||
}
|
||||
back_notification::PrivacyModeState::OnFailedPlugin => {
|
||||
back_notification::PrivacyModeState::PrvOnFailedPlugin => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Privacy mode", "Please install plugins");
|
||||
self.update_privacy_mode(false);
|
||||
}
|
||||
back_notification::PrivacyModeState::OnFailed => {
|
||||
back_notification::PrivacyModeState::PrvOnFailed => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Privacy mode", "Failed");
|
||||
self.update_privacy_mode(false);
|
||||
}
|
||||
back_notification::PrivacyModeState::OffSucceeded => {
|
||||
back_notification::PrivacyModeState::PrvOffSucceeded => {
|
||||
self.handler
|
||||
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
|
||||
self.update_privacy_mode(false);
|
||||
}
|
||||
back_notification::PrivacyModeState::OffByPeer => {
|
||||
back_notification::PrivacyModeState::PrvOffByPeer => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Privacy mode", "Peer exit");
|
||||
self.update_privacy_mode(false);
|
||||
}
|
||||
back_notification::PrivacyModeState::OffFailed => {
|
||||
back_notification::PrivacyModeState::PrvOffFailed => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Privacy mode", "Failed to turn off");
|
||||
}
|
||||
back_notification::PrivacyModeState::OffUnknown => {
|
||||
back_notification::PrivacyModeState::PrvOffUnknown => {
|
||||
self.handler
|
||||
.msgbox("custom-error", "Privacy mode", "Turned off");
|
||||
// log::error!("Privacy mode is turned off with unknown reason");
|
||||
@@ -2710,6 +2746,9 @@ impl Interface for Handler {
|
||||
pi_sciter.set_item("hostname", pi.hostname.clone());
|
||||
pi_sciter.set_item("platform", pi.platform.clone());
|
||||
pi_sciter.set_item("sas_enabled", pi.sas_enabled);
|
||||
if get_version_number(&pi.version) < get_version_number("1.1.10") {
|
||||
self.call2("setPermission", &make_args!("restart", false));
|
||||
}
|
||||
if self.is_file_transfer() {
|
||||
if pi.username.is_empty() {
|
||||
self.on_error("No active console user logged on, please connect and logon first.");
|
||||
@@ -2777,8 +2816,8 @@ impl Interface for Handler {
|
||||
self.start_keyboard_hook();
|
||||
}
|
||||
|
||||
async fn handle_hash(&mut self, hash: Hash, peer: &mut Stream) {
|
||||
handle_hash(self.lc.clone(), hash, self, peer).await;
|
||||
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream) {
|
||||
handle_hash(self.lc.clone(), pass, hash, self, peer).await;
|
||||
}
|
||||
|
||||
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
|
||||
|
||||
@@ -11,6 +11,7 @@ var keyboard_enabled = true; // server side
|
||||
var clipboard_enabled = true; // server side
|
||||
var audio_enabled = true; // server side
|
||||
var file_enabled = true; // server side
|
||||
var restart_enabled = true; // server side
|
||||
var scroll_body = $(body);
|
||||
|
||||
handler.setDisplay = function(x, y, w, h) {
|
||||
@@ -505,6 +506,7 @@ handler.setPermission = function(name, enabled) {
|
||||
if (name == "audio") audio_enabled = enabled;
|
||||
if (name == "file") file_enabled = enabled;
|
||||
if (name == "clipboard") clipboard_enabled = enabled;
|
||||
if (name == "restart") restart_enabled = enabled;
|
||||
input_blocked = false;
|
||||
header.update();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user