mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge branch 'master' into lan_discovery
This commit is contained in:
290
src/ui/ab.tis
Normal file
290
src/ui/ab.tis
Normal file
@@ -0,0 +1,290 @@
|
||||
var svg_tile = <svg #session-tile viewBox="0 0 158.6 158.6"><path style="stroke-width:.309756" d="M5.4 157.7c-1-.3-2-1-3.2-2.1-2.8-2.8-2.6-1-2.5-32 0-26.7 0-27 .7-28.3a9.3 9.3 0 0 1 4-4.2c1.2-.6 2.3-.6 29-.7 27.5 0 27.6 0 29.1.6.8.4 2 1.2 2.7 2 2.4 2.5 2.3.7 2.2 31.6-.1 26.5-.1 27.6-.7 28.8a9.3 9.3 0 0 1-4.2 4c-1.4.6-1.6.6-28.5.7a235 235 0 0 1-28.6-.4zm91 0a8.5 8.5 0 0 1-5.7-5.4c-.2-.7-.3-8.3-.3-28.3V96.7l.7-1.6a8.9 8.9 0 0 1 4.6-4.3c1.2-.4 3.8-.5 28.9-.4 26.6.1 27.6.1 28.8.7 1.6.8 3.2 2.5 4 4.2.7 1.4.7 1.6.7 28.3.1 31 .3 29.2-2.5 32-2.8 2.7-1 2.6-31.4 2.6-21.4 0-26.8-.1-27.9-.5zM5.3 67a8.7 8.7 0 0 1-4-3C-.5 61.6-.5 62.3-.5 33.6-.4 3.2-.5 5 2.2 2.2 5-.6 3.2-.4 34.2-.3c26.7 0 27 0 28.3.7 1.7.8 3.4 2.4 4.2 4 .6 1.2.6 2.2.7 28.8 0 25.1 0 27.7-.4 29a9 9 0 0 1-4.3 4.5l-1.6.7H33.7c-20.2 0-27.7-.1-28.4-.4Zm89.8-.3a9 9 0 0 1-4.3-4.6c-.5-1.2-.5-3.8-.5-28.9.1-26.6.2-27.6.7-28.8a9.3 9.3 0 0 1 4.2-4c1.4-.7 1.6-.7 28.3-.7 31-.1 29.2-.3 32 2.5 2.8 2.8 2.6 1 2.5 32 0 26.7 0 26.9-.7 28.3a9.3 9.3 0 0 1-4 4.2c-1.2.5-2.2.6-29 .6l-27.7.1z" transform="translate(.4 .4)"/></svg>;
|
||||
var svg_list = <svg #session-list viewBox="0 0 246.8 185.8"><path style="stroke-width:.482473" d="M-69.2 102.7A16.5 16.5 0 0 1-67 70.4c7.3-1 15 4 17.3 11 1 3 1 8 0 10.8a16.7 16.7 0 0 1-19.5 10.5zm53-3.4a12.3 12.3 0 0 1-7-16.8c1.3-3 3.1-4.7 6-6 2.2-1 2.8-1 87.2-1 92.4 0 87-.2 90.6 2.6.9.7 2.2 2.4 3 3.7 1.2 2.2 1.4 3.1 1.4 6 0 4.8-2.3 8.6-6.8 11l-1.9 1-85.2.1c-71.9 0-85.5 0-87.3-.6zm-53.5-73c-4.7-1.5-8.6-5-10.6-9.1-1.8-4-1.8-9.8 0-13.7 1.6-3.3 4.4-6.2 7.8-8 2.2-1.2 3-1.3 7.1-1.3 4 0 5 .1 7.3 1.3a16.6 16.6 0 0 1 0 29.6c-2 1-3.4 1.4-6.5 1.5-2.2 0-4.5 0-5.1-.3zm52.3-4.8c-2.4-1.1-5.3-4-6.2-6.5-1-2.4-1-7.3.1-9.7.5-1.1 1.8-2.8 2.8-3.8 3.7-3.5-4-3.2 91-3.2h85.5l2.5 1.1a12 12 0 0 1 0 21.8l-2.5 1.2H70.2c-82.5 0-85.7 0-87.6-1zm-52.1-71.6a18 18 0 0 1-10-7.7 17 17 0 0 1-.7-15c2.3-5 5.8-7.9 11.4-9.3 9-2.3 18.3 4 19.8 13.4a16.4 16.4 0 0 1-15.2 19c-2.1.1-4.1 0-5.3-.4zm52.1-5.9c-1.3-.6-3-1.7-3.7-2.5-4.7-5-4.2-13.7 1-18 3.7-3.1-1.8-3 91.5-2.8l84.9.1 2 1a12 12 0 0 1 6.7 11c0 3-.2 3.9-1.4 6-.8 1.4-2.1 3-3 3.8-3.7 2.7 1.8 2.6-90.6 2.6h-85l-2.4-1.2z" transform="translate(81.7 82.6)"/></svg>;
|
||||
var search_icon = <svg viewBox="0 0 655.278 655.024"><g transform="translate(-24.497 -195.01)"><path d="m649.96 847.92c-2.9592-1.3629-27.183-24.243-63.36-59.846-32.213-31.702-70.814-69.663-85.78-84.357l-27.21-26.717-4.7897 3.5287c-66.337 48.872-145.32 66.878-224.31 51.138-72.966-14.539-136.58-58.184-178.47-122.44-15.945-24.462-30.723-61.471-36.413-91.191-8.9404-46.696-6.2422-90.39 8.3388-135.04 13.39-41.003 34.756-75.42 66.479-107.09 74.506-74.377 183.71-99.89 284.22-66.397 62.352 20.777 117.67 65.579 150.79 122.12 38.716 66.101 46.59 147.55 21.43 221.66-9.9038 29.171-29.788 63.725-49.916 86.743l-7.0583 8.0717 3.0992 2.919c1.7046 1.6054 40.675 39.928 86.602 85.161 89.007 87.664 87.558 86.034 85.619 96.293-1.2888 6.8209-5.2313 12.041-11.321 14.989-6.7901 3.287-11.55 3.4093-17.952 0.46117zm-316.64-154.63c32.373-5.0481 61.075-15.115 86.553-30.358 47.942-28.683 83.505-72.09 100.89-123.14 35.043-102.91-6.4362-214.07-100.89-270.37-52.514-31.302-117.76-40.564-178.06-25.277-81.183 20.579-145.19 82.918-166.86 162.52-5.5757 20.478-7.445 35.423-7.445 59.52s1.8693 39.042 7.445 59.52c21.409 78.63 85.366 141.52 164.81 162.05 29.22 7.5511 66.493 9.756 93.564 5.5347z" stroke-width="1.28"/></g></svg>;
|
||||
var clear_icon = <svg viewBox="0 0 478.94 479.03"><path d="M217.488 478.45c-30.264-3.146-55.348-10.265-82.714-23.477C62.54 420.1 14.214 353.763 1.824 272.463c-2.412-15.82-2.434-50.027-.043-66.058 16.004-107.32 97.008-188.28 204.71-204.6 14.33-2.172 49.054-2.447 63-.498C323.95 8.915 371.3 32.2 409.03 69.927c37.697 37.698 61.125 85.349 68.605 139.54 1.943 14.08 1.68 48.804-.478 63-6.616 43.533-24.01 83.859-50.468 117-37.556 47.046-92.812 78.608-153.26 87.54-12.553 1.855-44.144 2.671-55.936 1.445zm42.144-32.045c15.649-1.602 29.895-4.63 44.856-9.531 78.146-25.604 133.49-94.718 141.94-177.26 6.245-60.993-16.1-123.3-59.94-167.14-55.797-55.797-139.4-75.365-213.52-49.98-77.69 26.609-131.51 94.14-140.42 176.19-4.761 43.843 6.392 91.899 30.274 130.44 41.468 66.926 119.01 105.26 196.82 97.29zm-138.69-80.346c-4.096-1.784-8.225-6.874-9.022-11.123-1.676-8.935-3.495-6.761 52.877-63.221l52.17-52.25-52.17-52.25c-56.544-56.632-54.56-54.249-52.834-63.451.924-4.923 6.905-10.904 11.828-11.828 9.201-1.726 6.819-3.71 63.451 52.834l52.25 52.169 52.25-52.169c56.632-56.544 54.25-54.56 63.451-52.834 4.923.923 10.904 6.905 11.828 11.828 1.726 9.201 3.71 6.818-52.834 63.451l-52.169 52.25 52.17 52.25c56.543 56.632 54.56 54.249 52.833 63.451-.923 4.923-6.905 10.904-11.828 11.828-9.201 1.726-6.818 3.71-63.455-52.838l-52.255-52.173-51.745 51.696c-28.496 28.469-53.01 52.166-54.56 52.742-3.766 1.4-8.515 1.26-12.234-.36z"/></svg>;
|
||||
|
||||
function getSessionsStyleOption(type) {
|
||||
return (type || "recent") + "-sessions-style";
|
||||
}
|
||||
|
||||
function getSessionsStyle(type) {
|
||||
var v = handler.get_local_option(getSessionsStyleOption(type));
|
||||
if (!v) v = type == "ab" ? "list" : "tile";
|
||||
return v;
|
||||
}
|
||||
|
||||
var searchPatterns = {};
|
||||
|
||||
class SearchBar: Reactor.Component {
|
||||
this var type = "";
|
||||
|
||||
function this(params) {
|
||||
this.type = (params || {}).type || "";
|
||||
}
|
||||
|
||||
function render() {
|
||||
var value = searchPatterns[this.type] || "";
|
||||
var me = this;
|
||||
self.timer(1ms, function() { me.search_id.value = value; });
|
||||
return <div .search-id>
|
||||
<span .search-icon>{search_icon}</span>
|
||||
<input|text @{this.search_id} novalue={translate("Search ID")} />
|
||||
{value && <span .clear-input>{clear_icon}</span>}
|
||||
</div>;
|
||||
}
|
||||
|
||||
event click $(span.clear-input) {
|
||||
this.onChange('');
|
||||
}
|
||||
|
||||
event change $(input) (_, el) {
|
||||
this.onChange(el.value.trim());
|
||||
}
|
||||
|
||||
function onChange(v) {
|
||||
searchPatterns[this.type] = v;
|
||||
app.multipleSessions.update();
|
||||
}
|
||||
}
|
||||
|
||||
class SessionStyle: Reactor.Component {
|
||||
this var type = "";
|
||||
|
||||
function this(params) {
|
||||
this.type = (params || {}).type || "";
|
||||
}
|
||||
|
||||
function render() {
|
||||
var sessionsStyle = getSessionsStyle(this.type);
|
||||
return <div .sessions-tab style="margin-left: 0.5em;">
|
||||
<span class={sessionsStyle == "tile" ? "active" : "inactive"}>{svg_tile}</span>
|
||||
<span class={sessionsStyle != "tile" ? "active" : "inactive"}>{svg_list}</span>
|
||||
</div>;
|
||||
}
|
||||
|
||||
event click $(span.inactive) {
|
||||
var option = getSessionsStyleOption(this.type);
|
||||
var sessionsStyle = getSessionsStyle(this.type);
|
||||
handler.set_option(option, sessionsStyle == "tile" ? "list" : "tile");
|
||||
app.multipleSessions.update();
|
||||
}
|
||||
}
|
||||
|
||||
class SessionList: Reactor.Component {
|
||||
this var sessions = [];
|
||||
this var type = "";
|
||||
this var style;
|
||||
|
||||
function this(params) {
|
||||
this.sessions = params.sessions;
|
||||
this.type = params.type || "";
|
||||
this.style = getSessionsStyle(this.type);
|
||||
}
|
||||
|
||||
function getSessions() {
|
||||
var p = searchPatterns[this.type];
|
||||
if (!p) return this.sessions;
|
||||
var tmp = [];
|
||||
this.sessions.map(function(s) {
|
||||
var name = s[4] || s.alias || s[0] || s.id || "";
|
||||
if (name.indexOf(p) >= 0) tmp.push(s);
|
||||
});
|
||||
return tmp;
|
||||
}
|
||||
|
||||
function render() {
|
||||
var sessions = this.getSessions();
|
||||
if (sessions.length == 0) {
|
||||
return <div style="margin: *; font-size: 1.6em;">{translate("Empty")}</div>;
|
||||
}
|
||||
var me = this;
|
||||
sessions = sessions.map(function(x) { return me.getSession(x); });
|
||||
return <div .recent-sessions-content key={sessions.length}>
|
||||
<popup>
|
||||
<menu.context #remote-context>
|
||||
<li #connect>{translate('Connect')}</li>
|
||||
<li #transfer>{translate('Transfer File')}</li>
|
||||
<li #tunnel>{translate('TCP Tunneling')}</li>
|
||||
<li #rdp>RDP<EditRdpPort /></li>
|
||||
<div .separator />
|
||||
<li #rename>{translate('Rename')}</li>
|
||||
{this.type != "fav" && <li #remove>{translate('Remove')}</li>}
|
||||
{is_win && <li #shortcut>{translate('Create Desktop Shortcut')}</li>}
|
||||
<li #forget-password>{translate('Unremember Password')}</li>
|
||||
{(!this.type || this.type == "fav") && <li #add-fav>{translate('Add to Favorites')}</li>}
|
||||
{(!this.type || this.type == "fav") && <li #remove-fav>{translate('Remove from Favorites')}</li>}
|
||||
</menu>
|
||||
</popup>
|
||||
{sessions}
|
||||
</div>;
|
||||
}
|
||||
|
||||
function getSession(s) {
|
||||
var id = s[0] || s.id || "";
|
||||
var username = s[1] || s.username || "";
|
||||
var hostname = s[2] || s.hostname || "";
|
||||
var platform = s[3] || s.platform || "";
|
||||
var alias = s[4] || s.alias || "";
|
||||
if (this.style == "list") {
|
||||
return <div .remote-session-link .remote-session-list id={id} platform={platform} title={alias ? "ID: " + id : ""}>
|
||||
<div .platform style={"background:"+string2RGB(id+platform, 0.5)}>
|
||||
{platform && platformSvg(platform, "white")}
|
||||
</div>
|
||||
<div .name>
|
||||
<div>
|
||||
<div #alias .ellipsis>{alias ? alias : formatId(id)}</div>
|
||||
<div .username .ellipsis>{username}@{hostname}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{svg_menu}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
return <div .remote-session-link .remote-session id={id} platform={platform} title={alias ? "ID: " + id : ""} style={"background:"+string2RGB(id+platform, 0.5)}>
|
||||
<div .platform>
|
||||
{platform && platformSvg(platform, "white")}
|
||||
<div .username .ellipsis>{username}@{hostname}</div>
|
||||
</div>
|
||||
<div .text>
|
||||
<div #alias .ellipsis>{alias ? alias : formatId(id)}</div>
|
||||
{svg_menu}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
event dblclick $(div.remote-session-link) (evt, me) {
|
||||
createNewConnect(me.id, "connect");
|
||||
}
|
||||
|
||||
event click $(#menu) (_, me) {
|
||||
var id = me.parent.parent.id;
|
||||
var platform = me.parent.parent.attributes["platform"];
|
||||
this.$(#rdp).style.set{
|
||||
display: (platform == "Windows" && is_win) ? "block" : "none",
|
||||
};
|
||||
this.$(#forget-password).style.set{
|
||||
display: handler.peer_has_password(id) ? "block" : "none",
|
||||
};
|
||||
if (!this.type || this.type == "fav") {
|
||||
var in_fav = handler.get_fav().indexOf(id) >= 0;
|
||||
this.$(#add-fav).style.set{
|
||||
display: in_fav ? "none" : "block",
|
||||
};
|
||||
this.$(#remove-fav).style.set{
|
||||
display: in_fav ? "block" : "none",
|
||||
};
|
||||
}
|
||||
// https://sciter.com/forums/topic/replacecustomize-context-menu/
|
||||
var menu = this.$(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") {
|
||||
if (!this.type) {
|
||||
handler.remove_peer(id);
|
||||
app.update();
|
||||
}
|
||||
} else if (action == "forget-password") {
|
||||
handler.forget_password(id);
|
||||
} else if (action == "shortcut") {
|
||||
handler.create_shortcut(id);
|
||||
} else if (action == "rdp") {
|
||||
createNewConnect(id, "rdp");
|
||||
} else if (action == "add-fav") {
|
||||
var favs = handler.get_fav();
|
||||
if (favs.indexOf(id) < 0) {
|
||||
favs = [id].concat(favs);
|
||||
handler.store_fav(favs);
|
||||
}
|
||||
app.multipleSessions.update();
|
||||
app.update();
|
||||
} else if (action == "remove-fav") {
|
||||
var favs = handler.get_fav();
|
||||
var i = favs.indexOf(id);
|
||||
favs.splice(i, 1);
|
||||
handler.store_fav(favs);
|
||||
app.multipleSessions.update();
|
||||
} else if (action == "tunnel") {
|
||||
createNewConnect(id, "port-forward");
|
||||
} else if (action == "rename") {
|
||||
var old_name = handler.get_peer_option(id, "alias");
|
||||
msgbox("custom-rename", "Rename", "<div .form> \
|
||||
<div><input name='name' .outline-focus 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);
|
||||
}
|
||||
app.update();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSessionsType() {
|
||||
return handler.get_local_option("show-sessions-type");
|
||||
}
|
||||
|
||||
class Favorites: Reactor.Component {
|
||||
function render() {
|
||||
var sessions = handler.get_fav().map(function(f) {
|
||||
return handler.get_peer(f);
|
||||
});
|
||||
return <SessionList sessions={sessions} type="fav" />;
|
||||
}
|
||||
}
|
||||
|
||||
class MultipleSessions: Reactor.Component {
|
||||
function render() {
|
||||
var type = getSessionsType();
|
||||
return <div style="size: *">
|
||||
<div .sessions-bar>
|
||||
<div style="width:*" .sessions-tab #sessions-type>
|
||||
<span class={!type ? 'active' : 'inactive'}>{translate('Recent Sessions')}</span>
|
||||
<span #fav class={type == "fav" ? 'active' : 'inactive'}>{translate('Favorites')}</span>
|
||||
</div>
|
||||
{!this.hidden && <SearchBar type={type} />}
|
||||
{!this.hidden && <SessionStyle type={type} />}
|
||||
</div>
|
||||
{!this.hidden &&
|
||||
((type == "fav" && <Favorites />) ||
|
||||
<SessionList sessions={handler.get_recent_sessions()} />)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
function stupidUpdate() {
|
||||
/* hidden is workaround of stupid sciter bug */
|
||||
this.hidden = true;
|
||||
this.update();
|
||||
var me = this;
|
||||
self.timer(60ms, function() {
|
||||
me.hidden = false;
|
||||
me.update();
|
||||
});
|
||||
}
|
||||
|
||||
event click $(div#sessions-type span.inactive) (_, el) {
|
||||
handler.set_option('show-sessions-type', el.id || "");
|
||||
this.stupidUpdate();
|
||||
}
|
||||
|
||||
function onSize() {
|
||||
var w = this.$(.sessions-bar).box(#width) - 220;
|
||||
this.$(#sessions-type span).style.set{
|
||||
"max-width": (w / 2) + "px",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
view.on("size", function() { if (app && app.multipleSessions) app.multipleSessions.onSize(); });
|
||||
30
src/ui/cm.rs
30
src/ui/cm.rs
@@ -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);
|
||||
@@ -405,12 +410,14 @@ async fn start_pa() {
|
||||
break;
|
||||
}
|
||||
let spec = pulse::sample::Spec {
|
||||
format: pulse::sample::Format::F32be,
|
||||
format: pulse::sample::Format::F32le,
|
||||
channels: 2,
|
||||
rate: crate::platform::linux::PA_SAMPLE_RATE,
|
||||
};
|
||||
log::info!("pa monitor: {:?}", device);
|
||||
if let Ok(s) = psimple::Simple::new(
|
||||
// systemctl --user status pulseaudio.service
|
||||
let mut buf: Vec<u8> = vec![0; 480 * 4];
|
||||
match psimple::Simple::new(
|
||||
None, // Use the default server
|
||||
APP_NAME, // Our application’s name
|
||||
pulse::stream::Direction::Record, // We want a record stream
|
||||
@@ -420,22 +427,19 @@ async fn start_pa() {
|
||||
None, // Use default channel map
|
||||
None, // Use default buffering attributes
|
||||
) {
|
||||
loop {
|
||||
Ok(s) => loop {
|
||||
if let Some(Err(_)) = stream.next_timeout2(1).await {
|
||||
break;
|
||||
}
|
||||
let mut out: Vec<u8> = Vec::with_capacity(480 * 4);
|
||||
unsafe {
|
||||
out.set_len(out.capacity());
|
||||
}
|
||||
if let Ok(_) = s.read(&mut out) {
|
||||
if out.iter().filter(|x| **x != 0).next().is_some() {
|
||||
allow_err!(stream.send(&Data::RawMessage(out)).await);
|
||||
}
|
||||
if let Ok(_) = s.read(&mut buf) {
|
||||
allow_err!(
|
||||
stream.send(&Data::RawMessage(buf.clone())).await
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log::error!("Could not create simple pulse: {}", err);
|
||||
}
|
||||
} else {
|
||||
log::error!("Could not create simple pulse");
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
@@ -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,9 @@ class Body: Reactor.Component
|
||||
connection.authorized = true;
|
||||
body.update();
|
||||
handler.authorize(cid);
|
||||
self.timer(30ms, function() {
|
||||
view.windowState = View.WINDOW_MINIMIZED;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ html {
|
||||
var(gray-bg): #eee;
|
||||
var(bg): white;
|
||||
var(border): #ccc;
|
||||
var(hover-border): #999;
|
||||
var(text): #222;
|
||||
var(placeholder): #aaa;
|
||||
var(lighter-text): #888;
|
||||
@@ -52,6 +53,10 @@ button.button:active, button.active {
|
||||
border-color: color(accent);
|
||||
}
|
||||
|
||||
button.button:hover, button.outline:hover {
|
||||
border-color: color(hover-border);
|
||||
}
|
||||
|
||||
input[type=text], input[type=password], input[type=number] {
|
||||
width: *;
|
||||
font-size: 1.5em;
|
||||
@@ -156,9 +161,7 @@ header caption {
|
||||
top: 0;
|
||||
padding: 0 10px;
|
||||
width: 22px;
|
||||
height: *;
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
color: black;
|
||||
border: none;
|
||||
background: none;
|
||||
@@ -321,5 +324,5 @@ menu li.selected span {
|
||||
|
||||
menu li.line-through {
|
||||
text-decoration-line: line-through;
|
||||
color: grey;
|
||||
color: red;
|
||||
}
|
||||
|
||||
@@ -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,19 @@ function getMsgboxParams() {
|
||||
return msgbox_params;
|
||||
}
|
||||
|
||||
function msgbox(type, title, text, callback, height, width, retry=0) {
|
||||
// tmp workaround https://sciter.com/forums/topic/menu-not-be-hidden-when-open-dialog-on-linux/
|
||||
function msgbox(type, title, text, callback=null, height=180, width=500, retry=0, contentStyle="") {
|
||||
if (is_linux) { // fix menu not hidden issue
|
||||
self.timer(1ms,
|
||||
function() {
|
||||
msgbox_(type, title, text, callback, height, width, retry, contentStyle);
|
||||
});
|
||||
} else {
|
||||
msgbox_(type, title, text, callback, height, width, retry, contentStyle);
|
||||
}
|
||||
}
|
||||
|
||||
function msgbox_(type, title, text, callback, height, width, retry, contentStyle) {
|
||||
var has_msgbox = msgbox_params != null;
|
||||
if (!has_msgbox && !type) return;
|
||||
var remember = false;
|
||||
@@ -217,7 +238,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,8 +261,8 @@ 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();
|
||||
handler.login(res.password, res.remember);
|
||||
if (!is_port_forward) msgbox("connecting", "Connecting...", "Logging in...");
|
||||
} else if (res.reconnect) {
|
||||
if (!is_port_forward) connecting();
|
||||
handler.reconnect();
|
||||
@@ -251,19 +273,13 @@ 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) {
|
||||
// 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); });
|
||||
}
|
||||
|
||||
handler.block_msgbox = function(type, title, text, callback=null, height=180, width=500, retry=0) {
|
||||
msgbox(type, title, text, callback, height, width, retry);
|
||||
handler.msgbox = function(type, title, text, retry=0) {
|
||||
self.timer(30ms, function() { msgbox(type, title, text, null, 180, 500, retry); });
|
||||
}
|
||||
|
||||
var reconnectTimeout = 1;
|
||||
handler.msgbox_retry = function(type, title, text, hasRetry, callback=null, height=180, width=500) {
|
||||
handler.msgbox(type, title, text, callback, height, width, hasRetry ? reconnectTimeout : 0);
|
||||
handler.msgbox_retry = function(type, title, text, hasRetry) {
|
||||
handler.msgbox(type, title, text, hasRetry ? reconnectTimeout : 0);
|
||||
if (hasRetry) {
|
||||
reconnectTimeout *= 2;
|
||||
} else {
|
||||
@@ -311,3 +327,52 @@ 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 });
|
||||
var me = this;
|
||||
self.timer(30ms, function() {
|
||||
var el = me.$(input);
|
||||
view.focus = el;
|
||||
el.value = value;
|
||||
el.xcall(#setSelection, start, end);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isReasonableSize(r) {
|
||||
var x = r[0];
|
||||
var y = r[1];
|
||||
return !(x < -3200 || x > 3200 || y < -3200 || y > 3200);
|
||||
}
|
||||
|
||||
|
||||
@@ -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> \
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -576,12 +576,12 @@ handler.jobDone = function(id, file_num = -1) {
|
||||
handler.jobError = function(id, err, file_num = -1) {
|
||||
var job = deleting_single_file_jobs[id];
|
||||
if (job) {
|
||||
handler.msgbox("custom-error", "Delete File", err);
|
||||
msgbox("custom-error", "Delete File", err);
|
||||
return;
|
||||
}
|
||||
job = create_dir_jobs[id];
|
||||
if (job) {
|
||||
handler.msgbox("custom-error", "Create Folder", err);
|
||||
msgbox("custom-error", "Create Folder", err);
|
||||
return;
|
||||
}
|
||||
if (file_num < 0) {
|
||||
@@ -599,8 +599,8 @@ var deleting_single_file_jobs = {};
|
||||
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> \
|
||||
msgbox("custom-skip", "Confirm Delete", "<div .form> \
|
||||
<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) {
|
||||
@@ -619,11 +619,11 @@ handler.confirmDeleteFiles = function(id, i, name) {
|
||||
if (i >= n) return;
|
||||
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> \
|
||||
msgbox("custom-skip", "Confirm Delete", "<div .form> \
|
||||
<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");
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
var last_key_time = 0;
|
||||
var keymap = {};
|
||||
for (var (k, v) in Event) {
|
||||
k = k + ""
|
||||
if (k[0] == "V" && k[1] == "K") {
|
||||
keymap[v] = k;
|
||||
}
|
||||
}
|
||||
|
||||
class Grid: Behavior {
|
||||
const TABLE_HEADER_CLICK = 0x81;
|
||||
const TABLE_ROW_CLICK = 0x82;
|
||||
@@ -144,7 +153,7 @@ class Grid: Behavior {
|
||||
|
||||
function onKey(evt)
|
||||
{
|
||||
|
||||
last_key_time = getTime();
|
||||
if (evt.type != Event.KEY_DOWN)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -42,6 +42,24 @@ header .remote-id {
|
||||
margin: * 0;
|
||||
}
|
||||
|
||||
header span:hover {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
@media platform != "OSX" {
|
||||
header span:hover {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
}
|
||||
|
||||
header #screen:hover {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
|
||||
header #secure:hover {
|
||||
background: unset;
|
||||
}
|
||||
|
||||
header span:active, header #screen:active {
|
||||
color: black;
|
||||
background: color(gray-bg);
|
||||
|
||||
@@ -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();
|
||||
@@ -288,9 +277,9 @@ function handle_custom_image_quality() {
|
||||
var tmp = handler.get_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> \
|
||||
msgbox("custom", "Custom Image Quality", "<div .form> \
|
||||
<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;
|
||||
@@ -408,7 +397,7 @@ function startChat() {
|
||||
height: h,
|
||||
client: true,
|
||||
parameters: { msgs: chat_msgs, callback: sendMsg, icon: icon },
|
||||
caption: handler.get_id(),
|
||||
caption: get_id(),
|
||||
};
|
||||
var html = handler.get_chatbox();
|
||||
if (html) params.html = html;
|
||||
|
||||
127
src/ui/index.css
127
src/ui/index.css
@@ -39,6 +39,77 @@ body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
div.sessions-bar {
|
||||
color: color(light-text);
|
||||
padding-top: 0.5em;
|
||||
border-top: color(border) solid 1px;
|
||||
margin-bottom: 1em;
|
||||
position: relative;
|
||||
flow: horizontal;
|
||||
}
|
||||
|
||||
div.sessions-tab span {
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
div.sessions-tab svg {
|
||||
size: 14px;
|
||||
}
|
||||
|
||||
div.sessions-tab span.active {
|
||||
cursor: default;
|
||||
border-radius: 3px;
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
div.search-id {
|
||||
width: 120px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.search-id input {
|
||||
font-size: 1em;
|
||||
height: 20px;
|
||||
border: none;
|
||||
padding-left: 26px;
|
||||
}
|
||||
|
||||
div.search-id span {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
padding: 6px;
|
||||
color: color(border);
|
||||
}
|
||||
|
||||
div.search-id svg {
|
||||
size: 14px;
|
||||
}
|
||||
|
||||
span.search-icon {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
span.clear-input {
|
||||
display: none;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
div.search-id:hover span.clear-input {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
span.clear-input:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.your-desktop {
|
||||
border-spacing: 0.5em;
|
||||
border-left: color(accent) solid 2px;
|
||||
@@ -132,13 +203,6 @@ div.recent-sessions-content {
|
||||
flow: horizontal-flow;
|
||||
}
|
||||
|
||||
div.recent-sessions-title {
|
||||
color: color(light-text);
|
||||
padding-top: 0.5em;
|
||||
border-top: color(border) solid 1px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
div.remote-session {
|
||||
border-radius: 1em;
|
||||
height: 140px;
|
||||
@@ -148,7 +212,7 @@ div.remote-session {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.remote-session:hover {
|
||||
div.remote-session:hover, div.remote-session-list:hover {
|
||||
outline: color(button) solid 2px -2px;
|
||||
}
|
||||
|
||||
@@ -176,6 +240,41 @@ div.remote-session .platform svg {
|
||||
background: none;
|
||||
}
|
||||
|
||||
div.remote-session-list {
|
||||
background: white;
|
||||
width: 220px;
|
||||
flow: horizontal;
|
||||
}
|
||||
|
||||
div.remote-session-list .platform {
|
||||
size: 42px;
|
||||
}
|
||||
|
||||
div.remote-session-list .platform svg {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: none;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
div.remote-session-list .name {
|
||||
size: *;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
div.remote-session-list .name >div {
|
||||
margin-top: *;
|
||||
margin-bottom: *;
|
||||
width: *;
|
||||
}
|
||||
|
||||
div.remote-session-list .name .username {
|
||||
margin-top: 3px;
|
||||
font-size: 0.8em;
|
||||
color: color(lighter-text);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.remote-session .text {
|
||||
background: white;
|
||||
position: absolute;
|
||||
@@ -200,20 +299,22 @@ svg#menu {
|
||||
color: color(light-text);
|
||||
}
|
||||
|
||||
svg#menu:active {
|
||||
.remote-session-list svg#menu {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
svg#menu:hover {
|
||||
color: black;
|
||||
border-radius: 1em;
|
||||
background: color(gray-bg);
|
||||
}
|
||||
|
||||
svg#edit:active {
|
||||
opacity: 0.5;
|
||||
svg#edit:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
svg#edit {
|
||||
display: inline-block;
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.install-me, div.trust-me {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
</style>
|
||||
<script type="text/tiscript">
|
||||
include "common.tis";
|
||||
include "ab.tis";
|
||||
include "index.tis";
|
||||
</script>
|
||||
</head>
|
||||
|
||||
315
src/ui/index.tis
315
src/ui/index.tis
@@ -18,112 +18,39 @@ 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", "");
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
{ false && <button .button #discover>DISCOVER</button>}
|
||||
<div .recent-sessions-content key={sessions.length}>
|
||||
{sessions}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
event click $(button#discover) {
|
||||
handler.lan_discover();
|
||||
}
|
||||
|
||||
function getSession(s) {
|
||||
var id = s[0];
|
||||
var username = s[1];
|
||||
var hostname = s[2];
|
||||
var platform = s[3];
|
||||
var alias = s[4];
|
||||
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>
|
||||
<div #alias>{alias ? alias : formatId(id)}</div>
|
||||
{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();
|
||||
} else if (action == "shortcut") {
|
||||
handler.create_shortcut(id);
|
||||
} else if (action == "rdp") {
|
||||
createNewConnect(id, "rdp");
|
||||
} else if (action == "tunnel") {
|
||||
createNewConnect(id, "port-forward");
|
||||
} else if (action == "rename") {
|
||||
var old_name = handler.get_peer_option(id, "alias");
|
||||
handler.msgbox("custom-rename", "Rename", "<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;
|
||||
});
|
||||
event click $(#start-service) () {
|
||||
handler.set_option("stop-service", "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,14 +58,32 @@ function createNewConnect(id, type) {
|
||||
id = id.replace(/\s/g, "");
|
||||
app.remote_id.value = formatId(id);
|
||||
if (!id) return;
|
||||
if (id == handler.get_id()) {
|
||||
handler.msgbox("custom-error", "Error", "You cannot connect to your own computer");
|
||||
if (id == my_id) {
|
||||
msgbox("custom-error", "Error", "You cannot connect to your own computer");
|
||||
return;
|
||||
}
|
||||
handler.set_remote_id(id);
|
||||
handler.new_remote(id, type);
|
||||
}
|
||||
|
||||
var direct_server;
|
||||
class DirectServer: Reactor.Component {
|
||||
function this() {
|
||||
direct_server = this;
|
||||
}
|
||||
|
||||
function render() {
|
||||
var text = translate("Enable Direct IP Access");
|
||||
var cls = handler.get_option("direct-server") == "Y" ? "selected" : "line-through";
|
||||
return <li class={cls}><span>{svg_checkmark}</span>{text}</li>;
|
||||
}
|
||||
|
||||
function onClick() {
|
||||
handler.set_option("direct-server", handler.get_option("direct-server") == "Y" ? "" : "Y");
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
var myIdMenu;
|
||||
var audioInputMenu;
|
||||
class AudioInputs: Reactor.Component {
|
||||
@@ -154,10 +99,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>;
|
||||
@@ -195,7 +140,6 @@ class MyIdMenu: Reactor.Component {
|
||||
}
|
||||
|
||||
function render() {
|
||||
var me = this;
|
||||
return <div #myid>
|
||||
{this.renderPop()}
|
||||
ID{svg_menu}
|
||||
@@ -205,19 +149,20 @@ 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>
|
||||
<li #socks5-server>{translate('Socks5 Proxy')}</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>
|
||||
<DirectServer />
|
||||
<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>;
|
||||
}
|
||||
@@ -225,6 +170,7 @@ class MyIdMenu: Reactor.Component {
|
||||
event click $(svg#menu) (_, me) {
|
||||
audioInputMenu.update({ show: true });
|
||||
this.toggleMenuState();
|
||||
if (direct_server) direct_server.update();
|
||||
var menu = $(menu#config-options);
|
||||
me.popup(menu);
|
||||
}
|
||||
@@ -245,8 +191,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>\
|
||||
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;
|
||||
@@ -255,7 +202,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");
|
||||
@@ -265,12 +212,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> \
|
||||
msgbox("custom-server", "ID/Relay Server", "<div .form> \
|
||||
<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;
|
||||
@@ -279,26 +226,46 @@ 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 == "socks5-server") {
|
||||
var socks5 = handler.get_socks() || {};
|
||||
var old_proxy = socks5[0] || "";
|
||||
var old_username = socks5[1] || "";
|
||||
var old_password = socks5[2] || "";
|
||||
msgbox("custom-server", "Socks5 Proxy", <div .form .set-password>
|
||||
<div><span>{translate("Hostname")}</span><input .outline-focus style='width: *' name='proxy' value={old_proxy} /></div>
|
||||
<div><span>{translate("Username")}</span><input style='width: *' name='username' value={old_username} /></div>
|
||||
<div><span>{translate("Password")}</span><PasswordComponent value={old_password} /></div>
|
||||
</div>
|
||||
, function(res=null) {
|
||||
if (!res) return;
|
||||
var proxy = (res.proxy || "").trim();
|
||||
var username = (res.username || "").trim();
|
||||
var password = (res.password || "").trim();
|
||||
if (proxy == old_proxy && username == old_username && password == old_password) return;
|
||||
if (proxy) {
|
||||
var err = handler.test_if_valid_server(proxy);
|
||||
if (err) return translate("Server") + ": " + err;
|
||||
}
|
||||
handler.set_socks(proxy, username, password);
|
||||
}, 240);
|
||||
} else if (me.id == "stop-service") {
|
||||
handler.set_option("stop-service", service_stopped ? "" : "Y");
|
||||
} else if (me.id == "about") {
|
||||
var name = handler.get_app_name();
|
||||
handler.msgbox("custom-nocancel-nook-hasclose", "About " + name, "<div style='line-height: 2em'> \
|
||||
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 © 2020 CarrieZ Studio \
|
||||
<br /> Author: Carrie \
|
||||
<p style='font-weight: bold'>Made with heart in this chaotic world!</p>\
|
||||
@@ -322,37 +289,24 @@ class App: Reactor.Component
|
||||
var is_can_screen_recording = handler.is_can_screen_recording(false);
|
||||
return
|
||||
<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>}
|
||||
</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>
|
||||
<div .left-pane>
|
||||
<popup><menu.context #edit-password-context>
|
||||
<li #refresh-password>{translate('Refresh random password')}</li>
|
||||
<li #set-password>{translate('Set your own password')}</li>
|
||||
</menu></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 />}
|
||||
@@ -364,14 +318,14 @@ 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} />
|
||||
<MultipleSessions @{this.multipleSessions} />
|
||||
</div>
|
||||
<ConnectStatus @{this.connect_status} />
|
||||
</div>
|
||||
@@ -391,11 +345,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>
|
||||
<div style="text-align: center; margin-top: 1em;"><button #install-me .button>{translate('Install')}</button></div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -450,9 +405,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>;
|
||||
}
|
||||
|
||||
@@ -463,9 +418,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;" />
|
||||
@@ -473,18 +428,20 @@ 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) {
|
||||
handler.msgbox("custom-error", "Download Error", "Failed to download");
|
||||
msgbox("custom-error", "Download Error", "Failed to download");
|
||||
};
|
||||
var onprogress = function(loaded, total) {
|
||||
if (!total) total = 5 * 1024 * 1024;
|
||||
@@ -511,9 +468,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>;
|
||||
}
|
||||
|
||||
@@ -526,9 +483,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>;
|
||||
}
|
||||
|
||||
@@ -541,9 +498,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>;
|
||||
}
|
||||
|
||||
@@ -556,15 +514,16 @@ 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>;
|
||||
}
|
||||
|
||||
event click $(#modify-default-login) {
|
||||
if (var r = handler.modify_default_login()) {
|
||||
handler.msgbox("custom-error", "Error", r);
|
||||
msgbox("custom-error", "Error", r);
|
||||
}
|
||||
app.update();
|
||||
}
|
||||
@@ -627,19 +586,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> \
|
||||
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();
|
||||
@@ -649,7 +608,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())} />;
|
||||
}
|
||||
|
||||
@@ -713,10 +672,10 @@ function self.closing() {
|
||||
|
||||
function self.ready() {
|
||||
var r = handler.get_size();
|
||||
if (r[2] == 0) {
|
||||
centerize(800, 600);
|
||||
} else {
|
||||
if (isReasonableSize(r) && r[2] > 0) {
|
||||
view.move(r[0], r[1], r[2], r[3]);
|
||||
} else {
|
||||
centerize(800, 600);
|
||||
}
|
||||
if (!handler.get_remote_id()) {
|
||||
view.focus = $(#remote_id);
|
||||
@@ -740,6 +699,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;
|
||||
@@ -752,7 +715,7 @@ function checkConnectStatus() {
|
||||
}
|
||||
if (handler.recent_sessions_updated()) {
|
||||
stdout.println("recent sessions updated");
|
||||
app.recent_sessions.update();
|
||||
app.update();
|
||||
}
|
||||
checkConnectStatus();
|
||||
});
|
||||
|
||||
@@ -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 />);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
|
||||
333
src/ui/remote.rs
333
src/ui/remote.rs
@@ -49,13 +49,16 @@ fn get_key_state(key: enigo::Key) -> bool {
|
||||
ENIGO.lock().unwrap().get_key_state(key)
|
||||
}
|
||||
|
||||
static mut IS_IN: bool = false;
|
||||
static mut KEYBOARD_HOOKED: bool = false;
|
||||
static mut KEYBOARD_ENABLED: bool = true;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HandlerInner {
|
||||
element: Option<Element>,
|
||||
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)]
|
||||
@@ -65,7 +68,6 @@ pub struct Handler {
|
||||
id: String,
|
||||
args: Vec<String>,
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
super_on: bool,
|
||||
}
|
||||
|
||||
impl Deref for Handler {
|
||||
@@ -147,6 +149,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();
|
||||
@@ -154,11 +158,9 @@ impl sciter::EventHandler for Handler {
|
||||
fn login(String, bool);
|
||||
fn new_rdp();
|
||||
fn send_mouse(i32, i32, i32, bool, bool, bool, bool);
|
||||
fn key_down_or_up(bool, String, i32, bool, bool, bool, bool, bool);
|
||||
fn enter();
|
||||
fn leave();
|
||||
fn ctrl_alt_del();
|
||||
fn ctrl_space();
|
||||
fn alt_tab();
|
||||
fn super_x();
|
||||
fn transfer_file();
|
||||
fn tunnel();
|
||||
fn lock_screen();
|
||||
@@ -218,6 +220,139 @@ impl Handler {
|
||||
me
|
||||
}
|
||||
|
||||
fn start_keyboard_hook(&self) {
|
||||
if self.is_port_forward() || self.is_file_transfer() {
|
||||
return;
|
||||
}
|
||||
if unsafe { KEYBOARD_HOOKED } {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
KEYBOARD_HOOKED = true;
|
||||
}
|
||||
log::info!("keyboard hooked");
|
||||
let mut me = self.clone();
|
||||
let peer = self.peer_platform();
|
||||
let is_win = peer == "Windows";
|
||||
std::thread::spawn(move || {
|
||||
// This will block.
|
||||
std::env::set_var("KEYBOARD_ONLY", "y"); // pass to rdev
|
||||
use rdev::{EventType::*, *};
|
||||
let func = move |evt: Event| {
|
||||
if unsafe { !IS_IN || !KEYBOARD_ENABLED } {
|
||||
return;
|
||||
}
|
||||
let (key, down) = match evt.event_type {
|
||||
KeyPress(k) => (k, 1),
|
||||
KeyRelease(k) => (k, 0),
|
||||
_ => return,
|
||||
};
|
||||
let alt = get_key_state(enigo::Key::Alt);
|
||||
let ctrl = get_key_state(enigo::Key::Control);
|
||||
let shift = get_key_state(enigo::Key::Shift);
|
||||
let command = get_key_state(enigo::Key::Meta);
|
||||
let control_key = match key {
|
||||
Key::Alt => Some(ControlKey::Alt),
|
||||
Key::AltGr => Some(ControlKey::RAlt),
|
||||
Key::Backspace => Some(ControlKey::Backspace),
|
||||
Key::ControlLeft => Some(ControlKey::Control),
|
||||
Key::ControlRight => Some(ControlKey::RControl),
|
||||
Key::DownArrow => Some(ControlKey::DownArrow),
|
||||
Key::Escape => Some(ControlKey::Escape),
|
||||
Key::F1 => Some(ControlKey::F1),
|
||||
Key::F10 => Some(ControlKey::F10),
|
||||
Key::F11 => Some(ControlKey::F11),
|
||||
Key::F12 => Some(ControlKey::F12),
|
||||
Key::F2 => Some(ControlKey::F2),
|
||||
Key::F3 => Some(ControlKey::F3),
|
||||
Key::F4 => Some(ControlKey::F4),
|
||||
Key::F5 => Some(ControlKey::F5),
|
||||
Key::F6 => Some(ControlKey::F6),
|
||||
Key::F7 => Some(ControlKey::F7),
|
||||
Key::F8 => Some(ControlKey::F8),
|
||||
Key::F9 => Some(ControlKey::F9),
|
||||
Key::LeftArrow => Some(ControlKey::LeftArrow),
|
||||
Key::MetaLeft => Some(ControlKey::Meta),
|
||||
Key::MetaRight => Some(ControlKey::RWin),
|
||||
Key::Return => Some(ControlKey::Return),
|
||||
Key::RightArrow => Some(ControlKey::RightArrow),
|
||||
Key::ShiftLeft => Some(ControlKey::Shift),
|
||||
Key::ShiftRight => Some(ControlKey::RShift),
|
||||
Key::Space => Some(ControlKey::Space),
|
||||
Key::Tab => Some(ControlKey::Tab),
|
||||
Key::UpArrow => Some(ControlKey::UpArrow),
|
||||
Key::Delete => {
|
||||
if is_win && ctrl && alt {
|
||||
me.ctrl_alt_del();
|
||||
return;
|
||||
}
|
||||
Some(ControlKey::Delete)
|
||||
}
|
||||
Key::Apps => Some(ControlKey::Apps),
|
||||
Key::Cancel => Some(ControlKey::Cancel),
|
||||
Key::Clear => Some(ControlKey::Clear),
|
||||
Key::Kana => Some(ControlKey::Kana),
|
||||
Key::Hangul => Some(ControlKey::Hangul),
|
||||
Key::Junja => Some(ControlKey::Junja),
|
||||
Key::Final => Some(ControlKey::Final),
|
||||
Key::Hanja => Some(ControlKey::Hanja),
|
||||
Key::Hanji => Some(ControlKey::Hanja),
|
||||
Key::Convert => Some(ControlKey::Convert),
|
||||
Key::Print => Some(ControlKey::Print),
|
||||
Key::Select => Some(ControlKey::Select),
|
||||
Key::Execute => Some(ControlKey::Execute),
|
||||
Key::PrintScreen => Some(ControlKey::Snapshot),
|
||||
Key::Help => Some(ControlKey::Help),
|
||||
Key::Sleep => Some(ControlKey::Sleep),
|
||||
Key::Separator => Some(ControlKey::Separator),
|
||||
Key::KpReturn => Some(ControlKey::NumpadEnter),
|
||||
Key::Kp0 => Some(ControlKey::Numpad0),
|
||||
Key::Kp1 => Some(ControlKey::Numpad1),
|
||||
Key::Kp2 => Some(ControlKey::Numpad2),
|
||||
Key::Kp3 => Some(ControlKey::Numpad3),
|
||||
Key::Kp4 => Some(ControlKey::Numpad4),
|
||||
Key::Kp5 => Some(ControlKey::Numpad5),
|
||||
Key::Kp6 => Some(ControlKey::Numpad6),
|
||||
Key::Kp7 => Some(ControlKey::Numpad7),
|
||||
Key::Kp8 => Some(ControlKey::Numpad8),
|
||||
Key::Kp9 => Some(ControlKey::Numpad9),
|
||||
Key::KpDivide => Some(ControlKey::Divide),
|
||||
Key::KpMultiply => Some(ControlKey::Subtract),
|
||||
Key::KpDecimal => Some(ControlKey::Decimal),
|
||||
Key::KpMinus => Some(ControlKey::Subtract),
|
||||
Key::KpPlus => Some(ControlKey::Add),
|
||||
Key::CapsLock | Key::NumLock | Key::ScrollLock => {
|
||||
return;
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let mut key_event = KeyEvent::new();
|
||||
if let Some(k) = control_key {
|
||||
key_event.set_control_key(k);
|
||||
} else {
|
||||
let chr = match evt.name {
|
||||
Some(ref s) => s.chars().next().unwrap_or('\0'),
|
||||
_ => '\0',
|
||||
};
|
||||
if chr != '\0' {
|
||||
if chr == 'l' && is_win && command {
|
||||
me.lock_screen();
|
||||
return;
|
||||
}
|
||||
key_event.set_chr(chr as _);
|
||||
} else {
|
||||
log::error!("Unknown key {:?}", evt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
me.key_down_or_up(down, key_event, alt, ctrl, shift, command);
|
||||
};
|
||||
if let Err(error) = rdev::listen(func) {
|
||||
log::error!("rdev: {:?}", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_view_style(&mut self) -> String {
|
||||
return self.lc.read().unwrap().view_style.clone();
|
||||
}
|
||||
@@ -287,6 +422,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()
|
||||
}
|
||||
@@ -409,6 +548,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);
|
||||
}
|
||||
@@ -535,7 +678,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);
|
||||
@@ -629,6 +771,18 @@ impl Handler {
|
||||
self.send(Data::NewRDP);
|
||||
}
|
||||
|
||||
fn enter(&mut self) {
|
||||
unsafe {
|
||||
IS_IN = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn leave(&mut self) {
|
||||
unsafe {
|
||||
IS_IN = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn send_mouse(
|
||||
&mut self,
|
||||
mask: i32,
|
||||
@@ -668,7 +822,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -754,6 +907,7 @@ impl Handler {
|
||||
65300 => key_event.set_control_key(ControlKey::Scroll),
|
||||
65421 => key_event.set_control_key(ControlKey::NumpadEnter), // numpad enter
|
||||
65407 => key_event.set_control_key(ControlKey::NumLock),
|
||||
65515 => key_event.set_control_key(ControlKey::Meta),
|
||||
65516 => key_event.set_control_key(ControlKey::RWin),
|
||||
65513 => key_event.set_control_key(ControlKey::Alt),
|
||||
65514 => key_event.set_control_key(ControlKey::RAlt),
|
||||
@@ -811,32 +965,20 @@ impl Handler {
|
||||
|
||||
fn ctrl_alt_del(&mut self) {
|
||||
if self.peer_platform() == "Windows" {
|
||||
let del = "CTRL_ALT_DEL".to_owned();
|
||||
self.key_down_or_up(1, del, 0, false, false, false, false, false);
|
||||
let mut key_event = KeyEvent::new();
|
||||
key_event.set_control_key(ControlKey::CtrlAltDel);
|
||||
self.key_down_or_up(1, key_event, false, false, false, false);
|
||||
} else {
|
||||
let del = "VK_DELETE".to_owned();
|
||||
self.key_down_or_up(1, del.clone(), 0, true, true, false, false, false);
|
||||
self.key_down_or_up(0, del, 0, true, true, false, false, false);
|
||||
let mut key_event = KeyEvent::new();
|
||||
key_event.set_control_key(ControlKey::Delete);
|
||||
self.key_down_or_up(3, key_event, true, true, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
let mut key_event = KeyEvent::new();
|
||||
key_event.set_control_key(ControlKey::LockScreen);
|
||||
self.key_down_or_up(1, key_event, false, false, false, false);
|
||||
}
|
||||
|
||||
fn transfer_file(&mut self) {
|
||||
@@ -858,104 +1000,55 @@ impl Handler {
|
||||
fn key_down_or_up(
|
||||
&mut self,
|
||||
down_or_up: i32,
|
||||
name: String,
|
||||
code: i32,
|
||||
evt: KeyEvent,
|
||||
alt: bool,
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
command: bool,
|
||||
extended: bool,
|
||||
) {
|
||||
// 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!(
|
||||
"{:?} {} {} {} {} {} {} {} {}",
|
||||
std::time::SystemTime::now(),
|
||||
down_or_up,
|
||||
name,
|
||||
code,
|
||||
alt,
|
||||
ctrl,
|
||||
shift,
|
||||
command,
|
||||
extended,
|
||||
);
|
||||
let mut key_event = evt;
|
||||
|
||||
let mut command = command;
|
||||
if self.super_on {
|
||||
command = true;
|
||||
if alt
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Alt)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RAlt)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Alt.into());
|
||||
}
|
||||
|
||||
if down_or_up == 0 {
|
||||
self.super_on = false;
|
||||
if shift
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Shift)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RShift)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Shift.into());
|
||||
}
|
||||
|
||||
let mut name = name;
|
||||
#[cfg(target_os = "linux")]
|
||||
if code == 65383 {
|
||||
// VK_MENU
|
||||
name = "Apps".to_owned();
|
||||
if ctrl
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Control)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RControl)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Control.into());
|
||||
}
|
||||
|
||||
if extended {
|
||||
match name.as_ref() {
|
||||
"VK_CONTROL" => name = "RControl".to_owned(),
|
||||
"VK_MENU" => name = "RAlt".to_owned(),
|
||||
"VK_SHIFT" => name = "RShift".to_owned(),
|
||||
_ => {}
|
||||
if command
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Meta)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RWin)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Meta.into());
|
||||
}
|
||||
if get_key_state(enigo::Key::CapsLock) {
|
||||
key_event.modifiers.push(ControlKey::CapsLock.into());
|
||||
}
|
||||
if self.peer_platform() != "Mac OS" {
|
||||
if get_key_state(enigo::Key::NumLock) && common::valid_for_numlock(&key_event) {
|
||||
key_event.modifiers.push(ControlKey::NumLock.into());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mut key_event) = self.get_key_event(down_or_up, &name, code) {
|
||||
if alt
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Alt)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RAlt)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Alt.into());
|
||||
}
|
||||
if shift
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Shift)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RShift)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Shift.into());
|
||||
}
|
||||
if ctrl
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Control)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RControl)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Control.into());
|
||||
}
|
||||
if command
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::Meta)
|
||||
&& !crate::is_control_key(&key_event, &ControlKey::RWin)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::Meta.into());
|
||||
}
|
||||
if crate::is_control_key(&key_event, &ControlKey::CapsLock) {
|
||||
return;
|
||||
} else if get_key_state(enigo::Key::CapsLock) && common::valid_for_capslock(&key_event)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::CapsLock.into());
|
||||
}
|
||||
if self.peer_platform() != "Mac OS" {
|
||||
if crate::is_control_key(&key_event, &ControlKey::NumLock) {
|
||||
return;
|
||||
} else if get_key_state(enigo::Key::NumLock)
|
||||
&& common::valid_for_numlock(&key_event)
|
||||
{
|
||||
key_event.modifiers.push(ControlKey::NumLock.into());
|
||||
}
|
||||
}
|
||||
if down_or_up == 1 {
|
||||
key_event.down = true;
|
||||
} else if down_or_up == 3 {
|
||||
key_event.press = true;
|
||||
}
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_key_event(key_event);
|
||||
log::debug!("{:?}", msg_out);
|
||||
self.send(Data::Message(msg_out));
|
||||
if down_or_up == 1 {
|
||||
key_event.down = true;
|
||||
} else if down_or_up == 3 {
|
||||
key_event.press = true;
|
||||
}
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_key_event(key_event);
|
||||
log::debug!("{:?}", msg_out);
|
||||
self.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1132,6 +1225,9 @@ impl Remote {
|
||||
};
|
||||
match Client::start(&self.handler.id, conn_type).await {
|
||||
Ok((mut peer, direct)) => {
|
||||
unsafe {
|
||||
KEYBOARD_ENABLED = true;
|
||||
}
|
||||
self.handler
|
||||
.call("setConnectionType", &make_args!(peer.is_secured(), direct));
|
||||
loop {
|
||||
@@ -1191,6 +1287,9 @@ impl Remote {
|
||||
if let Some(stop) = stop_clipboard {
|
||||
stop.send(()).ok();
|
||||
}
|
||||
unsafe {
|
||||
KEYBOARD_ENABLED = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option<String>) {
|
||||
@@ -1586,6 +1685,9 @@ impl Remote {
|
||||
log::info!("Change permission {:?} -> {}", p.permission, p.enabled);
|
||||
match p.permission.enum_value_or_default() {
|
||||
Permission::Keyboard => {
|
||||
unsafe {
|
||||
KEYBOARD_ENABLED = p.enabled;
|
||||
}
|
||||
self.handler
|
||||
.call("setPermission", &make_args!("keyboard", p.enabled));
|
||||
}
|
||||
@@ -1738,6 +1840,7 @@ impl Interface for Handler {
|
||||
crate::platform::windows::add_recent_document(&path);
|
||||
}
|
||||
}
|
||||
self.start_keyboard_hook();
|
||||
}
|
||||
|
||||
async fn handle_hash(&mut self, hash: Hash, peer: &mut Stream) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
var cursor_img = $(img#cursor);
|
||||
var last_key_time = 0;
|
||||
is_file_transfer = handler.is_file_transfer();
|
||||
var is_port_forward = handler.is_port_forward();
|
||||
var display_width = 0;
|
||||
@@ -72,46 +71,14 @@ function adaptDisplay() {
|
||||
// https://sciter.com/docs/content/sciter/Event.htm
|
||||
|
||||
var entered = false;
|
||||
|
||||
var keymap = {};
|
||||
for (var (k, v) in Event) {
|
||||
k = k + ""
|
||||
if (k[0] == "V" && k[1] == "K") {
|
||||
keymap[v] = k;
|
||||
if (!is_file_transfer && !is_port_forward) {
|
||||
self.onKey = function(evt) {
|
||||
if (!entered) return false;
|
||||
// so that arrow key not move scrollbar
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// VK_ENTER = VK_RETURN
|
||||
// somehow, handler.onKey and view.onKey not working
|
||||
function self.onKey(evt) {
|
||||
last_key_time = getTime();
|
||||
if (is_file_transfer || is_port_forward) return false;
|
||||
if (!entered) return false;
|
||||
if (!keyboard_enabled) return false;
|
||||
switch (evt.type) {
|
||||
case Event.KEY_DOWN:
|
||||
handler.key_down_or_up(1, keymap[evt.keyCode] || "", evt.keyCode, evt.altKey,
|
||||
evt.ctrlKey, evt.shiftKey, evt.commandKey, evt.extendedKey);
|
||||
if (is_osx && evt.commandKey) {
|
||||
handler.key_down_or_up(0, keymap[evt.keyCode] || "", evt.keyCode, evt.altKey,
|
||||
evt.ctrlKey, evt.shiftKey, evt.commandKey, evt.extendedKey);
|
||||
}
|
||||
break;
|
||||
case Event.KEY_UP:
|
||||
handler.key_down_or_up(0, keymap[evt.keyCode] || "", evt.keyCode, evt.altKey,
|
||||
evt.ctrlKey, evt.shiftKey, evt.commandKey, evt.extendedKey);
|
||||
break;
|
||||
case Event.KEY_CHAR:
|
||||
// the keypress event is fired when the element receives character value. Event.keyCode is a UNICODE code point of the character
|
||||
handler.key_down_or_up(2, "", evt.keyCode, evt.altKey,
|
||||
evt.ctrlKey, evt.shiftKey, evt.commandKey, evt.extendedKey);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var wait_window_toolbar = false;
|
||||
var last_mouse_mask;
|
||||
var acc_wheel_delta_x = 0;
|
||||
@@ -173,10 +140,14 @@ function handler.onMouse(evt)
|
||||
wait_window_toolbar = true;
|
||||
self.timer(300ms, function() {
|
||||
if (!wait_window_toolbar) return;
|
||||
var extra = 0;
|
||||
// workaround for stupid Sciter, without this, click
|
||||
// event not triggered on top part of buttons on toolbar
|
||||
if (is_osx) extra = 10;
|
||||
if (view.windowState == View.WINDOW_FULL_SCREEN) {
|
||||
$(header).style.set {
|
||||
display: "block",
|
||||
padding: (2 * workarea_offset) + "px 0 0 0",
|
||||
padding: (2 * workarea_offset + extra) + "px 0 0 0",
|
||||
};
|
||||
}
|
||||
wait_window_toolbar = false;
|
||||
@@ -280,10 +251,12 @@ function handler.onMouse(evt)
|
||||
case Event.MOUSE_ENTER:
|
||||
entered = true;
|
||||
stdout.println("enter");
|
||||
handler.enter();
|
||||
return keyboard_enabled;
|
||||
case Event.MOUSE_LEAVE:
|
||||
entered = false;
|
||||
stdout.println("leave");
|
||||
handler.leave();
|
||||
return keyboard_enabled;
|
||||
default:
|
||||
return false;
|
||||
@@ -396,7 +369,7 @@ function self.ready() {
|
||||
var h = 640;
|
||||
if (is_file_transfer || is_port_forward) {
|
||||
var r = handler.get_size();
|
||||
if (r[0] > 0) {
|
||||
if (isReasonableSize(r) && r[2] > 0) {
|
||||
view.move(r[0], r[1], r[2], r[3]);
|
||||
} else {
|
||||
centerize(w, h);
|
||||
@@ -418,7 +391,7 @@ handler.adaptSize = function() {
|
||||
var (fx, fy, fw, fh) = view.screenBox(#frame, #rectw);
|
||||
if (is_osx) workarea_offset = sy;
|
||||
var r = handler.get_size();
|
||||
if (r[2] > 0) {
|
||||
if (isReasonableSize(r) && r[2] > 0) {
|
||||
if (r[2] >= fw && r[3] >= fh && !is_linux) {
|
||||
view.windowState = View.WINDOW_FULL_SCREEN;
|
||||
stdout.println("Initialize to full screen");
|
||||
|
||||
Reference in New Issue
Block a user