Merge remote-tracking branch 'rustdesk/master' into flutter_desktop

# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	build.rs
#	flutter/.gitignore
#	flutter/lib/common.dart
#	flutter/lib/mobile/pages/remote_page.dart
#	flutter/lib/models/model.dart
#	flutter/lib/models/native_model.dart
#	flutter/lib/models/server_model.dart
#	flutter/pubspec.lock
#	flutter/pubspec.yaml
#	src/client.rs
#	src/client/file_trait.rs
#	src/flutter.rs
#	src/mobile_ffi.rs
#	src/ui.rs
This commit is contained in:
Kingtous
2022-06-27 11:18:53 +08:00
138 changed files with 5534 additions and 798 deletions

View File

@@ -21,7 +21,7 @@ impl Session {
pub fn new(id: &str, sender: mpsc::UnboundedSender<Data>) -> Self {
let mut password = "".to_owned();
if PeerConfig::load(id).password.is_empty() {
password = rpassword::read_password_from_tty(Some("Enter password: ")).unwrap();
password = rpassword::prompt_password("Enter password: ").unwrap();
}
let session = Self {
id: id.to_owned(),
@@ -47,7 +47,7 @@ impl Interface for Session {
.ok();
} else if msgtype == "re-input-password" {
log::error!("{}: {}", title, text);
let pass = rpassword::read_password_from_tty(Some("Enter password: ")).unwrap();
let pass = rpassword::prompt_password("Enter password: ").unwrap();
self.sender.send(Data::Login((pass, true))).ok();
} else if msgtype.contains("error") {
log::error!("{}: {}: {}", msgtype, title, text);
@@ -76,6 +76,10 @@ impl Interface for Session {
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
handle_test_delay(t, peer).await;
}
fn send(&self, data: Data) {
self.sender.send(data).ok();
}
}
#[tokio::main(flavor = "current_thread")]
@@ -85,6 +89,7 @@ pub async fn start_one_port_forward(
remote_host: String,
remote_port: i32,
key: String,
token: String,
) {
crate::common::test_rendezvous_server();
crate::common::test_nat_type();
@@ -92,7 +97,7 @@ pub async fn start_one_port_forward(
let handler = Session::new(&id, sender);
handler.lc.write().unwrap().port_forward = (remote_host, remote_port);
if let Err(err) =
crate::port_forward::listen(handler.id.clone(), port, handler.clone(), receiver, &key).await
crate::port_forward::listen(handler.id.clone(), port, handler.clone(), receiver, &key, &token).await
{
log::error!("Failed to listen on {}: {}", port, err);
}

View File

@@ -795,6 +795,7 @@ pub struct LoginConfigHandler {
pub port_forward: (String, i32),
pub version: i64,
pub conn_id: i32,
features: Option<Features>,
}
impl Deref for LoginConfigHandler {
@@ -924,11 +925,11 @@ impl LoginConfigHandler {
})
.into();
} else if name == "privacy-mode" {
config.privacy_mode = !config.privacy_mode;
// try toggle privacy mode
option.privacy_mode = (if config.privacy_mode {
BoolOption::Yes
} else {
BoolOption::No
} else {
BoolOption::Yes
})
.into();
} else if name == "enable-file-transfer" {
@@ -1068,6 +1069,14 @@ impl LoginConfigHandler {
}
}
pub fn is_privacy_mode_supported(&self) -> bool {
if let Some(features) = &self.features {
features.privacy_mode
} else {
false
}
}
/// Create a [`Message`] for refreshing video.
pub fn refresh() -> Message {
let mut misc = Misc::new();
@@ -1166,6 +1175,7 @@ impl LoginConfigHandler {
if !pi.version.is_empty() {
self.version = hbb_common::get_version_number(&pi.version);
}
self.features = pi.features.into_option();
let serde = PeerInfoSerde {
username,
hostname: pi.hostname.clone(),
@@ -1378,9 +1388,9 @@ pub fn send_mouse(
}
/// Avtivate OS by sending mouse movement.
///
///
/// # Arguments
///
///
/// * `interface` - The interface for sending data.
fn activate_os(interface: &impl Interface) {
send_mouse(0, 0, 0, false, false, false, false, interface);
@@ -1401,9 +1411,9 @@ fn activate_os(interface: &impl Interface) {
}
/// Input the OS's password.
///
///
/// # Arguments
///
///
/// * `p` - The password.
/// * `avtivate` - Whether to activate OS.
/// * `interface` - The interface for sending data.
@@ -1414,9 +1424,9 @@ pub fn input_os_password(p: String, activate: bool, interface: impl Interface) {
}
/// Input the OS's password.
///
///
/// # Arguments
///
///
/// * `p` - The password.
/// * `avtivate` - Whether to activate OS.
/// * `interface` - The interface for sending data.
@@ -1673,9 +1683,9 @@ lazy_static::lazy_static! {
}
/// Check if the given message is an error and can be retried.
///
///
/// # Arguments
///
///
/// * `msgtype` - The message type.
/// * `title` - The title of the message.
/// * `text` - The text of the message.

View File

@@ -6,7 +6,7 @@ pub trait FileManager: Interface {
fs::get_home_as_string()
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))]
fn read_dir(&self, path: String, include_hidden: bool) -> sciter::Value {
match fs::read_dir(&fs::get_path(&path), include_hidden) {
Err(_) => sciter::Value::null(),
@@ -19,7 +19,7 @@ pub trait FileManager: Interface {
}
}
#[cfg(any(target_os = "android", target_os = "ios"))]
#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))]
fn read_dir(&self, path: &str, include_hidden: bool) -> String {
use crate::flutter::make_fd_to_json;
match fs::read_dir(&fs::get_path(path), include_hidden) {

View File

@@ -376,8 +376,18 @@ pub fn get_time() -> i64 {
}
pub fn run_me<T: AsRef<std::ffi::OsStr>>(args: Vec<T>) -> std::io::Result<std::process::Child> {
let cmd = std::env::current_exe()?;
return std::process::Command::new(cmd).args(&args).spawn();
#[cfg(not(feature = "appimage"))]
{
let cmd = std::env::current_exe()?;
return std::process::Command::new(cmd).args(&args).spawn();
}
#[cfg(feature = "appimage")]
{
let appdir = std::env::var("APPDIR").unwrap();
let appimage_cmd = std::path::Path::new(&appdir).join("AppRun");
log::info!("path: {:?}", appimage_cmd);
return std::process::Command::new(appimage_cmd).args(&args).spawn();
}
}
pub fn username() -> String {
@@ -482,7 +492,7 @@ pub fn is_ip(id: &str) -> bool {
}
pub fn is_setup(name: &str) -> bool {
name.to_lowercase().ends_with("putes.exe") || name.to_lowercase().ends_with("安装.exe")
name.to_lowercase().ends_with("setdown.exe") || name.to_lowercase().ends_with("安装.exe")
}
pub fn get_uuid() -> Vec<u8> {
@@ -593,3 +603,33 @@ pub async fn post_request(url: String, body: String, header: &str) -> ResultType
pub async fn post_request_sync(url: String, body: String, header: &str) -> ResultType<String> {
post_request(url, body, header).await
}
#[inline]
pub fn make_privacy_mode_msg(state: back_notification::PrivacyModeState) -> Message {
let mut misc = Misc::new();
let mut back_notification = BackNotification::new();
back_notification.set_privacy_mode_state(state);
misc.set_back_notification(back_notification);
let mut msg_out = Message::new();
msg_out.set_misc(misc);
msg_out
}
pub fn make_fd_to_json(fd: FileDirectory) -> String {
use serde_json::json;
let mut fd_json = serde_json::Map::new();
fd_json.insert("id".into(), json!(fd.id));
fd_json.insert("path".into(), json!(fd.path));
let mut entries = vec![];
for entry in fd.entries {
let mut entry_map = serde_json::Map::new();
entry_map.insert("entry_type".into(), json!(entry.entry_type.value()));
entry_map.insert("name".into(), json!(entry.name));
entry_map.insert("size".into(), json!(entry.size));
entry_map.insert("modified_time".into(), json!(entry.modified_time));
entries.push(entry_map);
}
fd_json.insert("entries".into(), json!(entries));
serde_json::to_string(&fd_json).unwrap_or("".into())
}

View File

@@ -1,4 +1,5 @@
use crate::{client::*, flutter_ffi::EventToUI};
use crate::common::{make_fd_to_json};
use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer};
use hbb_common::{
allow_err,
@@ -499,7 +500,12 @@ impl Interface for Session {
let mut displays = Vec::new();
let mut current = pi.current_display as usize;
if !lc.is_file_transfer {
if lc.is_file_transfer {
if pi.username.is_empty() {
self.msgbox("error", "Error", "No active console user logged on, please connect and logon first.");
return;
}
} else {
if pi.displays.is_empty() {
self.msgbox("error", "Remote Error", "No Display");
}

View File

@@ -20,6 +20,16 @@ use std::{collections::HashMap, sync::atomic::Ordering};
#[cfg(not(windows))]
use std::{fs::File, io::prelude::*};
// State with timestamp, because std::time::Instant cannot be serialized
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
#[serde(tag = "t", content = "c")]
pub enum PrivacyModeState {
OffSucceeded,
OffFailed,
OffByPeer,
OffUnknown,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "t", content = "c")]
pub enum FS {
@@ -116,6 +126,7 @@ pub enum Data {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
ClipbaordFile(ClipbaordFile),
ClipboardFileEnabled(bool),
PrivacyModeState((i32, PrivacyModeState)),
}
#[tokio::main(flavor = "current_thread")]

View File

@@ -1,16 +1,20 @@
use std::ops::Deref;
mod cn;
mod en;
mod fr;
mod it;
mod tw;
mod cs;
mod da;
mod sk;
mod de;
mod ru;
mod en;
mod es;
mod eo;
mod ptbr;
mod fr;
mod id;
mod it;
mod ptbr;
mod ru;
mod tr;
mod tw;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn translate(name: String) -> String {
@@ -22,15 +26,15 @@ pub fn translate_locale(name: String, locale: &str) -> String {
let mut lang = hbb_common::config::LocalConfig::get_option("lang").to_lowercase();
if lang.is_empty() {
// zh_CN on Linux, zh-Hans-CN on mac, zh_CN_#Hans on Android
if locale.starts_with("zh") && (locale.ends_with("CN") || locale.ends_with("SG") || locale.ends_with("Hans")) {
lang = "cn".to_owned();
if locale.starts_with("zh") {
lang = (if locale.contains("TW") { "tw" } else { "cn" }).to_owned();
}
}
if lang.is_empty() {
lang = locale
.split("-")
.last()
.map(|x| x.split("_").last().unwrap_or_default())
.next()
.map(|x| x.split("_").next().unwrap_or_default())
.unwrap_or_default()
.to_owned();
}
@@ -41,6 +45,7 @@ pub fn translate_locale(name: String, locale: &str) -> String {
"it" => it::T.deref(),
"tw" => tw::T.deref(),
"de" => de::T.deref(),
"es" => es::T.deref(),
"ru" => ru::T.deref(),
"eo" => eo::T.deref(),
"id" => id::T.deref(),
@@ -48,6 +53,9 @@ pub fn translate_locale(name: String, locale: &str) -> String {
"br" => ptbr::T.deref(),
"pt" => ptbr::T.deref(),
"tr" => tr::T.deref(),
"cs" => cs::T.deref(),
"da" => da::T.deref(),
"sk" => sk::T.deref(),
_ => en::T.deref(),
};
if let Some(v) = m.get(&name as &str) {

View File

@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "允许使用键盘鼠标"),
("Allow using clipboard", "允许使用剪贴板"),
("Allow hearing sound", "允许听到声音"),
("Allow file transfer", "允许文件传输"),
("File transfer", "文件传输"),
("Allow file copy and paste", "允许复制粘贴文件"),
("Connected", "已经连接"),
("Direct and encrypted connection", "加密直连"),
("Relayed and encrypted connection", "加密中继连接"),
@@ -250,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", "已选择"),
("Screen Capture", "屏幕录制"),
("Input Control", "输入控制"),
("File Transfer", "文件传输"),
("Audio Capture", "音频录制"),
("File Connection", "文件连接"),
("Screen Connection", "屏幕连接"),
@@ -265,10 +263,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", "当前安卓版本不支持音频录制请升级至安卓10或更高。"),
("android_start_service_tip", "点击 [启动服务] 或打开 [屏幕录制] 权限开启手机屏幕共享服务。"),
("Account", "账号"),
("Quit", "退出"),
("Overwrite", "覆盖"),
("This file exists, skip or overwrite this file?", "这个文件/文件夹已存在,跳过/覆盖?"),
("Quit", "退出"),
("doc_mac_permission", "https://rustdesk.com/docs/zh-cn/manual/mac/#启用权限"),
("Help", "帮助"),
("Failed", "失败"),
("Succeeded", "成功"),
("Someone turns on privacy mode, exit", "其他用户使用隐私模式,退出"),
("Unsupported", "不支持"),
("Peer denied", "被控端拒绝"),
("Please install plugins", "请安装插件"),
("Peer exit", "被控端退出"),
("Failed to turn off", "退出失败"),
("Turned off", "退出"),
("In privacy mode", "进入隐私模式"),
("Out privacy mode", "退出隐私模式"),
].iter().cloned().collect();
}

283
src/lang/cs.rs Normal file
View File

@@ -0,0 +1,283 @@
lazy_static::lazy_static! {
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "Stav"),
("Your Desktop", "Vaše plocha"),
("desk_tip", "Pomocí tohoto identifikátoru a hesla můžete přistupovat ke své ploše."),
("Password", "Heslo"),
("Ready", "Připraveno"),
("Established", "Navázáno"),
("connecting_status", "Připojování se k Rusdesk síti…"),
("Enable Service", "Povolit službu"),
("Start Service", "Spustit službu"),
("Service is running", "Služba je spuštěná"),
("Service is not running", "Služba není spuštěná"),
("not_ready_status", "Nepřipraveno. Zkontrolujte své připojení."),
("Control Remote Desktop", "Ovládat vzdálenou plochu"),
("Transfer File", "Přenést soubor"),
("Connect", "Připojit"),
("Recent Sessions", "Nedávné relace"),
("Address Book", "Adresář kontaktů"),
("Confirmation", "Potvrzení"),
("TCP Tunneling", "TCP tunelování"),
("Remove", "Odebrat"),
("Refresh random password", "Vytvořit nové náhodné heslo"),
("Set your own password", "Nastavte si své vlastní heslo"),
("Enable Keyboard/Mouse", "Povolit klávesnici/myš"),
("Enable Clipboard", "Povolit schránku"),
("Enable File Transfer", "Povolit přenos souborů"),
("Enable TCP Tunneling", "Povolit TCP tunelování"),
("IP Whitelisting", "Povolování pouze z daných IP adres)"),
("ID/Relay Server", "Identifikátor / předávací (relay) server"),
("Stop service", "Zastavit službu"),
("Change ID", "Změnit identifikátor"),
("Website", "Webové stránky"),
("About", "O aplikaci"),
("Mute", "Ztlumit"),
("Audio Input", "Vstup zvuku"),
("ID Server", "Server pro identif."),
("Relay Server", "Předávací (relay) server"),
("API Server", "Server s API rozhraním"),
("invalid_http", "Je třeba, aby začínalo na http:// nebo https://"),
("Invalid IP", "Neplatná IP adresa"),
("id_change_tip", "Použít je mozné pouze znaky a-z, A-Z, 0-9 a _ (podtržítko). Dále je třeba aby začínalo na písmeno a-z, A-Z. Délka mezi 6 a 16 znaky."),
("Invalid format", "Neplatný formát"),
("server_not_support", "Server zatím nepodporuje"),
("Not available", "Není k dispozici"),
("Too frequent", "Příliš časté"),
("Cancel", "Storno"),
("Skip", "Přeskočit"),
("Close", "Zavřít"),
("Retry", "Zkusit znovu"),
("OK", "OK"),
("Password Required", "Vyžadováno heslo"),
("Please enter your password", "Zadejte své heslo"),
("Remember password", "Zapamatovat heslo"),
("Wrong Password", "Nesprávné heslo"),
("Do you want to enter again?", "Chcete se znovu připojit?"),
("Connection Error", "Chyba spojení"),
("Error", "Chyba"),
("Reset by the peer", "Resetováno protějškem"),
("Connecting...", "Připojování…"),
("Connection in progress. Please wait.", "Probíhá připojování vyčkejte."),
("Please try 1 minute later", "Zkuste to až za minutu či déle"),
("Login Error", "Chyba přihlášení se"),
("Successful", "Úspěšné"),
("Connected, waiting for image...", "Připojeno, čeká se na obraz…"),
("Name", "Název"),
("Type", "Typ"),
("Modified", "Změněno"),
("Size", "Velikost"),
("Show Hidden Files", "Zobrazit skryté soubory"),
("Receive", "Přijmout"),
("Send", "Odeslat"),
("Refresh File", "Znovu načíst soubor"),
("Local", "Místní"),
("Remote", "Vzdálené"),
("Remote Computer", "Vzdálený počítač"),
("Local Computer", "Místní počítač"),
("Confirm Delete", "Potvrdit smazání"),
("Delete", "Smazat"),
("Properties", "Vlastnosti"),
("Multi Select", "Vícenásobný výběr"),
("Empty Directory", "Prázdná složka"),
("Not an empty directory", "Neprázdná složka"),
("Are you sure you want to delete this file?", "Opravdu chcete tento soubor vymazat?"),
("Are you sure you want to delete this empty directory?", "Opravdu chcete tuto prázdnou složku smazat?"),
("Are you sure you want to delete the file of this directory?", "Opravdu chcete vymazat soubor, pocházející z této složky?"),
("Do this for all conflicts", "Naložit takto se všemi konflikty"),
("This is irreversible!", "Toto nelze vzít zpět"),
("Deleting", "Mazání"),
("files", "soubory"),
("Waiting", "Čeká se"),
("Finished", "Dokončeno"),
("Speed", "Rychlost"),
("Custom Image Quality", "Uživatelsky určená kvalita obrazu"),
("Privacy mode", "Režim soukromí"),
("Block user input", "Blokovat vstupní zařízení uživatele"),
("Unblock user input", "Odblokovat vstupní zařízení uživatele"),
("Adjust Window", "Přizpůsobit velikost okna"),
("Original", "Původní"),
("Shrink", "Oříznout"),
("Stretch", "Roztáhnout"),
("Good image quality", "Dobrá kvalita obrazu"),
("Balanced", "Vyvážené"),
("Optimize reaction time", "Optimalizovat pro co nejnižší prodlevu odezvy"),
("Custom", "Uživatelsky určené"),
("Show remote cursor", "Zobrazovat ukazatel myši z protějšku"),
("Disable clipboard", "Vypnout schránku"),
("Lock after session end", "Po ukončení relace zamknout plochu"),
("Insert", "Vložit"),
("Insert Lock", "Vložit zámek"),
("Refresh", "Načíst znovu"),
("ID does not exist", "Takový identifikátor neexistuje"),
("Failed to connect to rendezvous server", "Nepodařil se připojit ke zprostředkovávajícímu serveru"),
("Please try later", "Zkuste to později"),
("Remote desktop is offline", "Vzdálená plocha není připojená ke službě"),
("Key mismatch", "Neshoda klíčů"),
("Timeout", "Překročen časový limit pro navázání spojení"),
("Failed to connect to relay server", "Nepodařilo se připojit k předávacímu (relay) serveru"),
("Failed to connect via rendezvous server", "Nepodařilo se připojit prostřednictvím zprostředkovávajícího serveru"),
("Failed to connect via relay server", "Nepodařilo se připojit prostřednictvím předávacímu (relay) serveru"),
("Failed to make direct connection to remote desktop", "Nepodařilo s navázat přímé připojení ke vzdálené ploše"),
("Set Password", "Nastavit heslo"),
("OS Password", "Heslo do operačního systému"),
("install_tip", "Kvůli řízení oprávnění v systému (UAC), RustDesk v některých případech na protějšku nefunguje správně. Abyste se UAC vyhnuli, klikněte na níže uvedené tlačítko a nainstalujte tak RustDesk do systému."),
("Click to upgrade", "Aktualizaci nainstalujete kliknutím"),
("Click to download", "Stáhnete si kliknutím"),
("Click to update", "Znovu načtete kliknutím"),
("Configure", "Nastavit"),
("config_acc", "Aby bylo možné na dálku ovládat vaši plochu, je třeba aplikaci RustDesk udělit oprávnění pro „Zpřístupnění pro hendikepované“."),
("config_screen", "Aby bylo možné přistupovat k vaší ploše na dálku, je třeba aplikaci RustDesk udělit oprávněí pro „Nahrávání obsahu obrazovky“."),
("Installing ...", "Instaluje se…"),
("Install", "Nainstalovat"),
("Installation", "Instalace"),
("Installation Path", "Popis umístění instalace"),
("Create start menu shortcuts", "Vytvořit zástupce v nabídce Start"),
("Create desktop icon", "Vytvořit ikonu na ploše"),
("agreement_tip", "Spuštěním instalace přijímáte licenční ujednání."),
("Accept and Install", "Přijmout a nainstalovat"),
("End-user license agreement", "Licencenční ujednání s koncovým uživatelem"),
("Generating ...", "Vytváření…"),
("Your installation is lower version.", "Máte nainstalovanou starší verzi"),
("not_close_tcp_tip", "Po dobu, po kterou tunel potřebujete, nezavírejte toto okno"),
("Listening ...", "Očekávní spojení…"),
("Remote Host", "Vzdálený stroj"),
("Remote Port", "Port na protějšku"),
("Action", "Akce"),
("Add", "Přidat"),
("Local Port", "Místní port"),
("setup_server_tip", "Rychlejší připojení získáte vytvořením si svého vlastního serveru"),
("Too short, at least 6 characters.", "Příliš krátké alespoň 6 znaků."),
("The confirmation is not identical.", "Kontrolní zadání se neshoduje."),
("Permissions", "Oprávnění"),
("Accept", "Přijmout"),
("Dismiss", "Zahodit"),
("Disconnect", "Odpojit"),
("Allow using keyboard and mouse", "Umožnit ovládání mé klávesnice a myši"),
("Allow using clipboard", "Umožnit používání schránky"),
("Allow hearing sound", "Umožnit slyšet můj zvuk"),
("Allow file copy and paste", "Povolit kopírování a vkládání souborů"),
("Connected", "Připojeno"),
("Direct and encrypted connection", "Přímé a šifrované spojení"),
("Relayed and encrypted connection", "Předávané (relay) a šifrované spojení"),
("Direct and unencrypted connection", "Přímé a nešifrované spojení"),
("Relayed and unencrypted connection", "Předávané (relay) a nešifrované spojení"),
("Enter Remote ID", "Zadejte identifikátor protějšku"),
("Enter your password", "Zadejte své heslo"),
("Logging in...", "Přihlašování se…"),
("Enable RDP session sharing", "Zapnout sdílení relace RDP protokolu"),
("Auto Login", "Automatické přihlášení"),
("Enable Direct IP Access", "Zapnout přímý přístup na IP adresu"),
("Rename", "Přejmenovat"),
("Space", "Mezera"),
("Create Desktop Shortcut", "Vytvořit zástupce na ploše"),
("Change Path", "Změnit umístění"),
("Create Folder", "Vytvořit složku"),
("Please enter the folder name", "Zadejte název pro složku"),
("Fix it", "Opravit to"),
("Warning", "Upozornení"),
("Login screen using Wayland is not supported", "Přihlašovací obrazovka prostřednictvím Wayland není podporována"),
("Reboot required", "Je třeba restartovat"),
("Unsupported display server ", "Nepodporovaný zobrazovací server"),
("x11 expected", "očekávány x11"),
("Port", "Číslo portu"),
("Settings", "Nastavení"),
("Username", "Uživatelské jméno"),
("Invalid port", "Neplatné číslo portu"),
("Closed manually by the peer", "Ručně ukončeno protějškem"),
("Enable remote configuration modification", "Umožnit upravování nastavení vzdáleného"),
("Run without install", "Spustit bez instalování"),
("Always connected via relay", "Vždy spojováno prostřednictvím brány pro předávání (relay)"),
("Always connect via relay", "Vždy se spojovat prostřednictvím brány pro předávání (relay)"),
("whitelist_tip", "Přístup je umožněn pouze z IP adres, nacházejících se na seznamu povolených"),
("Login", "Přihlásit se"),
("Logout", "Odhlásit se"),
("Tags", "Štítky"),
("Search ID", "Hledat identifikátor"),
("Current Wayland display server is not supported", "Zobrazovací server Wayland zatím není podporován"),
("whitelist_sep", "Odělováno čárkou, středníkem, mezerou nebo koncem řádku"),
("Add ID", "Přidat identifikátor"),
("Add Tag", "Přidat štítek"),
("Unselect all tags", "Zrušit výběr všech štítků"),
("Network error", "Chyba sítě"),
("Username missed", "Chybí uživatelské jméno"),
("Password missed", "Chybí heslo"),
("Wrong credentials", "Nesprávné přihlašovací údaje"),
("Edit Tag", "Upravit štítek"),
("Unremember Password", "Přestat si heslo pamatovat"),
("Favorites", "Oblíbené"),
("Add to Favorites", "Přidat do oblíbených"),
("Remove from Favorites", "Odebrat z oblíbených"),
("Empty", "Prázdné"),
("Invalid folder name", "Neplatný název složky"),
("Socks5 Proxy", "Socks5 proxy"),
("Hostname", "Název stroje"),
("Discovered", "Objeveno"),
("install_daemon_tip", "Pokud má být spouštěno při startu systému, je třeba nainstalovat systémovou službu."),
("Remote ID", "Identif. protějšku"),
("Paste", "Vložit"),
("Paste here?", "Vložit sem?"),
("Are you sure to close the connection?", "Opravdu chcete spojení ukončit?"),
("Download new version", "Stáhnout si novou verzi"),
("Touch mode", "Režim dotyku"),
("Mouse mode", "Režim myši"),
("One-Finger Tap", "Klepnutí jedním prstem"),
("Left Mouse", "Levé tlačítko myši"),
("One-Long Tap", "Jedno dlouhé klepnutí"),
("Two-Finger Tap", "Klepnutí dvěma prsty"),
("Right Mouse", "Pravé tlačítko myši"),
("One-Finger Move", "Přesouvání jedním prstem"),
("Double Tap & Move", "Dvojité klepnutí a přesun"),
("Mouse Drag", "Přetažení myší"),
("Three-Finger vertically", "Třemi prsty svisle"),
("Mouse Wheel", "Kolečko myši"),
("Two-Finger Move", "Posun dvěma prsty"),
("Canvas Move", "Posun zobrazení"),
("Pinch to Zoom", "Přiblížíte roztažením dvěma prsty"),
("Canvas Zoom", "Přiblížení zobrazení"),
("Reset canvas", "Vrátit měřtko zobrazení na výchozí"),
("No permission of file transfer", "Žádné oprávnění přenosu souboru"),
("Note", "Poznámka"),
("Connection", "Připojení"),
("Share Screen", "Nasdílet obrazovku"),
("CLOSE", "ZAVŘÍT"),
("OPEN", "OTEVŘÍT"),
("Chat", "Chat"),
("Total", "Celkem"),
("items", "Položek"),
("Selected", "Vybráno"),
("Screen Capture", "Zachytávání obrazovky"),
("Input Control", "Ovládání vstupních zařízení"),
("Audio Capture", "Zachytávání zvuku"),
("File Connection", "Souborové spojení"),
("Screen Connection", "Spojení obrazovky"),
("Do you accept?", "Přijímáte?"),
("Open System Setting", "Otevřít nastavení systému"),
("How to get Android input permission?", "Jak v systému Android získat oprávnění pro vstupní zařízení?"),
("android_input_permission_tip1", "Aby vzdálené zařízení mohlo ovládat vaše Android zařízení prostřednictví myši či dotyků, je třeba povolit, aby RustDesk mohlo používat službu „Zpřístupnění hendikepovaným“."),
("android_input_permission_tip2", "Přejděte na následující stránku nastavení systému, najděte a přejděte do [Nainstalované služby] a zapněte službu [RustDesk vstup]."),
("android_new_connection_tip", "Obdržen nový požadavek na řízení zařízení, který chce ovládat vaše stávající zařízení."),
("android_service_will_start_tip", "Zapnutí „Zachytávání obsahu obrazovky“ automaticky spustí službu, což umožní ostatním zařízením žádat o připojení k vašemu zařízení."),
("android_stop_service_tip", "Zastavení služby automaticky ukončí veškerá navázaná spojení."),
("android_version_audio_tip", "Vámi nyní používaná verze systému Android nepodporuje zachytávání zvuku přejděte na Android 10 nebo novější."),
("android_start_service_tip", "Službu pro sdílení obrazovky spustíte klepnutím na [Spustit službu] nebo UDĚLTE pověření pro [Zachytávání obsahu obrazovky]."),
("Account", "Účet"),
("Overwrite", "Přepsat"),
("This file exists, skip or overwrite this file?", "Tento soubor existuje přeskočit ho nebo přepsat?"),
("Quit", "Ukončit"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Nápověda"),
("Failed", "Nepodařilo se"),
("Succeeded", "Uspěl"),
("Someone turns on privacy mode, exit", "Někdo zapne režim soukromí, ukončete ho"),
("Unsupported", "Nepodporováno"),
("Peer denied", "Peer popřel"),
("Please install plugins", "Nainstalujte si prosím pluginy"),
("Peer exit", "Peer exit"),
("Failed to turn off", "Nepodařilo se vypnout"),
("Turned off", "Vypnutý"),
("In privacy mode", "v režimu soukromí"),
("Out privacy mode", "mimo režim soukromí"),
].iter().cloned().collect();
}

283
src/lang/da.rs Normal file
View File

@@ -0,0 +1,283 @@
lazy_static::lazy_static! {
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "Status"),
("Your Desktop", "dit skrivebord"),
("desk_tip", "Du kan få adgang til dit skrivebord med dette ID og adgangskode."),
("Password", "Kodeord"),
("Ready", "Klar"),
("Established", "Etableret"),
("connecting_status", "Opretter forbindelse til RustDesk-netværket..."),
("Enable Service", "Tænd forbindelsesserveren"),
("Start Service", "Starte forbindelsesserveren"),
("Service is running", "Tjenesten kører"),
("Service is not running", "Den tilknyttede tjeneste kører ikke"),
("not_ready_status", "Ikke klar. Tjek venligst din forbindelse"),
("Control Remote Desktop", "Styr fjernskrivebord"),
("Transfer File", "Overføre fil"),
("Connect", "Forbind"),
("Recent Sessions", "Sidste sessioner"),
("Address Book", "Adressebog"),
("Confirmation", "Bekræftelse"),
("TCP Tunneling", "TCP tunneling"),
("Remove", "Fjern"),
("Refresh random password", "Opdater tilfældig adgangskode"),
("Set your own password", "Indstil din egen adgangskode"),
("Enable Keyboard/Mouse", "Tænd for tastatur/mus"),
("Enable Clipboard", "Tænd for udklipsholderen"),
("Enable File Transfer", "Aktiver filoverførsel"),
("Enable TCP Tunneling", "Slå TCP-tunneling til"),
("IP Whitelisting", "IP-udgivelsesliste"),
("ID/Relay Server", "ID/forbindelsesserver"),
("Stop service", "Sluk for forbindelsesserveren"),
("Change ID", "Ændre ID"),
("Website", "Hjemmeside"),
("About", "Omkring"),
("Mute", "Sluk for mikrofonen"),
("Audio Input", "Lydindgang"),
("ID Server", "identifikations Server"),
("Relay Server", "Relæ Server"),
("API Server", "API Server"),
("invalid_http", "Skal begynde med http:// eller https://"),
("Invalid IP", "Ugyldig IP-adresse"),
("id_change_tip", "Kun tegnene a-z, A-Z, 0-9 og _ (understregning) er tilladt. Det første bogstav skal være a-z, A-Z. Længde mellem 6 og 16."),
("Invalid format", "Ugyldigt format"),
("server_not_support", "Endnu ikke understøttet af serveren"),
("Not available", "ikke Tilgængelig"),
("Too frequent", "For ofte"),
("Cancel", "Abort"),
("Skip", "Spring over"),
("Close", "Luk"),
("Retry", "Prøv igen"),
("OK", "OK"),
("Password Required", "Adgangskode kræves"),
("Please enter your password", "Indtast venligst dit kodeord"),
("Remember password", "Husk kodeord"),
("Wrong Password", "Forkert kodeord"),
("Do you want to enter again?", "Vil du forbinde igen?"),
("Connection Error", "Forbindelsesfejl"),
("Error", "fejl"),
("Reset by the peer", "Nulstil ved peer"),
("Connecting...", "Opretter forbindelse..."),
("Connection in progress. Please wait.", "Forbindelsen er etableret. Vent venligst."),
("Please try 1 minute later", "Prøv igen, 1 minut senere"),
("Login Error", "Login fejl"),
("Successful", "Vellykket"),
("Connected, waiting for image...", "Tilsluttet, venter på billede..."),
("Name", "Navn"),
("Type", "Type"),
("Modified", "Ændret"),
("Size", "Størrelse"),
("Show Hidden Files", "Vis skjulte filer"),
("Receive", "Modtag"),
("Send", "Send"),
("Refresh File", "Genopfrisk fil"),
("Local", "Lokalt"),
("Remote", "Remote"),
("Remote Computer", "Fjern computer"),
("Local Computer", "Lokal Computer"),
("Confirm Delete", "Bekræft sletning"),
("Delete", "Slet"),
("Properties", "Egenskaber"),
("Multi Select", "Flere valg"),
("Empty Directory", "Tom bibliotek"),
("Not an empty directory", "Intet tomt bibliotek"),
("Are you sure you want to delete this file?", "Er du sikker på, at du vil slette denne fil?"),
("Are you sure you want to delete this empty directory?", "Er du sikker på, at du vil slette dette tomme bibliotek?"),
("Are you sure you want to delete the file of this directory?", "Er du sikker på, at du vil slette filen til dette bibliotek?"),
("Do this for all conflicts", "Gør dette for alle konflikter"),
("This is irreversible!", "Dette er irreversibelt!"),
("Deleting", "Sletter"),
("files", "Filer"),
("Waiting", "Venter"),
("Finished", "Færdig"),
("Speed", "hastighed"),
("Custom Image Quality", "Individuel billedkvalitet"),
("Privacy mode", "Databeskyttelsestilstand (Privatlivstilstand)"),
("Block user input", "Bloker brugerinput"),
("Unblock user input", "Fjern blokering af brugerinput"),
("Adjust Window", "Juster vinduet"),
("Original", "Original"),
("Shrink", "Krymp"),
("Stretch", "Strak"),
("Good image quality", "God billedkvalitet"),
("Balanced", "Afbalanceret"),
("Optimize reaction time", "Optimeret responstid"),
("Custom", "Brugerdefineret"),
("Show remote cursor", "Vis fjernbetjeningskontrolleret markør"),
("Disable clipboard", "Deaktiver udklipsholder"),
("Lock after session end", "Lås efter afslutningen af fjernstyring"),
("Insert", "Indsæt"),
("Insert Lock", "Indsæt lås"),
("Refresh", "Genopfrisk"),
("ID does not exist", "ID findes ikke"),
("Failed to connect to rendezvous server", "Forbindelse til forbindelsesserveren mislykkedes"),
("Please try later", "Prøv det senere"),
("Remote desktop is offline", "Fjernet desktop er offline"),
("Key mismatch", "Nøgle uoverensstemmelse"),
("Timeout", "Timeout"),
("Failed to connect to relay server", "Forbindelse til relæ-serveren mislykkedes"),
("Failed to connect via rendezvous server", "Forbindelse via Rendezvous-server mislykkedes"),
("Failed to connect via relay server", "Forbindelse via relæ-serveren mislykkedes"),
("Failed to make direct connection to remote desktop", "Direkte forbindelse til fjernskrivebord kunne ikke etableres"),
("Set Password", "Indstil adgangskode"),
("OS Password", "Operativsystemadgangskode"),
("install_tip", "På grund af UAC kan Rustdesk ikke fungere korrekt på den anden side i nogle tilfælde. For at undgå UAC skal du klikke på knappen nedenfor for at installere Rustdesk på systemet"),
("Click to upgrade", "Klik for at opgradere"),
("Click to download", "Klik for at downloade"),
("Click to update", "Klik for at opdatere"),
("Configure", "Konfigurer"),
("config_acc", "For at kontrollere dit skrivebord på afstand skal du give Rustdesk \"Access \" Rettigheder."),
("config_screen", "For at kunne få adgang til dit skrivebord langtfra, skal du give Rustdesk \"skærmstøtte \" tilladelser."),
("Installing ...", "Installere ..."),
("Install", "installere"),
("Installation", "Installation"),
("Installation Path", "Installationsti"),
("Create start menu shortcuts", "Opret startmenu links"),
("Create desktop icon", "Opret skrivebords-symbol"),
("agreement_tip", "Hvis du starter installationen, skal du acceptere licensaftalen"),
("Accept and Install", "Accepter og installer"),
("End-user license agreement", "Licensaftale for slutbrugere"),
("Generating ...", "Generer kode ..."),
("Your installation is lower version.", "Din installation er en lavere version."),
("not_close_tcp_tip", "Luk ikke dette vindue, mens du bruger tunnelen."),
("Listening ...", "Lytter ..."),
("Remote Host", "Fjern-Host"),
("Remote Port", "Fjern-Port"),
("Action", "Рandling"),
("Add", "Tilføj"),
("Local Port", "Lokal Port"),
("setup_server_tip", "For en hurtigere forbindelse skal du indstille din egen forbindelsesserver"),
("Too short, at least 6 characters.", "For kort, mindst 6 tegn."),
("The confirmation is not identical.", "Bekræftelsen er ikke identisk."),
("Permissions", "Tilladelser"),
("Accept", "Acceptere"),
("Dismiss", "Afvise"),
("Disconnect", "Frakobl"),
("Allow using keyboard and mouse", "Tillad brug af tastatur og mus"),
("Allow using clipboard", "Tillad brug af udklipsholderen"),
("Allow hearing sound", "Tillader hørelse fra lyd"),
("Allow file copy and paste", "Tillad fil kopiering og indsættelse"),
("Connected", "Forbundet"),
("Direct and encrypted connection", "Direkte og krypteret forbindelse"),
("Relayed and encrypted connection", "Brugt relæet og krypteret forbindelse"),
("Direct and unencrypted connection", "Direkte og ukrypteret forbindelse"),
("Relayed and unencrypted connection", "Brugt relæet og ukrypteret forbindelse"),
("Enter Remote ID", "Indtast Remote-ID"),
("Enter your password", "Skriv dit kodeord"),
("Logging in...", "Logger ind..."),
("Enable RDP session sharing", "RDP-Aktivér sessiongodkendelse"),
("Auto Login", "Automatisk login (kun gyldigt hvis du har konfigureret \"Lock efter afslutningen af sessionen\")"),
("Enable Direct IP Access", "Aktivér direkte IP-adgang"),
("Rename", "Omdøb"),
("Space", "Plads"),
("Create Desktop Shortcut", "Opret skrivebords-genvej"),
("Change Path", "Skift stien"),
("Create Folder", "Opret mappe"),
("Please enter the folder name", "Indtast venligst mappenavnet"),
("Fix it", "Kør reparation"),
("Warning", "Advarsel"),
("Login screen using Wayland is not supported", "Registreringsskærm med Wayland understøttes ikke"),
("Reboot required", "Genstart krævet"),
("Unsupported display server ", "Ikke-understøttet displayserver"),
("x11 expected", "X11 Forventet"),
("Port", "Port"),
("Settings", "Indstillinger"),
("Username", " Brugernavn"),
("Invalid port", "Ugyldig port"),
("Closed manually by the peer", "Manuelt lukket af peer"),
("Enable remote configuration modification", "Tillad at ændre afstandskonfigurationen"),
("Run without install", "Kør uden installation"),
("Always connected via relay", "Tilslut altid via relæ-server"),
("Always connect via relay", "Forbindelse via relæ-server"),
("whitelist_tip", "Kun IP'er på udgivelseslisten kan få adgang til mig"),
("Login", "Login"),
("Logout", "logger af"),
("Tags", "Nøgleord"),
("Search ID", "Søg ID"),
("Current Wayland display server is not supported", "Den aktuelle Wayland-Anzege-server understøttes ikke"),
("whitelist_sep", "Adskilt af komma, semikolon, rum eller linjepaus"),
("Add ID", "Tilføj ID"),
("Add Tag", "Tilføj nøgleord"),
("Unselect all tags", "Fravælg alle nøgleord"),
("Network error", "Netværksfejl"),
("Username missed", "Benutzername fehlt"),
("Password missed", "Glemt kodeord"),
("Wrong credentials", "Forkerte registreringsdata"),
("Edit Tag", "Rediger nøgleord"),
("Unremember Password", "Bemærk ikke adgangskoden"),
("Favorites", "Favorit"),
("Add to Favorites", "Tilføj til favoritter"),
("Remove from Favorites", "Fjern favoritter"),
("Empty", "Tom"),
("Invalid folder name", "Ugyldigt mappenavn"),
("Socks5 Proxy", "Socks5 Proxy"),
("Hostname", "Computernavn"),
("Discovered", "Fundet"),
("install_daemon_tip", "Til at begynde med opstart, skal du installere systemtjenesten"),
("Remote ID", "Fjern ID"),
("Paste", "Indsæt"),
("Paste here?", "Indsæt her?"),
("Are you sure to close the connection?", "Sind Sie sicher, dass Sie die Verbindung schließen wollen?"),
("Download new version", "Neue Version herunterladen"),
("Touch mode", "Touch-tilstand"),
("Mouse mode", "Musse-tilstand"),
("One-Finger Tap", "En fingerspids-tap"),
("Left Mouse", "Venstre mus"),
("One-Long Tap", "Tryk med en finger lang"),
("Two-Finger Tap", "Tryk med to fingre-tap"),
("Right Mouse", "Højre mus"),
("One-Finger Move", "En fingerbevægelse"),
("Double Tap & Move", "Dobbelt og flytte"),
("Mouse Drag", "Mus"),
("Three-Finger vertically", "Tre fingre lodret"),
("Mouse Wheel", "Mussehjul"),
("Two-Finger Move", "To fingreflytning"),
("Canvas Move", "Flyt lærred"),
("Pinch to Zoom", "Zoom ind"),
("Canvas Zoom", "Lærred zoom"),
("Reset canvas", "Nulstil skærm"),
("No permission of file transfer", "Ingen tilladelse til at overføre filen"),
("Note", "Note"),
("Connection", "Forbindelse"),
("Share Screen", "Del skærmen"),
("CLOSE", "LUK"),
("OPEN", "ÅBEN"),
("Chat", "Chat"),
("Total", "Total"),
("items", "artikel"),
("Selected", "Valgte"),
("Screen Capture", "Skærmoptagelse"),
("Input Control", "Inputkontrol"),
("Audio Capture", "Lydoptagelse"),
("File Connection", "Filforbindelse"),
("Screen Connection", "Færdiggørelse"),
("Do you accept?", "Accepterer du?"),
("Open System Setting", "Åbn systemindstillingen"),
("How to get Android input permission?", "Hvordan får jeg en Android-input tilladelse?"),
("android_input_permission_tip1", "For at en ekstern enhed kan kontrollere din Android-enhed via mus eller berøring, skal du give Rustdesk mulighed for at bruge tjenesten \"tilgængelighed \"."),
("android_input_permission_tip2", "Gå til den næste systemindstillingsside, søg og indtast [installerede tjenester], tænd for [Rustdesk Input] Service."),
("android_new_connection_tip", "En ny kontrolanmodning blev modtaget, der gerne ville kontrollere din nuværende enhed."),
("android_service_will_start_tip", "Ved at tænde for skærmoptagelsen startes tjenesten automatisk, så andre enheder kan anmode om en forbindelse fra denne enhed."),
("android_stop_service_tip", "Ved at lukke tjenesten lukkes alle fremstillede forbindelser automatisk."),
("android_version_audio_tip", "Den aktuelle Android -version understøtter ikke lydoptagelse, skal du opdatere om Android 10 eller højere."),
("android_start_service_tip", "Tryk på [Start Service] eller åbn autorisationen [skærmoptagelse] for at starte skærmudgivelsen."),
("Account", "Konto"),
("Overwrite", "Overskriv"),
("This file exists, skip or overwrite this file?", "Denne fil findes, springer over denne fil eller overskriver?"),
("Quit", "Afslut"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Hjælp"),
("Failed", "Mislykkedet"),
("Succeeded", "Vellykket"),
("Someone turns on privacy mode, exit", "Nogen aktiverede databeskyttelsestilstand, slut"),
("Unsupported", "Ikke understøttet"),
("Peer denied", "Peer nægtet"),
("Please install plugins", "Venligst Installer plugins"),
("Peer exit", "Peer-Afslut"),
("Failed to turn off", "Slukke"),
("Turned off", "Slukket"),
("In privacy mode", "I databeskyttelsestilstand"),
("Out privacy mode", "Databeskyttelsestilstand fra"),
].iter().cloned().collect();
}

View File

@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Erlaubt die Verwendung von Tastatur und Maus"),
("Allow using clipboard", "Verwendung der Zwischenablage zulassen"),
("Allow hearing sound", "Erlaubt das Hören von Sound"),
("Allow file transfer", "Dateiübertragung zulassen"),
("File transfer", "Datei Übertragung"),
("Allow file copy and paste", "Kopieren und Einfügen von Dateien zulassen"),
("Connected", "Verbunden"),
("Direct and encrypted connection", "Direkte und verschlüsselte Verbindung"),
("Relayed and encrypted connection", "Vermittelte und verschlüsselte Verbindung"),
@@ -250,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", "Ausgewählt"),
("Screen Capture", "Bildschirmaufnahme"),
("Input Control", "Eingabesteuerung"),
("File Transfer", "Datei Übertragung"),
("Audio Capture", "Audioaufnahme"),
("File Connection", "Dateiverbindung"),
("Screen Connection", "Bildschirmanschluss"),
@@ -265,8 +263,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", "Die aktuelle Android-Version unterstützt keine Audioaufnahme, bitte aktualisieren Sie auf Android 10 oder höher."),
("android_start_service_tip", "Tippen Sie auf [Dienst starten] oder ÖFFNEN Sie die Berechtigung [Bildschirmaufnahme], um den Bildschirmfreigabedienst zu starten."),
("Account", "Konto"),
("Quit", "Ausgang"),
("Overwrite", "Überschreiben"),
("This file exists, skip or overwrite this file?", "Diese Datei existiert, diese Datei überspringen oder überschreiben?"),
("Quit", "Aufhören"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Hilfe"),
("Failed", "Gescheitert"),
("Succeeded", "Erfolgreich"),
("Someone turns on privacy mode, exit", "Jemand aktiviert den Datenschutzmodus, beenden"),
("Unsupported", "Nicht unterstützt"),
("Peer denied", "Peer verweigert"),
("Please install plugins", "Bitte installieren Sie Plugins"),
("Peer exit", "Peer-Ausgang"),
("Failed to turn off", "Ausschalten fehlgeschlagen"),
("Turned off", "Ausgeschaltet"),
("In privacy mode", "im Datenschutzmodus"),
("Out privacy mode", "Datenschutzmodus aus"),
].iter().cloned().collect();
}

View File

@@ -42,6 +42,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Invalid IP", "IP nevalida"),
("id_change_tip", "Nur la signoj a-z, A-Z, 0-9, _ (substreko) povas esti uzataj. La unua litero povas esti inter a-z, A-Z. La longeco devas esti inter 6 kaj 16."),
("Invalid format", "Formato nevalida"),
("server_not_support", "Ankoraŭ ne subtenata de la servilo"),
("Not available", "Nedisponebla"),
("Too frequent", "Tro ofte ŝanĝita, bonvolu reprovi poste"),
("Cancel", "Nuligi"),
@@ -156,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Permesi la uzon de la klavaro kaj muso"),
("Allow using clipboard", "Permesi la uzon de la poŝo"),
("Allow hearing sound", "Permesi la uzon de la sono"),
("Allow file transfer", "Permesi la dosiertransigadon"),
("File transfer", "Dosiertransigado"),
("Allow file copy and paste", "Permesu kopii kaj alglui dosierojn"),
("Connected", "Konektata"),
("Direct and encrypted connection", "Konekcio direkta ĉifrata"),
("Relayed and encrypted connection", "Konekcio relajsa ĉifrata"),
@@ -249,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", ""),
("Screen Capture", ""),
("Input Control", ""),
("File Transfer", ""),
("Audio Capture", ""),
("File Connection", ""),
("Screen Connection", ""),
@@ -264,8 +263,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", ""),
("android_start_service_tip", ""),
("Account", ""),
("Overwrite", ""),
("This file exists, skip or overwrite this file?", ""),
("Quit", ""),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", ""),
("Failed", ""),
("Succeeded", ""),
("Someone turns on privacy mode, exit", ""),
("Unsupported", ""),
("Peer denied", ""),
("Please install plugins", ""),
("Peer exit", ""),
("Failed to turn off", ""),
("Turned off", ""),
("In privacy mode", ""),
("Out privacy mode", ""),
].iter().cloned().collect();
}

283
src/lang/es.rs Normal file
View File

@@ -0,0 +1,283 @@
lazy_static::lazy_static! {
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "Estado"),
("Your Desktop", "Tu escritorio"),
("desk_tip", "Puoi accedere al tuo desktop usando l'ID e la password riportati qui."),
("Password", "Contraseña"),
("Ready", "Listo"),
("Established", "Establecido"),
("connecting_status", "Conexión a la red RustDesk en progreso..."),
("Enable Service", "Habilitar Servicio"),
("Start Service", "Iniciar Servicio"),
("Service is running", "Servicio se está ejecutando"),
("Service is not running", "Servicio no se está ejecutando"),
("not_ready_status", "No está listo. Comprueba tu conexión"),
("Control Remote Desktop", "Controlar Escritorio Remoto"),
("Transfer File", "Transferir archivo"),
("Connect", "Conectar"),
("Recent Sessions", "Sesiones recientes"),
("Address Book", "Directorio"),
("Confirmation", "Confirmación"),
("TCP Tunneling", "Tunel TCP"),
("Remove", "Remover"),
("Refresh random password", "Actualizar contraseña aleatoria"),
("Set your own password", "Establece tu propia contraseña"),
("Enable Keyboard/Mouse", "Habilitar teclado/ratón"),
("Enable Clipboard", "Habilitar portapapeles"),
("Enable File Transfer", "Habilitar transferencia de archivos"),
("Enable TCP Tunneling", "Habilitar tunel TCP"),
("IP Whitelisting", "Lista blanca IP"),
("ID/Relay Server", "Servidor de ID/Relay"),
("Stop service", "Parar servicio"),
("Change ID", "Cambiar identificación"),
("Website", "Sitio web"),
("About", "Sobre"),
("Mute", "Silencio"),
("Audio Input", "Entrada de audio"),
("ID Server", "ID server"),
("Relay Server", "Server relay"),
("API Server", "Server API"),
("invalid_http", "debe comenzar con http:// o https://"),
("Invalid IP", "IP inválida"),
("id_change_tip", "Solo puedes usar caracteres a-z, A-Z, 0-9 e _ (guion bajo). El primer carácter debe ser a-z o A-Z. La longitud debe estar entre 6 a 16 caracteres."),
("Invalid format", "Formato inválido"),
("server_not_support", "Aún no es compatible con el servidor"),
("Not available", "Indisponible"),
("Too frequent", "Demasiado frecuente"),
("Cancel", "Cancelar"),
("Skip", "Saltar"),
("Close", "Cerrar"),
("Retry", "Volver"),
("OK", "OK"),
("Password Required", "Se requiere contraseña"),
("Please enter your password", "Por favor, introduzca su contraseña"),
("Remember password", "Recordar contraseña"),
("Wrong Password", "Contraseña incorrecta"),
("Do you want to enter again?", "Quieres volver a entrar?"),
("Connection Error", "Error de conexión"),
("Error", "Error"),
("Reset by the peer", "Restablecido por el par"),
("Connecting...", "Conectando..."),
("Connection in progress. Please wait.", "Conexión en curso. Espere por favor."),
("Please try 1 minute later", "Intente 1 minuto más tarde"),
("Login Error", "Error de inicio de sesión"),
("Successful", "Exitoso"),
("Connected, waiting for image...", "Conectado, esperando imagen..."),
("Name", "Nombre"),
("Type", "Tipo"),
("Modified", "Modificado"),
("Size", "Tamaño"),
("Show Hidden Files", "Mostrar archivos ocultos"),
("Receive", "Recibir"),
("Send", "Enviar"),
("Refresh File", "Actualizar archivo"),
("Local", "Local"),
("Remote", "Remoto"),
("Remote Computer", "Computadora remota"),
("Local Computer", "Computadora local"),
("Confirm Delete", "Confirmar eliminación"),
("Delete", "Borrar"),
("Properties", "Propiedades"),
("Multi Select", "Selección múltiple"),
("Empty Directory", "Directorio vacío"),
("Not an empty directory", "No es un directorio vacío"),
("Are you sure you want to delete this file?", "Estás seguro de que quieres eliminar este archivo?"),
("Are you sure you want to delete this empty directory?", "Está seguro de que desea eliminar este directorio vacío?"),
("Are you sure you want to delete the file of this directory?", "Está seguro de que desea eliminar el archivo de este directorio?"),
("Do this for all conflicts", "Haga esto para todos los conflictos"),
("This is irreversible!", "Esto es irreversible!"),
("Deleting", "Borrando"),
("files", "archivos"),
("Waiting", "Esperando"),
("Finished", "Acabado"),
("Speed", "Velocidad"),
("Custom Image Quality", "Calidad de imagen personalizada"),
("Privacy mode", "Modo privado"),
("Block user input", "Bloquear entrada de usuario"),
("Unblock user input", "Desbloquear entrada de usuario"),
("Adjust Window", "Ajustar ventana"),
("Original", "Original"),
("Shrink", "Encogerse"),
("Stretch", "Estirar"),
("Good image quality", "Buena calidad de imagen"),
("Balanced", "Equilibrado"),
("Optimize reaction time", "Optimizar el tiempo de reacción"),
("Custom", "Personalizado"),
("Show remote cursor", "Mostrar cursor remoto"),
("Disable clipboard", "Deshabilitar portapapeles"),
("Lock after session end", "Bloquear después del final de la sesión"),
("Insert", "Insertar"),
("Insert Lock", "Insertar bloqueo"),
("Refresh", "Actualizar"),
("ID does not exist", "ID no existe"),
("Failed to connect to rendezvous server", "No se pudo conectar al servidor de encuentro"),
("Please try later", "Por favor intente mas tarde"),
("Remote desktop is offline", "El escritorio remoto está fuera de línea"),
("Key mismatch", "La clave no coincide"),
("Timeout", "Timeout"),
("Failed to connect to relay server", "No se pudo conectar al servidor de retransmisión"),
("Failed to connect via rendezvous server", "No se pudo conectar a través del servidor de encuentro"),
("Failed to connect via relay server", "No se pudo conectar a través del servidor de retransmisión"),
("Failed to make direct connection to remote desktop", "No se pudo establecer la conexión directa con el escritorio remoto"),
("Set Password", "Configurar la clave"),
("OS Password", "Contraseña del sistema operativo"),
("install_tip", "Debido al Control de cuentas de usuario, es posible que RustDesk no funcione correctamente como escritorio remoto. Para evitar este problema, haga clic en el botón de abajo para instalar RustDesk a nivel de sistema."),
("Click to upgrade", "Clic para actualizar"),
("Click to download", "Clic para descargar"),
("Click to update", "Fare clic per aggiornare"),
("Configure", "Configurar"),
("config_acc", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Accesibilidad\"."),
("config_screen", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Grabación de pantalla\"."),
("Installing ...", "Instalando ..."),
("Install", "Instalar"),
("Installation", "Instalación"),
("Installation Path", "Ruta de instalación"),
("Create start menu shortcuts", "Crear accesos directos al menú de inicio"),
("Create desktop icon", "Crear icono de escritorio"),
("agreement_tip", "Al iniciar la instalación, acepta los términos del acuerdo de licencia."),
("Accept and Install", "Aceptar e instalar"),
("End-user license agreement", "Acuerdo de licencia de usuario final"),
("Generating ...", "Generando ..."),
("Your installation is lower version.", "Su instalación es una versión inferior."),
("not_close_tcp_tip", "No cierre esta ventana mientras esté usando el túnel"),
("Listening ...", "Escuchando ..."),
("Remote Host", "Servidor remoto"),
("Remote Port", "Puerto remoto"),
("Action", "Acción"),
("Add", "Agregar"),
("Local Port", "Puerto local"),
("setup_server_tip", "Para una conexión más rápida, configure su propio servidor"),
("Too short, at least 6 characters.", "Demasiado corto, al menos 6 caracteres."),
("The confirmation is not identical.", "La confirmación no es idéntica."),
("Permissions", "Permisos"),
("Accept", "Aceptar"),
("Dismiss", "Cancelar"),
("Disconnect", "Desconectar"),
("Allow using keyboard and mouse", "Permitir el uso del teclado y el mouse"),
("Allow using clipboard", "Permitir usar portapapeles"),
("Allow hearing sound", "Permitir escuchar sonido"),
("Allow file copy and paste", "Permitir copiar y pegar archivos"),
("Connected", "Conectado"),
("Direct and encrypted connection", "Conexión directa y encriptada"),
("Relayed and encrypted connection", "Conexión retransmitida y cifrada"),
("Direct and unencrypted connection", "Conexión directa y sin cifrar"),
("Relayed and unencrypted connection", "Conexión retransmitida y sin cifrar"),
("Enter Remote ID", "Ingrese el ID remoto"),
("Enter your password", "Ingrese su contraseña"),
("Logging in...", "Iniciando sesión..."),
("Enable RDP session sharing", "Habilitar el uso compartido de sesiones RDP"),
("Auto Login", "Ingreso automático"),
("Enable Direct IP Access", "Habilitar acceso IP directo"),
("Rename", "Renombrar"),
("Space", "Espacio"),
("Create Desktop Shortcut", "Crear acceso directo del escritorio"),
("Change Path", "Cambiar ruta"),
("Create Folder", "Crear carpeta"),
("Please enter the folder name", "Por favor ingrese el nombre de la carpeta"),
("Fix it", "Resolver"),
("Warning", "Aviso"),
("Login screen using Wayland is not supported", "La pantalla de inicio de sesión con Wayland no es compatible"),
("Reboot required", "Reinicio requerido"),
("Unsupported display server ", "Servidor de visualización no compatible"),
("x11 expected", "x11 necesario"),
("Port", "Puerto"),
("Settings", "Ajustes"),
("Username", " Nombre de usuario"),
("Invalid port", "Puerto inválido"),
("Closed manually by the peer", "Cerrado manualmente por el par"),
("Enable remote configuration modification", "Habilitar modificación de configuración remota"),
("Run without install", "Ejecutar sin instalar"),
("Always connected via relay", "Siempre conectado a través de relay"),
("Always connect via relay", "Conéctese siempre a través de relay"),
("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"),
("Login", "Iniciar sesión"),
("Logout", "Salir"),
("Tags", "Tags"),
("Search ID", "Buscar ID"),
("Current Wayland display server is not supported", "El servidor de visualización actual de Wayland no es compatible"),
("whitelist_sep", "Separados por coma, punto y coma, espacio o nueva línea"),
("Add ID", "Agregar ID"),
("Add Tag", "Agregar tag"),
("Unselect all tags", "Deseleccionar todos los tags"),
("Network error", "Error de red"),
("Username missed", "Olvidó su nombre de usuario"),
("Password missed", "Olvidó su contraseña"),
("Wrong credentials", "Credenciales incorrectas"),
("Edit Tag", "Editar tag"),
("Unremember Password", "Olvidaste tu contraseña"),
("Favorites", "Favoritos"),
("Add to Favorites", "Agregar a favoritos"),
("Remove from Favorites", "Quitar de favoritos"),
("Empty", "Vacío"),
("Invalid folder name", "Nombre de carpeta no válido"),
("Socks5 Proxy", "Proxy Socks5"),
("Hostname", "Nombre de host"),
("Discovered", "Descubierto"),
("install_daemon_tip", "Para comenzar en el encendido, debe instalar el servicio del sistema."),
("Remote ID", "ID remoto"),
("Paste", "Pegar"),
("Paste here?", "Pegar aqui?"),
("Are you sure to close the connection?", "Estás seguro de cerrar la conexión?"),
("Download new version", "Descargar nueva versión"),
("Touch mode", "Modo táctil"),
("Mouse mode", "Modo ratón"),
("One-Finger Tap", "Toque con un dedo"),
("Left Mouse", "Ratón izquierdo"),
("One-Long Tap", "Un toque largo"),
("Two-Finger Tap", "Toque con dos dedos"),
("Right Mouse", "Botón derecho"),
("One-Finger Move", "Movimiento con un dedo"),
("Double Tap & Move", "Toca dos veces y mueve"),
("Mouse Drag", "Arrastre de ratón"),
("Three-Finger vertically", "Tres dedos verticalmente"),
("Mouse Wheel", "Rueda de ratón"),
("Two-Finger Move", "Movimiento con dos dedos"),
("Canvas Move", "Movimiento de lienzo"),
("Pinch to Zoom", "Pellizcar para ampliar"),
("Canvas Zoom", "Ampliar lienzo"),
("Reset canvas", "Restablecer lienzo"),
("No permission of file transfer", "Sin permiso de transferencia de archivos"),
("Note", "Nota"),
("Connection", "Conexión"),
("Share Screen", "Compartir pantalla"),
("CLOSE", "CERRAR"),
("OPEN", "ABRIR"),
("Chat", "Chat"),
("Total", "Total"),
("items", "items"),
("Selected", "Seleccionado"),
("Screen Capture", "Captura de pantalla"),
("Input Control", "Control de entrada"),
("Audio Capture", "Captura de audio"),
("File Connection", "Conexión de archivos"),
("Screen Connection", "Conexión de pantalla"),
("Do you accept?", "Aceptas?"),
("Open System Setting", "Configuración del sistema abierto"),
("How to get Android input permission?", "Cómo obtener el permiso de entrada de Android?"),
("android_input_permission_tip1", "Para que un dispositivo remoto controle su dispositivo Android a través del mouse o toque, debe permitir que RustDesk use el servicio de \"Accesibilidad\"."),
("android_input_permission_tip2", "Vaya a la página de configuración del sistema que se abrirá a continuación, busque y acceda a [Servicios instalados], active el servicio [RustDesk Input]."),
("android_new_connection_tip", "Se recibió una nueva solicitud de control para el dispositivo actual."),
("android_service_will_start_tip", "Habilitar la captura de pantalla iniciará automáticamente el servicio, lo que permitirá que otros dispositivos soliciten una conexión desde este dispositivo."),
("android_stop_service_tip", "Cerrar el servicio cerrará automáticamente todas las conexiones establecidas."),
("android_version_audio_tip", "La versión actual de Android no admite la captura de audio, actualice a Android 10 o posterior."),
("android_start_service_tip", "Toque el permiso [Iniciar servicio] o ABRIR [Captura de pantalla] para iniciar el servicio de uso compartido de pantalla."),
("Account", "Cuenta"),
("Overwrite", "Sobrescribir"),
("This file exists, skip or overwrite this file?", "Este archivo existe, ¿omitir o sobrescribir este archivo?"),
("Quit", "Salir"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Ayuda"),
("Failed", "Fallido"),
("Succeeded", "Logrado"),
("Someone turns on privacy mode, exit", "Alguien active el modo privacidad, salga"),
("Unsupported", "No soportado"),
("Peer denied", "Par negado"),
("Please install plugins", "Instale complementos"),
("Peer exit", "Par salio"),
("Failed to turn off", "Error al apagar"),
("Turned off", "Apagado"),
("In privacy mode", "En modo de privacidad"),
("Out privacy mode", "Fuera del modo de privacidad"),
].iter().cloned().collect();
}

View File

@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Autoriser l'utilisation du clavier et de la souris"),
("Allow using clipboard", "Autoriser l'utilisation du presse-papier"),
("Allow hearing sound", "Autoriser l'audition du son"),
("Allow file transfer", "Autoriser le transfert de fichiers"),
("File transfer", "Transfert de fichiers"),
("Allow file copy and paste", "Autoriser le copier-coller de fichiers"),
("Connected", "Connecté"),
("Direct and encrypted connection", "Connexion directe chiffrée"),
("Relayed and encrypted connection", "Connexion relais chiffrée"),
@@ -250,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", "Choisi"),
("Screen Capture", "Capture d'écran"),
("Input Control", "Contrôle de saisie"),
("File Transfer", "Transfert de fichier"),
("Audio Capture", "Capture audio"),
("File Connection", "Connexion de fichier"),
("Screen Connection", "Connexion de l'écran"),
@@ -265,8 +263,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", "La version actuelle d'Android ne prend pas en charge la capture audio, veuillez passer à Android 10 ou supérieur."),
("android_start_service_tip", "Appuyez sur [Démarrer le service] ou sur l'autorisation OUVRIR [Capture d'écran] pour démarrer le service de partage d'écran."),
("Account", "Compte"),
("Overwrite", "Écraser"),
("This file exists, skip or overwrite this file?", "Ce fichier existe, ignorer ou écraser ce fichier ?"),
("Quit", "Quitter"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Aider"),
("Failed", "échouer"),
("Succeeded", "Succès"),
("Someone turns on privacy mode, exit", "Quelqu'un active le mode de confidentialité, quittez"),
("Unsupported", "Non pris en charge"),
("Peer denied", "Pair refusé"),
("Please install plugins", "Veuillez installer les plugins"),
("Peer exit", "Sortie des pairs"),
("Failed to turn off", "Échec de la désactivation"),
("Turned off", "Éteindre"),
("In privacy mode", "en mode privé"),
("Out privacy mode", "hors mode de confidentialité"),
].iter().cloned().collect();
}

View File

@@ -157,7 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Izinkan menggunakan keyboard dan mouse"),
("Allow using clipboard", "Izinkan menggunakan papan klip"),
("Allow hearing sound", "Izinkan mendengarkan suara"),
("Allow file transfer", "Izinkan transfer file"),
("Allow file copy and paste", "Izinkan penyalinan dan tempel file"),
("File transfer", "Transfer file"),
("Connected", "Terkoneksi"),
("Direct and encrypted connection", "Koneksi langsung dan terenkripsi"),
@@ -250,7 +250,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", "Dipilih"),
("Screen Capture", "Rekam Layar"),
("Input Control", "kontrol input"),
("File Transfer", "Transfer File"),
("Audio Capture", "Rekam Suara"),
("File Connection", "Koneksi File"),
("Screen Connection", "koneksi layar"),
@@ -265,8 +264,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", "Versi Android saat ini tidak mendukung pengambilan audio, harap tingkatkan ke Android 10 atau lebih tinggi."),
("android_start_service_tip", "Ketuk izin [Mulai Layanan] atau BUKA [Tangkapan Layar] untuk memulai layanan berbagi layar."),
("Account", "Akun"),
("Overwrite", "Timpa"),
("This file exists, skip or overwrite this file?", ""),
("Quit", "Keluar"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Bantuan"),
("Failed", "Gagal"),
("Succeeded", "Berhasil"),
("Someone turns on privacy mode, exit", "Seseorang mengaktifkan mode privasi, keluar"),
("Unsupported", "Tidak didukung"),
("Peer denied", "Rekan ditolak"),
("Please install plugins", "Silakan instal plugin"),
("Peer exit", "keluar rekan"),
("Failed to turn off", "Gagal mematikan"),
("Turned off", "Matikan"),
("In privacy mode", "Dalam mode privasi"),
("Out privacy mode", "Keluar dari mode privasi"),
].iter().cloned().collect();
}

View File

@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Consenti l'uso di tastiera e mouse"),
("Allow using clipboard", "Consenti l'uso degli appunti"),
("Allow hearing sound", "Consenti la riproduzione dell'audio"),
("Allow file transfer", "Consenti trasferimento file"),
("File transfer", "Trasferimento di file"),
("Allow file copy and paste", "Consenti copia e incolla di file"),
("Connected", "Connesso"),
("Direct and encrypted connection", "Connessione diretta e cifrata"),
("Relayed and encrypted connection", "Connessione tramite relay e cifrata"),
@@ -206,20 +205,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Password missed", "Password dimenticata"),
("Wrong credentials", "Credenziali errate"),
("Edit Tag", "Modifica tag"),
("Unremember Password", "Non ricordare la password"),
("Unremember Password", "Dimentica password"),
("Favorites", "Preferiti"),
("Add to Favorites", "Aggiungi ai preferiti"),
("Remove from Favorites", "Rimuovi dai preferiti"),
("Empty", "Vuoto"),
("Invalid folder name", "Nome della cartella non valido"),
("Socks5 Proxy", "Socks5 Proxy"),
("Socks5 Proxy", "Proxy Socks5"),
("Hostname", "Nome host"),
("Discovered", "Scoperto"),
("install_daemon_tip", "Per iniziare all'avvio, è necessario installare il servizio di sistema."),
("Discovered", "Rilevati"),
("install_daemon_tip", "Per avviarsi all'accensione, è necessario installare il servizio di sistema."),
("Remote ID", "ID remoto"),
("Paste", "Impasto"),
("Paste here?", "Incolla qui?"),
("Are you sure to close the connection?", "Sei sicuro di chiudere la connessione?"),
("Are you sure to close the connection?", "Sei sicuro di voler chiudere la connessione?"),
("Download new version", "Scarica nuova versione"),
("Touch mode", "Modalità tocco"),
("Mouse mode", "Modalità mouse"),
@@ -244,29 +243,41 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Share Screen", "Condividi schermo"),
("CLOSE", "CHIUDERE"),
("OPEN", "APRIRE"),
("Chat", "Chiacchierata"),
("Chat", "Chat"),
("Total", "Totale"),
("items", "Oggetti"),
("Selected", "Selezionato"),
("Screen Capture", "Cattura schermo"),
("Input Control", "Controllo di input"),
("File Transfer", "Trasferimento di file"),
("Audio Capture", "Acquisizione audio"),
("File Connection", "Connessione file"),
("Screen Connection", "Connessione schermo"),
("Do you accept?", "Accetti?"),
("Open System Setting", "Apri Impostazioni di sistema"),
("How to get Android input permission?", "Come ottenere l'autorizzazione di input Android?"),
("Open System Setting", "Apri impostazioni di sistema"),
("How to get Android input permission?", "Come ottenere l'autorizzazione di input su Android?"),
("android_input_permission_tip1", "Affinché un dispositivo remoto possa controllare il tuo dispositivo Android tramite mouse o tocco, devi consentire a RustDesk di utilizzare il servizio \"Accessibilità\"."),
("android_input_permission_tip2", "Vai alla pagina delle impostazioni di sistema successiva, trova e accedi a [Servizi installati], attiva il servizio [RustDesk Input]."),
("android_new_connection_tip", "È stata ricevuta una nuova richiesta di controllo, desidera controllare il dispositivo corrente."),
("android_input_permission_tip2", "Vai alla pagina delle impostazioni di sistema che si aprirà di seguito, trova e accedi a [Servizi installati], attiva il servizio [RustDesk Input]."),
("android_new_connection_tip", "È stata ricevuta una nuova richiesta di controllo per il dispositivo corrente."),
("android_service_will_start_tip", "L'attivazione di Cattura schermo avvierà automaticamente il servizio, consentendo ad altri dispositivi di richiedere una connessione da questo dispositivo."),
("android_stop_service_tip", "La chiusura del servizio chiuderà automaticamente tutte le connessioni stabilite."),
("android_version_audio_tip", "L'attuale versione di Android non supporta l'acquisizione audio, esegui l'upgrade ad Android 10 o versioni successive."),
("android_start_service_tip", "Toccare [Avvia servizio] o APRI l'autorizzazione [Cattura schermo] per avviare il servizio di condivisione dello schermo."),
("Account", "Account"),
("Overwrite", "Sovrascrivi"),
("This file exists, skip or overwrite this file?", "Questo file esiste, saltare o sovrascrivere questo file?"),
("Quit", "Esci"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Aiuto"),
("Failed", "Fallito"),
("Succeeded", "Successo"),
("Someone turns on privacy mode, exit", "Qualcuno attiva la modalità privacy, esci"),
("Unsupported", "Non supportato"),
("Peer denied", "Pari negato"),
("Please install plugins", "Si prega di installare i plugin"),
("Peer exit", "Uscita tra pari"),
("Failed to turn off", "Impossibile spegnere"),
("Turned off", "Spegni"),
("In privacy mode", "In modalità privacy"),
("Out privacy mode", "Fuori modalità privacy"),
].iter().cloned().collect();
}

View File

@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Permitir o uso de teclado e mouse"),
("Allow using clipboard", "Permitir o uso da área de transferência"),
("Allow hearing sound", "Permitir escutar som"),
("Allow file transfer", "Permitir transferência de arquivo"),
("File transfer", "Transferência de arquivo"),
("Allow file copy and paste", "Permitir copiar e pegar arquivos"),
("Connected", "Conectado"),
("Direct and encrypted connection", "Conexão direta e criptografada"),
("Relayed and encrypted connection", "Conexão via relay e criptografada"),
@@ -250,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", "Selecionado"),
("Screen Capture", "Captura de Tela"),
("Input Control", "Controle de Entrada"),
("File Transfer", "Transferência de Arquivo"),
("Audio Capture", "Captura de Áudio"),
("File Connection", "Conexão de Arquivo"),
("Screen Connection", "Conexão de Tela"),
@@ -265,8 +263,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", "A versão atual do Android não suporta captura de áudio, por favor atualize para o Android 10 ou maior."),
("android_start_service_tip", "Toque [Iniciar Serviço] ou abra a permissão [Captura de Tela] para iniciar o serviço de compartilhamento de tela."),
("Account", "Conta"),
("Overwrite", "Substituir"),
("This file exists, skip or overwrite this file?", "Este arquivo existe, pular ou substituir este arquivo?"),
("Quit", "Saída"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Ajuda"),
("Failed", "Falhou"),
("Succeeded", "Conseguiu"),
("Someone turns on privacy mode, exit", "Alguém liga o modo de privacidade, saia"),
("Unsupported", "Sem suporte"),
("Peer denied", "Par negado"),
("Please install plugins", "Por favor instale plugins"),
("Peer exit", "Saída de pares"),
("Failed to turn off", "Falha ao desligar"),
("Turned off", "Desligado"),
("In privacy mode", "No modo de privacidade"),
("Out privacy mode", "Fora do modo de privacidade"),
].iter().cloned().collect();
}

View File

@@ -35,9 +35,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("About", "О RustDesk"),
("Mute", "Отключить звук"),
("Audio Input", "Аудиовход"),
("ID Server", "ID сервер"),
("ID Server", "ID-сервер"),
("Relay Server", "Сервер ретрансляции"),
("API Server", "API сервер"),
("API Server", "API-сервер"),
("invalid_http", "Должен начинаться с http:// или https://"),
("Invalid IP", "Неверный IP-адрес"),
("id_change_tip", "Допускаются только символы a-z, A-Z, 0-9 и _ (подчеркивание). Первая буква должна быть a-z, A-Z. Длина от 6 до 16"),
@@ -48,7 +48,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Cancel", "Отменить"),
("Skip", "Пропустить"),
("Close", "Закрыть"),
("Retry", "Попробовать еще раз"),
("Retry", "Попробовать снова"),
("OK", "ОК"),
("Password Required", "Требуется пароль"),
("Please enter your password", "Пожалуйста, введите ваш пароль"),
@@ -57,9 +57,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Do you want to enter again?", "Вы хотите снова войти?"),
("Connection Error", "Ошибка подключения"),
("Error", "Ошибка"),
("Reset by the peer", "Сброшено узлом"),
("Reset by the peer", "Сброшено пиром"),
("Connecting...", "Подключение..."),
("Connection in progress. Please wait.", "Выполняется подключение. Подождите."),
("Connection in progress. Please wait.", "Выполняется подключение. Пожалуйста, подождите."),
("Please try 1 minute later", "Попробуйте через 1 минуту"),
("Login Error", "Ошибка входа"),
("Successful", "Операция успешна"),
@@ -92,7 +92,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Waiting", "Ожидание"),
("Finished", "Завершено"),
("Speed", "Скорость"),
("Custom Image Quality", "Пользовательские настройки изображения"),
("Custom Image Quality", "Пользовательское качество изображения"),
("Privacy mode", "Режим конфиденциальности"),
("Block user input", "Блокировать пользовательский ввод"),
("Unblock user input", "Разблокировать пользовательский ввод"),
@@ -113,7 +113,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("ID does not exist", "ID не существует"),
("Failed to connect to rendezvous server", "Не удалось подключиться к промежуточному серверу"),
("Please try later", "Пожалуйста, попробуйте позже"),
("Remote desktop is offline", "Удаленный рабочий стол не онлайн"),
("Remote desktop is offline", "Удаленный рабочий стол не в сети"),
("Key mismatch", "Несоответствие ключей"),
("Timeout", "Тайм-аут"),
("Failed to connect to relay server", "Не удалось подключиться к серверу ретрансляции"),
@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Разрешить использование клавиатуры и мыши"),
("Allow using clipboard", "Разрешить использование буфера обмена"),
("Allow hearing sound", "Разрешить передачу звука"),
("Allow file transfer", "Разрешить передачу файлов"),
("File transfer", "Передача файлов"),
("Allow file copy and paste", "Разрешить копирование и вставку файлов"),
("Connected", "Подключено"),
("Direct and encrypted connection", "Прямое и шифрованное соединение"),
("Relayed and encrypted connection", "Коммутируемое и зашифрованное соединение"),
@@ -168,7 +167,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Enter your password", "Введите пароль"),
("Logging in...", "Вход..."),
("Enable RDP session sharing", "Включить общий доступ к сеансу RDP"),
("Auto Login", "Автоматический вход (действителен, только если вы установили \"Завершения пользовательского сеанса после завершения удалённого подключения\""),
("Auto Login", "Автоматический вход (действителен, только если вы установили \"Завершение пользовательского сеанса после завершения удалённого подключения\""),
("Enable Direct IP Access", "Включить прямой IP-доступ"),
("Rename", "Переименовать"),
("Space", "Место"),
@@ -246,11 +245,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("OPEN", "ОТКРЫТЬ"),
("Chat", "Чат"),
("Total", "Всего"),
("items", "обьекты"),
("items", "элементы"),
("Selected", "Выбрано"),
("Screen Capture", "Захват экрана"),
("Input Control", "Входной контроль"),
("File Transfer", "Передача файлов"),
("Audio Capture", "Захват аудио"),
("File Connection", "Файловое соединение"),
("Screen Connection", "Подключение экрана"),
@@ -270,5 +268,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("This file exists, skip or overwrite this file?", "Этот файл существует, пропустить или перезаписать этот файл?"),
("doc_mac_permission", "https://rustdesk.com/docs/ru/manual/mac/#включение-разрешений"),
("Help", "Помощь"),
("Failed", "Неуспешный"),
("Succeeded", "Успешно"),
("Someone turns on privacy mode, exit", "Кто-то включает режим конфиденциальности, выйдите"),
("Unsupported", "Не поддерживается"),
("Peer denied", "Отказано в пире"),
("Please install plugins", "Пожалуйста, установите плагины"),
("Peer exit", "Одноранговый выход"),
("Failed to turn off", "Не удалось отключить"),
("Turned off", "Выключен"),
("In privacy mode", "В режиме конфиденциальности"),
("Out privacy mode", "Выход из режима конфиденциальности"),
].iter().cloned().collect();
}

283
src/lang/sk.rs Normal file
View File

@@ -0,0 +1,283 @@
lazy_static::lazy_static! {
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
[
("Status", "Stav"),
("Your Desktop", "Vaša plocha"),
("desk_tip", "K svojej ploche sa môžete pripojiť pomocou zobrazeného ID a hesla."),
("Password", "Heslo"),
("Ready", "Pripravené"),
("Established", "Nadviazané"),
("connecting_status", "Pripájam sa na RusDesk server..."),
("Enable Service", "Povoliť službu"),
("Start Service", "Spustiť službu"),
("Service is running", "Služba je aktívna"),
("Service is not running", "Služba je vypnutá"),
("not_ready_status", "Nepripravené. Skontrolujte svoje sieťové pripojenie."),
("Control Remote Desktop", "Ovládať vzdialenú plochu"),
("Transfer File", "Prenos súborov"),
("Connect", "Pripojiť"),
("Recent Sessions", "Nedávne pripojenie"),
("Address Book", "Adresár kontaktov"),
("Confirmation", "Potvrdenie"),
("TCP Tunneling", "TCP tunelovanie"),
("Remove", "Odstrániť"),
("Refresh random password", "Aktualizovať náhodné heslo"),
("Set your own password", "Nastavte si svoje vlastné heslo"),
("Enable Keyboard/Mouse", "Povoliť klávesnicu/myš"),
("Enable Clipboard", "Povoliť schránku"),
("Enable File Transfer", "Povoliť prenos súborov"),
("Enable TCP Tunneling", "Povoliť TCP tunelovanie"),
("IP Whitelisting", "Zoznam povolených IP adries"),
("ID/Relay Server", "ID/Prepojovací server"),
("Stop service", "Zastaviť službu"),
("Change ID", "Zmeniť ID"),
("Website", "Webová stránka"),
("About", "O RustDesk"),
("Mute", "Stíšiť"),
("Audio Input", "Zvukový vstup"),
("ID Server", "ID server"),
("Relay Server", "Prepojovací server"),
("API Server", "API server"),
("invalid_http", "Musí začínať http:// alebo https://"),
("Invalid IP", "Neplatná IP adresa"),
("id_change_tip", "Povolené sú len znaky a-z, A-Z, 0-9 a _ (podčiarkovník). Prvý znak musí byť a-z, A-Z. Dĺžka musí byť medzi 6 a 16 znakmi."),
("Invalid format", "Neplatný formát"),
("server_not_support", "Zatiaľ serverom nepodporované"),
("Not available", "Nie je k dispozícii"),
("Too frequent", "Príliš často"),
("Cancel", "Zrušiť"),
("Skip", "Preskočiť"),
("Close", "Zatvoriť"),
("Retry", "Zopakovať"),
("OK", "OK"),
("Password Required", "Vyžaduje sa heslo"),
("Please enter your password", "Zadajte vaše heslo"),
("Remember password", "Zapamätať heslo"),
("Wrong Password", "Chybné heslo"),
("Do you want to enter again?", "Chcete ho znova zadať?"),
("Connection Error", "Chyba spojenia"),
("Error", "Chyba"),
("Reset by the peer", "Odmietnuté druhou stranou spojenia"),
("Connecting...", "Pripájanie sa..."),
("Connection in progress. Please wait.", "Pokúšam sa pripojiť. Počkajte chvíľu."),
("Please try 1 minute later", "Skúte znova za minútu, alebo ešte neskôr"),
("Login Error", "Chyba prihlásenia"),
("Successful", "Úspech"),
("Connected, waiting for image...", "Pripojené, čakám na obraz..."),
("Name", "Názov"),
("Type", "Typ"),
("Modified", "Zmenené"),
("Size", "Veľkosť"),
("Show Hidden Files", "Zobrazovať skryté súbory"),
("Receive", "Prijať"),
("Send", "Odoslať"),
("Refresh File", "Aktualizovať súbor"),
("Local", "Miestne"),
("Remote", "Vzdialené"),
("Remote Computer", "Vzdialený počítač"),
("Local Computer", "Miestny počítač"),
("Confirm Delete", "Potvrdenie zmazania"),
("Delete", "Zmazať"),
("Properties", "Vlastnosti"),
("Multi Select", "Viacnásobný výber"),
("Empty Directory", "Prázdny adresár"),
("Not an empty directory", "Nie prázdny adresár"),
("Are you sure you want to delete this file?", "Ste si istý, že chcete zmazať tento súbor?"),
("Are you sure you want to delete this empty directory?", "Ste si istý, že chcete zmazať tento adresár?"),
("Are you sure you want to delete the file of this directory?", "Ste si istý, že chcete zmazať tento súbor alebo adresár?"),
("Do this for all conflicts", "Všetky konflikty riešiť týmto spôsobom"),
("This is irreversible!", "Toto je nezvratná operácia"),
("Deleting", "Mazanie"),
("files", "súbory"),
("Waiting", "Čaká sa"),
("Finished", "Ukončené"),
("Speed", "Rýchlosť"),
("Custom Image Quality", "Vlastná kvalita obrazu"),
("Privacy mode", "Režim súkromia"),
("Block user input", "Blokovať vstupné zariadenia užívateľa"),
("Unblock user input", "Odblokovať vstupné zariadenia užívateľa"),
("Adjust Window", "Prispôsobiť okno"),
("Original", "Pôvodný"),
("Shrink", "Zmenšené"),
("Stretch", "Roztiahnuté"),
("Good image quality", "Dobrá kvalita obrazu"),
("Balanced", "Vyvážené"),
("Optimize reaction time", "Optimalizované pre čas odozvy"),
("Custom", "Vlastné"),
("Show remote cursor", "Zobrazovať vzdialený ukazovateľ myši"),
("Disable clipboard", "Vypnúť schránku"),
("Lock after session end", "Po skončení uzamknúť plochu"),
("Insert", "Vložiť"),
("Insert Lock", "Uzamknúť"),
("Refresh", "Aktualizovať"),
("ID does not exist", "ID neexistuje"),
("Failed to connect to rendezvous server", "Nepodarilo sa pripojiť k zoznamovaciemu serveru"),
("Please try later", "Vyskúšajte neskôr"),
("Remote desktop is offline", "Vzdialená plocha je pripojená"),
("Key mismatch", "Kľúče sa nezhodujú"),
("Timeout", "Čas pre nadviazanie pripojenia vypršal"),
("Failed to connect to relay server", "Nepodarilo sa pripojiť k prepojovaciemu serveru"),
("Failed to connect via rendezvous server", "Nepodarilo sa pripojiť cez zoznamovací server"),
("Failed to connect via relay server", "Nepodarilo sa pripojiť cez prepojovací server"),
("Failed to make direct connection to remote desktop", "Nepodarilo sa nadviazať priamu komunikáciu so vzdialenou plochou"),
("Set Password", "Nastaviť heslo"),
("OS Password", "Heslo do operačného systému"),
("install_tip", "V niektorých prípadoch RustDesk nefunguje správne z dôvodu riadenia užívateľských oprávnení (UAC). Vyhnete sa tomu kliknutím na nižšie zobrazene tlačítko a nainštalovaním RuskDesk do systému."),
("Click to upgrade", "Kliknutím nainštalujete aktualizáciu"),
("Click to download", "Kliknutím potvrďte stiahnutie"),
("Click to update", "Kliknutím aktualizovať"),
("Configure", "Nastaviť"),
("config_acc", "Aby bolo možné na diaľku ovládať vašu plochu, je potrebné aplikácii RustDesk udeliť práva \"Dostupnosť\"."),
("config_screen", "Aby bolo možné na diaľku sledovať vašu obrazovku, je potrebné aplikácii RustDesk udeliť práva \"Zachytávanie obsahu obrazovky\"."),
("Installing ...", "Inštaluje sa"),
("Install", "Inštalovať"),
("Installation", "Inštalácia"),
("Installation Path", "Inštalačný adresár"),
("Create start menu shortcuts", "Vytvoriť zástupcu do ponuky Štart"),
("Create desktop icon", "Vytvoriť zástupcu na plochu"),
("agreement_tip", "Spustením inštalácie prijímate licenčné podmienky."),
("Accept and Install", "Prijať a inštalovať"),
("End-user license agreement", "Licenčné podmienky dohodnuté s koncovým užívateľom"),
("Generating ...", "Generujem ..."),
("Your installation is lower version.", "Vaša inštalácia je staršia"),
("not_close_tcp_tip", "Nezatvárajte toto okno po celý čas, kedy používate TCP tunel"),
("Listening ...", "Čakám na pripojenie ..."),
("Remote Host", "Vzdialený počítač"),
("Remote Port", "Vzdialený port"),
("Action", "Akcia"),
("Add", "Pridať"),
("Local Port", "Lokálny port"),
("setup_server_tip", "Pre zrýchlenie pripojenia si nainštalujte svoj vlastný server"),
("Too short, at least 6 characters.", "Príliš krátke, vyžaduje sa aspoň 6 znakov."),
("The confirmation is not identical.", "Potvrdenie nie je zhodné."),
("Permissions", "Práva"),
("Accept", "Prijať"),
("Dismiss", "Odmietnuť"),
("Disconnect", "Odpojiť"),
("Allow using keyboard and mouse", "Povoliť používanie klávesnice a myši"),
("Allow using clipboard", "Povoliť používanie schránky"),
("Allow hearing sound", "Povoliť zvuky"),
("Allow file copy and paste", "Povoliť kopírovanie a vkladanie súborov"),
("Connected", "Pripojené"),
("Direct and encrypted connection", "Priame a šifrované spojenie"),
("Relayed and encrypted connection", "Sprostredkované a šifrované spojenie"),
("Direct and unencrypted connection", "Priame a nešifrované spojenie"),
("Relayed and unencrypted connection", "Sprostredkované a nešifrované spojenie"),
("Enter Remote ID", "Zadajte ID vzdialenej plochy"),
("Enter your password", "Zadajte svoje heslo"),
("Logging in...", "Prihlasovanie sa...."),
("Enable RDP session sharing", "Povoliť zdieľanie RDP relácie"),
("Auto Login", "Automatické prihlásenie"),
("Enable Direct IP Access", "Povoliť priame pripojenie cez IP"),
("Rename", "Premenovať"),
("Space", "Medzera"),
("Create Desktop Shortcut", "Vytvoriť zástupcu na ploche"),
("Change Path", "Zmeniť adresár"),
("Create Folder", "Vytvoriť adresár"),
("Please enter the folder name", "Zadajte názov adresára"),
("Fix it", "Opraviť to"),
("Warning", "Upozornenie"),
("Login screen using Wayland is not supported", "Prihlasovacia obrazovka prostredníctvom Wayland nie je podporovaná"),
("Reboot required", "Vyžaduje sa reštart"),
("Unsupported display server ", "Nepodporovaný zobrazovací (display) server"),
("x11 expected", "očakáva sa x11"),
("Port", "Port"),
("Settings", "Nastavenia"),
("Username", "Uživateľské meno"),
("Invalid port", "Neplatný port"),
("Closed manually by the peer", "Manuálne ukončené opačnou stranou pripojenia"),
("Enable remote configuration modification", "Povoliť zmeny konfigurácie zo vzdialeného PC"),
("Run without install", "Spustiť bez inštalácie"),
("Always connected via relay", "Vždy pripojené cez prepájací server"),
("Always connect via relay", "Vždy pripájať cez prepájací server"),
("whitelist_tip", "Len vymenované IP adresy majú oprávnenie sa pripojiť k vzdialenej správe"),
("Login", "Prihlásenie"),
("Logout", "Odhlásenie"),
("Tags", "Štítky"),
("Search ID", "Hľadať ID"),
("Current Wayland display server is not supported", "Zobrazovací (display) server Wayland nie je podporovaný"),
("whitelist_sep", "Oddelené čiarkou, bodkočiarkou, medzerou alebo koncom riadku"),
("Add ID", "Pridať ID"),
("Add Tag", "Pridať štítok"),
("Unselect all tags", "Zrušiť výber všetkých štítkov"),
("Network error", "Chyba siete"),
("Username missed", "Chýba užívateľské meno"),
("Password missed", "Chýba heslo"),
("Wrong credentials", "Nesprávne prihlasovacie údaje"),
("Edit Tag", "Upraviť štítok"),
("Unremember Password", "Zabudnúť heslo"),
("Favorites", "Obľúbené"),
("Add to Favorites", "Pridať medzi obľúbené"),
("Remove from Favorites", "Odstrániť z obľúbených"),
("Empty", "Prázdne"),
("Invalid folder name", "Neplatný názov adresára"),
("Socks5 Proxy", "Socks5 Proxy"),
("Hostname", "Názov počítača"),
("Discovered", "Objavené"),
("install_daemon_tip", "Ak chcete, aby sa spúšťal pri štarte systému, musíte nainštalovať systémovú službu."),
("Remote ID", "Vzdialené ID"),
("Paste", "Vložiť"),
("Paste here?", "Vložiť sem?"),
("Are you sure to close the connection?", "Ste si istý, že chcete ukončiť spojenie?"),
("Download new version", "Stiahnuť novú verziu"),
("Touch mode", "Dotykový režim"),
("Mouse mode", "Režim ovládania myšou"),
("One-Finger Tap", "Klepnutie jedným prstom"),
("Left Mouse", "Ľavé tlačidlo myši"),
("One-Long Tap", "Jedno dlhé klepnutie"),
("Two-Finger Tap", "Klepnutie dvoma prstami"),
("Right Mouse", "Pravé tlačidlo myši"),
("One-Finger Move", "Presúvanie jedným prstom"),
("Double Tap & Move", "Dvojité klepnutie a presun"),
("Mouse Drag", "Presun myšou"),
("Three-Finger vertically", "Pohyb tromi prstami zvisle"),
("Mouse Wheel", "Koliesko myši"),
("Two-Finger Move", "Pohyb dvoma prstami"),
("Canvas Move", "Pohyb zobrazenia"),
("Pinch to Zoom", "Roztiahnutím prstov priblížiť"),
("Canvas Zoom", "Priblíženie zobrazenia"),
("Reset canvas", "Obnoviť zobrazenie"),
("No permission of file transfer", "Prenos súborov nie je povolený"),
("Note", "Poznámka"),
("Connection", "Pripojenie"),
("Share Screen", "Zdielať obrazovku"),
("CLOSE", "ZATVORIŤ"),
("OPEN", "OTVORIŤ"),
("Chat", "Chat"),
("Total", "Celkom"),
("items", "položiek"),
("Selected", "Vybrané"),
("Screen Capture", "Snímanie obrazovky"),
("Input Control", "Ovládanie vstupných zariadení"),
("Audio Capture", "Snímanie zvuku"),
("File Connection", "Pripojenie súborov"),
("Screen Connection", "Pripojenie obrazu"),
("Do you accept?", "Súhlasíte?"),
("Open System Setting", "Otvorenie nastavení systému"),
("How to get Android input permission?", "Ako v systéme Android povoliť oprávnenie písať zo vstupného zariadenia?"),
("android_input_permission_tip1", "Aby bolo možné na diaľku ovládať vašu plochu pomocou myši alebo dotykov, je potrebné aplikácii RustDesk udeliť práva \"Dostupnosť\"."),
("android_input_permission_tip2", "Prejdite na stránku nastavení systému, nájdite a vstúpte do [Stiahnuté služby], zapnite [RustDesk Input] službu."),
("android_new_connection_tip", "Bola prijatá nová požiadavka na ovládanie vášho zariadenia."),
("android_service_will_start_tip", "Zapnutie \"Zachytávanie obsahu obrazovky\" automaticky spistí službu, čo iným zariadeniam umožní požiadať o pripojenie k tomuto zariadeniu."),
("android_stop_service_tip", "Zastavenie služby automaticky ukončí všetky naviazané spojenia."),
("android_version_audio_tip", "Vaša verzia Androidu neumožňuje zaznamenávanie zvuku. Prejdite na verziu Android 10 alebo vyššiu."),
("android_start_service_tip", "Klepnite na [Spustiť službu] alebo OTVORTE oprávnenie [Zachytávanie obsahu obrazovky], aby sa aktivovala služba zdieľania obrazovky."),
("Account", "Účet"),
("Overwrite", "Prepísať"),
("This file exists, skip or overwrite this file?", "Preskočiť alebo prepísať existujúci súbor?"),
("Quit", "Ukončiť"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Nápoveda"),
("Failed", "Nepodarilo sa"),
("Succeeded", "Podarilo sa"),
("Someone turns on privacy mode, exit", "Niekto zapne režim súkromia, ukončite ho"),
("Unsupported", "Nepodporované"),
("Peer denied", "Peer poprel"),
("Please install plugins", "Nainštalujte si prosím pluginy"),
("Peer exit", "Peer exit"),
("Failed to turn off", "Nepodarilo sa vypnúť"),
("Turned off", "Vypnutý"),
("In privacy mode", "V režime súkromia"),
("Out privacy mode", "Mimo režimu súkromia"),
].iter().cloned().collect();
}

View File

@@ -42,7 +42,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Invalid IP", ""),
("id_change_tip", ""),
("Invalid format", ""),
("This function is turned off by the server", ""),
("server_not_support", ""),
("Not available", ""),
("Too frequent", ""),
("Cancel", ""),
@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", ""),
("Allow using clipboard", ""),
("Allow hearing sound", ""),
("Allow file transfer", ""),
("File transfer", ""),
("Allow file copy and paste", ""),
("Connected", ""),
("Direct and encrypted connection", ""),
("Relayed and encrypted connection", ""),
@@ -250,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", ""),
("Screen Capture", ""),
("Input Control", ""),
("File Transfer", ""),
("Audio Capture", ""),
("File Connection", ""),
("Screen Connection", ""),
@@ -270,5 +268,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Quit", ""),
("doc_mac_permission", ""),
("Help", ""),
("Failed", ""),
("Succeeded", ""),
("Someone turns on privacy mode, exit", ""),
("Unsupported", ""),
("Peer denied", ""),
("Please install plugins", ""),
("Peer exit", ""),
("Failed to turn off", ""),
("Turned off", ""),
("In privacy mode", ""),
("Out privacy mode", ""),
].iter().cloned().collect();
}

View File

@@ -42,7 +42,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Invalid IP", "Geçersiz IP adresi"),
("id_change_tip", "Yalnızca a-z, A-Z, 0-9 ve _ (alt çizgi) karakterlerini kullanabilirsiniz. İlk karakter a-z veya A-Z olmalıdır. Uzunluk 6 ile 16 karakter arasında olmalıdır."),
("Invalid format", "Hatalı Format"),
("This function is turned off by the server", "Bu özellik sunucu tarafından kapatıldı"),
("server_not_support", "Henüz sunucu tarafından desteklenmiyor"),
("Not available", "Erişilebilir değil"),
("Too frequent", "Çok sık"),
("Cancel", "İptal"),
@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "Klavye ve fare kullanımına izin ver"),
("Allow using clipboard", "Pano kullanımına izin ver"),
("Allow hearing sound", "Sesi duymaya izin ver"),
("Allow file transfer", "Dosya aktarımına izin ver"),
("File transfer", "Dosya transferi"),
("Allow file copy and paste", "Dosya kopyalamaya ve yapıştırmaya izin ver"),
("Connected", "Bağlandı"),
("Direct and encrypted connection", "Doğrudan ve şifreli bağlantı"),
("Relayed and encrypted connection", "Aktarmalı ve şifreli bağlantı"),
@@ -250,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", "Seçildi"),
("Screen Capture", "Ekran görüntüsü"),
("Input Control", "Giriş Kontrolü"),
("File Transfer", "Dosya transferi"),
("Audio Capture", "Ses Yakalama"),
("File Connection", "Dosya Bağlantısı"),
("Screen Connection", "Ekran Bağlantısı"),
@@ -265,8 +263,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("android_version_audio_tip", "Mevcut Android sürümü ses yakalamayı desteklemiyor, lütfen Android 10 veya sonraki bir sürüme yükseltin."),
("android_start_service_tip", "Ekran paylaşım hizmetini başlatmak için [Hizmeti Başlat] veya AÇ [Ekran Yakalama] iznine dokunun."),
("Account", "Hesap"),
("Overwrite", "üzerine yaz"),
("This file exists, skip or overwrite this file?", "Bu dosya var, bu dosya atlansın veya üzerine yazılsın mı?"),
("Quit", "Çıkış"),
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
("Help", "Yardım"),
("Failed", "Arızalı"),
("Succeeded", "başarılı"),
("Someone turns on privacy mode, exit", "Birisi gizlilik modunu açar, çık"),
("Unsupported", "desteklenmiyor"),
("Peer denied", "akran reddedildi"),
("Please install plugins", "Lütfen eklentileri yükleyin"),
("Peer exit", "akran çıkışı"),
("Failed to turn off", "kapatılamadı"),
("Turned off", "Kapalı"),
("In privacy mode", "Gizlilik modunda"),
("Out privacy mode", "Gizlilik modu dışında"),
].iter().cloned().collect();
}

View File

@@ -157,8 +157,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"),
("Allow using clipboard", "允許使用剪貼簿"),
("Allow hearing sound", "允許分享音訊"),
("Allow file transfer", "允許文件傳輸"),
("File transfer", "文件傳輸"),
("Allow file copy and paste", "允許文件複製和粘貼"),
("Connected", "已連接"),
("Direct and encrypted connection", "加密直接連線"),
("Relayed and encrypted connection", "加密轉送連線"),
@@ -184,7 +183,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("x11 expected", "預期 x11"),
("Port", "連接埠"),
("Settings", "設定"),
("Username", " 使用者名稱"),
("Username", "使用者名稱"),
("Invalid port", "連接埠無效"),
("Closed manually by the peer", "由對方手動關閉"),
("Enable remote configuration modification", "啟用遠端更改設定"),
@@ -250,7 +249,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Selected", "已選擇"),
("Screen Capture", "畫面錄製"),
("Input Control", "輸入控制"),
("File Transfer", "檔案傳輸"),
("Audio Capture", "音訊錄製"),
("File Connection", "檔案連線"),
("Screen Connection", "畫面連線"),
@@ -270,5 +268,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要跳過或是覆寫此檔案嗎?"),
("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"),
("Help", "幫助"),
("Account", "帳戶"),
("Failed", "失敗"),
("Succeeded", "成功"),
("Someone turns on privacy mode, exit", "其他用戶開啟隱私模式,退出"),
("Unsupported", "不支持"),
("Peer denied", "被控端拒絕"),
("Please install plugins", "請安裝插件"),
("Peer exit", "被控端退出"),
("Failed to turn off", "退出失敗"),
("Turned off", "退出"),
("In privacy mode", "開啟隱私模式"),
("Out privacy mode", "退出隱私模式"),
].iter().cloned().collect();
}

View File

@@ -49,3 +49,6 @@ mod ui_interface;
#[cfg(windows)]
pub mod clipboard_file;
#[cfg(all(windows, feature = "with_rc"))]
pub mod rc;

View File

@@ -95,13 +95,17 @@ fn main() {
hbb_common::allow_err!(platform::uninstall_me());
hbb_common::allow_err!(platform::install_me(
"desktopicon startmenu",
"".to_owned()
"".to_owned(),
false,
false,
));
return;
} else if args[0] == "--silent-install" {
hbb_common::allow_err!(platform::install_me(
"desktopicon startmenu",
"".to_owned()
"".to_owned(),
true,
args.len() > 1,
));
return;
}
@@ -130,7 +134,16 @@ fn main() {
}
} else if args[0] == "--import-config" {
if args.len() == 2 {
import_config(&args[1]);
let filepath;
let path = std::path::Path::new(&args[1]);
if !path.is_absolute() {
let mut cur = std::env::current_dir().unwrap();
cur.push(path);
filepath = cur.to_str().unwrap().to_string();
} else {
filepath = path.to_str().unwrap().to_string();
}
import_config(&filepath);
}
return;
} else if args[0] == "--password" {

View File

@@ -1,7 +1,6 @@
use super::{CursorData, ResultType};
use hbb_common::{allow_err, bail, log};
use libc::{c_char, c_int, c_void};
use std::io::prelude::*;
use std::{
cell::RefCell,
sync::{
@@ -371,8 +370,7 @@ fn get_display_server_of_session(session: &str) -> String {
.replace("TTY=", "")
.trim_end()
.into();
if let Ok(Some(xorg_results)) =
run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty))
if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty))
// And check if Xorg is running on that tty
{
if xorg_results.trim_end().to_string() != "" {
@@ -441,9 +439,7 @@ pub fn current_is_wayland() -> bool {
pub fn modify_default_login() -> String {
let dsession = std::env::var("DESKTOP_SESSION").unwrap();
let user_name = std::env::var("USERNAME").unwrap();
if let Ok(Some(x)) =
run_cmds("ls /usr/share/* | grep ${DESKTOP_SESSION}-xorg.desktop".to_owned())
{
if let Ok(x) = run_cmds("ls /usr/share/* | grep ${DESKTOP_SESSION}-xorg.desktop".to_owned()) {
if x.trim_end().to_string() != "" {
match std::process::Command::new("pkexec")
.args(vec![
@@ -471,7 +467,7 @@ pub fn modify_default_login() -> String {
return "Fix failed! Please re-login with X server manually".to_owned();
}
}
} else if let Ok(Some(z)) =
} else if let Ok(z) =
run_cmds("ls /usr/share/* | grep ${DESKTOP_SESSION:0:-8}.desktop".to_owned())
{
if z.trim_end().to_string() != "" {
@@ -605,20 +601,11 @@ pub fn is_installed() -> bool {
true
}
fn run_cmds(cmds: String) -> ResultType<Option<String>> {
let mut tmp = std::env::temp_dir();
tmp.push(format!("{}_{}", crate::get_app_name(), crate::get_time()));
let mut file = std::fs::File::create(&tmp)?;
file.write_all(cmds.as_bytes())?;
file.sync_all()?;
if let Ok(output) = std::process::Command::new("bash")
.arg(tmp.to_str().unwrap_or(""))
.output()
{
Ok(Some(String::from_utf8_lossy(&output.stdout).to_string()))
} else {
Ok(None)
}
pub fn run_cmds(cmds: String) -> ResultType<String> {
let output = std::process::Command::new("sh")
.args(vec!["-c", &cmds])
.output()?;
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
fn get_env_tries(name: &str, uid: &str, n: usize) -> String {
@@ -635,7 +622,7 @@ fn get_env_tries(name: &str, uid: &str, n: usize) -> String {
fn get_env(name: &str, uid: &str) -> String {
let cmd = format!("ps -u {} -o pid= | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, name, name);
log::debug!("Run: {}", &cmd);
if let Ok(Some(x)) = run_cmds(cmd) {
if let Ok(x) = run_cmds(cmd) {
x.trim_end().to_string()
} else {
"".to_owned()

View File

@@ -9,7 +9,7 @@ use hbb_common::{
use std::io::prelude::*;
use std::{
ffi::OsString,
io, mem,
fs, io, mem,
sync::{Arc, Mutex},
time::{Duration, Instant},
};
@@ -404,6 +404,7 @@ extern "C" {
fn has_rdp_service() -> BOOL;
fn get_current_session(rdp: BOOL) -> DWORD;
fn LaunchProcessWin(cmd: *const u16, session_id: DWORD, as_user: BOOL) -> HANDLE;
fn GetSessionUserTokenWin(lphUserToken: LPHANDLE, dwSessionId: DWORD, as_user: BOOL) -> BOOL;
fn selectInputDesktop() -> BOOL;
fn inputDesktopSelected() -> BOOL;
fn is_windows_server() -> BOOL;
@@ -558,7 +559,7 @@ async fn launch_server(session_id: DWORD, close_first: bool) -> ResultType<HANDL
let wstr = wstr.as_ptr();
let h = unsafe { LaunchProcessWin(wstr, session_id, FALSE) };
if h.is_null() {
log::error!("Failed to luanch server: {}", get_error());
log::error!("Failed to launch server: {}", get_error());
}
Ok(h)
}
@@ -697,7 +698,7 @@ pub fn set_share_rdp(enable: bool) {
subkey,
if enable { "true" } else { "false" }
);
run_cmds(cmd, false).ok();
run_cmds(cmd, false, "share_rdp").ok();
}
pub fn get_active_username() -> String {
@@ -796,6 +797,49 @@ fn get_default_install_path() -> String {
format!("{}\\{}", pf, crate::get_app_name())
}
pub fn check_update_broker_process() -> ResultType<()> {
// let (_, path, _, _) = get_install_info();
let process_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE;
let origin_process_exe = crate::ui::win_privacy::ORIGIN_PROCESS_EXE;
let exe_file = std::env::current_exe()?;
if exe_file.parent().is_none() {
bail!("Cannot get parent of current exe file");
}
let cur_dir = exe_file.parent().unwrap();
let cur_exe = cur_dir.join(process_exe);
let ori_modified = fs::metadata(origin_process_exe)?.modified()?;
if let Ok(metadata) = fs::metadata(&cur_exe) {
if let Ok(cur_modified) = metadata.modified() {
if cur_modified == ori_modified {
return Ok(());
} else {
log::info!(
"broker process updated, modify time from {:?} to {:?}",
cur_modified,
ori_modified
);
}
}
}
// Force update broker exe if failed to check modified time.
let cmds = format!(
"
chcp 65001
taskkill /F /IM {broker_exe}
copy /Y \"{origin_process_exe}\" \"{cur_exe}\"
",
broker_exe = process_exe,
origin_process_exe = origin_process_exe,
cur_exe = cur_exe.to_string_lossy().to_string(),
);
run_cmds(cmds, false, "update_broker")?;
Ok(())
}
fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String) {
let mut path = get_reg_of(&subkey, "InstallLocation");
if path.is_empty() {
@@ -811,24 +855,28 @@ fn get_install_info_with_subkey(subkey: String) -> (String, String, String, Stri
}
pub fn update_me() -> ResultType<()> {
let (_, _, _, exe) = get_install_info();
let (_, path, _, exe) = get_install_info();
let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned();
let cmds = format!(
"
chcp 65001
sc stop {app_name}
taskkill /F /IM {broker_exe}
taskkill /F /IM {app_name}.exe
copy /Y \"{src_exe}\" \"{exe}\"
\"{src_exe}\" --extract \"{path}\"
sc start {app_name}
{lic}
",
src_exe = src_exe,
exe = exe,
broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
path = path,
app_name = crate::get_app_name(),
lic = register_licence(),
);
std::thread::sleep(std::time::Duration::from_millis(1000));
run_cmds(cmds, false)?;
run_cmds(cmds, false, "update")?;
std::thread::sleep(std::time::Duration::from_millis(2000));
std::process::Command::new(&exe).arg("--tray").spawn().ok();
std::process::Command::new(&exe).spawn().ok();
@@ -857,7 +905,7 @@ fn get_after_install(exe: &str) -> String {
", ext=ext, exe=exe, app_name=app_name)
}
pub fn install_me(options: &str, path: String) -> ResultType<()> {
pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> ResultType<()> {
let uninstall_str = get_uninstall();
let mut path = path.trim_end_matches('\\').to_owned();
let (subkey, _path, start_menu, exe) = get_default_install_info();
@@ -881,7 +929,7 @@ pub fn install_me(options: &str, path: String) -> ResultType<()> {
version_build = versions[2];
}
let tmp_path = "C:\\Windows\\temp";
let tmp_path = std::env::temp_dir().to_string_lossy().to_string();
let mk_shortcut = write_cmds(
format!(
"
@@ -897,6 +945,7 @@ oLink.Save
exe = exe,
),
"vbs",
"mk_shortcut",
)?
.to_str()
.unwrap_or("")
@@ -918,6 +967,7 @@ oLink.Save
exe = exe,
),
"vbs",
"uninstall_shortcut",
)?
.to_str()
.unwrap_or("")
@@ -938,6 +988,7 @@ oLink.Save
exe = exe,
),
"vbs",
"tray_shortcut",
)?
.to_str()
.unwrap_or("")
@@ -975,9 +1026,12 @@ copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{start_menu}\\\"
chcp 65001
md \"{path}\"
copy /Y \"{src_exe}\" \"{exe}\"
copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\"
\"{src_exe}\" --extract \"{path}\"
reg add {subkey} /f
reg add {subkey} /f /v DisplayIcon /t REG_SZ /d \"{exe}\"
reg add {subkey} /f /v DisplayName /t REG_SZ /d \"{app_name}\"
reg add {subkey} /f /v DisplayVersion /t REG_SZ /d \"{version}\"
reg add {subkey} /f /v Version /t REG_SZ /d \"{version}\"
reg add {subkey} /f /v InstallLocation /t REG_SZ /d \"{path}\"
reg add {subkey} /f /v Publisher /t REG_SZ /d \"{app_name}\"
@@ -988,10 +1042,10 @@ reg add {subkey} /f /v UninstallString /t REG_SZ /d \"\\\"{exe}\\\" --uninstall\
reg add {subkey} /f /v EstimatedSize /t REG_DWORD /d {size}
reg add {subkey} /f /v WindowsInstaller /t REG_DWORD /d 0
{lic}
\"{mk_shortcut}\"
\"{uninstall_shortcut}\"
\"{tray_shortcut}\"
copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\"
cscript \"{mk_shortcut}\"
cscript \"{uninstall_shortcut}\"
cscript \"{tray_shortcut}\"
copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\"
{shortcuts}
copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\"
del /f \"{mk_shortcut}\"
@@ -1010,6 +1064,8 @@ sc delete {app_name}
path=path,
src_exe=std::env::current_exe()?.to_str().unwrap_or(""),
exe=exe,
ORIGIN_PROCESS_EXE = crate::ui::win_privacy::ORIGIN_PROCESS_EXE,
broker_exe=crate::ui::win_privacy::INJECTED_PROCESS_EXE,
subkey=subkey,
app_name=crate::get_app_name(),
version=crate::VERSION,
@@ -1026,21 +1082,23 @@ sc delete {app_name}
lic=register_licence(),
after_install=get_after_install(&exe),
);
run_cmds(cmds, false)?;
run_cmds(cmds, debug, "install")?;
std::thread::sleep(std::time::Duration::from_millis(2000));
std::process::Command::new(&exe).spawn()?;
std::process::Command::new(&exe).arg("--tray").spawn()?;
std::thread::sleep(std::time::Duration::from_millis(1000));
if !silent {
std::process::Command::new(&exe).spawn()?;
std::process::Command::new(&exe).arg("--tray").spawn()?;
std::thread::sleep(std::time::Duration::from_millis(1000));
}
Ok(())
}
pub fn run_after_install() -> ResultType<()> {
let (_, _, _, exe) = get_install_info();
run_cmds(get_after_install(&exe), true)
run_cmds(get_after_install(&exe), true, "after_install")
}
pub fn run_before_uninstall() -> ResultType<()> {
run_cmds(get_before_uninstall(), true)
run_cmds(get_before_uninstall(), true, "before_install")
}
fn get_before_uninstall() -> String {
@@ -1051,11 +1109,13 @@ fn get_before_uninstall() -> String {
chcp 65001
sc stop {app_name}
sc delete {app_name}
taskkill /F /IM {broker_exe}
taskkill /F /IM {app_name}.exe
reg delete HKEY_CLASSES_ROOT\\.{ext} /f
netsh advfirewall firewall delete rule name=\"{app_name} Service\"
",
app_name = app_name,
broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
ext = ext
)
}
@@ -1069,7 +1129,7 @@ fn get_uninstall() -> String {
rd /s /q \"{path}\"
rd /s /q \"{start_menu}\"
del /f /q \"%PUBLIC%\\Desktop\\{app_name}*\"
del /f /q \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\"
",
before_uninstall=get_before_uninstall(),
subkey=subkey,
@@ -1080,21 +1140,12 @@ fn get_uninstall() -> String {
}
pub fn uninstall_me() -> ResultType<()> {
run_cmds(get_uninstall(), true)
run_cmds(get_uninstall(), true, "uninstall")
}
fn write_cmds(cmds: String, ext: &str) -> ResultType<std::path::PathBuf> {
fn write_cmds(cmds: String, ext: &str, tip: &str) -> ResultType<std::path::PathBuf> {
let mut tmp = std::env::temp_dir();
tmp.push(format!(
"{}_{:?}.{}",
crate::get_app_name(),
cmds.as_ptr(),
ext
));
let mut cmds = cmds;
if ext == "cmd" {
cmds = format!("{}\ndel /f \"{}\"", cmds, tmp.to_str().unwrap_or(""));
}
tmp.push(format!("{}_{}.{}", crate::get_app_name(), tip, ext));
let mut file = std::fs::File::create(&tmp)?;
// in case cmds mixed with \r\n and \n, make sure all ending with \r\n
// in some windows, \r\n required for cmd file to run
@@ -1117,15 +1168,20 @@ fn to_le(v: &mut [u16]) -> &[u8] {
unsafe { v.align_to().1 }
}
fn run_cmds(cmds: String, show: bool) -> ResultType<()> {
let tmp = write_cmds(cmds, "cmd")?;
let res = runas::Command::new(tmp.to_str().unwrap_or(""))
fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> {
let tmp = write_cmds(cmds, "bat", tip)?;
let tmp_fn = tmp.to_str().unwrap_or("");
let res = runas::Command::new("cmd")
.args(&["/C", &tmp_fn])
.show(show)
.force_prompt(true)
.status();
// double confirm delete, because below delete not work if program
// exit immediately such as --uninstall
allow_err!(std::fs::remove_file(tmp));
// leave the file for debug if execution failed
if let Ok(res) = res {
if res.success() {
allow_err!(std::fs::remove_file(tmp));
}
}
let _ = res?;
Ok(())
}
@@ -1290,6 +1346,7 @@ oLink.Save
id = id,
),
"vbs",
"connect_shortcut",
)?
.to_str()
.unwrap_or("")
@@ -1325,3 +1382,20 @@ pub fn quit_gui() {
std::process::exit(0);
// unsafe { PostQuitMessage(0) }; // some how not work
}
pub fn get_user_token(session_id: u32, as_user: bool) -> HANDLE {
let mut token = NULL as HANDLE;
unsafe {
if FALSE
== GetSessionUserTokenWin(
&mut token as _,
session_id,
if as_user { TRUE } else { FALSE },
)
{
NULL as _
} else {
token
}
}
}

38
src/rc.rs Normal file
View File

@@ -0,0 +1,38 @@
use hbb_common::{bail, ResultType};
use std::{
fs::{self, File},
io::prelude::*,
path::Path,
};
#[allow(dead_code)]
pub fn extract_resources(root_path: &str) -> ResultType<()> {
let mut resources: Vec<(&str, &[u8])> = Vec::new();
resources.push((outfile_4, &outdata_4));
do_extract(root_path, resources)?;
Ok(())
}
#[allow(dead_code)]
fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> {
let mut root_path = root_path.replace("\\", "/");
if !root_path.ends_with('/') {
root_path.push('/');
}
let root_path = Path::new(&root_path);
for (outfile, data) in resources {
let outfile_path = root_path.join(outfile);
match outfile_path.parent().and_then(|p| p.to_str()) {
None => {
bail!("Failed to get parent of {}", outfile_path.display());
}
Some(p) => {
fs::create_dir_all(p)?;
let mut of = File::create(outfile_path)?;
of.write_all(data)?;
of.flush()?;
}
}
}
Ok(())
}

View File

@@ -216,7 +216,7 @@ impl RendezvousMediator {
},
Some(Err(e)) => bail!("Failed to receive next {}", e), // maybe socks5 tcp disconnected
None => {
// unreachable!()
bail!("Socket receive none. Maybe socks5 server is down.");
},
}
},
@@ -231,7 +231,7 @@ impl RendezvousMediator {
}
last_timer = now;
let elapsed_resp = last_register_resp.map(|x| x.elapsed().as_millis() as i64).unwrap_or(REG_INTERVAL);
let timeout = (last_register_sent.map(|x| x.elapsed().as_millis() as i64).unwrap_or(REG_INTERVAL) - elapsed_resp) > REG_TIMEOUT;
let timeout = (elapsed_resp - last_register_sent.map(|x| x.elapsed().as_millis() as i64).unwrap_or(REG_INTERVAL)) > REG_TIMEOUT;
if timeout || elapsed_resp >= REG_INTERVAL {
allow_err!(rz.register_peer(&mut socket).await);
last_register_sent = now;

View File

@@ -91,6 +91,15 @@ async fn accept_connection_(server: ServerPtr, socket: Stream, secure: bool) ->
Ok(())
}
async fn check_privacy_mode_on(stream: &mut Stream) -> ResultType<()> {
if video_service::get_privacy_mode_conn_id() > 0 {
let msg_out =
crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::OnByOther);
timeout(CONNECT_TIMEOUT, stream.send(&msg_out)).await??;
}
Ok(())
}
pub async fn create_tcp_connection(
server: ServerPtr,
stream: Stream,
@@ -98,6 +107,8 @@ pub async fn create_tcp_connection(
secure: bool,
) -> ResultType<()> {
let mut stream = stream;
check_privacy_mode_on(&mut stream).await?;
let id = {
let mut w = server.write().unwrap();
w.id_count += 1;
@@ -423,5 +434,5 @@ async fn sync_and_watch_config_dir() {
}
}
}
log::error!("skipped config sync");
log::warn!("skipped config sync");
}

View File

@@ -22,7 +22,7 @@ use hbb_common::{
tokio_util::codec::{BytesCodec, Framed},
};
#[cfg(any(target_os = "android", target_os = "ios"))]
use scrap::android::call_input_service_mouse_input;
use scrap::android::call_main_service_mouse_input;
use serde_json::{json, value::Value};
use sha2::{Digest, Sha256};
use std::sync::{
@@ -50,8 +50,6 @@ enum MessageInput {
Key((KeyEvent, bool)),
BlockOn,
BlockOff,
PrivacyOn,
PrivacyOff,
}
pub struct Connection {
@@ -74,7 +72,6 @@ pub struct Connection {
image_quality: i32,
lock_after_session_end: bool,
show_remote_cursor: bool, // by peer
privacy_mode: bool,
ip: String,
disable_clipboard: bool, // by peer
disable_audio: bool, // by peer
@@ -160,7 +157,6 @@ impl Connection {
image_quality: ImageQuality::Balanced.value(),
lock_after_session_end: false,
show_remote_cursor: false,
privacy_mode: false,
ip: "".to_owned(),
disable_audio: false,
enable_file_transfer: false,
@@ -281,6 +277,34 @@ impl Connection {
allow_err!(conn.stream.send(&clip_2_msg(_clip)).await);
}
}
ipc::Data::PrivacyModeState((_, state)) => {
let msg_out = match state {
ipc::PrivacyModeState::OffSucceeded => {
video_service::set_privacy_mode_conn_id(0);
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OffSucceeded,
)
}
ipc::PrivacyModeState::OffFailed => {
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OffFailed,
)
}
ipc::PrivacyModeState::OffByPeer => {
video_service::set_privacy_mode_conn_id(0);
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OffByPeer,
)
}
ipc::PrivacyModeState::OffUnknown => {
video_service::set_privacy_mode_conn_id(0);
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OffUnknown,
)
}
};
conn.send(msg_out).await;
}
_ => {}
}
},
@@ -362,9 +386,16 @@ impl Connection {
}
}
let video_privacy_conn_id = video_service::get_privacy_mode_conn_id();
if video_privacy_conn_id == id {
video_service::set_privacy_mode_conn_id(0);
let _ = privacy_mode::turn_off_privacy(id);
} else if video_privacy_conn_id == 0 {
let _ = privacy_mode::turn_off_privacy(0);
}
video_service::notify_video_frame_feched(id, None);
super::video_service::update_test_latency(id, 0);
super::video_service::update_image_quality(id, None);
video_service::update_test_latency(id, 0);
video_service::update_image_quality(id, None);
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
conn.on_close(&err.to_string(), false);
}
@@ -378,9 +409,6 @@ impl Connection {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn handle_input(receiver: std_mpsc::Receiver<MessageInput>, tx: Sender) {
let mut block_input_mode = false;
let (tx_blank, rx_blank) = std_mpsc::channel();
std::thread::spawn(|| Self::handle_blank(rx_blank));
loop {
match receiver.recv_timeout(std::time::Duration::from_millis(500)) {
@@ -402,28 +430,22 @@ impl Connection {
if crate::platform::block_input(true) {
block_input_mode = true;
} else {
Self::send_option_error(&tx, "Failed to turn on block input mode");
Self::send_block_input_error(
&tx,
back_notification::BlockInputState::OnFailed,
);
}
}
MessageInput::BlockOff => {
if crate::platform::block_input(false) {
block_input_mode = false;
} else {
Self::send_option_error(&tx, "Failed to turn off block input mode");
Self::send_block_input_error(
&tx,
back_notification::BlockInputState::OffFailed,
);
}
}
MessageInput::PrivacyOn => {
if crate::platform::block_input(true) {
block_input_mode = true;
}
tx_blank.send(MessageInput::PrivacyOn).ok();
}
MessageInput::PrivacyOff => {
if crate::platform::block_input(false) {
block_input_mode = false;
}
tx_blank.send(MessageInput::PrivacyOff).ok();
}
},
Err(err) => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -439,35 +461,6 @@ impl Connection {
log::info!("Input thread exited");
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn handle_blank(receiver: std_mpsc::Receiver<MessageInput>) {
let mut last_privacy = false;
loop {
match receiver.recv_timeout(std::time::Duration::from_millis(500)) {
Ok(v) => match v {
MessageInput::PrivacyOn => {
crate::platform::toggle_blank_screen(true);
last_privacy = true;
}
MessageInput::PrivacyOff => {
crate::platform::toggle_blank_screen(false);
last_privacy = false;
}
_ => break,
},
Err(err) => {
if last_privacy {
crate::platform::toggle_blank_screen(true);
}
if std_mpsc::RecvTimeoutError::Disconnected == err {
break;
}
}
}
}
log::info!("Blank thread exited");
}
async fn try_port_forward_loop(
&mut self,
rx_from_cm: &mut mpsc::UnboundedReceiver<Data>,
@@ -657,8 +650,15 @@ impl Connection {
}
}
self.authorized = true;
pi.username = username;
pi.sas_enabled = sas_enabled;
pi.features = Some(Features {
privacy_mode: video_service::is_privacy_mode_supported(),
..Default::default()
})
.into();
let mut sub_service = false;
if self.file_transfer.is_some() {
res.set_peer_info(pi);
@@ -755,13 +755,13 @@ impl Connection {
self.send(msg_out).await;
}
fn send_option_error<T: std::string::ToString>(s: &Sender, err: T) {
let mut msg_out = Message::new();
let mut res = OptionResponse::new();
#[inline]
pub fn send_block_input_error(s: &Sender, state: back_notification::BlockInputState) {
let mut misc = Misc::new();
res.error = err.to_string();
misc.set_option_response(res);
let mut back_notification = BackNotification::new();
back_notification.set_block_input_state(state);
misc.set_back_notification(back_notification);
let mut msg_out = Message::new();
msg_out.set_misc(misc);
s.send((Instant::now(), Arc::new(msg_out))).ok();
}
@@ -895,8 +895,8 @@ impl Connection {
match msg.union {
Some(message::Union::mouse_event(me)) => {
#[cfg(any(target_os = "android", target_os = "ios"))]
if let Err(e) = call_input_service_mouse_input(me.mask, me.x, me.y) {
log::debug!("call_input_service_mouse_input fail:{}", e);
if let Err(e) = call_main_service_mouse_input(me.mask, me.x, me.y) {
log::debug!("call_main_service_mouse_input fail:{}", e);
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.keyboard {
@@ -1162,12 +1162,55 @@ impl Connection {
if self.keyboard {
match q {
BoolOption::Yes => {
self.privacy_mode = true;
self.tx_input.send(MessageInput::PrivacyOn).ok();
let msg_out = if !video_service::is_privacy_mode_supported() {
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::NotSupported,
)
} else {
match privacy_mode::turn_on_privacy(self.inner.id) {
Ok(true) => {
if video_service::test_create_capturer(self.inner.id, 5_000) {
video_service::set_privacy_mode_conn_id(self.inner.id);
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OnSucceeded,
)
} else {
log::error!(
"Wait privacy mode timeout, turn off privacy mode"
);
video_service::set_privacy_mode_conn_id(0);
let _ = privacy_mode::turn_off_privacy(self.inner.id);
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OnFailed,
)
}
}
Ok(false) => crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OnFailedPlugin,
),
Err(e) => {
log::error!("Failed to turn on privacy mode. {}", e);
if video_service::get_privacy_mode_conn_id() == 0 {
let _ = privacy_mode::turn_off_privacy(0);
}
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OnFailed,
)
}
}
};
self.send(msg_out).await;
}
BoolOption::No => {
self.privacy_mode = false;
self.tx_input.send(MessageInput::PrivacyOff).ok();
let msg_out = if !video_service::is_privacy_mode_supported() {
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::NotSupported,
)
} else {
video_service::set_privacy_mode_conn_id(0);
privacy_mode::turn_off_privacy(self.inner.id)
};
self.send(msg_out).await;
}
_ => {}
}
@@ -1318,3 +1361,43 @@ fn try_activate_screen() {
mouse_move_relative(6, 6);
});
}
mod privacy_mode {
use super::*;
pub(super) fn turn_off_privacy(_conn_id: i32) -> Message {
#[cfg(windows)]
{
use crate::ui::win_privacy::*;
let res = turn_off_privacy(_conn_id, None);
match res {
Ok(_) => crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OffSucceeded,
),
Err(e) => {
log::error!("Failed to turn off privacy mode {}", e);
crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OffFailed,
)
}
}
}
#[cfg(not(windows))]
{
crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::OffFailed)
}
}
pub(super) fn turn_on_privacy(_conn_id: i32) -> ResultType<bool> {
#[cfg(windows)]
{
let plugin_exitst = crate::ui::win_privacy::turn_on_privacy(_conn_id)?;
Ok(plugin_exitst)
}
#[cfg(not(windows))]
{
Ok(true)
}
}
}

View File

@@ -89,7 +89,12 @@ impl<T: Subscriber + From<ConnInner>> Service for ServiceTmpl<T> {
fn join(&self) {
self.0.write().unwrap().active = false;
self.0.write().unwrap().handle.take().map(JoinHandle::join);
let handle = self.0.write().unwrap().handle.take();
if let Some(handle) = handle {
if let Err(e) = handle.join() {
log::error!("Failed to join thread for service {}, {:?}", self.name(), e);
}
}
}
}
@@ -143,6 +148,16 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
}
}
pub fn send_to_others(&self, msg: Message, id: i32) {
let msg = Arc::new(msg);
let mut lock = self.0.write().unwrap();
for (sid, s) in lock.subscribes.iter_mut() {
if *sid != id {
s.send(msg.clone());
}
}
}
pub fn send_shared(&self, msg: Arc<Message>) {
let mut lock = self.0.write().unwrap();
for s in lock.subscribes.values_mut() {

View File

@@ -19,19 +19,18 @@
// https://slhck.info/video/2017/03/01/rate-control.html
use super::*;
use hbb_common::tokio::{
runtime::Runtime,
sync::{
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
Mutex as TokioMutex,
},
use hbb_common::tokio::sync::{
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
Mutex as TokioMutex,
};
use scrap::{Capturer, Config, Display, EncodeFrame, Encoder, VideoCodecId, STRIDE_ALIGN};
use scrap::{Capturer, Config, Display, EncodeFrame, Encoder, Frame, VideoCodecId, STRIDE_ALIGN};
use std::{
collections::HashSet,
io::ErrorKind::WouldBlock,
io::{ErrorKind::WouldBlock, Result},
time::{self, Duration, Instant},
};
#[cfg(windows)]
use virtual_display;
pub const NAME: &'static str = "video";
@@ -45,16 +44,39 @@ lazy_static::lazy_static! {
let (tx, rx) = unbounded_channel();
(tx, Arc::new(TokioMutex::new(rx)))
};
static ref PRIVACY_MODE_CONN_ID: Mutex<i32> = Mutex::new(0);
static ref IS_CAPTURER_MAGNIFIER_SUPPORTED: bool = is_capturer_mag_supported();
}
fn is_capturer_mag_supported() -> bool {
#[cfg(windows)]
return scrap::CapturerMag::is_supported();
#[cfg(not(windows))]
false
}
pub fn notify_video_frame_feched(conn_id: i32, frame_tm: Option<Instant>) {
FRAME_FETCHED_NOTIFIER.0.send((conn_id, frame_tm)).unwrap()
}
pub fn set_privacy_mode_conn_id(conn_id: i32) {
*PRIVACY_MODE_CONN_ID.lock().unwrap() = conn_id
}
pub fn get_privacy_mode_conn_id() -> i32 {
*PRIVACY_MODE_CONN_ID.lock().unwrap()
}
pub fn is_privacy_mode_supported() -> bool {
#[cfg(windows)]
return *IS_CAPTURER_MAGNIFIER_SUPPORTED;
#[cfg(not(windows))]
return false;
}
struct VideoFrameController {
cur: Instant,
send_conn_ids: HashSet<i32>,
rt: Runtime,
}
impl VideoFrameController {
@@ -62,7 +84,6 @@ impl VideoFrameController {
Self {
cur: Instant::now(),
send_conn_ids: HashSet::new(),
rt: Runtime::new().unwrap(),
}
}
@@ -77,46 +98,69 @@ impl VideoFrameController {
}
}
fn blocking_wait_next(&mut self, timeout_millis: u128) {
#[tokio::main(flavor = "current_thread")]
async fn try_wait_next(&mut self, fetched_conn_ids: &mut HashSet<i32>, timeout_millis: u64) {
if self.send_conn_ids.is_empty() {
return;
}
let send_conn_ids = self.send_conn_ids.clone();
self.rt.block_on(async move {
let mut fetched_conn_ids = HashSet::new();
let begin = Instant::now();
while begin.elapsed().as_millis() < timeout_millis {
let timeout_dur =
Duration::from_millis((timeout_millis - begin.elapsed().as_millis()) as u64);
match tokio::time::timeout(
timeout_dur,
FRAME_FETCHED_NOTIFIER.1.lock().await.recv(),
)
.await
{
Err(_) => {
// break if timeout
// log::error!("blocking wait frame receiving timeout {}", timeout_millis);
break;
}
Ok(Some((id, instant))) => {
if let Some(tm) = instant {
log::trace!("Channel recv latency: {}", tm.elapsed().as_secs_f32());
}
fetched_conn_ids.insert(id);
// break if all connections have received current frame
if fetched_conn_ids.len() >= send_conn_ids.len() {
break;
}
}
Ok(None) => {
// this branch would nerver be reached
}
}
let timeout_dur = Duration::from_millis(timeout_millis as u64);
match tokio::time::timeout(timeout_dur, FRAME_FETCHED_NOTIFIER.1.lock().await.recv()).await
{
Err(_) => {
// break if timeout
// log::error!("blocking wait frame receiving timeout {}", timeout_millis);
}
});
Ok(Some((id, instant))) => {
if let Some(tm) = instant {
log::trace!("Channel recv latency: {}", tm.elapsed().as_secs_f32());
}
fetched_conn_ids.insert(id);
}
Ok(None) => {
// this branch would nerver be reached
}
}
}
}
trait TraitCapturer {
fn frame<'a>(&'a mut self, timeout_ms: u32) -> Result<Frame<'a>>;
#[cfg(windows)]
fn is_gdi(&self) -> bool;
#[cfg(windows)]
fn set_gdi(&mut self) -> bool;
}
impl TraitCapturer for Capturer {
fn frame<'a>(&'a mut self, timeout_ms: u32) -> Result<Frame<'a>> {
self.frame(timeout_ms)
}
#[cfg(windows)]
fn is_gdi(&self) -> bool {
self.is_gdi()
}
#[cfg(windows)]
fn set_gdi(&mut self) -> bool {
self.set_gdi()
}
}
#[cfg(windows)]
impl TraitCapturer for scrap::CapturerMag {
fn frame<'a>(&'a mut self, _timeout_ms: u32) -> Result<Frame<'a>> {
self.frame(_timeout_ms)
}
fn is_gdi(&self) -> bool {
false
}
fn set_gdi(&mut self) -> bool {
false
}
}
@@ -132,7 +176,7 @@ fn check_display_changed(
last_width: usize,
last_hegiht: usize,
) -> bool {
let displays = match Display::all() {
let displays = match try_get_displays() {
Ok(d) => d,
_ => return false,
};
@@ -156,7 +200,126 @@ fn check_display_changed(
return false;
}
// Capturer object is expensive, avoiding to create it frequently.
fn create_capturer(privacy_mode_id: i32, display: Display) -> ResultType<Box<dyn TraitCapturer>> {
let use_yuv = true;
#[cfg(not(windows))]
let c: Option<Box<dyn TraitCapturer>> = None;
#[cfg(windows)]
let mut c: Option<Box<dyn TraitCapturer>> = None;
if privacy_mode_id > 0 {
#[cfg(windows)]
{
use crate::ui::win_privacy::*;
match scrap::CapturerMag::new(
display.origin(),
display.width(),
display.height(),
use_yuv,
) {
Ok(mut c1) => {
let mut ok = false;
let check_begin = Instant::now();
while check_begin.elapsed().as_secs() < 5 {
match c1.exclude("", PRIVACY_WINDOW_NAME) {
Ok(false) => {
ok = false;
std::thread::sleep(std::time::Duration::from_millis(500));
}
Err(e) => {
bail!(
"Failed to exclude privacy window {} - {}, err: {}",
"",
PRIVACY_WINDOW_NAME,
e
);
}
_ => {
ok = true;
break;
}
}
}
if !ok {
bail!(
"Failed to exclude privacy window {} - {} ",
"",
PRIVACY_WINDOW_NAME
);
}
log::debug!("Create maginifier capture for {}", privacy_mode_id);
c = Some(Box::new(c1));
}
Err(e) => {
bail!(format!("Failed to create magnifier capture {}", e));
}
}
}
}
let c = match c {
Some(c1) => c1,
None => {
let c1 =
Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?;
log::debug!("Create capturer dxgi|gdi");
Box::new(c1)
}
};
Ok(c)
}
#[cfg(windows)]
fn ensure_close_virtual_device() -> ResultType<()> {
let num_displays = Display::all()?.len();
if num_displays == 0 {
// Device may sometimes be uninstalled by user in "Device Manager" Window.
// Closing device will clear the instance data.
virtual_display::close_device();
} else if num_displays > 1 {
// Try close device, if display device changed.
if virtual_display::is_device_created() {
virtual_display::close_device();
}
}
Ok(())
}
pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool {
let test_begin = Instant::now();
while test_begin.elapsed().as_millis() < timeout_millis as _ {
if let Ok((_, _, display)) = get_current_display() {
if let Ok(_) = create_capturer(privacy_mode_id, display) {
return true;
}
}
std::thread::sleep(Duration::from_millis(300));
}
false
}
#[cfg(windows)]
fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> ResultType<()> {
if captuerer_privacy_mode_id != 0 {
if privacy_mode_id != captuerer_privacy_mode_id {
if !crate::ui::win_privacy::is_process_consent_running()? {
bail!("consent.exe is running");
}
}
if crate::ui::win_privacy::is_process_consent_running()? {
bail!("consent.exe is running");
}
}
Ok(())
}
fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)]
ensure_close_virtual_device()?;
let fps = 30;
let wait = 1000 / fps;
let spf = time::Duration::from_secs_f32(1. / (fps as f32));
@@ -172,8 +335,27 @@ fn run(sp: GenericService) -> ResultType<()> {
num_cpus::get_physical(),
num_cpus::get(),
);
// Capturer object is expensive, avoiding to create it frequently.
let mut c = Capturer::new(display, true).with_context(|| "Failed to create capturer")?;
let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap();
#[cfg(not(windows))]
let captuerer_privacy_mode_id = privacy_mode_id;
#[cfg(windows)]
let mut captuerer_privacy_mode_id = privacy_mode_id;
#[cfg(windows)]
if crate::ui::win_privacy::is_process_consent_running()? {
captuerer_privacy_mode_id = 0;
}
log::debug!(
"Try create capturer with captuerer privacy mode id {}",
captuerer_privacy_mode_id,
);
if privacy_mode_id != captuerer_privacy_mode_id {
log::info!("In privacy mode, but show UAC prompt window for now");
} else {
log::info!("In privacy mode, the peer side cannot watch the screen");
}
let mut c = create_capturer(captuerer_privacy_mode_id, display)?;
let q = get_image_quality();
let (bitrate, rc_min_quantizer, rc_max_quantizer, speed) = get_quality(width, height, q);
@@ -220,6 +402,9 @@ fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)]
log::info!("gdi: {}", c.is_gdi());
while sp.ok() {
#[cfg(windows)]
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
if *SWITCH.lock().unwrap() {
bail!("SWITCH");
}
@@ -227,6 +412,7 @@ fn run(sp: GenericService) -> ResultType<()> {
*SWITCH.lock().unwrap() = true;
bail!("SWITCH");
}
check_privacy_mode_changed(&sp, privacy_mode_id)?;
if get_image_quality() != q {
bail!("SWITCH");
}
@@ -245,12 +431,13 @@ fn run(sp: GenericService) -> ResultType<()> {
bail!("SWITCH");
}
}
*LAST_ACTIVE.lock().unwrap() = now;
frame_controller.reset();
#[cfg(any(target_os = "android", target_os = "ios"))]
let res = match c.frame(wait as _) {
let res = match (*c).frame(wait as _) {
Ok(frame) => {
let time = now - start;
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
@@ -273,7 +460,7 @@ fn run(sp: GenericService) -> ResultType<()> {
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let res = match c.frame(wait as _) {
let res = match (*c).frame(wait as _) {
Ok(frame) => {
let time = now - start;
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
@@ -320,8 +507,19 @@ fn run(sp: GenericService) -> ResultType<()> {
_ => {}
}
// i love 3, 6, 8
frame_controller.blocking_wait_next(3_000);
let mut fetched_conn_ids = HashSet::new();
let timeout_millis = 3_000u64;
let wait_begin = Instant::now();
while wait_begin.elapsed().as_millis() < timeout_millis as _ {
check_privacy_mode_changed(&sp, privacy_mode_id)?;
#[cfg(windows)]
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
frame_controller.try_wait_next(&mut fetched_conn_ids, 300);
// break if all connections have received current frame
if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() {
break;
}
}
let elapsed = now.elapsed();
// may need to enable frame(timeout)
@@ -333,6 +531,21 @@ fn run(sp: GenericService) -> ResultType<()> {
Ok(())
}
#[inline]
fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> ResultType<()> {
let privacy_mode_id_2 = *PRIVACY_MODE_CONN_ID.lock().unwrap();
if privacy_mode_id != privacy_mode_id_2 {
if privacy_mode_id_2 != 0 {
let msg_out = crate::common::make_privacy_mode_msg(
back_notification::PrivacyModeState::OnByOther,
);
sp.send_to_others(msg_out, privacy_mode_id_2);
}
bail!("SWITCH");
}
Ok(())
}
#[inline]
fn create_msg(vp9s: Vec<VP9>) -> Message {
let mut msg_out = Message::new();
@@ -416,7 +629,7 @@ pub fn handle_one_frame_encoded(
}
fn get_display_num() -> usize {
if let Ok(d) = Display::all() {
if let Ok(d) = try_get_displays() {
d.len()
} else {
0
@@ -430,7 +643,7 @@ pub fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
}
let mut displays = Vec::new();
let mut primary = 0;
for (i, d) in Display::all()?.iter().enumerate() {
for (i, d) in try_get_displays()?.iter().enumerate() {
if d.is_primary() {
primary = i;
}
@@ -467,7 +680,7 @@ pub fn refresh() {
}
fn get_primary() -> usize {
if let Ok(all) = Display::all() {
if let Ok(all) = try_get_displays() {
for (i, d) in all.iter().enumerate() {
if d.is_primary() {
return i;
@@ -481,9 +694,44 @@ pub fn switch_to_primary() {
switch_display(get_primary() as _);
}
#[cfg(not(windows))]
fn try_get_displays() -> ResultType<Vec<Display>> {
Ok(Display::all()?)
}
#[cfg(windows)]
fn try_get_displays() -> ResultType<Vec<Display>> {
let mut displays = Display::all()?;
if displays.len() == 0 {
log::debug!("no displays, create virtual display");
// Try plugin monitor
if !virtual_display::is_device_created() {
if let Err(e) = virtual_display::create_device() {
log::debug!("Create device failed {}", e);
}
}
if virtual_display::is_device_created() {
if let Err(e) = virtual_display::plug_in_monitor() {
log::debug!("Plug in monitor failed {}", e);
} else {
if let Err(e) = virtual_display::update_monitor_modes() {
log::debug!("Update monitor modes failed {}", e);
}
}
}
displays = Display::all()?;
} else if displays.len() > 1 {
// If more than one displays exists, close RustDeskVirtualDisplay
if virtual_display::is_device_created() {
virtual_display::close_device()
}
}
Ok(displays)
}
fn get_current_display() -> ResultType<(usize, usize, Display)> {
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
let mut displays = Display::all()?;
let mut displays = try_get_displays()?;
if displays.len() == 0 {
bail!("No displays");
}
@@ -567,5 +815,15 @@ fn get_quality(w: usize, h: usize, q: i32) -> (u32, u32, u32, i32) {
let bitrate = q >> 8 & 0xFF;
let quantizer = q & 0xFF;
let b = ((w * h) / 1000) as u32;
#[cfg(target_os = "android")]
{
// fix when andorid screen shrinks
let fix = Display::fix_quality() as u32;
log::debug!("Android screen, fix quality:{}", fix);
let b = b * fix;
return (bitrate as u32 * b / 100, quantizer as _, 56, 7);
}
(bitrate as u32 * b / 100, quantizer as _, 56, 7)
}

View File

@@ -3,6 +3,8 @@ mod cm;
mod inline;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "windows")]
pub mod win_privacy;
pub mod remote;
use crate::ui_interface::*;
use hbb_common::{allow_err, config::PeerConfig, log};
@@ -29,7 +31,13 @@ pub fn start(args: &mut [String]) {
macos::show_dock();
}
#[cfg(all(target_os = "linux", feature = "inline"))]
sciter::set_library("/usr/lib/rustdesk/libsciter-gtk.so").ok();
{
#[cfg(feature = "appimage")]
let prefix = std::env::var("APPDIR").unwrap_or("".to_string());
#[cfg(not(feature = "appimage"))]
let prefix = "".to_string();
sciter::set_library(&(prefix + "/usr/lib/rustdesk/libsciter-gtk.so")).ok();
}
// https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-types.h
// https://github.com/rustdesk/rustdesk/issues/132#issuecomment-886069737
#[cfg(windows)]

View File

@@ -399,6 +399,13 @@ impl ConnectionManager {
}
}
fn send_data(&self, id: i32, data: Data) {
let lock = self.read().unwrap();
if let Some(s) = lock.senders.get(&id) {
allow_err!(s.send(data));
}
}
fn authorize(&self, id: i32) {
let lock = self.read().unwrap();
if let Some(s) = lock.senders.get(&id) {
@@ -442,6 +449,21 @@ async fn start_ipc(cm: ConnectionManager) {
#[cfg(windows)]
std::thread::spawn(move || start_clipboard_file(cm_clip, _rx_file));
#[cfg(windows)]
std::thread::spawn(move || {
log::info!("try create privacy mode window");
#[cfg(windows)]
{
if let Err(e) = crate::platform::windows::check_update_broker_process() {
log::warn!(
"Failed to check update broker process. Privacy mode may not work properly. {}",
e
);
}
}
allow_err!(crate::ui::win_privacy::start());
});
match new_listener("_cm").await {
Ok(mut incoming) => {
while let Some(result) = incoming.next().await {
@@ -452,6 +474,8 @@ async fn start_ipc(cm: ConnectionManager) {
let cm = cm.clone();
let tx_file = tx_file.clone();
tokio::spawn(async move {
// for tmp use, without real conn id
let conn_id_tmp = -1;
let mut conn_id: i32 = 0;
let (tx, mut rx) = mpsc::unbounded_channel::<Data>();
let mut write_jobs: Vec<fs::TransferJob> = Vec::new();
@@ -476,6 +500,10 @@ async fn start_ipc(cm: ConnectionManager) {
log::info!("cm ipc connection closed from connection request");
break;
}
Data::PrivacyModeState((id, _)) => {
conn_id = conn_id_tmp;
cm.send_data(id, data)
}
_ => {
cm.handle_data(conn_id, data, &tx_file, &mut write_jobs, &mut stream).await;
}
@@ -491,7 +519,9 @@ async fn start_ipc(cm: ConnectionManager) {
}
}
}
cm.remove_connection(conn_id);
if conn_id != conn_id_tmp {
cm.remove_connection(conn_id);
}
});
}
Err(err) => {

View File

@@ -45,7 +45,7 @@ class Body: Reactor.Component
<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 class={!c.file ? "disabled" : ""} title={translate('Allow file transfer')}><icon .file /></div>
<div class={!c.file ? "disabled" : ""} title={translate('Allow file copy and paste')}><icon .file /></div>
</div>}
{c.port_forward ? <div>Port Forwarding: {c.port_forward}</div> : ""}
<div style="size:*"/>
@@ -267,6 +267,7 @@ function bring_to_top(idx=-1) {
}
handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file) {
stdout.println("new connection #" + id + ": " + peer_id);
var conn;
connections.map(function(c) {
if (c.id == id) conn = c;

View File

@@ -126,6 +126,7 @@ class Header: Reactor.Component {
updateWindowToolbarPosition();
var style = "flow:horizontal;";
if (is_osx) style += "margin:*";
self.timer(1ms, updatePrivacyMode);
self.timer(1ms, toggleMenuState);
return <div style={style}>
{is_osx || is_xfce ? "" : <span #fullscreen>{svg_fullscreen}</span>}
@@ -159,10 +160,10 @@ class Header: Reactor.Component {
<div .separator />
<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> : ""}
{is_win && pi.platform == 'Windows' && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('File transfer')}</li> : ""}
{is_win && pi.platform == 'Windows' && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('Allow file copy and paste')}</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 && keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode .toggle-option><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
{keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
</menu>
</popup>;
}
@@ -312,6 +313,8 @@ class Header: Reactor.Component {
event click $(menu#display-options>li) (_, me) {
if (me.id == "custom") {
handle_custom_image_quality();
} else if (me.id == "privacy-mode") {
togglePrivacyMode(me.id);
} else if (me.attributes.hasClass("toggle-option")) {
handler.toggle_option(me.id);
toggleMenuState();
@@ -354,17 +357,11 @@ function toggleMenuState() {
for (var el in $$(menu#display-options>li)) {
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
}
for (var id in ["show-remote-cursor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end", "privacy-mode"]) {
for (var id in ["show-remote-cursor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) {
var el = self.select('#' + id);
if (el) {
var value = handler.get_toggle_option(id);
el.attributes.toggleClass("selected", value);
if (id == "privacy-mode") {
var el = $(li#block-input);
if (el) {
el.state.disabled = value;
}
}
}
}
}
@@ -400,6 +397,46 @@ handler.updatePi = function(v) {
}
}
function updatePrivacyMode() {
var el = $(li#privacy-mode);
if (el) {
var supported = handler.is_privacy_mode_supported();
if (!supported) {
// el.attributes.toggleClass("line-through", true);
el.style["display"]="none";
} else {
var value = handler.get_toggle_option("privacy-mode");
el.attributes.toggleClass("selected", value);
var el = $(li#block-input);
if (el) {
el.state.disabled = value;
}
}
}
}
handler.updatePrivacyMode = updatePrivacyMode;
function togglePrivacyMode(privacy_id) {
var supported = handler.is_privacy_mode_supported();
if (!supported) {
msgbox("nocancel", translate("Privacy mode"), translate("Unsupported"), function() { });
} else {
handler.toggle_option(privacy_id);
}
}
handler.updateBlockInputState = function(input_blocked) {
if (!input_blocked) {
handler.toggle_option("block-input");
input_blocked = true;
$(#block-input).text = translate("Unblock user input");
} else {
handler.toggle_option("unblock-input");
input_blocked = false;
$(#block-input).text = translate("Block user input");
}
}
handler.switchDisplay = function(i) {
pi.current_display = i;
header.update();

View File

@@ -490,7 +490,6 @@ class App: Reactor.Component
<ConnectStatus @{this.connect_status} />
</div>
<div #overlay style="position: absolute;size:*;background:black;opacity:0.5;display:none" />
<div #msgbox />
</div>;
}
@@ -666,9 +665,7 @@ class FixWayland: Reactor.Component {
return <div .trust-me>
<div>{translate('Warning')}</div>
<div>{translate('Login screen using Wayland is not supported')}</div>
<div #fix-wayland .link>{translate('Fix it')}</div>
<div #help-me .link>{translate('Help')}</div>
<div>({translate('Reboot required')})</div>
</div>;
}
@@ -687,9 +684,7 @@ class ModifyDefaultLogin: Reactor.Component {
return <div .trust-me>
<div>{translate('Warning')}</div>
<div>{translate('Current Wayland display server is not supported')}</div>
<div #modify-default-login .link>{translate('Fix it')}</div>
<div #help-me .link>{translate('Help')}</div>
<div>({translate('Reboot required')})</div>
</div>;
}
@@ -858,7 +853,7 @@ event keydown (evt) {
}
}
$(body).content(<App />);
$(body).content(<div style="size:*"><App /><div #msgbox /></div>);
function self.closing() {
var (x, y, w, h) = view.box(#rectw, #border, #screen);

View File

@@ -91,8 +91,8 @@ class MsgboxComponent: Reactor.Component {
var color = this.getColor();
var icon = this.getIcon(color);
var content = this.getContent();
var hasCancel = this.type.indexOf("error") < 0 && this.type != "success" && this.type.indexOf("nocancel") < 0;
var hasOk = this.type != "connecting" && this.type.indexOf("nook") < 0;
var hasCancel = this.type.indexOf("error") < 0 && this.type.indexOf("nocancel") < 0;
var hasOk = this.type != "connecting" && this.type != "success" && this.type.indexOf("nook") < 0;
var hasClose = this.type.indexOf("hasclose") >= 0;
var show_progress = this.type == "connecting";
var me = this;

View File

@@ -1,7 +1,10 @@
use std::{
collections::HashMap,
ops::Deref,
sync::{Arc, Mutex, RwLock},
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex, RwLock,
},
};
use sciter::{
@@ -21,8 +24,8 @@ use clipboard::{
};
use enigo::{self, Enigo, KeyboardControllable};
use hbb_common::fs::{
can_enable_overwrite_detection, get_string, new_send_confirm,
DigestCheckResult, RemoveJobMeta, get_job,
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
RemoveJobMeta,
};
use hbb_common::{
allow_err,
@@ -45,7 +48,7 @@ use hbb_common::{config::TransferSerde, fs::TransferJobMeta};
use crate::clipboard_file::*;
use crate::{
client::*,
common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}
common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL},
};
type Video = AssetPtr<video_destination>;
@@ -63,11 +66,11 @@ 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 SERVER_KEYBOARD_ENABLED: bool = true;
static mut SERVER_FILE_TRANSFER_ENABLED: bool = true;
static mut SERVER_CLIPBOARD_ENABLED: bool = true;
static IS_IN: AtomicBool = AtomicBool::new(false);
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true);
static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
#[cfg(windows)]
static mut IS_ALT_GR: bool = false;
@@ -223,6 +226,7 @@ impl sciter::EventHandler for Handler {
fn save_custom_image_quality(i32, i32);
fn refresh_video();
fn get_toggle_option(String);
fn is_privacy_mode_supported();
fn toggle_option(String);
fn get_remember();
fn peer_platform();
@@ -249,12 +253,9 @@ impl Handler {
if self.is_port_forward() || self.is_file_transfer() {
return;
}
if unsafe { KEYBOARD_HOOKED } {
if KEYBOARD_HOOKED.swap(true, Ordering::SeqCst) {
return;
}
unsafe {
KEYBOARD_HOOKED = true;
}
log::info!("keyboard hooked");
let mut me = self.clone();
let peer = self.peer_platform();
@@ -266,7 +267,8 @@ impl Handler {
std::env::set_var("KEYBOARD_ONLY", "y"); // pass to rdev
use rdev::{EventType::*, *};
let func = move |evt: Event| {
if unsafe { !IS_IN || !SERVER_KEYBOARD_ENABLED } {
if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
{
return;
}
let (key, down) = match evt.event_type {
@@ -496,7 +498,7 @@ impl Handler {
}
#[inline]
fn save_config(&self, config: PeerConfig) {
pub(super) fn save_config(&self, config: PeerConfig) {
self.lc.write().unwrap().save_config(config);
}
@@ -505,7 +507,7 @@ impl Handler {
}
#[inline]
fn load_config(&self) -> PeerConfig {
pub(super) fn load_config(&self) -> PeerConfig {
load_config(&self.id)
}
@@ -523,6 +525,10 @@ impl Handler {
self.lc.read().unwrap().get_toggle_option(&name)
}
fn is_privacy_mode_supported(&self) -> bool {
self.lc.read().unwrap().is_privacy_mode_supported()
}
fn refresh_video(&mut self) {
self.send(Data::Message(LoginConfigHandler::refresh()));
}
@@ -865,17 +871,13 @@ impl Handler {
fn enter(&mut self) {
#[cfg(windows)]
crate::platform::windows::stop_system_key_propagate(true);
unsafe {
IS_IN = true;
}
IS_IN.store(true, Ordering::SeqCst);
}
fn leave(&mut self) {
#[cfg(windows)]
crate::platform::windows::stop_system_key_propagate(false);
unsafe {
IS_IN = false;
}
IS_IN.store(false, Ordering::SeqCst);
}
fn send_mouse(
@@ -1380,11 +1382,9 @@ impl Remote {
};
match Client::start(&self.handler.id, key, token, conn_type).await {
Ok((mut peer, direct)) => {
unsafe {
SERVER_KEYBOARD_ENABLED = true;
SERVER_CLIPBOARD_ENABLED = true;
SERVER_FILE_TRANSFER_ENABLED = true;
}
SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst);
SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst);
SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst);
self.handler
.call("setConnectionType", &make_args!(peer.is_secured(), direct));
@@ -1462,11 +1462,9 @@ impl Remote {
if let Some(stop) = stop_clipboard {
stop.send(()).ok();
}
unsafe {
SERVER_KEYBOARD_ENABLED = false;
SERVER_CLIPBOARD_ENABLED = false;
SERVER_FILE_TRANSFER_ENABLED = false;
}
SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst);
SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst);
SERVER_FILE_TRANSFER_ENABLED.store(false, Ordering::SeqCst);
}
fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option<String>) {
@@ -1518,8 +1516,8 @@ impl Remote {
}
_ => {}
}
if !unsafe { SERVER_CLIPBOARD_ENABLED }
|| !unsafe { SERVER_KEYBOARD_ENABLED }
if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst)
|| !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
|| lc.read().unwrap().disable_clipboard
{
continue;
@@ -1663,7 +1661,12 @@ impl Remote {
Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => {
let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version);
if is_remote {
log::debug!("new write waiting job {}, write to {} from remote {}", id, to, path);
log::debug!(
"new write waiting job {}, write to {} from remote {}",
id,
to,
path
);
let mut job = fs::TransferJob::new_write(
id,
path.clone(),
@@ -1711,15 +1714,27 @@ impl Remote {
if let Some(job) = get_job(id, &mut self.write_jobs) {
job.is_last_job = false;
allow_err!(
peer.send(&fs::new_send(id, job.remote.clone(), job.file_num, job.show_hidden))
.await
peer.send(&fs::new_send(
id,
job.remote.clone(),
job.file_num,
job.show_hidden
))
.await
);
}
} else {
if let Some(job) = get_job(id, &mut self.read_jobs) {
job.is_last_job = false;
allow_err!(peer.send(&fs::new_receive(id, job.path.to_string_lossy().to_string(),
job.file_num, job.files.clone())).await);
allow_err!(
peer.send(&fs::new_receive(
id,
job.path.to_string_lossy().to_string(),
job.file_num,
job.files.clone()
))
.await
);
}
}
}
@@ -1947,7 +1962,7 @@ impl Remote {
let json_str = serde_json::to_string(&job.gen_meta()).unwrap();
transfer_metas.write_jobs.push(json_str);
}
log::info!("meta: {:?}",transfer_metas);
log::info!("meta: {:?}", transfer_metas);
config.transfer = transfer_metas;
self.handler.save_config(config);
true
@@ -1978,8 +1993,8 @@ impl Remote {
self.check_clipboard_file_context();
if !(self.handler.is_file_transfer()
|| self.handler.is_port_forward()
|| !unsafe { SERVER_CLIPBOARD_ENABLED }
|| !unsafe { SERVER_KEYBOARD_ENABLED }
|| !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst)
|| !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
|| self.handler.lc.read().unwrap().disable_clipboard)
{
let txt = self.old_clipboard.lock().unwrap().clone();
@@ -2027,13 +2042,16 @@ impl Remote {
Some(message::Union::file_response(fr)) => {
match fr.union {
Some(file_response::Union::dir(fd)) => {
#[cfg(windows)]
let entries = fd.entries.to_vec();
#[cfg(not(windows))]
let mut entries = fd.entries.to_vec();
#[cfg(not(windows))]
{
if self.handler.peer_platform() == "Windows" {
fs::transform_windows_path(&mut entries);
}
}
}
let mut m = make_fd(fd.id, &entries, fd.id > 0);
if fd.id <= 0 {
m.set_item("path", fd.path);
@@ -2181,16 +2199,12 @@ impl Remote {
log::info!("Change permission {:?} -> {}", p.permission, p.enabled);
match p.permission.enum_value_or_default() {
Permission::Keyboard => {
unsafe {
SERVER_KEYBOARD_ENABLED = p.enabled;
}
SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst);
self.handler
.call2("setPermission", &make_args!("keyboard", p.enabled));
}
Permission::Clipboard => {
unsafe {
SERVER_CLIPBOARD_ENABLED = p.enabled;
}
SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst);
self.handler
.call2("setPermission", &make_args!("clipboard", p.enabled));
}
@@ -2199,9 +2213,7 @@ impl Remote {
.call2("setPermission", &make_args!("audio", p.enabled));
}
Permission::File => {
unsafe {
SERVER_FILE_TRANSFER_ENABLED = p.enabled;
}
SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst);
if !p.enabled && self.handler.is_file_transfer() {
return true;
}
@@ -2231,9 +2243,10 @@ impl Remote {
self.handler.msgbox("error", "Connection Error", &c);
return false;
}
Some(misc::Union::option_response(resp)) => {
self.handler
.msgbox("custom-error", "Option Error", &resp.error);
Some(misc::Union::back_notification(notification)) => {
if !self.handle_back_notification(notification).await {
return false;
}
}
_ => {}
},
@@ -2259,10 +2272,131 @@ impl Remote {
true
}
async fn handle_back_notification(&mut self, notification: BackNotification) -> bool {
match notification.union {
Some(back_notification::Union::block_input_state(state)) => {
self.handle_back_msg_block_input(
state.enum_value_or(back_notification::BlockInputState::StateUnknown),
)
.await;
}
Some(back_notification::Union::privacy_mode_state(state)) => {
if !self
.handle_back_msg_privacy_mode(
state.enum_value_or(back_notification::PrivacyModeState::StateUnknown),
)
.await
{
return false;
}
}
_ => {}
}
true
}
#[inline(always)]
fn update_block_input_state(&mut self, on: bool) {
self.handler.call("updateBlockInputState", &make_args!(on));
}
async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) {
match state {
back_notification::BlockInputState::OnSucceeded => {
self.update_block_input_state(true);
}
back_notification::BlockInputState::OnFailed => {
self.handler
.msgbox("custom-error", "Block user input", "Failed");
self.update_block_input_state(false);
}
back_notification::BlockInputState::OffSucceeded => {
self.update_block_input_state(false);
}
back_notification::BlockInputState::OffFailed => {
self.handler
.msgbox("custom-error", "Unblock user input", "Failed");
}
_ => {}
}
}
#[inline(always)]
fn update_privacy_mode(&mut self, on: bool) {
let mut config = self.handler.load_config();
config.privacy_mode = on;
self.handler.save_config(config);
self.handler.call("updatePrivacyMode", &[]);
}
async fn handle_back_msg_privacy_mode(
&mut self,
state: back_notification::PrivacyModeState,
) -> bool {
match state {
back_notification::PrivacyModeState::OnByOther => {
self.handler.msgbox(
"error",
"Connecting...",
"Someone turns on privacy mode, exit",
);
return false;
}
back_notification::PrivacyModeState::NotSupported => {
self.handler
.msgbox("custom-error", "Privacy mode", "Unsupported");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OnSucceeded => {
self.handler
.msgbox("custom-nocancel", "Privacy mode", "In privacy mode");
self.update_privacy_mode(true);
}
back_notification::PrivacyModeState::OnFailedDenied => {
self.handler
.msgbox("custom-error", "Privacy mode", "Peer denied");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OnFailedPlugin => {
self.handler
.msgbox("custom-error", "Privacy mode", "Please install plugins");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OnFailed => {
self.handler
.msgbox("custom-error", "Privacy mode", "Failed");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OffSucceeded => {
self.handler
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OffByPeer => {
self.handler
.msgbox("custom-error", "Privacy mode", "Peer exit");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OffFailed => {
self.handler
.msgbox("custom-error", "Privacy mode", "Failed to turn off");
}
back_notification::PrivacyModeState::OffUnknown => {
self.handler
.msgbox("custom-error", "Privacy mode", "Turned off");
// log::error!("Privacy mode is turned off with unknown reason");
self.update_privacy_mode(false);
}
_ => {}
}
true
}
fn check_clipboard_file_context(&mut self) {
#[cfg(windows)]
{
let enabled = unsafe { SERVER_FILE_TRANSFER_ENABLED }
let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst)
&& self.handler.lc.read().unwrap().enable_file_transfer;
if enabled == self.clipboard_file_context.is_none() {
self.clipboard_file_context = if enabled {
@@ -2347,6 +2481,7 @@ impl Interface for Handler {
} else if !self.is_port_forward() {
if pi.displays.is_empty() {
self.lc.write().unwrap().handle_peer_info(username, pi);
self.call("updatePrivacyMode", &[]);
self.msgbox("error", "Remote Error", "No Display");
return;
}
@@ -2385,6 +2520,7 @@ impl Interface for Handler {
}
}
self.lc.write().unwrap().handle_peer_info(username, pi);
self.call("updatePrivacyMode", &[]);
self.call("updatePi", &make_args!(pi_sciter));
if self.is_file_transfer() {
self.call2("closeSuccess", &make_args!());

571
src/ui/win_privacy.rs Normal file
View File

@@ -0,0 +1,571 @@
use crate::{
ipc::{connect, Data, PrivacyModeState},
platform::windows::get_user_token,
};
use hbb_common::{allow_err, bail, lazy_static, log, tokio, ResultType};
use std::{
ffi::CString,
sync::Mutex,
time::{Duration, Instant},
};
use winapi::{
ctypes::c_int,
shared::{
minwindef::{DWORD, FALSE, HMODULE, LOBYTE, LPARAM, LRESULT, UINT, WPARAM},
ntdef::{HANDLE, NULL},
windef::{HHOOK, HWND, POINT},
},
um::{
errhandlingapi::GetLastError,
handleapi::CloseHandle,
libloaderapi::{GetModuleHandleA, GetModuleHandleExA, GetProcAddress},
memoryapi::{VirtualAllocEx, WriteProcessMemory},
processthreadsapi::{
CreateProcessAsUserW, GetCurrentThreadId, QueueUserAPC, ResumeThread,
PROCESS_INFORMATION, STARTUPINFOW,
},
winbase::{WTSGetActiveConsoleSessionId, CREATE_SUSPENDED, DETACHED_PROCESS},
winnt::{MEM_COMMIT, PAGE_READWRITE},
winuser::*,
},
};
pub const ORIGIN_PROCESS_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe";
pub const INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe";
pub const PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow";
pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2;
pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 4;
const WM_USER_EXIT_HOOK: u32 = WM_USER + 1;
lazy_static::lazy_static! {
static ref DLL_FOUND: Mutex<bool> = Mutex::new(false);
static ref CONN_ID: Mutex<i32> = Mutex::new(0);
static ref CUR_HOOK_THREAD_ID: Mutex<DWORD> = Mutex::new(0);
static ref WND_HANDLERS: Mutex<WindowHandlers> = Mutex::new(WindowHandlers{hthread: 0, hprocess: 0});
}
struct WindowHandlers {
hthread: u64,
hprocess: u64,
}
impl Drop for WindowHandlers {
fn drop(&mut self) {
unsafe {
if self.hthread != 0 {
CloseHandle(self.hthread as _);
}
self.hthread = 0;
if self.hprocess != 0 {
CloseHandle(self.hprocess as _);
}
self.hprocess = 0;
}
}
}
pub fn turn_on_privacy(conn_id: i32) -> ResultType<bool> {
let exe_file = std::env::current_exe()?;
if let Some(cur_dir) = exe_file.parent() {
if !cur_dir.join("WindowInjection.dll").exists() {
return Ok(false);
}
} else {
bail!(
"Invalid exe parent for {}",
exe_file.to_string_lossy().as_ref()
);
}
if !*DLL_FOUND.lock().unwrap() {
log::info!("turn_on_privacy, dll not found when started, try start");
start()?;
std::thread::sleep(std::time::Duration::from_millis(1_000));
}
let pre_conn_id = *CONN_ID.lock().unwrap();
if pre_conn_id == conn_id {
return Ok(true);
}
if pre_conn_id != 0 {
bail!("Privacy occupied by another one");
}
let hwnd = wait_find_privacy_hwnd(0)?;
if hwnd.is_null() {
bail!("No privacy window created");
}
privacy_hook::hook()?;
unsafe {
ShowWindow(hwnd as _, SW_SHOW);
}
*CONN_ID.lock().unwrap() = conn_id;
Ok(true)
}
pub fn turn_off_privacy(conn_id: i32, state: Option<PrivacyModeState>) -> ResultType<()> {
let pre_conn_id = *CONN_ID.lock().unwrap();
if pre_conn_id != 0 && conn_id != 0 && pre_conn_id != conn_id {
bail!("Failed to turn off privacy mode that belongs to someone else")
}
privacy_hook::unhook()?;
unsafe {
let hwnd = wait_find_privacy_hwnd(0)?;
if !hwnd.is_null() {
ShowWindow(hwnd, SW_HIDE);
}
}
if pre_conn_id != 0 {
if let Some(state) = state {
allow_err!(set_privacy_mode_state(pre_conn_id, state, 1_000));
}
*CONN_ID.lock().unwrap() = 0;
}
Ok(())
}
pub fn start() -> ResultType<()> {
let mut wnd_handlers = WND_HANDLERS.lock().unwrap();
if wnd_handlers.hprocess != 0 {
return Ok(());
}
let exe_file = std::env::current_exe()?;
if exe_file.parent().is_none() {
bail!("Cannot get parent of current exe file");
}
let cur_dir = exe_file.parent().unwrap();
let dll_file = cur_dir.join("WindowInjection.dll");
if !dll_file.exists() {
bail!(
"Failed to find required file {}",
dll_file.to_string_lossy().as_ref()
);
}
*DLL_FOUND.lock().unwrap() = true;
let hwnd = wait_find_privacy_hwnd(1_000)?;
if !hwnd.is_null() {
log::info!("Privacy window is ready");
return Ok(());
}
// let cmdline = cur_dir.join("MiniBroker.exe").to_string_lossy().to_string();
let cmdline = cur_dir
.join(INJECTED_PROCESS_EXE)
.to_string_lossy()
.to_string();
unsafe {
let cmd_utf16: Vec<u16> = cmdline.encode_utf16().chain(Some(0).into_iter()).collect();
let mut start_info = STARTUPINFOW {
cb: 0,
lpReserved: NULL as _,
lpDesktop: NULL as _,
lpTitle: NULL as _,
dwX: 0,
dwY: 0,
dwXSize: 0,
dwYSize: 0,
dwXCountChars: 0,
dwYCountChars: 0,
dwFillAttribute: 0,
dwFlags: 0,
wShowWindow: 0,
cbReserved2: 0,
lpReserved2: NULL as _,
hStdInput: NULL as _,
hStdOutput: NULL as _,
hStdError: NULL as _,
};
let mut proc_info = PROCESS_INFORMATION {
hProcess: NULL as _,
hThread: NULL as _,
dwProcessId: 0,
dwThreadId: 0,
};
let session_id = WTSGetActiveConsoleSessionId();
let token = get_user_token(session_id, true);
if token.is_null() {
bail!("Failed to get token of current user");
}
let create_res = CreateProcessAsUserW(
token,
NULL as _,
cmd_utf16.as_ptr() as _,
NULL as _,
NULL as _,
FALSE,
CREATE_SUSPENDED | DETACHED_PROCESS,
NULL,
NULL as _,
&mut start_info,
&mut proc_info,
);
CloseHandle(token);
if 0 == create_res {
bail!(
"Failed to create privacy window process {}, code {}",
cmdline,
GetLastError()
);
};
inject_dll(
proc_info.hProcess,
proc_info.hThread,
dll_file.to_string_lossy().as_ref(),
)?;
if 0xffffffff == ResumeThread(proc_info.hThread) {
// CloseHandle
CloseHandle(proc_info.hThread);
CloseHandle(proc_info.hProcess);
bail!(
"Failed to create privacy window process, {}",
GetLastError()
);
}
wnd_handlers.hthread = proc_info.hThread as _;
wnd_handlers.hprocess = proc_info.hProcess as _;
let hwnd = wait_find_privacy_hwnd(1_000)?;
if hwnd.is_null() {
bail!("Failed to get hwnd after started");
}
}
Ok(())
}
unsafe fn inject_dll<'a>(hproc: HANDLE, hthread: HANDLE, dll_file: &'a str) -> ResultType<()> {
let dll_file_utf16: Vec<u16> = dll_file.encode_utf16().chain(Some(0).into_iter()).collect();
let buf = VirtualAllocEx(
hproc,
NULL as _,
dll_file_utf16.len() * 2,
MEM_COMMIT,
PAGE_READWRITE,
);
if buf.is_null() {
bail!("Failed VirtualAllocEx");
}
let mut written: usize = 0;
if 0 == WriteProcessMemory(
hproc,
buf,
dll_file_utf16.as_ptr() as _,
dll_file_utf16.len() * 2,
&mut written,
) {
bail!("Failed WriteProcessMemory");
}
let kernel32_modulename = CString::new("kernel32")?;
let hmodule = GetModuleHandleA(kernel32_modulename.as_ptr() as _);
if hmodule.is_null() {
bail!("Failed GetModuleHandleA");
}
let load_librarya_name = CString::new("LoadLibraryW")?;
let load_librarya = GetProcAddress(hmodule, load_librarya_name.as_ptr() as _);
if load_librarya.is_null() {
bail!("Failed GetProcAddress of LoadLibraryW");
}
if 0 == QueueUserAPC(Some(std::mem::transmute(load_librarya)), hthread, buf as _) {
bail!("Failed QueueUserAPC");
}
Ok(())
}
fn wait_find_privacy_hwnd(msecs: u128) -> ResultType<HWND> {
let tm_begin = Instant::now();
let wndname = CString::new(PRIVACY_WINDOW_NAME)?;
loop {
unsafe {
let hwnd = FindWindowA(NULL as _, wndname.as_ptr() as _);
if !hwnd.is_null() {
return Ok(hwnd);
}
}
if msecs == 0 || tm_begin.elapsed().as_millis() > msecs {
return Ok(NULL as _);
}
std::thread::sleep(Duration::from_millis(100));
}
}
pub fn is_process_consent_running() -> ResultType<bool> {
let output = std::process::Command::new("cmd")
.args(&["/C", "tasklist | findstr consent.exe"])
.output()?;
Ok(output.status.success() && !output.stdout.is_empty())
}
#[tokio::main(flavor = "current_thread")]
async fn set_privacy_mode_state(
conn_id: i32,
state: PrivacyModeState,
ms_timeout: u64,
) -> ResultType<()> {
let mut c = connect(ms_timeout, "_cm").await?;
c.send(&Data::PrivacyModeState((conn_id, state))).await
}
pub(super) mod privacy_hook {
use super::*;
use std::sync::mpsc::{channel, Sender};
fn do_hook(tx: Sender<String>) -> ResultType<(HHOOK, HHOOK)> {
let invalid_ret = (0 as HHOOK, 0 as HHOOK);
let mut cur_hook_thread_id = CUR_HOOK_THREAD_ID.lock().unwrap();
if *cur_hook_thread_id != 0 {
// unreachable!
tx.send("Already hooked".to_owned())?;
return Ok(invalid_ret);
}
unsafe {
let mut hm_keyboard = 0 as HMODULE;
if 0 == GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
DefWindowProcA as _,
&mut hm_keyboard as _,
) {
tx.send(format!(
"Failed to GetModuleHandleExA, error: {}",
GetLastError()
))?;
return Ok(invalid_ret);
}
let mut hm_mouse = 0 as HMODULE;
if 0 == GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
DefWindowProcA as _,
&mut hm_mouse as _,
) {
tx.send(format!(
"Failed to GetModuleHandleExA, error: {}",
GetLastError()
))?;
return Ok(invalid_ret);
}
let hook_keyboard = SetWindowsHookExA(
WH_KEYBOARD_LL,
Some(privacy_mode_hook_keyboard),
hm_keyboard,
0,
);
if hook_keyboard.is_null() {
tx.send(format!(" SetWindowsHookExA keyboard {}", GetLastError()))?;
return Ok(invalid_ret);
}
let hook_mouse =
SetWindowsHookExA(WH_MOUSE_LL, Some(privacy_mode_hook_mouse), hm_mouse, 0);
if hook_mouse.is_null() {
if FALSE == UnhookWindowsHookEx(hook_keyboard) {
// Fatal error
log::error!(" UnhookWindowsHookEx keyboard {}", GetLastError());
}
tx.send(format!(" SetWindowsHookExA mouse {}", GetLastError()))?;
return Ok(invalid_ret);
}
*cur_hook_thread_id = GetCurrentThreadId();
tx.send("".to_owned())?;
return Ok((hook_keyboard, hook_mouse));
}
}
pub fn hook() -> ResultType<()> {
let (tx, rx) = channel();
std::thread::spawn(move || {
let hook_keyboard;
let hook_mouse;
unsafe {
match do_hook(tx.clone()) {
Ok(hooks) => {
hook_keyboard = hooks.0;
hook_mouse = hooks.1;
}
Err(e) => {
// Fatal error
tx.send(format!("Unexpected err when hook {}", e)).unwrap();
return;
}
}
if hook_keyboard.is_null() {
return;
}
let mut msg = MSG {
hwnd: NULL as _,
message: 0 as _,
wParam: 0 as _,
lParam: 0 as _,
time: 0 as _,
pt: POINT {
x: 0 as _,
y: 0 as _,
},
};
while FALSE != GetMessageA(&mut msg, NULL as _, 0, 0) {
if msg.message == WM_USER_EXIT_HOOK {
break;
}
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
if FALSE == UnhookWindowsHookEx(hook_keyboard as _) {
// Fatal error
log::error!("Failed UnhookWindowsHookEx keyboard {}", GetLastError());
}
if FALSE == UnhookWindowsHookEx(hook_mouse as _) {
// Fatal error
log::error!("Failed UnhookWindowsHookEx mouse {}", GetLastError());
}
*CUR_HOOK_THREAD_ID.lock().unwrap() = 0;
}
});
match rx.recv() {
Ok(msg) => {
if msg == "" {
Ok(())
} else {
bail!(msg)
}
}
Err(e) => {
bail!("Failed to wait hook result {}", e)
}
}
}
pub fn unhook() -> ResultType<()> {
unsafe {
let cur_hook_thread_id = CUR_HOOK_THREAD_ID.lock().unwrap();
if *cur_hook_thread_id != 0 {
if FALSE == PostThreadMessageA(*cur_hook_thread_id, WM_USER_EXIT_HOOK, 0, 0) {
bail!("Failed to post message to exit hook, {}", GetLastError());
}
}
}
Ok(())
}
#[no_mangle]
pub extern "system" fn privacy_mode_hook_keyboard(
code: c_int,
w_param: WPARAM,
l_param: LPARAM,
) -> LRESULT {
if code < 0 {
unsafe {
return CallNextHookEx(NULL as _, code, w_param, l_param);
}
}
let ks = l_param as PKBDLLHOOKSTRUCT;
let w_param2 = w_param as UINT;
unsafe {
if (*ks).dwExtraInfo != enigo::ENIGO_INPUT_EXTRA_VALUE {
// Disable alt key. Alt + Tab will switch windows.
if (*ks).flags & LLKHF_ALTDOWN == LLKHF_ALTDOWN {
return 1;
}
match w_param2 {
WM_KEYDOWN => {
// Disable all keys other than P and Ctrl.
if ![80, 162, 163].contains(&(*ks).vkCode) {
return 1;
}
// NOTE: GetKeyboardState may not work well...
// Check if Ctrl + P is pressed
let cltr_down = (GetKeyState(VK_CONTROL) as u16) & (0x8000 as u16) > 0;
let key = LOBYTE((*ks).vkCode as _);
if cltr_down && (key == 'p' as u8 || key == 'P' as u8) {
// Ctrl + P is pressed, turn off privacy mode
if let Err(e) =
turn_off_privacy(0, Some(crate::ipc::PrivacyModeState::OffByPeer))
{
log::error!("Failed to off_privacy {}", e);
}
}
}
WM_KEYUP => {
log::trace!("WM_KEYUP {}", (*ks).vkCode);
}
_ => {
log::trace!("KEYBOARD OTHER {} {}", w_param2, (*ks).vkCode);
}
}
}
}
unsafe { CallNextHookEx(NULL as _, code, w_param, l_param) }
}
#[no_mangle]
pub extern "system" fn privacy_mode_hook_mouse(
code: c_int,
w_param: WPARAM,
l_param: LPARAM,
) -> LRESULT {
if code < 0 {
unsafe {
return CallNextHookEx(NULL as _, code, w_param, l_param);
}
}
let ms = l_param as PMOUSEHOOKSTRUCT;
unsafe {
if (*ms).dwExtraInfo != enigo::ENIGO_INPUT_EXTRA_VALUE {
return 1;
}
}
unsafe { CallNextHookEx(NULL as _, code, w_param, l_param) }
}
}
mod test {
#[test]
fn privacy_hook() {
//use super::*;
// privacy_hook::hook().unwrap();
// std::thread::sleep(std::time::Duration::from_millis(50));
// privacy_hook::unhook().unwrap();
}
}

View File

@@ -53,26 +53,28 @@ DWORD GetLogonPid(DWORD dwSessionId, BOOL as_user)
return dwLogonPid;
}
// if should try WTSQueryUserToken?
// https://stackoverflow.com/questions/7285666/example-code-a-service-calls-createprocessasuser-i-want-the-process-to-run-in
BOOL GetSessionUserTokenWin(OUT LPHANDLE lphUserToken, DWORD dwSessionId, BOOL as_user)
{
BOOL bResult = FALSE;
DWORD Id = GetLogonPid(dwSessionId, as_user);
if (HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Id))
{
bResult = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, lphUserToken);
CloseHandle(hProcess);
}
return bResult;
}
// START the app as system
extern "C"
{
// if should try WTSQueryUserToken?
// https://stackoverflow.com/questions/7285666/example-code-a-service-calls-createprocessasuser-i-want-the-process-to-run-in
BOOL GetSessionUserTokenWin(OUT LPHANDLE lphUserToken, DWORD dwSessionId, BOOL as_user)
{
BOOL bResult = FALSE;
DWORD Id = GetLogonPid(dwSessionId, as_user);
if (HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Id))
{
bResult = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, lphUserToken);
CloseHandle(hProcess);
}
return bResult;
}
bool is_windows_server()
{
return IsWindowsServer();
}
HANDLE LaunchProcessWin(LPCWSTR cmd, DWORD dwSessionId, BOOL as_user)
{
HANDLE hProcess = NULL;