From 7f50fe3ea0d38c0e5106b0428c9aaeb4f10a5f25 Mon Sep 17 00:00:00 2001
From: Saverio Miroddi
Date: Sun, 22 May 2022 18:03:38 +0200
Subject: [PATCH 001/120] Remove KEYBOARD_HOOKED unsafe code, by using
AtomicBool
For supported types, static R/W globals unsafe code can be replaced by safe `Atomic*` types.
The pattern of usage is simple:
- AtomicBool#swap is used to fetch the old `KEYBOARD_HOOKED` value, while setting it to true;
- if the old value was true, there is effectively no change to `KEYBOARD_HOOKED`, and the flow exits from the enclosing function;
- if the old value was false, execute the function (the new `KEYBOARD_HOOKED` has been set to true by swap()).
The most conservative ordering is used, as the context is not performance-sensitive.
Atomics are not supported on every platform, but the project assumes x86-64, which supports them.
---
src/ui/remote.rs | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/ui/remote.rs b/src/ui/remote.rs
index ac32726aa..b4881954b 100644
--- a/src/ui/remote.rs
+++ b/src/ui/remote.rs
@@ -1,7 +1,10 @@
use std::{
collections::HashMap,
ops::Deref,
- sync::{Arc, Mutex, RwLock},
+ sync::{
+ atomic::{AtomicBool, Ordering},
+ Arc, Mutex, RwLock,
+ },
};
use sciter::{
@@ -64,7 +67,7 @@ fn get_key_state(key: enigo::Key) -> bool {
}
static mut IS_IN: bool = false;
-static mut KEYBOARD_HOOKED: bool = false;
+static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
static mut SERVER_KEYBOARD_ENABLED: bool = true;
static mut SERVER_FILE_TRANSFER_ENABLED: bool = true;
static mut SERVER_CLIPBOARD_ENABLED: bool = true;
@@ -249,12 +252,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();
From c7f452752d72834dddeb090889b7a0b789a0a3c9 Mon Sep 17 00:00:00 2001
From: Saverio Miroddi
Date: Tue, 24 May 2022 18:02:33 +0200
Subject: [PATCH 002/120] Remove SERVER_CLIPBOARD_ENABLED unsafe code, by using
AtomicBool
---
src/ui/remote.rs | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/src/ui/remote.rs b/src/ui/remote.rs
index b4881954b..3340632e4 100644
--- a/src/ui/remote.rs
+++ b/src/ui/remote.rs
@@ -70,7 +70,7 @@ static mut IS_IN: bool = false;
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
static mut SERVER_KEYBOARD_ENABLED: bool = true;
static mut SERVER_FILE_TRANSFER_ENABLED: bool = true;
-static mut SERVER_CLIPBOARD_ENABLED: bool = true;
+static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
#[cfg(windows)]
static mut IS_ALT_GR: bool = false;
@@ -1382,7 +1382,7 @@ impl Remote {
Ok((mut peer, direct)) => {
unsafe {
SERVER_KEYBOARD_ENABLED = true;
- SERVER_CLIPBOARD_ENABLED = true;
+ SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst);
SERVER_FILE_TRANSFER_ENABLED = true;
}
self.handler
@@ -1464,7 +1464,7 @@ impl Remote {
}
unsafe {
SERVER_KEYBOARD_ENABLED = false;
- SERVER_CLIPBOARD_ENABLED = false;
+ SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst);
SERVER_FILE_TRANSFER_ENABLED = false;
}
}
@@ -1518,7 +1518,7 @@ impl Remote {
}
_ => {}
}
- if !unsafe { SERVER_CLIPBOARD_ENABLED }
+ if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst)
|| !unsafe { SERVER_KEYBOARD_ENABLED }
|| lc.read().unwrap().disable_clipboard
{
@@ -1712,7 +1712,7 @@ impl Remote {
job.is_last_job = false;
allow_err!(
peer.send(&fs::new_send(id, job.remote.clone(), job.file_num, job.show_hidden))
- .await
+ .await
);
}
} else {
@@ -1947,7 +1947,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,7 +1978,7 @@ impl Remote {
self.check_clipboard_file_context();
if !(self.handler.is_file_transfer()
|| self.handler.is_port_forward()
- || !unsafe { SERVER_CLIPBOARD_ENABLED }
+ || !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst)
|| !unsafe { SERVER_KEYBOARD_ENABLED }
|| self.handler.lc.read().unwrap().disable_clipboard)
{
@@ -2033,7 +2033,7 @@ impl Remote {
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);
@@ -2188,9 +2188,7 @@ impl Remote {
.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));
}
From 45bb271c883e304071b653017153343761026445 Mon Sep 17 00:00:00 2001
From: Saverio Miroddi
Date: Tue, 24 May 2022 18:12:28 +0200
Subject: [PATCH 003/120] Remove SERVER_KEYBOARD_ENABLED unsafe code, by using
AtomicBool
---
src/ui/remote.rs | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/src/ui/remote.rs b/src/ui/remote.rs
index 3340632e4..bfac5c74e 100644
--- a/src/ui/remote.rs
+++ b/src/ui/remote.rs
@@ -68,7 +68,7 @@ fn get_key_state(key: enigo::Key) -> bool {
static mut IS_IN: bool = false;
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
-static mut SERVER_KEYBOARD_ENABLED: bool = true;
+static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
static mut SERVER_FILE_TRANSFER_ENABLED: bool = true;
static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
#[cfg(windows)]
@@ -266,7 +266,7 @@ 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 unsafe { !IS_IN || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) } {
return;
}
let (key, down) = match evt.event_type {
@@ -1381,7 +1381,7 @@ impl Remote {
match Client::start(&self.handler.id, key, token, conn_type).await {
Ok((mut peer, direct)) => {
unsafe {
- SERVER_KEYBOARD_ENABLED = true;
+ SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst);
SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst);
SERVER_FILE_TRANSFER_ENABLED = true;
}
@@ -1463,7 +1463,7 @@ impl Remote {
stop.send(()).ok();
}
unsafe {
- SERVER_KEYBOARD_ENABLED = false;
+ SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst);
SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst);
SERVER_FILE_TRANSFER_ENABLED = false;
}
@@ -1519,7 +1519,7 @@ impl Remote {
_ => {}
}
if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst)
- || !unsafe { SERVER_KEYBOARD_ENABLED }
+ || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
|| lc.read().unwrap().disable_clipboard
{
continue;
@@ -1979,7 +1979,7 @@ impl Remote {
if !(self.handler.is_file_transfer()
|| self.handler.is_port_forward()
|| !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst)
- || !unsafe { SERVER_KEYBOARD_ENABLED }
+ || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst)
|| self.handler.lc.read().unwrap().disable_clipboard)
{
let txt = self.old_clipboard.lock().unwrap().clone();
@@ -2181,9 +2181,7 @@ 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));
}
From 0c0051d59acbf10d360e03aff44d0c36dd779b79 Mon Sep 17 00:00:00 2001
From: Saverio Miroddi
Date: Tue, 24 May 2022 18:15:46 +0200
Subject: [PATCH 004/120] Remove SERVER_FILE_TRANSFER_ENABLED unsafe code, by
using AtomicBool
---
src/ui/remote.rs | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/src/ui/remote.rs b/src/ui/remote.rs
index bfac5c74e..c535f6b52 100644
--- a/src/ui/remote.rs
+++ b/src/ui/remote.rs
@@ -69,7 +69,7 @@ fn get_key_state(key: enigo::Key) -> bool {
static mut IS_IN: bool = false;
static KEYBOARD_HOOKED: AtomicBool = AtomicBool::new(false);
static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
-static mut SERVER_FILE_TRANSFER_ENABLED: bool = 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;
@@ -1380,11 +1380,9 @@ impl Remote {
};
match Client::start(&self.handler.id, key, token, conn_type).await {
Ok((mut peer, direct)) => {
- unsafe {
- SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst);
- SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst);
- 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 +1460,9 @@ impl Remote {
if let Some(stop) = stop_clipboard {
stop.send(()).ok();
}
- unsafe {
- SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst);
- SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst);
- 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) {
@@ -2195,9 +2191,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;
}
@@ -2258,7 +2252,7 @@ impl Remote {
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 {
From 230f74da2ec700b3183b0af4c611263d7579ac64 Mon Sep 17 00:00:00 2001
From: Saverio Miroddi
Date: Tue, 24 May 2022 18:18:38 +0200
Subject: [PATCH 005/120] Remove IS_IN unsafe code, by using AtomicBool
---
src/ui/remote.rs | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/src/ui/remote.rs b/src/ui/remote.rs
index c535f6b52..80d85acd7 100644
--- a/src/ui/remote.rs
+++ b/src/ui/remote.rs
@@ -66,7 +66,7 @@ fn get_key_state(key: enigo::Key) -> bool {
ENIGO.lock().unwrap().get_key_state(key)
}
-static mut IS_IN: bool = false;
+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);
@@ -266,7 +266,7 @@ 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.load(Ordering::SeqCst) } {
+ if !IS_IN.load(Ordering::SeqCst) || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) {
return;
}
let (key, down) = match evt.event_type {
@@ -865,17 +865,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(
From 34f5aa303b093e78fccd1dfd39ea5ff5a34302de Mon Sep 17 00:00:00 2001
From: Saverio Miroddi
Date: Tue, 24 May 2022 19:30:56 +0200
Subject: [PATCH 006/120] Add pynput installation section to README
Prevents flooding the server log with warnings.
Closes #591.
---
README.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/README.md b/README.md
index 43e23bd60..a948361ea 100644
--- a/README.md
+++ b/README.md
@@ -72,6 +72,12 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio
```
+### Install pynput package
+
+```sh
+pip3 install pynput
+```
+
### Install vcpkg
```sh
From ba5321c4a030103f067fcd248d959fa75524267f Mon Sep 17 00:00:00 2001
From: Saverio Miroddi
Date: Tue, 24 May 2022 19:37:01 +0200
Subject: [PATCH 007/120] Strip release binary via Rust toolchain
As of Rust 1.59, full stripping support has been added (see https://blog.rust-lang.org/2022/02/24/Rust-1.59.0.html#creating-stripped-binaries).
---
Cargo.toml | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 68ba2ab3f..4b5b5dd29 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -131,10 +131,9 @@ osx_minimum_system_version = "10.14"
resources = ["mac-tray.png"]
#https://github.com/johnthagen/min-sized-rust
-#!!! rembember call "strip target/release/rustdesk"
-# which reduce binary size a lot
[profile.release]
lto = true
codegen-units = 1
panic = 'abort'
+strip = true
#opt-level = 'z' # only have smaller size after strip
From c519ade08808323fa8ddf91e1534233f553fb843 Mon Sep 17 00:00:00 2001
From: Asura <99897242+asur4s@users.noreply.github.com>
Date: Wed, 25 May 2022 17:49:41 +0800
Subject: [PATCH 008/120] Add label for feature request
---
.github/ISSUE_TEMPLATE/feature_request.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 9e02f555b..0d21f017d 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -2,7 +2,7 @@
name: Feature Request
about: Suggest an idea for this project ((English only, Please).
title: ''
-labels: feature-request
+labels: enhancement
assignees: ''
---
From c7f36c44814be84e320a7fa0204bbe9925003cc4 Mon Sep 17 00:00:00 2001
From: Asura
Date: Wed, 25 May 2022 19:14:13 -0700
Subject: [PATCH 009/120] --release instead of ---release
---
README-EO.md | 2 +-
README-ES.md | 2 +-
README-FI.md | 2 +-
README-FR.md | 2 +-
README-ID.md | 2 +-
README-IT.md | 2 +-
README-JP.md | 2 +-
README-KR.md | 2 +-
README-ML.md | 2 +-
README-NL.md | 2 +-
README-PL.md | 2 +-
README-RU.md | 2 +-
README-ZH.md | 2 +-
README.md | 2 +-
14 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/README-EO.md b/README-EO.md
index 6cfc1e1ac..f37335341 100644
--- a/README-EO.md
+++ b/README-EO.md
@@ -125,7 +125,7 @@ Tiam, ĉiuj fojoj, kiuj vi bezonas kompili la programon, plenumu tiun komandon:
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Notu, ke la unua kompilado povas daŭri longe, antaŭ ke la dependantaĵoj estu kaŝitaj, sekvaj kompiladoj estos pli rapidaj. Aldone, se vi bezonas specifi diferentajn argumentojn por la kompilkomando, vi povas fari ĝin en la fine de la komando, en la posicio ``. Ekzemple, se vi volas kompili version de eldono optimigita, vi plenumus la komandon supre, kun `---release`. La plenumebla dosiero disponeblos en la cela dosierujo sur via sistemo, kaj povos esti plenumita kun:
+Notu, ke la unua kompilado povas daŭri longe, antaŭ ke la dependantaĵoj estu kaŝitaj, sekvaj kompiladoj estos pli rapidaj. Aldone, se vi bezonas specifi diferentajn argumentojn por la kompilkomando, vi povas fari ĝin en la fine de la komando, en la posicio ``. Ekzemple, se vi volas kompili version de eldono optimigita, vi plenumus la komandon supre, kun `--release`. La plenumebla dosiero disponeblos en la cela dosierujo sur via sistemo, kaj povos esti plenumita kun:
```sh
target/debug/rustdesk
diff --git a/README-ES.md b/README-ES.md
index 84bfcd88c..07aad1139 100644
--- a/README-ES.md
+++ b/README-ES.md
@@ -124,7 +124,7 @@ Entonces, cada vez que necesites compilar una modificación, ejecuta el siguient
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Ten en cuenta que la primera compilación puede tardar más tiempo antes de que las dependencias se almacenen en la caché, las siguientes compilaciones serán más rápidas. Además, si necesitas especificar diferentes argumentos a la orden de compilación, puede hacerlo al final de la linea de comandos en el apartado``. Por ejemplo, si desea compilar una versión optimizada para publicación, deberá ejecutar el comando anterior seguido de `---release`. El ejecutable resultante estará disponible en la carpeta de destino en su sistema, y puede ser ejecutado con:
+Ten en cuenta que la primera compilación puede tardar más tiempo antes de que las dependencias se almacenen en la caché, las siguientes compilaciones serán más rápidas. Además, si necesitas especificar diferentes argumentos a la orden de compilación, puede hacerlo al final de la linea de comandos en el apartado``. Por ejemplo, si desea compilar una versión optimizada para publicación, deberá ejecutar el comando anterior seguido de `--release`. El ejecutable resultante estará disponible en la carpeta de destino en su sistema, y puede ser ejecutado con:
```sh
target/debug/rustdesk
diff --git a/README-FI.md b/README-FI.md
index fc208b383..0bba302c1 100644
--- a/README-FI.md
+++ b/README-FI.md
@@ -125,7 +125,7 @@ Sitten, joka kerta kun sinun on rakennettava sovellus, aja seuraava komento:
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri argumentteja rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa `-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa argumentti`---release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
+Huomaa, että ensimmäinen rakentaminen saattaa kestää pitempään ennen kuin riippuvuudet on siirretty välimuistiin, seuraavat rakentamiset ovat nopeampia. Lisäksi, jos sinun on määritettävä eri argumentteja rakentamiskomennolle, saatat tehdä sen niin, että komennon lopussa `-kohdassa. Esimerkiksi, jos haluat rakentaa optimoidun julkaisuversion, sinun on ajettava komento yllä siten, että sitä seuraa argumentti`--release`. Suoritettava tiedosto on saatavilla järjestelmäsi kohdehakemistossa, ja se voidaan suorittaa seuraavan kera:
```sh
target/debug/rustdesk
diff --git a/README-FR.md b/README-FR.md
index bc7ebfd8d..514970321 100644
--- a/README-FR.md
+++ b/README-FR.md
@@ -124,7 +124,7 @@ Ensuite, chaque fois que vous devez compiler le logiciel, exécutez la commande
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Notez que la première compilation peut prendre plus de temps avant que les dépendances ne soient mises en cache, les compilations suivantes seront plus rapides. De plus, si vous devez spécifier différents arguments à la commande de compilation, vous pouvez le faire à la fin de la commande à la position ``. Par exemple, si vous voulez compiler une version de release optimisée, vous devez exécuter la commande ci-dessus suivie de `---release`. L'exécutable résultant sera disponible dans le dossier cible sur votre système, et peut être lancé avec :
+Notez que la première compilation peut prendre plus de temps avant que les dépendances ne soient mises en cache, les compilations suivantes seront plus rapides. De plus, si vous devez spécifier différents arguments à la commande de compilation, vous pouvez le faire à la fin de la commande à la position ``. Par exemple, si vous voulez compiler une version de release optimisée, vous devez exécuter la commande ci-dessus suivie de `--release`. L'exécutable résultant sera disponible dans le dossier cible sur votre système, et peut être lancé avec :
```sh
target/debug/rustdesk
diff --git a/README-ID.md b/README-ID.md
index c1ccfb345..874be8ee0 100644
--- a/README-ID.md
+++ b/README-ID.md
@@ -125,7 +125,7 @@ Kemudian, setiap kali Anda perlu build aplikasi, jalankan perintah berikut:
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Perhatikan bahwa build pertama mungkin memerlukan waktu lebih lama sebelum dependensi di-cache, build berikutnya akan lebih cepat. Selain itu, jika Anda perlu menentukan argumen yang berbeda untuk perintah build, Anda dapat melakukannya di akhir perintah di posisi ``. Misalnya, jika Anda ingin membangun versi rilis yang dioptimalkan, Anda akan menjalankan perintah di atas diikuti oleh `---release`. Hasil eksekusi akan tersedia pada target folder di sistem anda, dan dapat dijalankan dengan:
+Perhatikan bahwa build pertama mungkin memerlukan waktu lebih lama sebelum dependensi di-cache, build berikutnya akan lebih cepat. Selain itu, jika Anda perlu menentukan argumen yang berbeda untuk perintah build, Anda dapat melakukannya di akhir perintah di posisi ``. Misalnya, jika Anda ingin membangun versi rilis yang dioptimalkan, Anda akan menjalankan perintah di atas diikuti oleh `--release`. Hasil eksekusi akan tersedia pada target folder di sistem anda, dan dapat dijalankan dengan:
```sh
target/debug/rustdesk
diff --git a/README-IT.md b/README-IT.md
index cf74924e6..6d9e52a82 100644
--- a/README-IT.md
+++ b/README-IT.md
@@ -125,7 +125,7 @@ Quindi, ogni volta che devi compilare l'applicazione, esegui il comando seguente
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci. Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione ``. Ad esempio, se si desidera creare una versione di rilascio ottimizzata, eseguire il comando sopra seguito da `---release`. L'eseguibile generato sarà creato nella cartella di destinazione del proprio sistema e può essere eseguito con:
+Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci. Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione ``. Ad esempio, se si desidera creare una versione di rilascio ottimizzata, eseguire il comando sopra seguito da `--release`. L'eseguibile generato sarà creato nella cartella di destinazione del proprio sistema e può essere eseguito con:
```sh
target/debug/rustdesk
diff --git a/README-JP.md b/README-JP.md
index 3c2b6c686..e30c6ec0d 100644
--- a/README-JP.md
+++ b/README-JP.md
@@ -127,7 +127,7 @@ docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user
```
なお、最初のビルドでは、依存関係がキャッシュされるまで時間がかかることがありますが、その後のビルドではより速くなります。さらに、ビルドコマンドに別の引数を指定する必要がある場合は、コマンドの最後にある `` の位置で指定することができます。例えば、最適化されたリリースバージョンをビルドしたい場合は、上記のコマンドの後に
-`---release` を実行します。できあがった実行ファイルは、システムのターゲット・フォルダに格納され、次のコマンドで実行できます。
+`--release` を実行します。できあがった実行ファイルは、システムのターゲット・フォルダに格納され、次のコマンドで実行できます。
```sh
target/debug/rustdesk
diff --git a/README-KR.md b/README-KR.md
index fb90b9d99..9d259ab4c 100644
--- a/README-KR.md
+++ b/README-KR.md
@@ -130,7 +130,7 @@ docker build -t "rustdesk-builder" .
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-첫 빌드에서는 의존관계가 캐시될 때까지 시간이 거릴 수 있습니다만, 이후의 빌드때는 빨라집니다. 더불어 빌드 커맨드에 다른 인수를 지정할 필요가 있다면, 커맨드 끝에 있는 `` 에 지정할 수 있습니다. 예를 들어 최적화된 출시 버전을 빌드하고 싶다면 이렇게 상기한 커맨드 뒤에 `---release` 를 붙여 실행합니다. 성공했다면 실행파일은 시스템 타겟 폴더에 담겨지고, 다음 커맨드로 실행할 수 있습니다.
+첫 빌드에서는 의존관계가 캐시될 때까지 시간이 거릴 수 있습니다만, 이후의 빌드때는 빨라집니다. 더불어 빌드 커맨드에 다른 인수를 지정할 필요가 있다면, 커맨드 끝에 있는 `` 에 지정할 수 있습니다. 예를 들어 최적화된 출시 버전을 빌드하고 싶다면 이렇게 상기한 커맨드 뒤에 `--release` 를 붙여 실행합니다. 성공했다면 실행파일은 시스템 타겟 폴더에 담겨지고, 다음 커맨드로 실행할 수 있습니다.
```sh
target/debug/rustdesk
diff --git a/README-ML.md b/README-ML.md
index 6dae55486..a40b70c37 100644
--- a/README-ML.md
+++ b/README-ML.md
@@ -125,7 +125,7 @@ docker build -t "rustdesk-builder" .
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-ഡിപൻഡൻസികൾ കാഷെ ചെയ്യുന്നതിനുമുമ്പ് ആദ്യ ബിൽഡ് കൂടുതൽ സമയമെടുത്തേക്കാം, തുടർന്നുള്ള ബിൽഡുകൾ വേഗത്തിലാകും. കൂടാതെ, നിങ്ങൾക്ക് ബിൽഡ് കമാൻഡിലേക്ക് വ്യത്യസ്ത ആർഗ്യുമെന്റുകൾ വ്യക്തമാക്കണമെങ്കിൽ, കമാൻഡിന്റെ അവസാനം `` സ്ഥാനത്ത് നിങ്ങൾക്ക് അങ്ങനെ ചെയ്യാം. ഉദാഹരണത്തിന്, നിങ്ങൾ ഒരു ഒപ്റ്റിമൈസ് ചെയ്ത റിലീസ് പതിപ്പ് നിർമ്മിക്കാൻ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, മുകളിലുള്ള കമാൻഡ് തുടർന്ന് `---release` നിങ്ങൾ പ്രവർത്തിപ്പിക്കും. തത്ഫലമായുണ്ടാകുന്ന എക്സിക്യൂട്ടബിൾ നിങ്ങളുടെ സിസ്റ്റത്തിലെ ടാർഗെറ്റ് ഫോൾഡറിൽ ലഭ്യമാകും, കൂടാതെ ഇത് ഉപയോഗിച്ച് പ്രവർത്തിപ്പിക്കാം:
+ഡിപൻഡൻസികൾ കാഷെ ചെയ്യുന്നതിനുമുമ്പ് ആദ്യ ബിൽഡ് കൂടുതൽ സമയമെടുത്തേക്കാം, തുടർന്നുള്ള ബിൽഡുകൾ വേഗത്തിലാകും. കൂടാതെ, നിങ്ങൾക്ക് ബിൽഡ് കമാൻഡിലേക്ക് വ്യത്യസ്ത ആർഗ്യുമെന്റുകൾ വ്യക്തമാക്കണമെങ്കിൽ, കമാൻഡിന്റെ അവസാനം `` സ്ഥാനത്ത് നിങ്ങൾക്ക് അങ്ങനെ ചെയ്യാം. ഉദാഹരണത്തിന്, നിങ്ങൾ ഒരു ഒപ്റ്റിമൈസ് ചെയ്ത റിലീസ് പതിപ്പ് നിർമ്മിക്കാൻ ആഗ്രഹിക്കുന്നുവെങ്കിൽ, മുകളിലുള്ള കമാൻഡ് തുടർന്ന് `--release` നിങ്ങൾ പ്രവർത്തിപ്പിക്കും. തത്ഫലമായുണ്ടാകുന്ന എക്സിക്യൂട്ടബിൾ നിങ്ങളുടെ സിസ്റ്റത്തിലെ ടാർഗെറ്റ് ഫോൾഡറിൽ ലഭ്യമാകും, കൂടാതെ ഇത് ഉപയോഗിച്ച് പ്രവർത്തിപ്പിക്കാം:
```sh
target/debug/rustdesk
diff --git a/README-NL.md b/README-NL.md
index 2fdf77ca0..a493911b6 100644
--- a/README-NL.md
+++ b/README-NL.md
@@ -125,7 +125,7 @@ Voer vervolgens de volgende commando's uit iedere keer dat je de applicatie opni
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Let op dat de eerste build langer kan duren omdat de dependencies nog niet zijn gecached; latere builds zullen sneller zijn. Als je extra command line arguments wilt toevoegen aan het build-commando, dan kun je dat doen aan het einde van de opdrachtregel in plaats van ``. Bijvoorbeeld: als je een geoptimaliseerde releaseversie wilt bouwen, draai dan het bovenstaande commando gevolgd door `---release`.
+Let op dat de eerste build langer kan duren omdat de dependencies nog niet zijn gecached; latere builds zullen sneller zijn. Als je extra command line arguments wilt toevoegen aan het build-commando, dan kun je dat doen aan het einde van de opdrachtregel in plaats van ``. Bijvoorbeeld: als je een geoptimaliseerde releaseversie wilt bouwen, draai dan het bovenstaande commando gevolgd door `--release`.
Het uitvoerbare bestand, in debug-modus, zal verschijnen in de target-map, en kan als volgt worden uitgevoerd:
diff --git a/README-PL.md b/README-PL.md
index a43dfc165..1a6a072a6 100644
--- a/README-PL.md
+++ b/README-PL.md
@@ -125,7 +125,7 @@ Następnie, za każdym razem, gdy potrzebujesz skompilować aplikację, uruchom
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Zauważ, że pierwsza kompilacja może potrwać dłużej zanim zależności zostaną zbuforowane, kolejne będą szybsze. Dodatkowo, jeśli potrzebujesz określić inne argumenty dla polecenia budowania, możesz to zrobić na końcu komendy w miejscu ``. Na przykład, jeśli chciałbyś zbudować zoptymalizowaną wersję wydania, uruchomiłbyś powyższą komendę a następnie `---release`. Powstały plik wykonywalny będzie dostępny w folderze docelowym w twoim systemie, i może być uruchomiony z:
+Zauważ, że pierwsza kompilacja może potrwać dłużej zanim zależności zostaną zbuforowane, kolejne będą szybsze. Dodatkowo, jeśli potrzebujesz określić inne argumenty dla polecenia budowania, możesz to zrobić na końcu komendy w miejscu ``. Na przykład, jeśli chciałbyś zbudować zoptymalizowaną wersję wydania, uruchomiłbyś powyższą komendę a następnie `--release`. Powstały plik wykonywalny będzie dostępny w folderze docelowym w twoim systemie, i może być uruchomiony z:
```sh
target/debug/rustdesk
diff --git a/README-RU.md b/README-RU.md
index b1a6accfe..f1c74b33d 100644
--- a/README-RU.md
+++ b/README-RU.md
@@ -127,7 +127,7 @@ docker build -t "rustdesk-builder" .
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Обратите внимание, что первая сборка может занять больше времени, прежде чем зависимости будут кэшированы, но последующие сборки будут выполняться быстрее. Кроме того, если вам нужно указать другие аргументы для команды сборки, вы можете сделать это в конце команды в переменной ``. Например, если вы хотите создать оптимизированную версию, вы должны запустить приведенную выше команду и в конце строки добавить `---release`. Полученный исполняемый файл будет доступен в целевой папке вашей системы и может быть запущен с помощью:
+Обратите внимание, что первая сборка может занять больше времени, прежде чем зависимости будут кэшированы, но последующие сборки будут выполняться быстрее. Кроме того, если вам нужно указать другие аргументы для команды сборки, вы можете сделать это в конце команды в переменной ``. Например, если вы хотите создать оптимизированную версию, вы должны запустить приведенную выше команду и в конце строки добавить `--release`. Полученный исполняемый файл будет доступен в целевой папке вашей системы и может быть запущен с помощью:
```sh
target/debug/rustdesk
diff --git a/README-ZH.md b/README-ZH.md
index 696b537c4..13e810c6b 100644
--- a/README-ZH.md
+++ b/README-ZH.md
@@ -180,7 +180,7 @@ groupmod: cannot lock /etc/group; try again later.
可以尝试把`-e PUID="$(id -u)" -e PGID="$(id -g)"`参数去掉。(出现这一问题的原因是容器中的 entrypoint 脚本中判定 uid 和 gid 与给定的环境变量不一致时会修改 user 的 uid 和 gid 重新运行,但是重新运行时取不到环境变量中的 uid 和 gid 了,会再次进入 uid 与 gid 与给定值不一致的逻辑分支)
请注意,第一次构建可能需要比较长的时间,因为需要缓存依赖项(国内网络经常出现拉取失败,可多尝试几次),后续构建会更快。此外,如果您需要为构建命令指定不同的参数,
-您可以在命令末尾的 `` 位置执行此操作。例如,如果你想构建一个优化的发布版本,你可以在命令后跟 `---release`。
+您可以在命令末尾的 `` 位置执行此操作。例如,如果你想构建一个优化的发布版本,你可以在命令后跟 `--release`。
将在 target 下产生可执行程序,请通过以下方式运行调试版本:
```sh
diff --git a/README.md b/README.md
index a948361ea..3351edd27 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,7 @@ Then, each time you need to build the application, run the following command:
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-Note that the first build may take longer before dependencies are cached, subsequent builds will be faster. Additionally, if you need to specify different arguments to the build command, you may do so at the end of the command in the `` position. For instance, if you wanted to build an optimized release version, you would run the command above followed by `---release`. The resulting executable will be available in the target folder on your system, and can be run with:
+Note that the first build may take longer before dependencies are cached, subsequent builds will be faster. Additionally, if you need to specify different arguments to the build command, you may do so at the end of the command in the `` position. For instance, if you wanted to build an optimized release version, you would run the command above followed by `--release`. The resulting executable will be available in the target folder on your system, and can be run with:
```sh
target/debug/rustdesk
From d9d1f4d4f6e5269234bd7e8236cf2b34a91513a9 Mon Sep 17 00:00:00 2001
From: Lorenzo Milesi
Date: Thu, 26 May 2022 09:15:02 +0200
Subject: [PATCH 010/120] Minor fixes to Italian translation
---
src/lang/it.rs | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/lang/it.rs b/src/lang/it.rs
index 15d741297..c85542413 100644
--- a/src/lang/it.rs
+++ b/src/lang/it.rs
@@ -206,20 +206,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,7 +244,7 @@ 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"),
@@ -255,11 +255,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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."),
From 7c99ac2ba291dbe28caf1257963cd3dc935c9752 Mon Sep 17 00:00:00 2001
From: fufesou
Date: Thu, 26 May 2022 17:07:00 +0800
Subject: [PATCH 011/120] fix_deadlock_general_service: fix deadlock when join
general service.
Signed-off-by: fufesou
---
src/server/service.rs | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/server/service.rs b/src/server/service.rs
index 35cfdbd77..576c93262 100644
--- a/src/server/service.rs
+++ b/src/server/service.rs
@@ -89,7 +89,12 @@ impl> Service for ServiceTmpl {
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);
+ }
+ }
}
}
From 95e18c6e926e64aaff05ff81b6a6fd4be9982076 Mon Sep 17 00:00:00 2001
From: RustDesk <71636191+rustdesk@users.noreply.github.com>
Date: Thu, 26 May 2022 17:46:23 +0800
Subject: [PATCH 012/120] Update README.md
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 3351edd27..7cea6df81 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,8 @@ Yet another remote desktop software, written in Rust. Works out of the box, no c
RustDesk welcomes contribution from everyone. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for help getting started.
+[**How does RustDesk work?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
+
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
## Free Public Servers
From 09ea396b54688269256f1bd6082b73ce4a6a79a8 Mon Sep 17 00:00:00 2001
From: p-bo
Date: Thu, 26 May 2022 15:04:13 +0200
Subject: [PATCH 013/120] Add Czech localization to application
---
src/lang/cs.rs | 274 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 274 insertions(+)
create mode 100644 src/lang/cs.rs
diff --git a/src/lang/cs.rs b/src/lang/cs.rs
new file mode 100644
index 000000000..a68dc54b5
--- /dev/null
+++ b/src/lang/cs.rs
@@ -0,0 +1,274 @@
+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"),
+ ("This function is turned off by the server", "Tato funkce je vypnuta serverem"),
+ ("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 transfer", "Umožnit přenos souborů"),
+ ("File transfer", "Přenos 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", "Čeká se"),
+ ("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", "Žádné"),
+ ("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í"),
+ ("File Transfer", "Přenos souborů"),
+ ("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"),
+ ].iter().cloned().collect();
+}
From f2f12ffb6b2c40db1488b9a5fa57a0b300642df2 Mon Sep 17 00:00:00 2001
From: RustDesk <71636191+rustdesk@users.noreply.github.com>
Date: Thu, 26 May 2022 21:07:39 +0800
Subject: [PATCH 014/120] Update lang.rs
---
src/lang.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/lang.rs b/src/lang.rs
index d79cfd32f..100672b8d 100644
--- a/src/lang.rs
+++ b/src/lang.rs
@@ -11,6 +11,7 @@ mod eo;
mod ptbr;
mod id;
mod tr;
+mod cs;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn translate(name: String) -> String {
@@ -48,6 +49,7 @@ 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(),
_ => en::T.deref(),
};
if let Some(v) = m.get(&name as &str) {
From 1c447fe4388801e1503f64587aab2fd16a3161b7 Mon Sep 17 00:00:00 2001
From: rustdesk
Date: Thu, 26 May 2022 21:22:42 +0800
Subject: [PATCH 015/120] fix cs
---
src/lang/cs.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lang/cs.rs b/src/lang/cs.rs
index a68dc54b5..42593d8ec 100644
--- a/src/lang/cs.rs
+++ b/src/lang/cs.rs
@@ -266,7 +266,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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?")
+ ("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"),
From 5fc32971df7eb968ab230fad71a059dd277bc570 Mon Sep 17 00:00:00 2001
From: fufesou
Date: Fri, 27 May 2022 11:07:24 +0800
Subject: [PATCH 016/120] better_linux_run_cmds: run cmds without creating tmp
files
Signed-off-by: fufesou
---
src/lang/cs.rs | 2 +-
src/lang/eo.rs | 1 +
src/lang/template.rs | 2 +-
src/lang/tr.rs | 2 +-
src/lang/tw.rs | 2 +-
src/platform/linux.rs | 31 +++++++++----------------------
6 files changed, 14 insertions(+), 26 deletions(-)
diff --git a/src/lang/cs.rs b/src/lang/cs.rs
index 42593d8ec..7018e909d 100644
--- a/src/lang/cs.rs
+++ b/src/lang/cs.rs
@@ -42,7 +42,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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"),
- ("This function is turned off by the server", "Tato funkce je vypnuta serverem"),
+ ("server_not_support", "Server zatím nepodporuje"),
("Not available", "Není k dispozici"),
("Too frequent", "Příliš časté"),
("Cancel", "Storno"),
diff --git a/src/lang/eo.rs b/src/lang/eo.rs
index e7be14e51..0edd74e25 100644
--- a/src/lang/eo.rs
+++ b/src/lang/eo.rs
@@ -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"),
diff --git a/src/lang/template.rs b/src/lang/template.rs
index 84be6ed8d..cc56af085 100644
--- a/src/lang/template.rs
+++ b/src/lang/template.rs
@@ -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", ""),
diff --git a/src/lang/tr.rs b/src/lang/tr.rs
index 02da22e2b..d29fe9d50 100644
--- a/src/lang/tr.rs
+++ b/src/lang/tr.rs
@@ -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"),
diff --git a/src/lang/tw.rs b/src/lang/tw.rs
index f72140af6..3316e89c7 100644
--- a/src/lang/tw.rs
+++ b/src/lang/tw.rs
@@ -184,7 +184,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", "啟用遠端更改設定"),
diff --git a/src/platform/linux.rs b/src/platform/linux.rs
index 79d87492a..efd6476b6 100644
--- a/src/platform/linux.rs
+++ b/src/platform/linux.rs
@@ -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
From 9688a3157a3920ad87f9178b5d80653c00ff994e Mon Sep 17 00:00:00 2001
From: Martin Minka
Date: Sat, 28 May 2022 18:50:28 +0200
Subject: [PATCH 019/120] add slovak translation
---
src/lang/sk.rs | 274 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 274 insertions(+)
create mode 100644 src/lang/sk.rs
diff --git a/src/lang/sk.rs b/src/lang/sk.rs
new file mode 100644
index 000000000..7a3af5bcd
--- /dev/null
+++ b/src/lang/sk.rs
@@ -0,0 +1,274 @@
+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 transfer", "Povoliť prenos súborov"),
+ ("File transfer", "Prenos 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í"),
+ ("File Transfer", "Prenos súborov"),
+ ("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"),
+ ].iter().cloned().collect();
+}
From 4f843b98c79b84125815e5b0ca9b62a89167ef71 Mon Sep 17 00:00:00 2001
From: k2s
Date: Sat, 28 May 2022 18:55:03 +0200
Subject: [PATCH 020/120] fix some issues in Czech translation
---
src/lang/cs.rs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/lang/cs.rs b/src/lang/cs.rs
index 7018e909d..ab8cbebfa 100644
--- a/src/lang/cs.rs
+++ b/src/lang/cs.rs
@@ -105,7 +105,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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"),
+ ("Disable clipboard", "Vypnout schránku"),
("Lock after session end", "Po ukončení relace zamknout plochu"),
("Insert", "Vložit"),
("Insert Lock", "Vložit zámek"),
@@ -177,7 +177,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Create Folder", "Vytvořit složku"),
("Please enter the folder name", "Zadejte název pro složku"),
("Fix it", "Opravit to"),
- ("Warning", "Čeká se"),
+ ("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"),
@@ -239,7 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", "Žádné"),
+ ("Note", "Poznámka"),
("Connection", "Připojení"),
("Share Screen", "Nasdílet obrazovku"),
("CLOSE", "ZAVŘÍT"),
From bf736c80e99d411b58eff8f206e871e445854ce9 Mon Sep 17 00:00:00 2001
From: rustdesk
Date: Sun, 29 May 2022 23:00:12 +0800
Subject: [PATCH 021/120] changing license
---
LICENSE | 674 --------------------------------------------------------
1 file changed, 674 deletions(-)
delete mode 100644 LICENSE
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f288702d2..000000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
From 66070320b5c88836a6fa80f5c916276f583aed27 Mon Sep 17 00:00:00 2001
From: RustDesk <71636191+rustdesk@users.noreply.github.com>
Date: Sun, 29 May 2022 23:01:09 +0800
Subject: [PATCH 022/120] Create LICENCE
---
LICENCE | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 661 insertions(+)
create mode 100644 LICENCE
diff --git a/LICENCE b/LICENCE
new file mode 100644
index 000000000..0ad25db4b
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
From c269d1c831f0345a01fd738b9eed28bfb1295a48 Mon Sep 17 00:00:00 2001
From: fufesou
Date: Mon, 25 Apr 2022 12:28:28 +0800
Subject: [PATCH 023/120] privacy_mode: win10 magnifier
Signed-off-by: fufesou
---
libs/enigo/src/lib.rs | 2 +
libs/enigo/src/win/mod.rs | 1 +
libs/enigo/src/win/win_impl.rs | 9 +-
libs/hbb_common/protos/message.proto | 50 ++-
libs/scrap/Cargo.toml | 3 +-
libs/scrap/examples/capture_mag.rs | 105 +++++
libs/scrap/examples/screenshot.rs | 6 +-
libs/scrap/src/common/dxgi.rs | 29 ++
libs/scrap/src/dxgi/mag.rs | 626 +++++++++++++++++++++++++++
libs/scrap/src/dxgi/mod.rs | 1 +
src/client.rs | 16 +-
src/common.rs | 11 +
src/ipc.rs | 11 +
src/lang/cn.rs | 11 +-
src/lang/cs.rs | 9 +
src/lang/de.rs | 13 +-
src/lang/eo.rs | 11 +
src/lang/fr.rs | 11 +
src/lang/id.rs | 11 +
src/lang/it.rs | 11 +
src/lang/ptbr.rs | 11 +
src/lang/ru.rs | 9 +
src/lang/sk.rs | 9 +
src/lang/template.rs | 9 +
src/lang/tr.rs | 11 +
src/lang/tw.rs | 10 +
src/platform/windows.rs | 73 +++-
src/server.rs | 11 +
src/server/connection.rs | 201 ++++++---
src/server/service.rs | 10 +
src/server/video_service.rs | 163 ++++++-
src/ui.rs | 2 +
src/ui/cm.rs | 32 +-
src/ui/header.tis | 53 ++-
src/ui/remote.rs | 135 +++++-
src/ui/win_privacy.rs | 566 ++++++++++++++++++++++++
src/windows.cc | 30 +-
37 files changed, 2163 insertions(+), 119 deletions(-)
create mode 100644 libs/scrap/examples/capture_mag.rs
create mode 100644 libs/scrap/src/dxgi/mag.rs
create mode 100644 src/ui/win_privacy.rs
diff --git a/libs/enigo/src/lib.rs b/libs/enigo/src/lib.rs
index 893f5918c..10cde9cbe 100644
--- a/libs/enigo/src/lib.rs
+++ b/libs/enigo/src/lib.rs
@@ -63,6 +63,8 @@ extern crate objc;
mod win;
#[cfg(target_os = "windows")]
pub use win::Enigo;
+#[cfg(target_os = "windows")]
+pub use win::ENIGO_INPUT_EXTRA_VALUE;
#[cfg(target_os = "macos")]
mod macos;
diff --git a/libs/enigo/src/win/mod.rs b/libs/enigo/src/win/mod.rs
index 024d7a3fd..62cdbd6e6 100644
--- a/libs/enigo/src/win/mod.rs
+++ b/libs/enigo/src/win/mod.rs
@@ -2,3 +2,4 @@ mod win_impl;
pub mod keycodes;
pub use self::win_impl::Enigo;
+pub use self::win_impl::ENIGO_INPUT_EXTRA_VALUE;
diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs
index c1fc32131..152c65b6a 100644
--- a/libs/enigo/src/win/win_impl.rs
+++ b/libs/enigo/src/win/win_impl.rs
@@ -1,7 +1,7 @@
use winapi;
use self::winapi::ctypes::c_int;
-use self::winapi::shared::{minwindef::*, windef::*};
+use self::winapi::shared::{basetsd::ULONG_PTR, minwindef::*, windef::*};
use self::winapi::um::winbase::*;
use self::winapi::um::winuser::*;
@@ -18,6 +18,9 @@ extern "system" {
pub struct Enigo;
static mut LAYOUT: HKL = std::ptr::null_mut();
+/// The dwExtraInfo value in keyboard and mouse structure that used in SendInput()
+pub const ENIGO_INPUT_EXTRA_VALUE: ULONG_PTR = 100;
+
fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
let mut input = INPUT {
type_: INPUT_MOUSE,
@@ -28,7 +31,7 @@ fn mouse_event(flags: u32, data: u32, dx: i32, dy: i32) -> DWORD {
mouseData: data,
dwFlags: flags,
time: 0,
- dwExtraInfo: 0,
+ dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
})
},
};
@@ -56,7 +59,7 @@ fn keybd_event(flags: u32, vk: u16, scan: u16) -> DWORD {
wScan: scan,
dwFlags: flags,
time: 0,
- dwExtraInfo: 0,
+ dwExtraInfo: ENIGO_INPUT_EXTRA_VALUE,
})
},
};
diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto
index c296e6a7f..048301d7e 100644
--- a/libs/hbb_common/protos/message.proto
+++ b/libs/hbb_common/protos/message.proto
@@ -65,6 +65,10 @@ message LoginRequest {
message ChatMessage { string text = 1; }
+message Features {
+ bool privacy_mode = 1;
+}
+
message PeerInfo {
string username = 1;
string hostname = 2;
@@ -74,6 +78,7 @@ message PeerInfo {
bool sas_enabled = 6;
string version = 7;
int32 conn_id = 8;
+ Features features = 9;
}
message LoginResponse {
@@ -442,11 +447,6 @@ message OptionMessage {
BoolOption enable_file_transfer = 9;
}
-message OptionResponse {
- OptionMessage opt = 1;
- string error = 2;
-}
-
message TestDelay {
int64 time = 1;
bool from_client = 2;
@@ -469,6 +469,44 @@ message AudioFrame {
int64 timestamp = 2;
}
+message BackNotification {
+ // no need to consider block input by someone else
+ enum BlockInputState {
+ StateUnknown = 1;
+ OnSucceeded = 2;
+ OnFailed = 3;
+ OffSucceeded = 4;
+ OffFailed = 5;
+ }
+ enum PrivacyModeState {
+ StateUnknown = 1;
+ // Privacy mode on by someone else
+ OnByOther = 2;
+ // Privacy mode is not supported on the remote side
+ NotSupported = 3;
+ // Privacy mode on by self
+ OnSucceeded = 4;
+ // Privacy mode on by self, but denied
+ OnFailedDenied = 5;
+ // Some plugins are not found
+ OnFailedPlugin = 6;
+ // Privacy mode on by self, but failed
+ OnFailed = 7;
+ // Privacy mode off by self
+ OffSucceeded = 8;
+ // Ctrl + P
+ OffByPeer = 9;
+ // Privacy mode off by self, but failed
+ OffFailed = 10;
+ OffUnknown = 11;
+ }
+
+ oneof union {
+ PrivacyModeState privacy_mode_state = 1;
+ BlockInputState block_input_state = 2;
+ }
+}
+
message Misc {
oneof union {
ChatMessage chat_message = 4;
@@ -478,8 +516,8 @@ message Misc {
AudioFormat audio_format = 8;
string close_reason = 9;
bool refresh_video = 10;
- OptionResponse option_response = 11;
bool video_received = 12;
+ BackNotification back_notification = 13;
}
}
diff --git a/libs/scrap/Cargo.toml b/libs/scrap/Cargo.toml
index 00c4509ab..424189785 100644
--- a/libs/scrap/Cargo.toml
+++ b/libs/scrap/Cargo.toml
@@ -17,11 +17,12 @@ block = "0.1"
cfg-if = "1.0"
libc = "0.2"
num_cpus = "1.13"
+lazy_static = "1.4"
[dependencies.winapi]
version = "0.3"
default-features = true
-features = ["dxgi", "dxgi1_2", "dxgi1_5", "d3d11", "winuser"]
+features = ["dxgi", "dxgi1_2", "dxgi1_5", "d3d11", "winuser", "winerror", "errhandlingapi", "libloaderapi"]
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.10"
diff --git a/libs/scrap/examples/capture_mag.rs b/libs/scrap/examples/capture_mag.rs
new file mode 100644
index 000000000..3e15b4e69
--- /dev/null
+++ b/libs/scrap/examples/capture_mag.rs
@@ -0,0 +1,105 @@
+extern crate repng;
+extern crate scrap;
+
+use std::fs::File;
+
+#[cfg(windows)]
+use scrap::CapturerMag;
+use scrap::{i420_to_rgb, Display};
+
+fn main() {
+ let n = Display::all().unwrap().len();
+ for i in 0..n {
+ #[cfg(windows)]
+ record(i);
+ }
+}
+
+fn get_display(i: usize) -> Display {
+ Display::all().unwrap().remove(i)
+}
+
+#[cfg(windows)]
+fn record(i: usize) {
+ for d in Display::all().unwrap() {
+ println!("{:?} {} {}", d.origin(), d.width(), d.height());
+ }
+
+ let display = get_display(i);
+ let (w, h) = (display.width(), display.height());
+
+ {
+ let mut capture_mag =
+ CapturerMag::new(display.origin(), display.width(), display.height(), false)
+ .expect("Couldn't begin capture.");
+ let wnd_cls = "";
+ let wnd_name = "RustDeskPrivacyWindow";
+ if false == capture_mag.exclude(wnd_cls, wnd_name).unwrap() {
+ println!("No window found for cls {} name {}", wnd_cls, wnd_name);
+ } else {
+ println!("Filter window for cls {} name {}", wnd_cls, wnd_name);
+ }
+
+ let frame = capture_mag.frame(0).unwrap();
+ println!("Capture data len: {}, Saving...", frame.len());
+
+ let mut bitflipped = Vec::with_capacity(w * h * 4);
+ let stride = frame.len() / h;
+
+ for y in 0..h {
+ for x in 0..w {
+ let i = stride * y + 4 * x;
+ bitflipped.extend_from_slice(&[frame[i + 2], frame[i + 1], frame[i], 255]);
+ }
+ }
+ // Save the image.
+ let name = format!("capture_mag_{}_1.png", i);
+ repng::encode(
+ File::create(name.clone()).unwrap(),
+ w as u32,
+ h as u32,
+ &bitflipped,
+ )
+ .unwrap();
+ println!("Image saved to `{}`.", name);
+ }
+
+ {
+ let mut capture_mag =
+ CapturerMag::new(display.origin(), display.width(), display.height(), true)
+ .expect("Couldn't begin capture.");
+ let wnd_cls = "";
+ let wnd_title = "RustDeskPrivacyWindow";
+ if false == capture_mag.exclude(wnd_cls, wnd_title).unwrap() {
+ println!("No window found for cls {} title {}", wnd_cls, wnd_title);
+ } else {
+ println!("Filter window for cls {} title {}", wnd_cls, wnd_title);
+ }
+
+ let buffer = capture_mag.frame(0).unwrap();
+ println!("Capture data len: {}, Saving...", buffer.len());
+
+ let mut frame = Default::default();
+ i420_to_rgb(w, h, &buffer, &mut frame);
+
+ let mut bitflipped = Vec::with_capacity(w * h * 4);
+ let stride = frame.len() / h;
+
+ for y in 0..h {
+ for x in 0..w {
+ let i = stride * y + 3 * x;
+ bitflipped.extend_from_slice(&[frame[i], frame[i + 1], frame[i + 2], 255]);
+ }
+ }
+ let name = format!("capture_mag_{}_2.png", i);
+ repng::encode(
+ File::create(name.clone()).unwrap(),
+ w as u32,
+ h as u32,
+ &bitflipped,
+ )
+ .unwrap();
+
+ println!("Image saved to `{}`.", name);
+ }
+}
diff --git a/libs/scrap/examples/screenshot.rs b/libs/scrap/examples/screenshot.rs
index e2da3b3d8..b52ea11f7 100644
--- a/libs/scrap/examples/screenshot.rs
+++ b/libs/scrap/examples/screenshot.rs
@@ -46,8 +46,7 @@ fn record(i: usize) {
}
}
};
-
- println!("Captured! Saving...");
+ println!("Captured data len: {}, Saving...", buffer.len());
// Flip the BGRA image into a RGBA image.
@@ -96,8 +95,7 @@ fn record(i: usize) {
}
}
};
-
- println!("Captured! Saving...");
+ println!("Captured data len: {}, Saving...", buffer.len());
let mut frame = Default::default();
i420_to_rgb(w, h, &buffer, &mut frame);
diff --git a/libs/scrap/src/common/dxgi.rs b/libs/scrap/src/common/dxgi.rs
index 4683ad5f2..c0b4130bb 100644
--- a/libs/scrap/src/common/dxgi.rs
+++ b/libs/scrap/src/common/dxgi.rs
@@ -111,3 +111,32 @@ impl Display {
self.origin() == (0, 0)
}
}
+
+pub struct CapturerMag {
+ inner: dxgi::mag::CapturerMag,
+ data: Vec,
+}
+
+impl CapturerMag {
+ pub fn is_supported() -> bool {
+ dxgi::mag::CapturerMag::is_supported()
+ }
+
+ pub fn new(origin: (i32, i32), width: usize, height: usize, use_yuv: bool) -> io::Result {
+ Ok(CapturerMag {
+ inner: dxgi::mag::CapturerMag::new(origin, width, height, use_yuv)?,
+ data: Vec::new(),
+ })
+ }
+ pub fn exclude(&mut self, cls: &str, name: &str) -> io::Result {
+ self.inner.exclude(cls, name)
+ }
+ // ((x, y), w, h)
+ pub fn get_rect(&self) -> ((i32, i32), usize, usize) {
+ self.inner.get_rect()
+ }
+ pub fn frame<'a>(&'a mut self, _timeout_ms: u32) -> io::Result> {
+ self.inner.frame(&mut self.data)?;
+ Ok(Frame(&self.data))
+ }
+}
diff --git a/libs/scrap/src/dxgi/mag.rs b/libs/scrap/src/dxgi/mag.rs
new file mode 100644
index 000000000..b7cfe9088
--- /dev/null
+++ b/libs/scrap/src/dxgi/mag.rs
@@ -0,0 +1,626 @@
+// logic from webrtc -- https://github.com/shiguredo/libwebrtc/blob/main/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
+use lazy_static;
+use std::{
+ ffi::CString,
+ io::{Error, ErrorKind, Result},
+ mem::size_of,
+ sync::Mutex,
+};
+use winapi::{
+ shared::{
+ basetsd::SIZE_T,
+ guiddef::{IsEqualGUID, GUID},
+ minwindef::{BOOL, DWORD, FALSE, FARPROC, HINSTANCE, HMODULE, HRGN, TRUE, UINT},
+ ntdef::{LONG, NULL},
+ windef::{HWND, RECT},
+ winerror::ERROR_CLASS_ALREADY_EXISTS,
+ },
+ um::{
+ errhandlingapi::GetLastError,
+ libloaderapi::{FreeLibrary, GetModuleHandleExA, GetProcAddress, LoadLibraryExA},
+ winuser::*,
+ },
+};
+
+pub const MW_FILTERMODE_EXCLUDE: u32 = 0;
+pub const MW_FILTERMODE_INCLUDE: u32 = 1;
+pub const GET_MODULE_HANDLE_EX_FLAG_PIN: u32 = 1;
+pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2;
+pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 4;
+pub const LOAD_LIBRARY_AS_DATAFILE: u32 = 2;
+pub const LOAD_WITH_ALTERED_SEARCH_PATH: u32 = 8;
+pub const LOAD_IGNORE_CODE_AUTHZ_LEVEL: u32 = 16;
+pub const LOAD_LIBRARY_AS_IMAGE_RESOURCE: u32 = 32;
+pub const LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE: u32 = 64;
+pub const LOAD_LIBRARY_REQUIRE_SIGNED_TARGET: u32 = 128;
+pub const LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR: u32 = 256;
+pub const LOAD_LIBRARY_SEARCH_APPLICATION_DIR: u32 = 512;
+pub const LOAD_LIBRARY_SEARCH_USER_DIRS: u32 = 1024;
+pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = 2048;
+pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: u32 = 4096;
+pub const LOAD_LIBRARY_SAFE_CURRENT_DIRS: u32 = 8192;
+pub const LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER: u32 = 16384;
+pub const LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY: u32 = 32768;
+
+extern "C" {
+ pub static GUID_WICPixelFormat32bppRGBA: GUID;
+}
+
+lazy_static::lazy_static! {
+ static ref MAG_BUFFER: Mutex<(bool, Vec)> = Default::default();
+}
+
+pub type REFWICPixelFormatGUID = *const GUID;
+pub type WICPixelFormatGUID = GUID;
+
+#[allow(non_snake_case)]
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct tagMAGIMAGEHEADER {
+ pub width: UINT,
+ pub height: UINT,
+ pub format: WICPixelFormatGUID,
+ pub stride: UINT,
+ pub offset: UINT,
+ pub cbSize: SIZE_T,
+}
+pub type MAGIMAGEHEADER = tagMAGIMAGEHEADER;
+pub type PMAGIMAGEHEADER = *mut tagMAGIMAGEHEADER;
+
+// Function types
+pub type MagImageScalingCallback = ::std::option::Option<
+ unsafe extern "C" fn(
+ hwnd: HWND,
+ srcdata: *mut ::std::os::raw::c_void,
+ srcheader: MAGIMAGEHEADER,
+ destdata: *mut ::std::os::raw::c_void,
+ destheader: MAGIMAGEHEADER,
+ unclipped: RECT,
+ clipped: RECT,
+ dirty: HRGN,
+ ) -> BOOL,
+>;
+
+extern "C" {
+ pub fn MagShowSystemCursor(fShowCursor: BOOL) -> BOOL;
+}
+pub type MagInitializeFunc = ::std::option::Option BOOL>;
+pub type MagUninitializeFunc = ::std::option::Option BOOL>;
+pub type MagSetWindowSourceFunc =
+ ::std::option::Option BOOL>;
+pub type MagSetWindowFilterListFunc = ::std::option::Option<
+ unsafe extern "C" fn(
+ hwnd: HWND,
+ dwFilterMode: DWORD,
+ count: ::std::os::raw::c_int,
+ pHWND: *mut HWND,
+ ) -> BOOL,
+>;
+pub type MagSetImageScalingCallbackFunc = ::std::option::Option<
+ unsafe extern "C" fn(hwnd: HWND, callback: MagImageScalingCallback) -> BOOL,
+>;
+
+#[repr(C)]
+#[derive(Debug, Clone)]
+struct MagInterface {
+ init_succeeded: bool,
+ lib_handle: HINSTANCE,
+ pub mag_initialize_func: MagInitializeFunc,
+ pub mag_uninitialize_func: MagUninitializeFunc,
+ pub set_window_source_func: MagSetWindowSourceFunc,
+ pub set_window_filter_list_func: MagSetWindowFilterListFunc,
+ pub set_image_scaling_callback_func: MagSetImageScalingCallbackFunc,
+}
+
+// NOTE: MagInitialize and MagUninitialize should not be called in global init and uninit.
+// If so, strange errors occur.
+impl MagInterface {
+ fn new() -> Result {
+ let mut s = MagInterface {
+ init_succeeded: false,
+ lib_handle: NULL as _,
+ mag_initialize_func: None,
+ mag_uninitialize_func: None,
+ set_window_source_func: None,
+ set_window_filter_list_func: None,
+ set_image_scaling_callback_func: None,
+ };
+ s.init_succeeded = false;
+ unsafe {
+ if GetSystemMetrics(SM_CMONITORS) != 1 {
+ // Do not try to use the magnifier in multi-screen setup (where the API
+ // crashes sometimes).
+ return Err(Error::new(
+ ErrorKind::Other,
+ "Magnifier capturer cannot work on multi-screen system.",
+ ));
+ }
+
+ // load lib
+ let lib_file_name = "Magnification.dll";
+ let lib_file_name_c = CString::new(lib_file_name).unwrap();
+ s.lib_handle = LoadLibraryExA(
+ lib_file_name_c.as_ptr() as _,
+ NULL,
+ LOAD_WITH_ALTERED_SEARCH_PATH,
+ );
+ if s.lib_handle.is_null() {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!(
+ "Failed to LoadLibraryExA {}, error: {}",
+ lib_file_name,
+ GetLastError()
+ ),
+ ));
+ };
+
+ // load functions
+ s.mag_initialize_func = Some(std::mem::transmute(Self::load_func(
+ s.lib_handle,
+ "MagInitialize",
+ )?));
+ s.mag_uninitialize_func = Some(std::mem::transmute(Self::load_func(
+ s.lib_handle,
+ "MagUninitialize",
+ )?));
+ s.set_window_source_func = Some(std::mem::transmute(Self::load_func(
+ s.lib_handle,
+ "MagSetWindowSource",
+ )?));
+ s.set_window_filter_list_func = Some(std::mem::transmute(Self::load_func(
+ s.lib_handle,
+ "MagSetWindowFilterList",
+ )?));
+ s.set_image_scaling_callback_func = Some(std::mem::transmute(Self::load_func(
+ s.lib_handle,
+ "MagSetImageScalingCallback",
+ )?));
+
+ // MagInitialize
+ if let Some(init_func) = s.mag_initialize_func {
+ if FALSE == init_func() {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!("Failed to MagInitialize, error: {}", GetLastError()),
+ ));
+ } else {
+ s.init_succeeded = true;
+ }
+ } else {
+ return Err(Error::new(
+ ErrorKind::Other,
+ "Unreachable, mag_initialize_func should not be none",
+ ));
+ }
+ }
+ Ok(s)
+ }
+
+ unsafe fn load_func(lib_module: HMODULE, func_name: &str) -> Result {
+ let func_name_c = CString::new(func_name).unwrap();
+ let func = GetProcAddress(lib_module, func_name_c.as_ptr() as _);
+ if func.is_null() {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!(
+ "Failed to GetProcAddress {}, error: {}",
+ func_name,
+ GetLastError()
+ ),
+ ));
+ }
+ Ok(func)
+ }
+
+ pub(super) fn uninit(&mut self) {
+ if self.init_succeeded {
+ if let Some(uninit_func) = self.mag_uninitialize_func {
+ unsafe {
+ if FALSE == uninit_func() {
+ println!("Failed MagUninitialize {}", GetLastError())
+ }
+ }
+ }
+ if !self.lib_handle.is_null() {
+ unsafe {
+ if FALSE == FreeLibrary(self.lib_handle) {
+ println!("Failed FreeLibrary {}", GetLastError())
+ }
+ }
+ self.lib_handle = NULL as _;
+ }
+ }
+ self.init_succeeded = false;
+ }
+}
+
+impl Drop for MagInterface {
+ fn drop(&mut self) {
+ self.uninit();
+ }
+}
+
+pub struct CapturerMag {
+ mag_interface: MagInterface,
+ host_window: HWND,
+ magnifier_window: HWND,
+
+ magnifier_host_class: CString,
+ host_window_name: CString,
+ magnifier_window_class: CString,
+ magnifier_window_name: CString,
+
+ rect: RECT,
+ width: usize,
+ height: usize,
+
+ use_yuv: bool,
+ data: Vec,
+}
+
+impl Drop for CapturerMag {
+ fn drop(&mut self) {
+ self.destroy_windows();
+ self.mag_interface.uninit();
+ }
+}
+
+impl CapturerMag {
+ pub(crate) fn is_supported() -> bool {
+ MagInterface::new().is_ok()
+ }
+
+ pub(crate) fn new(
+ origin: (i32, i32),
+ width: usize,
+ height: usize,
+ use_yuv: bool,
+ ) -> Result {
+ let mut s = Self {
+ mag_interface: MagInterface::new()?,
+ host_window: 0 as _,
+ magnifier_window: 0 as _,
+ magnifier_host_class: CString::new("ScreenCapturerWinMagnifierHost")?,
+ host_window_name: CString::new("MagnifierHost")?,
+ magnifier_window_class: CString::new("Magnifier")?,
+ magnifier_window_name: CString::new("MagnifierWindow")?,
+ rect: RECT {
+ left: origin.0 as _,
+ top: origin.1 as _,
+ right: origin.0 + width as LONG,
+ bottom: origin.1 + height as LONG,
+ },
+ width,
+ height,
+ use_yuv,
+ data: Vec::new(),
+ };
+
+ unsafe {
+ let mut instance = 0 as HMODULE;
+ if 0 == GetModuleHandleExA(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
+ | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ DefWindowProcA as _,
+ &mut instance as _,
+ ) {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!("Failed to GetModuleHandleExA, error: {}", GetLastError()),
+ ));
+ }
+
+ // Register the host window class. See the MSDN documentation of the
+ // Magnification API for more infomation.
+ let wcex = WNDCLASSEXA {
+ cbSize: size_of::() as _,
+ style: 0,
+ lpfnWndProc: Some(DefWindowProcA),
+ cbClsExtra: 0,
+ cbWndExtra: 0,
+ hInstance: instance,
+ hIcon: 0 as _,
+ hCursor: LoadCursorA(NULL as _, IDC_ARROW as _),
+ hbrBackground: 0 as _,
+ lpszClassName: s.magnifier_host_class.as_ptr() as _,
+ lpszMenuName: 0 as _,
+ hIconSm: 0 as _,
+ };
+
+ // Ignore the error which may happen when the class is already registered.
+ if 0 == RegisterClassExA(&wcex) {
+ let code = GetLastError();
+ if code != ERROR_CLASS_ALREADY_EXISTS {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!("Failed to RegisterClassExA, error: {}", code),
+ ));
+ }
+ }
+
+ // Create the host window.
+ s.host_window = CreateWindowExA(
+ WS_EX_LAYERED,
+ s.magnifier_host_class.as_ptr(),
+ s.host_window_name.as_ptr(),
+ WS_POPUP,
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL as _,
+ NULL as _,
+ instance,
+ NULL,
+ );
+ if s.host_window.is_null() {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!(
+ "Failed to CreateWindowExA host_window, error: {}",
+ GetLastError()
+ ),
+ ));
+ }
+
+ // Create the magnifier control.
+ s.magnifier_window = CreateWindowExA(
+ 0,
+ s.magnifier_window_class.as_ptr(),
+ s.magnifier_window_name.as_ptr(),
+ WS_CHILD | WS_VISIBLE,
+ 0,
+ 0,
+ 0,
+ 0,
+ s.host_window,
+ NULL as _,
+ instance,
+ NULL,
+ );
+ if s.magnifier_window.is_null() {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!(
+ "Failed CreateWindowA magnifier_window, error: {}",
+ GetLastError()
+ ),
+ ));
+ }
+
+ // Hide the host window.
+ let _ = ShowWindow(s.host_window, SW_HIDE);
+
+ // Set the scaling callback to receive captured image.
+ if let Some(set_callback_func) = s.mag_interface.set_image_scaling_callback_func {
+ if FALSE
+ == set_callback_func(
+ s.magnifier_window,
+ Some(Self::on_gag_image_scaling_callback),
+ )
+ {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!(
+ "Failed to MagSetImageScalingCallback, error: {}",
+ GetLastError()
+ ),
+ ));
+ }
+ } else {
+ return Err(Error::new(
+ ErrorKind::Other,
+ "Unreachable, set_image_scaling_callback_func should not be none",
+ ));
+ }
+ }
+
+ Ok(s)
+ }
+
+ pub(crate) fn exclude(&mut self, cls: &str, name: &str) -> Result {
+ let name_c = CString::new(name).unwrap();
+ unsafe {
+ let mut hwnd = if cls.len() == 0 {
+ FindWindowExA(NULL as _, NULL as _, NULL as _, name_c.as_ptr())
+ } else {
+ let cls_c = CString::new(cls).unwrap();
+ FindWindowExA(NULL as _, NULL as _, cls_c.as_ptr(), name_c.as_ptr())
+ };
+
+ if hwnd.is_null() {
+ return Ok(false);
+ }
+
+ if let Some(set_window_filter_list_func) =
+ self.mag_interface.set_window_filter_list_func
+ {
+ if FALSE
+ == set_window_filter_list_func(
+ self.magnifier_window,
+ MW_FILTERMODE_EXCLUDE,
+ 1,
+ &mut hwnd,
+ )
+ {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!(
+ "Failed MagSetWindowFilterList for cls {} name {}, err: {}",
+ cls,
+ name,
+ GetLastError()
+ ),
+ ));
+ }
+ } else {
+ return Err(Error::new(
+ ErrorKind::Other,
+ "Unreachable, MagSetWindowFilterList should not be none",
+ ));
+ }
+ }
+
+ Ok(true)
+ }
+
+ pub(crate) fn get_rect(&self) -> ((i32, i32), usize, usize) {
+ (
+ (self.rect.left as _, self.rect.top as _),
+ self.width as _,
+ self.height as _,
+ )
+ }
+
+ fn clear_data() {
+ let mut lock = MAG_BUFFER.lock().unwrap();
+ lock.0 = false;
+ lock.1.clear();
+ }
+
+ pub(crate) fn frame(&mut self, data: &mut Vec) -> Result<()> {
+ Self::clear_data();
+
+ unsafe {
+ let x = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ let y = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ let w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ let h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+
+ self.rect = RECT {
+ left: x as _,
+ top: y as _,
+ right: (x + w) as _,
+ bottom: (y + h) as _,
+ };
+
+ if FALSE
+ == SetWindowPos(
+ self.magnifier_window,
+ HWND_TOP,
+ self.rect.left,
+ self.rect.top,
+ self.rect.right,
+ self.rect.bottom,
+ 0,
+ )
+ {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!(
+ "Failed SetWindowPos (x, y, w , h) - ({}, {}, {}, {}), error {}",
+ self.rect.left,
+ self.rect.top,
+ self.rect.right,
+ self.rect.bottom,
+ GetLastError()
+ ),
+ ));
+ }
+
+ // on_gag_image_scaling_callback will be called and fill in the
+ // frame before set_window_source_func_ returns.
+ if let Some(set_window_source_func) = self.mag_interface.set_window_source_func {
+ if FALSE == set_window_source_func(self.magnifier_window, self.rect) {
+ return Err(Error::new(
+ ErrorKind::Other,
+ format!("Failed to MagSetWindowSource, error: {}", GetLastError()),
+ ));
+ }
+ } else {
+ return Err(Error::new(
+ ErrorKind::Other,
+ "Unreachable, set_window_source_func should not be none",
+ ));
+ }
+ }
+
+ let mut lock = MAG_BUFFER.lock().unwrap();
+ if !lock.0 {
+ return Err(Error::new(
+ ErrorKind::Other,
+ "No data captured by magnifier",
+ ));
+ }
+
+ if self.use_yuv {
+ self.data.resize(lock.1.len(), 0);
+ unsafe {
+ std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut self.data[0], self.data.len());
+ }
+ crate::common::bgra_to_i420(
+ self.width as usize,
+ self.height as usize,
+ &self.data,
+ data,
+ );
+ } else {
+ data.resize(lock.1.len(), 0);
+ unsafe {
+ std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut data[0], data.len());
+ }
+ }
+
+ Ok(())
+ }
+
+ fn destroy_windows(&mut self) {
+ if !self.magnifier_window.is_null() {
+ unsafe {
+ if FALSE == DestroyWindow(self.magnifier_window) {
+ //
+ println!("Failed DestroyWindow magnifier window {}", GetLastError())
+ }
+ }
+ }
+ self.magnifier_window = NULL as _;
+
+ if !self.host_window.is_null() {
+ unsafe {
+ if FALSE == DestroyWindow(self.host_window) {
+ //
+ println!("Failed DestroyWindow host window {}", GetLastError())
+ }
+ }
+ }
+ self.host_window = NULL as _;
+ }
+
+ unsafe extern "C" fn on_gag_image_scaling_callback(
+ _hwnd: HWND,
+ srcdata: *mut ::std::os::raw::c_void,
+ srcheader: MAGIMAGEHEADER,
+ _destdata: *mut ::std::os::raw::c_void,
+ _destheader: MAGIMAGEHEADER,
+ _unclipped: RECT,
+ _clipped: RECT,
+ _dirty: HRGN,
+ ) -> BOOL {
+ Self::clear_data();
+
+ if !IsEqualGUID(&srcheader.format, &GUID_WICPixelFormat32bppRGBA) {
+ // log warning?
+ return FALSE;
+ }
+ let mut lock = MAG_BUFFER.lock().unwrap();
+ lock.1.resize(srcheader.cbSize, 0);
+ std::ptr::copy_nonoverlapping(srcdata as _, &mut lock.1[0], srcheader.cbSize);
+ lock.0 = true;
+ TRUE
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn test() {
+ let mut capture_mag = CapturerMag::new((0, 0), 1920, 1080, false).unwrap();
+ capture_mag.exclude("", "RustDeskPrivacyWindow").unwrap();
+ std::thread::sleep(std::time::Duration::from_millis(1000 * 10));
+ let mut data = Vec::new();
+ capture_mag.frame(&mut data).unwrap();
+ println!("capture data len: {}", data.len());
+ }
+}
\ No newline at end of file
diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs
index 0d343962d..46692535d 100644
--- a/libs/scrap/src/dxgi/mod.rs
+++ b/libs/scrap/src/dxgi/mod.rs
@@ -1,6 +1,7 @@
use std::{io, mem, ptr, slice};
pub mod gdi;
pub use gdi::CapturerGDI;
+pub mod mag;
use winapi::{
shared::{
diff --git a/src/client.rs b/src/client.rs
index be2b788ab..fed83cece 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -773,6 +773,7 @@ pub struct LoginConfigHandler {
pub port_forward: (String, i32),
pub version: i64,
pub conn_id: i32,
+ features: Option,
}
impl Deref for LoginConfigHandler {
@@ -866,11 +867,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" {
@@ -992,6 +993,14 @@ impl LoginConfigHandler {
}
}
+ pub fn is_privacy_mode_supported(&self) -> bool {
+ if let Some(features) = &self.features {
+ features.privacy_mode
+ } else {
+ false
+ }
+ }
+
pub fn refresh() -> Message {
let mut misc = Misc::new();
misc.set_refresh_video(true);
@@ -1064,6 +1073,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(),
diff --git a/src/common.rs b/src/common.rs
index 2a865afbb..dac98fb76 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -594,3 +594,14 @@ 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 {
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
+}
diff --git a/src/ipc.rs b/src/ipc.rs
index 2388a7d9c..5eabbab66 100644
--- a/src/ipc.rs
+++ b/src/ipc.rs
@@ -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")]
diff --git a/src/lang/cn.rs b/src/lang/cn.rs
index b2a3dc9a4..5c83fffc8 100644
--- a/src/lang/cn.rs
+++ b/src/lang/cn.rs
@@ -265,10 +265,19 @@ 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", "退出"),
].iter().cloned().collect();
}
diff --git a/src/lang/cs.rs b/src/lang/cs.rs
index ab8cbebfa..b369e2dd4 100644
--- a/src/lang/cs.rs
+++ b/src/lang/cs.rs
@@ -270,5 +270,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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ý"),
].iter().cloned().collect();
}
diff --git a/src/lang/de.rs b/src/lang/de.rs
index d05ad481b..752cb0df1 100644
--- a/src/lang/de.rs
+++ b/src/lang/de.rs
@@ -265,8 +265,19 @@ 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"),
].iter().cloned().collect();
}
diff --git a/src/lang/eo.rs b/src/lang/eo.rs
index 0edd74e25..2f12d63b9 100644
--- a/src/lang/eo.rs
+++ b/src/lang/eo.rs
@@ -265,8 +265,19 @@ 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", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/fr.rs b/src/lang/fr.rs
index 8f6c3f83b..8f1ca3356 100644
--- a/src/lang/fr.rs
+++ b/src/lang/fr.rs
@@ -265,8 +265,19 @@ 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"),
].iter().cloned().collect();
}
diff --git a/src/lang/id.rs b/src/lang/id.rs
index 5b8475995..78c18250e 100644
--- a/src/lang/id.rs
+++ b/src/lang/id.rs
@@ -265,8 +265,19 @@ 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"),
].iter().cloned().collect();
}
diff --git a/src/lang/it.rs b/src/lang/it.rs
index c85542413..853f44de0 100644
--- a/src/lang/it.rs
+++ b/src/lang/it.rs
@@ -265,8 +265,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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"),
].iter().cloned().collect();
}
diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs
index d4a6824bf..2370cf35d 100644
--- a/src/lang/ptbr.rs
+++ b/src/lang/ptbr.rs
@@ -265,8 +265,19 @@ 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"),
].iter().cloned().collect();
}
diff --git a/src/lang/ru.rs b/src/lang/ru.rs
index 8cc699895..31317f9c3 100644
--- a/src/lang/ru.rs
+++ b/src/lang/ru.rs
@@ -270,5 +270,14 @@ 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", "Выключен"),
].iter().cloned().collect();
}
diff --git a/src/lang/sk.rs b/src/lang/sk.rs
index 7a3af5bcd..81885f176 100644
--- a/src/lang/sk.rs
+++ b/src/lang/sk.rs
@@ -270,5 +270,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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ý"),
].iter().cloned().collect();
}
diff --git a/src/lang/template.rs b/src/lang/template.rs
index cc56af085..c9551a0bf 100644
--- a/src/lang/template.rs
+++ b/src/lang/template.rs
@@ -270,5 +270,14 @@ 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", ""),
].iter().cloned().collect();
}
diff --git a/src/lang/tr.rs b/src/lang/tr.rs
index d29fe9d50..173438b75 100644
--- a/src/lang/tr.rs
+++ b/src/lang/tr.rs
@@ -265,8 +265,19 @@ 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ı"),
].iter().cloned().collect();
}
diff --git a/src/lang/tw.rs b/src/lang/tw.rs
index 3316e89c7..26ceeb014 100644
--- a/src/lang/tw.rs
+++ b/src/lang/tw.rs
@@ -270,5 +270,15 @@ 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", "退出"),
].iter().cloned().collect();
}
diff --git a/src/platform/windows.rs b/src/platform/windows.rs
index bcb9a8545..6f45ba8ff 100644
--- a/src/platform/windows.rs
+++ b/src/platform/windows.rs
@@ -9,7 +9,8 @@ use hbb_common::{
use std::io::prelude::*;
use std::{
ffi::OsString,
- io, mem,
+ fs, io, mem,
+ path::Path,
sync::{Arc, Mutex},
time::{Duration, Instant},
};
@@ -404,6 +405,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 +560,7 @@ async fn launch_server(session_id: DWORD, close_first: bool) -> ResultType 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)?;
+
+ 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() {
@@ -817,6 +862,7 @@ pub fn update_me() -> ResultType<()> {
"
chcp 65001
sc stop {app_name}
+ taskkill /F /IM {broker_exe}
taskkill /F /IM {app_name}.exe
copy /Y \"{src_exe}\" \"{exe}\"
sc start {app_name}
@@ -824,6 +870,7 @@ pub fn update_me() -> ResultType<()> {
",
src_exe = src_exe,
exe = exe,
+ broker_exe = crate::ui::win_privacy::INJECTED_PROCESS_EXE,
app_name = crate::get_app_name(),
lic = register_licence(),
);
@@ -975,6 +1022,7 @@ 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}\"
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}\"
@@ -1010,6 +1058,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,
@@ -1051,11 +1101,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
)
}
@@ -1325,3 +1377,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
+ }
+ }
+}
diff --git a/src/server.rs b/src/server.rs
index f4758e3fb..b41fbddf3 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -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;
diff --git a/src/server/connection.rs b/src/server/connection.rs
index 3a026d924..c2a10fa6b 100644
--- a/src/server/connection.rs
+++ b/src/server/connection.rs
@@ -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,13 @@ impl Connection {
}
}
+ if video_service::get_privacy_mode_conn_id() == id {
+ video_service::set_privacy_mode_conn_id(0);
+ let _ = privacy_mode::turn_off_privacy(id).await;
+ }
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 +406,6 @@ impl Connection {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn handle_input(receiver: std_mpsc::Receiver, 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 +427,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 +458,6 @@ impl Connection {
log::info!("Input thread exited");
}
- #[cfg(not(any(target_os = "android", target_os = "ios")))]
- fn handle_blank(receiver: std_mpsc::Receiver) {
- 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,
@@ -657,8 +647,19 @@ impl Connection {
}
}
self.authorized = true;
- pi.username = username;
- pi.sas_enabled = sas_enabled;
+
+ let mut pi = PeerInfo {
+ hostname: whoami::hostname(),
+ username,
+ platform: whoami::platform().to_string(),
+ version: crate::VERSION.to_owned(),
+ sas_enabled,
+ features: Some(Features {
+ privacy_mode: video_service::is_privacy_mode_supported(),
+ ..Default::default()
+ }).into(),
+ ..Default::default()
+ };
let mut sub_service = false;
if self.file_transfer.is_some() {
res.set_peer_info(pi);
@@ -755,13 +756,13 @@ impl Connection {
self.send(msg_out).await;
}
- fn send_option_error(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();
}
@@ -1162,12 +1163,44 @@ 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 {
+ video_service::set_privacy_mode_conn_id(0);
+ match privacy_mode::turn_on_privacy(self.inner.id) {
+ Ok(true) => {
+ video_service::set_privacy_mode_conn_id(self.inner.id);
+ crate::common::make_privacy_mode_msg(
+ back_notification::PrivacyModeState::OnSucceeded,
+ )
+ }
+ Ok(false) => {
+ crate::common::make_privacy_mode_msg(
+ back_notification::PrivacyModeState::OnFailedPlugin,
+ )
+ }
+ Err(e) => {
+ log::error!("Failed to turn on privacy mode. {}", e);
+ 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).await
+ };
+ self.send(msg_out).await;
}
_ => {}
}
@@ -1318,3 +1351,43 @@ fn try_activate_screen() {
mouse_move_relative(6, 6);
});
}
+
+mod privacy_mode {
+ use super::*;
+
+ pub(super) async 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 {
+ #[cfg(windows)]
+ {
+ let plugin_exitst = crate::ui::win_privacy::turn_on_privacy(_conn_id)?;
+ Ok(plugin_exitst)
+ }
+ #[cfg(not(windows))]
+ {
+ Ok(true)
+ }
+ }
+}
diff --git a/src/server/service.rs b/src/server/service.rs
index 576c93262..9cc1e860c 100644
--- a/src/server/service.rs
+++ b/src/server/service.rs
@@ -148,6 +148,16 @@ impl> ServiceTmpl {
}
}
+ 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) {
let mut lock = self.0.write().unwrap();
for s in lock.subscribes.values_mut() {
diff --git a/src/server/video_service.rs b/src/server/video_service.rs
index 17b545426..c143b680a 100644
--- a/src/server/video_service.rs
+++ b/src/server/video_service.rs
@@ -26,10 +26,10 @@ use hbb_common::tokio::{
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},
};
@@ -45,12 +45,36 @@ lazy_static::lazy_static! {
let (tx, rx) = unbounded_channel();
(tx, Arc::new(TokioMutex::new(rx)))
};
+ static ref PRIVACY_MODE_CONN_ID: Mutex = 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) {
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,
@@ -120,6 +144,46 @@ impl VideoFrameController {
}
}
+trait TraitCapturer {
+ fn frame<'a>(&'a mut self, timeout_ms: u32) -> Result>;
+
+ #[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> {
+ 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> {
+ self.frame(_timeout_ms)
+ }
+
+ fn is_gdi(&self) -> bool {
+ false
+ }
+
+ fn set_gdi(&mut self) -> bool {
+ false
+ }
+}
+
pub fn new() -> GenericService {
let sp = GenericService::new(NAME, true);
sp.run(run);
@@ -156,6 +220,76 @@ 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> {
+ let use_yuv = true;
+
+ #[cfg(not(windows))]
+ let c: Option> = None;
+ #[cfg(windows)]
+ let mut c: Option> = 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
+ );
+ }
+ 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")?;
+ Box::new(c1)
+ }
+ };
+
+ Ok(c)
+}
+
fn run(sp: GenericService) -> ResultType<()> {
let fps = 30;
let wait = 1000 / fps;
@@ -172,8 +306,9 @@ 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();
+ let mut c = create_capturer(privacy_mode_id, display)?;
let q = get_image_quality();
let (bitrate, rc_min_quantizer, rc_max_quantizer, speed) = get_quality(width, height, q);
@@ -227,6 +362,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");
}
@@ -250,7 +386,7 @@ fn run(sp: GenericService) -> ResultType<()> {
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 +409,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;
@@ -333,6 +469,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) -> Message {
let mut msg_out = Message::new();
diff --git a/src/ui.rs b/src/ui.rs
index 5e133ea79..0e83415aa 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -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::common::SOFTWARE_UPDATE_URL;
use crate::ipc;
diff --git a/src/ui/cm.rs b/src/ui/cm.rs
index 91ea8e513..90d066d62 100644
--- a/src/ui/cm.rs
+++ b/src/ui/cm.rs
@@ -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::();
let mut write_jobs: Vec = 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) => {
diff --git a/src/ui/header.tis b/src/ui/header.tis
index 2520549b7..19db4b08f 100644
--- a/src/ui/header.tis
+++ b/src/ui/header.tis
@@ -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