diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index dc025b8c8..bce4ab67e 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1437,20 +1437,7 @@ pub fn main_handle_relay_id(id: String) -> String { } pub fn main_is_option_fixed(key: String) -> SyncReturn { - SyncReturn( - config::OVERWRITE_DISPLAY_SETTINGS - .read() - .unwrap() - .contains_key(&key) - || config::OVERWRITE_LOCAL_SETTINGS - .read() - .unwrap() - .contains_key(&key) - || config::OVERWRITE_SETTINGS - .read() - .unwrap() - .contains_key(&key), - ) + SyncReturn(is_option_fixed(&key)) } pub fn main_get_main_display() -> SyncReturn { diff --git a/src/ui.rs b/src/ui.rs index a8e33f1ba..2a0f6e918 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -296,6 +296,22 @@ impl UI { crate::common::is_custom_client() } + pub fn is_disable_settings(&self) -> bool { + hbb_common::config::is_disable_settings() + } + + pub fn is_disable_account(&self) -> bool { + hbb_common::config::is_disable_account() + } + + pub fn is_disable_installation(&self) -> bool { + hbb_common::config::is_disable_installation() + } + + pub fn is_disable_ab(&self) -> bool { + hbb_common::config::is_disable_ab() + } + fn get_options(&self) -> Value { let hashmap: HashMap = serde_json::from_str(&get_options()).unwrap_or_default(); @@ -675,6 +691,14 @@ impl UI { pub fn check_hwcodec(&self) { check_hwcodec() } + + fn is_option_fixed(&self, key: String) -> bool { + crate::ui_interface::is_option_fixed(&key) + } + + fn get_builtin_option(&self, key: String) -> String { + crate::ui_interface::get_builtin_option(&key) + } } impl sciter::EventHandler for UI { @@ -686,6 +710,10 @@ impl sciter::EventHandler for UI { fn is_custom_client(); fn is_outgoing_only(); fn is_incoming_only(); + fn is_disable_settings(); + fn is_disable_account(); + fn is_disable_installation(); + fn is_disable_ab(); fn get_id(); fn temporary_password(); fn update_temporary_password(); @@ -771,6 +799,8 @@ impl sciter::EventHandler for UI { fn verify2fa(String); fn check_hwcodec(); fn verify_login(String, String); + fn is_option_fixed(String); + fn get_builtin_option(String); } } diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 2c8724750..d0c2e9edf 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -543,15 +543,15 @@ class MultipleSessions: Reactor.Component { {translate('Recent sessions')} {translate('Favorites')} {handler.is_installed() && {translate('Discovered')}} - {translate('Address book')} + {!disable_account && !disable_ab && {translate('Address book')}} - {!this.hidden && } - {!this.hidden && } + {!this.hidden && !(disable_account && type == "ab") && } + {!this.hidden && !(disable_account && type == "ab") && } {!this.hidden && ((type == "fav" && ) || (type == "lan" && handler.is_installed() && ) || - (type == "ab" && ) || + (type == "ab" && !disable_account && !disable_ab && ) || )} ; } diff --git a/src/ui/common.css b/src/ui/common.css index ff2f83883..3307e0965 100644 --- a/src/ui/common.css +++ b/src/ui/common.css @@ -458,6 +458,15 @@ div#msgbox div.set-password input { font-size: 1em; } +.wrap-text { + width: *; + word-wrap: break-word; + overflow-wrap: break-word; + white-space: normal; + height: auto; + overflow: hidden; +} + div#msgbox #error { color: red; } diff --git a/src/ui/common.tis b/src/ui/common.tis index b6d2b8ee2..049aa1a5f 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -10,6 +10,18 @@ var is_file_transfer; var is_xfce = false; try { is_xfce = handler.is_xfce(); } catch(e) {} +const incoming_only_width = 180; +const outgoing_only = handler.is_outgoing_only(); +const incoming_only = handler.is_incoming_only(); +const disable_installation = handler.is_disable_installation(); +const disable_account = handler.is_disable_account(); +const disable_settings = handler.is_disable_settings(); +const is_custom_client = handler.is_custom_client(); +const disable_ab = handler.is_disable_ab(); +const hide_server_settings = handler.get_builtin_option("hide-server-settings") == "Y"; +const hide_proxy_settings = handler.get_builtin_option("hide-proxy-settings") == "Y"; +const hide_websocket_settings = handler.get_builtin_option("hide-websocket-settings") == "Y"; + function isEnterKey(evt) { return (evt.keyCode == Event.VK_ENTER || (is_osx && evt.keyCode == 0x4C) || @@ -245,6 +257,10 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= try { autoLogin = handler.get_option("auto-login") != ''; } catch(e) {} width += is_xfce ? 50 : 0; height += is_xfce ? 50 : 0; + if (incoming_only) { + var maxw = scaleIt(incoming_only_width); + if (width > maxw) width = maxw; + } if (type.indexOf("input-password") >= 0) { callback = function (res) { diff --git a/src/ui/index.css b/src/ui/index.css index 2fb2f958d..d23e4f038 100644 --- a/src/ui/index.css +++ b/src/ui/index.css @@ -31,6 +31,7 @@ body { height: *; background: color(bg); border-right: color(border) 1px solid; + position: relative; } #ab .left-pane { @@ -49,6 +50,14 @@ body { .left-pane > div:nth-child(1) { border-spacing: 1em; padding: 20px; + padding-bottom: 60px; /* reserve space for bottom connect-status */ +} + +.left-pane > div.connect-status { + position: absolute; + bottom: 0; + left: 0; + right: 0; } .left-pane div { @@ -413,6 +422,16 @@ svg#refresh-password:hover { li:disabled, li:disabled:hover { color: color(lighter-text); background: color(menu); + opacity: 0.8; +} + +.grey-text { + color: #888 !important; +} + +input.grey-text, +textarea.grey-text { + color: #888 !important; } @media platform == "OSX" { diff --git a/src/ui/index.tis b/src/ui/index.tis index a35438358..a803aa6b3 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -3,7 +3,11 @@ stdout.println("current platform:", OS); stdout.println("is_xfce: ", is_xfce); // html min-width, min-height not working on mac, below works for all -view.windowMinSize = (scaleIt(560), scaleIt(300)); +if (incoming_only) { + view.windowMinSize = (scaleIt(incoming_only_width), scaleIt((handler.is_installed() || disable_installation) ? 300 : 390)); +} else { + view.windowMinSize = (scaleIt(560), scaleIt(300)); +} var app; var tmp = handler.get_connect_status(); @@ -11,11 +15,18 @@ var connect_status = tmp[0]; var service_stopped = handler.get_option("stop-service") == "Y"; var disable_udp = handler.get_option("disable-udp") == "Y"; var using_public_server = handler.using_public_server(); -var outgoing_only = handler.is_outgoing_only(); var software_update_url = ""; var key_confirmed = tmp[1]; var system_error = ""; +const default_option_lang = is_custom_client ? 'default' : ''; +const default_option_yes = is_custom_client ? 'Y' : ''; +const default_option_no = is_custom_client ? 'N' : ''; +const default_option_whitelist = is_custom_client ? ',' : ''; +const default_option_approve_mode = is_custom_client ? 'password-click' : ''; + +const grey_text_style = "color:#888;"; + var svg_menu = @@ -106,7 +117,7 @@ class DirectServer: Reactor.Component { is_edit_rdp_port = false; return; } - handler.set_option("direct-server", handler.get_option("direct-server") == "Y" ? "" : "Y"); + handler.set_option("direct-server", handler.get_option("direct-server") == "Y" ? default_option_no : "Y"); this.update(); } } @@ -149,6 +160,10 @@ class AudioInputs: Reactor.Component { var el = this.$(li#enable-audio); var enabled = handler.get_option(el.id) != "N"; el.attributes.toggleClass("selected", !enabled); + var is_opt_fixed = handler.is_option_fixed("enable-audio"); + if (disable_settings || is_opt_fixed) { + el.state.disabled = true; + } var v = this.get_value(); for (var el in this.$$(menu#audio-input>li)) { if (el.id == 'enable-audio') continue; @@ -158,9 +173,10 @@ class AudioInputs: Reactor.Component { } event click $(menu#audio-input>li) (_, me) { + if (me.state.disabled) return; var v = me.id; if (v == 'enable-audio') { - handler.set_option(v, handler.get_option(v) != 'N' ? 'N' : ''); + handler.set_option(v, handler.get_option(v) != 'N' ? 'N' : default_option_yes); } else { if (v == this.get_value()) return; if (v == this.get_default()) v = ""; @@ -189,15 +205,20 @@ class Languages: Reactor.Component { function toggleMenuState() { var cur = handler.get_local_option("lang") || "default"; + var is_opt_fixed = handler.is_option_fixed("lang"); for (var el in this.$$(menu#languages>li)) { var selected = cur == el.id; el.attributes.toggleClass("selected", selected); + if (is_opt_fixed) { + el.state.disabled = true; + } } } event click $(menu#languages>li) (_, me) { + if (me.state.disabled) return; var v = me.id; - if (v == "default") v = ""; + if (v == "default") v = default_option_lang; handler.set_local_option("lang", v); app.update(); this.toggleMenuState(); @@ -231,48 +252,64 @@ class Enhancements: Reactor.Component { if (el.id && el.id.indexOf("enable-") == 0) { var enabled = handler.get_option(el.id) != "N"; el.attributes.toggleClass("selected", enabled); + var is_opt_fixed = handler.is_option_fixed(el.id); + if (is_opt_fixed) { + el.state.disabled = true; + } } else if (el.id && el.id.indexOf("allow-") == 0) { var enabled = handler.get_option(el.id) == "Y"; el.attributes.toggleClass("selected", enabled); + var is_opt_fixed = handler.is_option_fixed(el.id); + if (is_opt_fixed) { + el.state.disabled = true; + } } } } event click $(menu#enhancements-menu>li) (_, me) { + if (me.state.disabled) return; var v = me.id; if (v.indexOf("enable-") == 0) { - var set_value = handler.get_option(v) != 'N' ? 'N' : ''; + var set_value = handler.get_option(v) != 'N' ? 'N' : default_option_yes; handler.set_option(v, set_value); - if (v == "enable-hwcodec" && set_value == '') { + if (v == "enable-hwcodec" && set_value != 'N') { handler.check_hwcodec(); } } else if (v.indexOf("allow-") == 0) { - handler.set_option(v, handler.get_option(v) == 'Y' ? '' : 'Y'); + handler.set_option(v, handler.get_option(v) == 'Y' ? default_option_no : 'Y'); } else if (v == 'screen-recording') { var show_root_dir = is_win && handler.is_installed(); var user_dir = handler.video_save_directory(false); var root_dir = show_root_dir ? handler.video_save_directory(true) : ""; - var ts0 = handler.get_option("enable-record-session") == '' ? { checked: true } : {}; + var ts0 = handler.get_option("enable-record-session") != 'N' ? { checked: true } : {}; var ts1 = handler.get_option("allow-auto-record-incoming") == 'Y' ? { checked: true } : {}; var ts2 = handler.get_local_option("allow-auto-record-outgoing") == 'Y' ? { checked: true } : {}; + var is_opt_fixed_enable_record = handler.is_option_fixed("enable-record-session"); + var is_opt_fixed_auto_incoming = handler.is_option_fixed("allow-auto-record-incoming"); + var is_opt_fixed_auto_outgoing = handler.is_option_fixed("allow-auto-record-outgoing"); + var is_opt_fixed_video_dir = handler.is_option_fixed("video-save-directory"); + if (is_opt_fixed_enable_record) { ts0.disabled = true; ts0.style = grey_text_style; } + if (is_opt_fixed_auto_incoming) { ts1.disabled = true; ts1.style = grey_text_style; } + if (is_opt_fixed_auto_outgoing) { ts2.disabled = true; ts2.style = grey_text_style; } msgbox("custom-recording", translate('Recording'),
-
{translate('Enable recording session')}
-
{translate('Automatically record incoming sessions')}
-
{translate('Automatically record outgoing sessions')}
-
+
{translate('Enable recording session')}
+
{translate('Automatically record incoming sessions')}
+
{translate('Automatically record outgoing sessions')}
+
{show_root_dir ?
{translate("Incoming")}:  {root_dir}
: ""}
{translate(show_root_dir ? "Outgoing" : "Directory")}:  {user_dir}
-
+ {is_opt_fixed_video_dir ? "" :
}
, "", function(res=null) { if (!res) return; - handler.set_option("enable-record-session", res.enable_record_session ? '' : 'N'); - handler.set_option("allow-auto-record-incoming", res.auto_record_incoming ? 'Y' : ''); - handler.set_local_option("allow-auto-record-outgoing", res.auto_record_outgoing ? 'Y' : ''); - handler.set_local_option("video-save-directory", $(#folderPath).text); + if (!is_opt_fixed_enable_record) handler.set_option("enable-record-session", res.enable_record_session ? default_option_yes : 'N'); + if (!is_opt_fixed_auto_incoming) handler.set_option("allow-auto-record-incoming", res.auto_record_incoming ? 'Y' : default_option_no); + if (!is_opt_fixed_auto_outgoing) handler.set_local_option("allow-auto-record-outgoing", res.auto_record_outgoing ? 'Y' : default_option_no); + if (!is_opt_fixed_video_dir) handler.set_local_option("video-save-directory", $(#folderPath).text); }); } this.toggleMenuState(); @@ -286,6 +323,117 @@ function getUserName() { return ''; } +// Shared dialog functions +function open_custom_server_dialog() { + var configOptions = handler.get_options(); + var old_relay = configOptions["relay-server"] || ""; + var old_api = configOptions["api-server"] || ""; + var old_id = configOptions["custom-rendezvous-server"] || ""; + var old_key = configOptions["key"] || ""; + msgbox("custom-server", "ID/Relay Server", "
\ +
" + translate("ID Server") + ":
\ +
" + translate("Relay Server") + ":
\ +
" + translate("API Server") + ":
\ +
" + translate("Key") + ":
\ +
\ + ", "", function(res=null, show_progress) { + if (!res) return; + if (typeof show_progress === 'function') show_progress(); + var id = (res.id || "").trim(); + var relay = (res.relay || "").trim(); + var api = (res.api || "").trim().toLowerCase(); + var key = (res.key || "").trim(); + if (id == old_id && relay == old_relay && key == old_key && api == old_api) return; + if (id) { + var err = handler.test_if_valid_server(id, true); + if (err) { if (typeof show_progress === 'function') show_progress(false, translate("ID Server") + ": " + err); return; } + } + if (relay) { + var err = handler.test_if_valid_server(relay, true); + if (err) { if (typeof show_progress === 'function') show_progress(false, translate("Relay Server") + ": " + err); return; } + } + if (api) { + if (0 != api.indexOf("https://") && 0 != api.indexOf("http://")) { + if (typeof show_progress === 'function') show_progress(false, translate("API Server") + ": " + translate("invalid_http")); + return; + } + } + configOptions["custom-rendezvous-server"] = id; + configOptions["relay-server"] = relay; + configOptions["api-server"] = api; + configOptions["key"] = key; + handler.set_options(configOptions); + if (typeof show_progress === 'function') show_progress(-1); + }, 260); +} + +function open_whitelist_dialog() { + var is_opt_fixed = handler.is_option_fixed("whitelist"); + var v = handler.get_option("whitelist"); + var old_value = v == default_option_whitelist ? '' : v.split(",").join("\n"); + var type_str = is_opt_fixed ? "custom-whitelist-nook" : "custom-whitelist"; + var readonly_attr = is_opt_fixed ? " readonly=\"readonly\"" : ""; + var grey_class = is_opt_fixed ? " class=\"grey-text\"" : ""; + msgbox(type_str, translate("IP Whitelisting"), "
\ + " + translate("whitelist_sep") + "
\ + \ +
\ + ", "", function(res=null, show_progress) { + if (!res) return; + if (typeof show_progress === 'function') show_progress(); + var value = (res.text || "").trim(); + if (value) { + var values = value.split(/[\s,;\n]+/g); + for (var ip in values) { + if (!ip.match(/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)(\/([1-9]|[1-2][0-9]|3[0-2])){0,1}$/) + && !ip.match(/^(((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7})(\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}$/)) { + if (typeof show_progress === 'function') show_progress(false, translate("Invalid IP") + ": " + ip); + return; + } + } + value = values.join("\n"); + } + if (value == old_value) return; + if (!value) value = default_option_whitelist; + handler.set_option("whitelist", value.replace("\n", ",")); + if (typeof show_progress === 'function') show_progress(-1); + }, 300); +} + +function open_proxy_dialog() { + var is_opt_fixed = handler.is_option_fixed("proxy-url"); + var socks5 = handler.get_socks() || {}; + var old_proxy = socks5[0] || ""; + var old_username = socks5[1] || ""; + var old_password = socks5[2] || ""; + var type_str = is_opt_fixed ? "custom-server-nook" : "custom-server"; + var greyStyle = is_opt_fixed ? grey_text_style : ""; + msgbox(type_str, "Socks5/Http(s) Proxy",
+
{translate("Server")}:
+
{translate("Username")}:
+
{translate("Password")}:{ is_opt_fixed ? : }
+
+ , "", function(res=null, show_progress) { + if (!res) return; + if (typeof show_progress === 'function') show_progress(); + 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 domain_port = proxy; + var protocol_index = domain_port.indexOf('://'); + if (protocol_index !== -1) { + domain_port = domain_port.substring(protocol_index + 3); + } + var err = handler.test_if_valid_server(domain_port, false); + if (err) { if (typeof show_progress === 'function') show_progress(false, translate("Server") + ": " + err); return; } + } + handler.set_socks(proxy, username, password); + if (typeof show_progress === 'function') show_progress(-1); + }, 240); +} + function updateTheme() { var root_element = self; if (handler.get_option("allow-darktheme") == "Y") { @@ -313,39 +461,39 @@ class MyIdMenu: Reactor.Component { var username = handler.get_local_option("access_token") ? getUserName() : ''; return -
  • {svg_checkmark}{translate('Enable keyboard/mouse')}
  • -
  • {svg_checkmark}{translate('Enable clipboard')}
  • -
  • {svg_checkmark}{translate('Enable file transfer')}
  • -
  • {svg_checkmark}{translate('Enable camera')}
  • -
  • {svg_checkmark}{translate('Enable terminal')}
  • -
  • {svg_checkmark}{translate('Enable remote restart')}
  • -
  • {svg_checkmark}{translate('Enable TCP tunneling')}
  • - {is_win ?
  • {svg_checkmark}{translate('Enable blocking user input')}
  • : ""} -
  • {svg_checkmark}{translate('Enable LAN discovery')}
  • + {!disable_settings &&
  • {svg_checkmark}{translate('Enable keyboard/mouse')}
  • } + {!disable_settings &&
  • {svg_checkmark}{translate('Enable clipboard')}
  • } + {!disable_settings &&
  • {svg_checkmark}{translate('Enable file transfer')}
  • } + {!disable_settings &&
  • {svg_checkmark}{translate('Enable camera')}
  • } + {!disable_settings &&
  • {svg_checkmark}{translate('Enable terminal')}
  • } + {!disable_settings &&
  • {svg_checkmark}{translate('Enable remote restart')}
  • } + {!disable_settings &&
  • {svg_checkmark}{translate('Enable TCP tunneling')}
  • } + {!disable_settings && is_win ?
  • {svg_checkmark}{translate('Enable blocking user input')}
  • : ""} + {!disable_settings &&
  • {svg_checkmark}{translate('Enable LAN discovery')}
  • } -
  • {svg_checkmark}{translate('Enable remote configuration modification')}
  • -
    -
  • {translate('ID/Relay Server')}
  • -
  • {translate('IP Whitelisting')}
  • -
  • {translate('Socks5/Http(s) Proxy')}
  • -
  • {svg_checkmark}{translate('Use WebSocket')}
  • - {!using_public_server && !outgoing_only &&
  • {svg_checkmark}{translate('Disable UDP')}
  • } - {!using_public_server &&
  • {svg_checkmark}{translate('Allow insecure TLS fallback')}
  • } + {!disable_settings &&
  • {svg_checkmark}{translate('Enable remote configuration modification')}
  • } + {!disable_settings &&
    } + {!disable_settings && !hide_server_settings &&
  • {translate('ID/Relay Server')}
  • } + {!disable_settings &&
  • {translate('IP Whitelisting')}
  • } + {!disable_settings && !hide_proxy_settings &&
  • {translate('Socks5/Http(s) Proxy')}
  • } + {!disable_settings && !hide_websocket_settings &&
  • {svg_checkmark}{translate('Use WebSocket')}
  • } + {!disable_settings && !using_public_server && !outgoing_only &&
  • {svg_checkmark}{translate('Disable UDP')}
  • } + {!disable_settings && !using_public_server &&
  • {svg_checkmark}{translate('Allow insecure TLS fallback')}
  • }
  • {svg_checkmark}{translate("Enable service")}
  • - {is_win && handler.is_installed() ? : ""} - - {false && handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • } + {!disable_settings && is_win && handler.is_installed() ? : ""} + {!disable_settings && } + {!disable_settings && false && handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • } {handler.is_ok_change_id() ?
    : ""} - {username ? + {!disable_account && (username ?
  • {translate('Logout')} ({username})
  • : -
  • {translate('Login')}
  • } - {handler.is_ok_change_id() && key_confirmed && connect_status > 0 ?
  • {translate('Change ID')}
  • : ""} +
  • {translate('Login')}
  • )} + {!disable_settings && handler.is_ok_change_id() && key_confirmed && connect_status > 0 ?
  • {translate('Change ID')}
  • : ""}
  • {svg_checkmark}{translate('Dark Theme')}
  • -
  • {svg_checkmark}{translate('Auto update')}
  • + {disable_installation ? "" :
  • {svg_checkmark}{translate('Auto update')}
  • }
  • {translate('About')} {" "}{handler.get_app_name()}
  • ; @@ -373,15 +521,24 @@ class MyIdMenu: Reactor.Component { function toggleMenuState() { for (var el in $$(menu#config-options>li)) { - if (el.id && el.id.indexOf("enable-") == 0) { - var enabled = handler.get_option(el.id) != "N"; + var id = el.id; + if (!id) continue; + var is_opt_fixed = handler.is_option_fixed(id); + if (id.indexOf("enable-") == 0) { + var enabled = handler.get_option(id) != "N"; el.attributes.toggleClass("selected", enabled); el.attributes.toggleClass("line-through", !enabled); + } else if (id.indexOf("allow-") == 0) { + var enabled = handler.get_option(id) == "Y"; + el.attributes.toggleClass("selected", enabled); + el.attributes.toggleClass("line-through", !enabled); + } else if (id == "whitelist") { + // whitelist should be clickable even when fixed (to view the content) + // The dialog will show readonly textarea and no OK button when fixed + continue; } - if (el.id && el.id.indexOf("allow-") == 0) { - var enabled = handler.get_option(el.id) == "Y"; - el.attributes.toggleClass("selected", enabled); - el.attributes.toggleClass("line-through", !enabled); + if (is_opt_fixed) { + el.state.disabled = true; } } } @@ -405,104 +562,23 @@ class MyIdMenu: Reactor.Component { } event click $(menu#config-options>li) (_, me) { + if (me.state.disabled) return; if (me.id && me.id.indexOf("enable-") == 0) { - handler.set_option(me.id, handler.get_option(me.id) == "N" ? "" : "N"); + handler.set_option(me.id, handler.get_option(me.id) == "N" ? default_option_yes : "N"); } if (me.id && me.id.indexOf("allow-") == 0) { - handler.set_option(me.id, handler.get_option(me.id) == "Y" ? "" : "Y"); + handler.set_option(me.id, handler.get_option(me.id) == "Y" ? default_option_no : "Y"); } if (me.id == "whitelist") { - var old_value = handler.get_option("whitelist").split(",").join("\n"); - msgbox("custom-whitelist", translate("IP Whitelisting"), "
    \ -
    " + translate("whitelist_sep") + "
    \ - \ -
    \ - ", "", function(res=null) { - if (!res) return; - var value = (res.text || "").trim(); - if (value) { - var values = value.split(/[\s,;\n]+/g); - for (var ip in values) { - if (!ip.match(/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)(\/([1-9]|[1-2][0-9]|3[0-2])){0,1}$/) - && !ip.match(/^(((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7})(\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}$/)) { - return translate("Invalid IP") + ": " + ip; - } - } - value = values.join("\n"); - } - if (value == old_value) return; - stdout.println("whitelist updated"); - handler.set_option("whitelist", value.replace("\n", ",")); - }, 300); + open_whitelist_dialog(); } else if (me.id == "custom-server") { - var configOptions = handler.get_options(); - var old_relay = configOptions["relay-server"] || ""; - var old_api = configOptions["api-server"] || ""; - var old_id = configOptions["custom-rendezvous-server"] || ""; - var old_key = configOptions["key"] || ""; - msgbox("custom-server", "ID/Relay Server", "
    \ -
    " + translate("ID Server") + ":
    \ -
    " + translate("Relay Server") + ":
    \ -
    " + translate("API Server") + ":
    \ -
    " + translate("Key") + ":
    \ -
    \ - ", "", function(res=null) { - if (!res) return; - var id = (res.id || "").trim(); - var relay = (res.relay || "").trim(); - var api = (res.api || "").trim().toLowerCase(); - var key = (res.key || "").trim(); - if (id == old_id && relay == old_relay && key == old_key && api == old_api) return; - if (id) { - var err = handler.test_if_valid_server(id, true); - if (err) return translate("ID Server") + ": " + err; - } - if (relay) { - var err = handler.test_if_valid_server(relay, true); - if (err) return translate("Relay Server") + ": " + err; - } - if (api) { - if (0 != api.indexOf("https://") && 0 != api.indexOf("http://")) { - return translate("API Server") + ": " + translate("invalid_http"); - } - } - configOptions["custom-rendezvous-server"] = id; - configOptions["relay-server"] = relay; - configOptions["api-server"] = api; - configOptions["key"] = key; - handler.set_options(configOptions); - }, 260); + open_custom_server_dialog(); } 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/Http(s) Proxy",
    -
    {translate("Server")}:
    -
    {translate("Username")}:
    -
    {translate("Password")}:
    -
    - , "", 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 domain_port = proxy; - var protocol_index = domain_port.indexOf('://'); - if (protocol_index !== -1) { - domain_port = domain_port.substring(protocol_index + 3); - } - var err = handler.test_if_valid_server(domain_port, false); - if (err) return translate("Server") + ": " + err; - } - handler.set_socks(proxy, username, password); - }, 240); + open_proxy_dialog(); } else if (me.id == "disable-udp") { handler.set_option("disable-udp", handler.get_option("disable-udp") == "Y" ? "N" : "Y"); } else if (me.id == "stop-service") { - handler.set_option("stop-service", service_stopped ? "" : "Y"); + handler.set_option("stop-service", service_stopped ? default_option_no : "Y"); } else if (me.id == "change-id") { msgbox("custom-id", translate("Change ID"), "
    \
    " + translate('id_change_tip') + "
    \ @@ -549,11 +625,14 @@ class EditDirectAccessPort: Reactor.Component { } function editDirectAccessPort() { + var is_opt_fixed = handler.is_option_fixed("direct-access-port"); var p0 = handler.get_option('direct-access-port'); - var port = p0 ? : - ; - msgbox("custom-direct-access-port", translate('Direct IP Access Settings'),
    -
    {translate('Port')}:{port}
    + var greyStyle = is_opt_fixed ? grey_text_style : ""; + var port = p0 ? : + ; + var type_str = is_opt_fixed ? "custom-direct-access-port-nook" : "custom-direct-access-port"; + msgbox(type_str, translate('Direct IP Access Settings'),
    +
    {translate('Port')}:{port}
    , "", function(res=null) { if (!res) return; var p = (res.port || '').trim(); @@ -578,27 +657,33 @@ class App: Reactor.Component var is_can_screen_recording = handler.is_can_screen_recording(false); return
    -
    +
    -
    {translate('Your Desktop')}
    -
    {translate('desk_tip')}
    -
    + {is_custom_client && handler.get_builtin_option("hide-powered-by-me") != "Y" ?
    {translate('powered_by_me')}
    : ""} +
    + {translate('Your Desktop')} + {outgoing_only ? {svg_menu} : ""} +
    +
    {outgoing_only ? translate('outgoing_only_desk_tip') : translate('desk_tip')}
    + {outgoing_only ?
    : ""} + {!outgoing_only &&
    {key_confirmed ? : translate("Generating ...")} -
    - +
    } + {!outgoing_only && }
    - {!is_win || handler.is_installed() ? "": } - {software_update_url ? : ""} - {is_win && handler.is_installed() && !software_update_url && handler.is_installed_lower_version() ? : ""} + {(!is_win || handler.is_installed() || disable_installation) ? "" : } + {software_update_url && !disable_installation ? : ""} + {is_win && handler.is_installed() && !software_update_url && handler.is_installed_lower_version() && !disable_installation ? : ""} {is_can_screen_recording ? "": } {is_can_screen_recording && !handler.is_process_trusted(false) ? : ""} {!service_stopped && is_can_screen_recording && handler.is_process_trusted(false) && handler.is_installed() && !handler.is_installed_daemon(false) ? : ""} {system_error ? : ""} {!system_error && handler.is_login_wayland() && !handler.current_is_wayland() ? : ""} {!system_error && handler.current_is_wayland() ? : ""} + {incoming_only ? : ""}
    -
    + {!incoming_only &&
    {translate('Control Remote Desktop')}
    @@ -610,10 +695,10 @@ class App: Reactor.Component
    - -
    + {!outgoing_only ? : ""} +
    }
    -
    ; +
    ; } event click $(button#connect) { @@ -872,7 +957,7 @@ class TemporaryPasswordLengthMenu: Reactor.Component { var me = this; var method = handler.get_option('verification-method'); self.timer(1ms, function() { me.toggleMenuState() }); - return
  • {translate("One-time password length")} + return
  • {translate("One-time password length")}
  • {svg_checkmark}6
  • {svg_checkmark}8
  • @@ -882,15 +967,20 @@ class TemporaryPasswordLengthMenu: Reactor.Component { } function toggleMenuState() { + var is_opt_fixed = handler.is_option_fixed('temporary-password-length'); var length = handler.get_option("temporary-password-length"); var index = ['6', '8', '10'].indexOf(length); if (index < 0) index = 0; for (var (i, el) in this.$$(menu#temporary-password-length>li)) { el.attributes.toggleClass("selected", i == index); + if (is_opt_fixed) { + el.state.disabled = true; + } } } event click $(menu#temporary-password-length>li) (_, me) { + if (me.state.disabled) return; var length = me.id.substring('temporary-password-length-'.length); var old_length = handler.get_option('temporary-password-length'); if (length != old_length) { @@ -917,7 +1007,7 @@ class PasswordArea: Reactor.Component {
    {this.renderPop()} - {svg_edit} + {!disable_settings && svg_edit}
  • ; } @@ -956,10 +1046,18 @@ class PasswordArea: Reactor.Component { pwd_id = 'use-both-passwords'; var has_valid_2fa = handler.has_valid_2fa(); for (var el in this.$$(menu#edit-password-context>li)) { - if (el.id.indexOf("approve-mode-") == 0) + if (el.id.indexOf("approve-mode-") == 0) { el.attributes.toggleClass("selected", el.id == mode_id); - if (el.id.indexOf("use-") == 0) + if (handler.is_option_fixed('approve-mode')) { + el.state.disabled = true; + } + } + if (el.id.indexOf("use-") == 0) { el.attributes.toggleClass("selected", el.id == pwd_id); + if (handler.is_option_fixed('verification-method')) { + el.state.disabled = true; + } + } if (el.id == "tfa") el.attributes.toggleClass("selected", has_valid_2fa); } @@ -997,6 +1095,7 @@ class PasswordArea: Reactor.Component { } event click $(menu#edit-password-context>li) (_, me) { + if (me.state.disabled) return; if (me.id.indexOf('use-') == 0) { handler.set_option('verification-method', me.id); this.toggleMenuState(); @@ -1008,7 +1107,7 @@ class PasswordArea: Reactor.Component { else if (me.id == 'approve-mode-click') approve_mode = 'click'; else - approve_mode = ''; + approve_mode = default_option_approve_mode; handler.set_option('approve-mode', approve_mode); this.toggleMenuState(); passwordArea.update(); @@ -1066,11 +1165,11 @@ function updatePasswordArea() { password_cache[3] = approve_mode; update = true; } - if (update) passwordArea.update(); + if (update && passwordArea) passwordArea.update(); updatePasswordArea(); }); } -updatePasswordArea(); +if (!outgoing_only) updatePasswordArea(); class ID: Reactor.Component { function render() { @@ -1131,6 +1230,35 @@ event keydown (evt) { $(body).content(
    ); +event click $(#powered-by) { + handler.open_url("https://rustdesk.com"); +} + +event click $(#open-settings) (_, me) { + showSettings(); +} + +// Event handlers for outgoing_only mode (when menu items are in main UI, not in MyIdMenu) +event click $(li#custom-server) (_, me) { + if (!outgoing_only) return; + open_custom_server_dialog(); +} + +event click $(li#whitelist) (_, me) { + if (!outgoing_only) return; + open_whitelist_dialog(); +} + +event click $(li#socks5-server) (_, me) { + if (!outgoing_only) return; + open_proxy_dialog(); +} + +event click $(li#login) (_, me) { + if (!outgoing_only) return; + login(); +} + function self.closing() { var (x, y, w, h) = view.box(#rectw, #border, #screen); handler.closing(x, y, w, h); @@ -1144,10 +1272,10 @@ function self.ready() { if (r[2] >= sw && r[3] >= sh) { self.timer(1ms, function() { view.windowState = View.WINDOW_MAXIMIZED; }); } else { - view.move(r[0], r[1], r[2], r[3]); + view.move(r[0], r[1], incoming_only ? scaleIt(incoming_only_width) : r[2], r[3]); } } else { - centerize(scaleIt(800), scaleIt(600)); + centerize(scaleIt(incoming_only ? incoming_only_width : 800), scaleIt(incoming_only ? 390 : 600)); } if (!handler.get_remote_id()) { view.focus = $(#remote_id); @@ -1162,7 +1290,16 @@ function showAbout() { function showSettings() { if ($(#overlay).style#display == 'block') return; - myIdMenu.showSettingMenu(); + var menu = myIdMenu.$(menu#config-options); + var anchor = $(#open-settings); + if (!anchor) anchor = myIdMenu.$(svg#menu); + // show immediately at button, then update menu state asynchronously + anchor.popup(menu); + self.timer(1ms, function() { + audioInputMenu.update({ show: true }); + myIdMenu.toggleMenuState(); + if (direct_server) direct_server.update(); + }); } function checkConnectStatus() { diff --git a/src/ui_interface.rs b/src/ui_interface.rs index b71470d31..516e4fede 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -203,6 +203,19 @@ pub fn use_texture_render() -> bool { } } +#[inline] +pub fn is_option_fixed(key: &str) -> bool { + config::OVERWRITE_DISPLAY_SETTINGS + .read() + .unwrap() + .contains_key(key) + || config::OVERWRITE_LOCAL_SETTINGS + .read() + .unwrap() + .contains_key(key) + || config::OVERWRITE_SETTINGS.read().unwrap().contains_key(key) +} + #[inline] pub fn get_local_option(key: String) -> String { crate::get_local_option(&key)