Merge branch 'rustdesk:master' into master

This commit is contained in:
Arash Hatami 2021-11-14 18:52:51 +03:30 committed by GitHub
commit 70a4ee4318
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 443 additions and 109 deletions

181
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,181 @@
name: CI
# env:
# MIN_SUPPORTED_RUST_VERSION: "1.46.0"
# CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on:
workflow_dispatch:
pull_request:
push:
branches:
- master
tags:
- '*'
jobs:
# ensure_cargo_fmt:
# name: Ensure 'cargo fmt' has been run
# runs-on: ubuntu-20.04
# steps:
# - uses: actions-rs/toolchain@v1
# with:
# toolchain: stable
# default: true
# profile: minimal
# components: rustfmt
# - uses: actions/checkout@v2
# - run: cargo fmt -- --check
# min_version:
# name: Minimum supported rust version
# runs-on: ubuntu-20.04
# steps:
# - name: Checkout source code
# uses: actions/checkout@v2
# - name: Install rust toolchain (v${{ env.MIN_SUPPORTED_RUST_VERSION }})
# uses: actions-rs/toolchain@v1
# with:
# toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
# default: true
# profile: minimal # minimal component installation (ie, no documentation)
# components: clippy
# - name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
# uses: actions-rs/cargo@v1
# with:
# command: clippy
# args: --locked --all-targets --all-features -- --allow clippy::unknown_clippy_lints
# - name: Run tests
# uses: actions-rs/cargo@v1
# with:
# command: test
# args: --locked
build:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
# - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
# - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
# - { target: i686-pc-windows-msvc , os: windows-2019 }
# - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
# - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
# - { target: x86_64-apple-darwin , os: macos-10.15 }
# - { target: x86_64-pc-windows-gnu , os: windows-2019 }
# - { target: x86_64-pc-windows-msvc , os: windows-2019 }
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04 }
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Install prerequisites
shell: bash
run: |
case ${{ matrix.job.target }} in
x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake ;;
# arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
# aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v7
with:
setupOnly: true
vcpkgGitCommitId: '1d4128f08e30cec31b94500840c7eca8ebc579cb'
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install libvpx libyuv opus
shell: bash
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Show version information (Rust, cargo, GCC)
shell: bash
run: |
gcc --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- uses: Swatinem/rust-cache@v1
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: build
args: --locked --release --target=${{ matrix.job.target }}
# - name: Strip debug information from executable
# id: strip
# shell: bash
# run: |
# # Figure out suffix of binary
# EXE_suffix=""
# case ${{ matrix.job.target }} in
# *-pc-windows-*) EXE_suffix=".exe" ;;
# esac;
# # Figure out what strip tool to use if any
# STRIP="strip"
# case ${{ matrix.job.target }} in
# arm-unknown-linux-*) STRIP="arm-linux-gnueabihf-strip" ;;
# aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;;
# *-pc-windows-msvc) STRIP="" ;;
# esac;
# # Setup paths
# BIN_DIR="${{ env.CICD_INTERMEDIATES_DIR }}/stripped-release-bin/"
# mkdir -p "${BIN_DIR}"
# BIN_NAME="${{ env.PROJECT_NAME }}${EXE_suffix}"
# BIN_PATH="${BIN_DIR}/${BIN_NAME}"
# # Copy the release build binary to the result location
# cp "target/${{ matrix.job.target }}/release/${BIN_NAME}" "${BIN_DIR}"
# # Also strip if possible
# if [ -n "${STRIP}" ]; then
# "${STRIP}" "${BIN_PATH}"
# fi
# # Let subsequent steps know where to find the (stripped) bin
# echo ::set-output name=BIN_PATH::${BIN_PATH}
# echo ::set-output name=BIN_NAME::${BIN_NAME}
- name: Set testing options
id: test-options
shell: bash
run: |
# test only library unit tests and binary for arm-type targets
unset CARGO_TEST_OPTIONS
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac;
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
- name: Build tests
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: build
args: --locked --tests --target=${{ matrix.job.target }}
# - name: Run tests
# uses: actions-rs/cargo@v1
# with:
# use-cross: ${{ matrix.job.use-cross }}
# command: test
# args: --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}

43
Cargo.lock generated
View File

@ -352,6 +352,20 @@ dependencies = [
"vec_map",
]
[[package]]
name = "clipboard-master"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd1cb2f0df685eb4a61c656faeb6ef391ad776943d3723afb12bdcbceff824d"
dependencies = [
"objc",
"objc-foundation",
"objc_id",
"winapi 0.3.9",
"windows-win",
"x11-clipboard",
]
[[package]]
name = "clipboard-win"
version = "4.2.1"
@ -2860,6 +2874,7 @@ dependencies = [
"cc",
"cfg-if 1.0.0",
"clap",
"clipboard-master",
"cocoa",
"core-foundation 0.9.1",
"core-graphics",
@ -3771,6 +3786,15 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "windows-win"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d4243ec23afe4e9b4e668b3c0a0e973f1b8265f6a46223cfcbc16fd267480c0"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "winreg"
version = "0.6.2"
@ -3814,6 +3838,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
[[package]]
name = "x11-clipboard"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b397ace6e980510de59a4fe3d4c758dffab231d6d747ce9fa1aba6b6035d5f32"
dependencies = [
"xcb",
]
[[package]]
name = "x11rb"
version = "0.8.1"
@ -3826,6 +3859,16 @@ dependencies = [
"winapi-wsapoll",
]
[[package]]
name = "xcb"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6"
dependencies = [
"libc",
"log",
]
[[package]]
name = "yansi"
version = "0.5.0"

View File

@ -51,6 +51,7 @@ mac_address = "1.1"
sciter-rs = { git = "https://github.com/open-trade/rust-sciter", branch = "dyn" }
ctrlc = "3.2"
arboard = "2.0"
clipboard-master = "3"
[target.'cfg(target_os = "windows")'.dependencies]
#systray = { git = "https://github.com/open-trade/systray-rs" }

View File

@ -7,7 +7,7 @@ RUN git clone https://github.com/microsoft/vcpkg && cd vcpkg && git checkout 134
RUN /vcpkg/bootstrap-vcpkg.sh -disableMetrics
RUN /vcpkg/vcpkg --disable-metrics install libvpx libyuv opus
RUN groupadd -r user && useradd -r -g user user --home /home/user && mkdir -p /home/user && chown user /home/user
RUN groupadd -r user && useradd -r -g user user --home /home/user && mkdir -p /home/user && chown user /home/user && echo "user ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/user
WORKDIR /home/user
RUN wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
USER user

View File

@ -106,6 +106,10 @@ mv libsciter-gtk.so target/debug
cargo run
```
### Ändere Wayland zu X11 (Xorg)
RustDesk unterstützt "Wayland" nicht. Siehe [hier](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) um Xorg als Standard GNOME Session zu nutzen.
## Auf Docker Kompilieren
Beginne damit das Repository zu klonen und den Docker Container zu bauen:
@ -136,10 +140,6 @@ target/release/rustdesk
Bitte gehe sicher, dass du diese Befehle vom Stammverzeichnis vom RustDesk Repository nutzt, sonst kann es passieren, dass das Programm die Ressourcen nicht finden kann. Bitte bedenke auch, dass Unterbefehle von Cargo, wie z.B. `install` oder `run` aktuell noch nicht unterstützt werden, da sie das Programm innerhalb des Containers starten oder installieren würden, anstatt auf deinem eigentlichen System.
### Ändere Wayland zu X11 (Xorg)
RustDesk unterstützt "Wayland" nicht. Siehe [hier](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) um Xorg als Standard GNOME Session zu nutzen.
## Dateistruktur
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: Video Codec, Konfiguration, TCP/UDP Wrapper, Protokoll Puffer, fs Funktionen für Dateitransfer, und ein paar andere nützliche Funktionen

View File

@ -104,6 +104,10 @@ mv libsciter-gtk.so target/debug
cargo run
```
### Cambia Wayland a X11 (Xorg)
RustDesk no soporta Wayland. Comprueba [aquí](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) para configurar Xorg en la sesión por defecto de GNOME.
## Como compilar con Docker
Empieza clonando el repositorio y compilando el contenedor de docker:
@ -134,10 +138,6 @@ target/release/rustdesk
Por favor, asegurate de que estás ejecutando estos comandos desde la raíz del repositorio de RustDesk, de lo contrario la aplicación puede ser incapaz de encontrar los recursos necesarios. También hay que tener en cuenta que otros subcomandos de carga como `install` o `run` no estan actualmente soportados via este metodo y podrían requerir ser instalados dentro del contenedor y no en el host.
### Cambia Wayland a X11 (Xorg)
RustDesk no soporta Wayland. Comprueba [aquí](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) para configurar Xorg en la sesión por defecto de GNOME.
## Estructura de archivos
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, configuración, tcp/udp wrapper, protobuf, fs funciones para transferencia de ficheros, y alguna función de utilidad.

View File

@ -104,6 +104,10 @@ mv libsciter-gtk.so target/debug
Exécution du cargo
```
### Changer Wayland en X11 (Xorg)
RustDesk ne supporte pas Wayland. Lisez [cela](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) pour configurer Xorg comme la session GNOME par défaut.
## Comment construire avec Docker
Commencez par cloner le dépôt et construire le conteneur Docker :
@ -134,10 +138,6 @@ target/release/rustdesk
Veuillez vous assurer que vous exécutez ces commandes à partir de la racine du référentiel RustDesk, sinon l'application ne pourra pas trouver les ressources requises. Notez également que les autres sous-commandes de cargo telles que `install` ou `run` ne sont pas actuellement supportées par cette méthode car elles installeraient ou exécuteraient le programme à l'intérieur du conteneur au lieu de l'hôte.
### Changer Wayland en X11 (Xorg)
RustDesk ne supporte pas Wayland. Lisez [cela](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) pour configurer Xorg comme la session GNOME par défaut.
## Structure du projet
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)** : codec vidéo, config, wrapper tcp/udp, protobuf, fonctions fs pour le transfert de fichiers, et quelques autres fonctions utilitaires.

View File

@ -105,6 +105,10 @@ mv libsciter-gtk.so target/debug
cargo run
```
### Zmień Wayland na X11 (Xorg)
RustDesk nie obsługuje Waylanda. Sprawdź [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) by skonfigurować Xorg jako domyślną sesję GNOME.
## Jak kompilować za pomocą Dockera
Rozpocznij od sklonowania repozytorium i stworzenia kontenera docker:
@ -135,10 +139,6 @@ target/release/rustdesk
Upewnij się, że uruchamiasz te polecenia z katalogu głównego repozytorium RustDesk, w przeciwnym razie aplikacja może nie być w stanie znaleźć wymaganych zasobów. Należy również pamiętać, że inne podpolecenia ładowania, takie jak `install` lub `run` nie są obecnie obsługiwane za pomocą tej metody, ponieważ instalowałyby lub uruchamiały program wewnątrz kontenera zamiast na hoście.
### Zmień Wayland na X11 (Xorg)
RustDesk nie obsługuje Waylanda. Sprawdź [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) by skonfigurować Xorg jako domyślną sesję GNOME.
## Struktura plików
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: kodek wideo, config, wrapper tcp/udp, protobuf, funkcje fs do transferu plików i kilka innych funkcji użytkowych

View File

@ -105,6 +105,11 @@ mv libsciter-gtk.so target/debug
cargo run
```
### 把 Wayland 修改成 X11 (Xorg)
RustDesk 暂时不支持 Wayland不过正在积极开发中.
请查看[this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/)配置 X11.
## 使用 Docker 编译
首先克隆存储库并构建 docker 容器:
@ -186,11 +191,6 @@ target/release/rustdesk
请确保您从 RustDesk 存储库的根目录运行这些命令,否则应用程序可能无法找到所需的资源。另请注意,此方法当前不支持其他`Cargo`子命令,
例如 `install``run`,因为运行在容器里,而不是宿主机上。
### 把 Wayland 修改成 X11 (Xorg)
RustDesk 暂时不支持 Wayland不过正在积极开发中.
请查看[this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/)配置 X11.
## 文件结构
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: 视频编解码, 配置, tcp/udp 封装, protobuf, 文件传输相关文件系统操作函数, 以及一些其他实用函数

View File

@ -105,6 +105,10 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run
```
### Change Wayland to X11 (Xorg)
RustDesk does not support Wayland. Check [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) to configuring Xorg as the default GNOME session.
## How to build with Docker
Begin by cloning the repository and building the docker container:
@ -135,10 +139,6 @@ target/release/rustdesk
Please ensure that you are running these commands from the root of the RustDesk repository, otherwise the application may be unable to find the required resources. Also note that other cargo subcommands such as `install` or `run` are not currently supported via this method as they would install or run the program inside the container instead of the host.
### Change Wayland to X11 (Xorg)
RustDesk does not support Wayland. Check [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) to configuring Xorg as the default GNOME session.
## File Structure
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions

View File

@ -1,13 +1,5 @@
#!/bin/sh
if [ "$(id -u)" != "${PUID:-1000}" ] || [ "$(id -g)" != "${PGID:-1000}" ]; then
usermod -o -u "${PUID:-1000}" user
groupmod -o -g "${PGID:-1000}" user
chown -R user /home/user
sudo -u user /entrypoint $@
exit 0
fi
cd $HOME/rustdesk
. $HOME/.cargo/env

View File

@ -48,6 +48,7 @@ message PunchHoleSent {
string id = 2;
string relay_server = 3;
NatType nat_type = 4;
string version = 5;
}
message RegisterPk {
@ -110,6 +111,7 @@ message RelayResponse {
bytes pk = 5;
}
string refuse_reason = 6;
string version = 7;
}
message SoftwareUpdate { string url = 1; }
@ -128,6 +130,7 @@ message LocalAddr {
bytes local_addr = 2;
string relay_server = 3;
string id = 4;
string version = 5;
}
message RendezvousMessage {

View File

@ -1,6 +1,6 @@
pub use arboard::Clipboard as ClipboardContext;
use hbb_common::{
allow_err,
allow_err, bail,
compress::{compress as compress_func, decompress},
config::{Config, COMPRESS_LEVEL, RENDEZVOUS_TIMEOUT},
log,
@ -240,6 +240,10 @@ async fn test_nat_type_() -> ResultType<bool> {
let rendezvous_server = get_rendezvous_server(100).await;
let server1 = rendezvous_server;
let mut server2 = server1;
if server1.port() == 0 { // offline
// avoid overflow crash
bail!("Offline");
}
server2.set_port(server1.port() - 1);
let mut msg_out = RendezvousMessage::new();
let serial = Config::get_serial();

View File

@ -285,6 +285,7 @@ impl RendezvousMediator {
let mut msg_out = Message::new();
let mut rr = RelayResponse {
socket_addr,
version: crate::VERSION.to_owned(),
..Default::default()
};
if initiate {
@ -321,6 +322,7 @@ impl RendezvousMediator {
socket_addr: AddrMangle::encode(peer_addr),
local_addr: AddrMangle::encode(local_addr),
relay_server,
version: crate::VERSION.to_owned(),
..Default::default()
});
let bytes = msg_out.write_to_bytes()?;
@ -359,6 +361,7 @@ impl RendezvousMediator {
id: Config::get_id(),
relay_server,
nat_type: nat_type.into(),
version: crate::VERSION.to_owned(),
..Default::default()
});
let bytes = msg_out.write_to_bytes()?;

View File

@ -3,49 +3,102 @@ pub use crate::common::{
check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME,
CONTENT,
};
struct State {
ctx: Option<ClipboardContext>,
}
impl Default for State {
fn default() -> Self {
let ctx = match ClipboardContext::new() {
Ok(ctx) => Some(ctx),
Err(err) => {
log::error!("Failed to start {}: {}", NAME, err);
None
}
};
Self { ctx }
}
}
impl super::service::Reset for State {
fn reset(&mut self) {
*CONTENT.lock().unwrap() = Default::default();
}
}
use clipboard_master::{CallbackResult, ClipboardHandler, Master};
use hbb_common::{anyhow, ResultType};
use std::{
io, sync,
sync::{
atomic::{AtomicBool, Ordering},
mpsc::SyncSender,
},
time::Duration,
};
pub fn new() -> GenericService {
let sp = GenericService::new(NAME, true);
sp.repeat::<State, _>(INTERVAL, run);
sp.run::<_>(listen::run);
sp
}
fn run(sp: GenericService, state: &mut State) -> ResultType<()> {
if let Some(ctx) = state.ctx.as_mut() {
if let Some(msg) = check_clipboard(ctx, None) {
sp.send(msg);
}
sp.snapshot(|sps| {
let txt = crate::CONTENT.lock().unwrap().clone();
if !txt.is_empty() {
let msg_out = crate::create_clipboard_msg(txt);
sps.send_shared(Arc::new(msg_out));
}
Ok(())
})?;
mod listen {
use super::*;
static RUNNING: AtomicBool = AtomicBool::new(true);
static WAIT: Duration = Duration::from_millis(1500);
struct ClipHandle {
tx: SyncSender<()>,
}
impl ClipboardHandler for ClipHandle {
fn on_clipboard_change(&mut self) -> CallbackResult {
if !RUNNING.load(Ordering::SeqCst) {
return CallbackResult::Stop;
}
let _ = self.tx.send(());
CallbackResult::Next
}
fn on_clipboard_error(&mut self, error: io::Error) -> CallbackResult {
if !RUNNING.load(Ordering::SeqCst) {
CallbackResult::Stop
} else {
CallbackResult::StopWithError(error)
}
}
}
#[tokio::main]
pub async fn run(sp: GenericService) -> ResultType<()> {
let mut ctx = match ClipboardContext::new() {
Ok(ctx) => ctx,
Err(err) => {
log::error!("Failed to start {}: {}", NAME, err);
return Err(anyhow::Error::from(err));
}
};
if !RUNNING.load(Ordering::SeqCst) {
RUNNING.store(true, Ordering::SeqCst);
}
let (tx, rx) = sync::mpsc::sync_channel(12);
let listener = tokio::spawn(async {
log::info!("Clipboard listener running!");
let _ = Master::new(ClipHandle { tx }).run();
});
while sp.ok() {
if let Ok(_) = rx.recv_timeout(WAIT) {
if let Some(msg) = check_clipboard(&mut ctx, None) {
sp.send(msg);
}
}
sp.snapshot(|sps| {
let txt = crate::CONTENT.lock().unwrap().clone();
if !txt.is_empty() {
let msg_out = crate::create_clipboard_msg(txt);
sps.send_shared(Arc::new(msg_out));
}
Ok(())
})?;
}
RUNNING.store(false, Ordering::SeqCst);
trigger(&mut ctx);
let _ = listener.await;
log::info!("Clipboard listener stopped!");
*CONTENT.lock().unwrap() = Default::default();
Ok(())
}
fn trigger(ctx: &mut ClipboardContext) {
let _ = match ctx.get_text() {
Ok(text) => ctx.set_text(text),
Err(_) => ctx.set_text(Default::default()),
};
}
Ok(())
}

View File

@ -333,9 +333,21 @@ impl UI {
}
}
}
*self.2.lock().unwrap() = m.clone();
ipc::set_options(m).ok();
}
fn set_option(&self, key: String, value: String) {
let mut options = self.2.lock().unwrap();
if value.is_empty() {
options.remove(&key);
} else {
options.insert(key, value);
}
ipc::set_options(options.clone()).ok();
}
fn install_path(&mut self) -> String {
#[cfg(windows)]
return crate::platform::windows::get_install_info().1;
@ -587,6 +599,7 @@ impl sciter::EventHandler for UI {
fn test_if_valid_server(String);
fn get_sound_inputs();
fn set_options(Value);
fn set_option(String, String);
fn get_software_update_url();
fn get_new_version();
fn get_version();

View File

@ -257,6 +257,10 @@ handler.msgbox = function(type, title, text, callback=null, height=180, width=50
self.timer(150ms, function() { msgbox(type, title, text, callback, height, width, retry); });
}
handler.block_msgbox = function(type, title, text, callback=null, height=180, width=500, retry=0) {
msgbox(type, title, text, callback, height, width, retry);
}
var reconnectTimeout = 1;
handler.msgbox_retry = function(type, title, text, hasRetry, callback=null, height=180, width=500) {
handler.msgbox(type, title, text, callback, height, width, hasRetry ? reconnectTimeout : 0);

View File

@ -30,6 +30,7 @@ table > thead {
}
table > tbody {
behavior: select-multiple;
overflow-y: scroll-indicator;
size: *;
background: white;
@ -85,6 +86,15 @@ table.has_current tr:current /* current row */
background-color: color(accent);
}
table.has_current tbody tr:checked
{
background-color: color(accent);
}
table.has_current tbody tr:checked td {
color: highlighttext;
}
table td
{
padding: 4px;

View File

@ -374,7 +374,7 @@ class FolderView : Reactor.Component {
path = this.joinPath(entry.name);
}
var tm = entry.time ? new Date(entry.time.toFloat() * 1000.).toLocaleString() : 0;
return <tr>
return <tr role="option">
<td type={entry.type} filename={path}></td>
<td>{entry.name}</td>
<td value={entry.time || 0}>{tm || ""}</td>
@ -401,20 +401,31 @@ class FolderView : Reactor.Component {
}
event click $(.trash) () {
var row = this.getCurrentRow();
if (!row) return;
var path = row[0];
var type = row[1];
var new_history = [];
for (var i = 0; i < this.history.length; ++i) {
var h = this.history[i];
if ((h + this.sep()).indexOf(path + this.sep()) == -1) new_history.push(h);
var rows = this.getCurrentRows();
if (!rows || rows.length == 0) return;
var delete_dirs = new Array();
for (var i = 0; i < rows.length; ++i) {
var row = rows[i];
var path = row[0];
var type = row[1];
var new_history = [];
for (var j = 0; j < this.history.length; ++j) {
var h = this.history[j];
if ((h + this.sep()).indexOf(path + this.sep()) == -1) new_history.push(h);
}
this.history = new_history;
if (type == 1) {
delete_dirs.push(path);
} else {
confirmDelete(path, this.is_remote);
}
}
this.history = new_history;
if (type == 1) {
file_transfer.job_table.addDelDir(path, this.is_remote);
} else {
confirmDelete(path, this.is_remote);
for (var i = 0; i < delete_dirs.length; ++i) {
file_transfer.job_table.addDelDir(delete_dirs[i], this.is_remote);
}
}
@ -463,10 +474,28 @@ class FolderView : Reactor.Component {
return [this.joinPath(name), type];
}
function getCurrentRows() {
var rows = this.table.getCurrentRows();
if (!rows || rows.length== 0) return;
var records = new Array();
for (var i = 0; i < rows.length; ++i) {
var name = rows[i][1].text;
if (!name || name == "..") continue;
var type = rows[i][0].attributes["type"];
records.push([this.joinPath(name), type]);
}
return records;
}
event click $(.send) () {
var cur = this.getCurrentRow();
if (!cur) return;
file_transfer.job_table.send(cur[0], this.is_remote);
var rows = this.getCurrentRows();
if (!rows || rows.length == 0) return;
for (var i = 0; i < rows.length; ++i) {
file_transfer.job_table.send(rows[i][0], this.is_remote);
}
}
event change $(.select-dir) (_, el) {
@ -570,7 +599,7 @@ var deleting_single_file_jobs = {};
var create_dir_jobs = {}
function confirmDelete(path, is_remote) {
handler.msgbox("custom-skip", "Confirm Delete", "<div .form> \
handler.block_msgbox("custom-skip", "Confirm Delete", "<div .form> \
<div>Are you sure you want to delete this file?</div> \
<div.ellipsis style=\"font-weight: bold;\">" + path + "</div> \
</div>", function(res=null) {
@ -590,7 +619,7 @@ handler.confirmDeleteFiles = function(id, i, name) {
if (i >= n) return;
var file_path = job.path;
if (name) file_path += handler.get_path_sep(job.is_remote) + name;
handler.msgbox("custom-skip", "Confirm Delete", "<div .form> \
handler.block_msgbox("custom-skip", "Confirm Delete", "<div .form> \
<div>Deleting #" + (i + 1) + " of " + n + " files.</div> \
<div>Are you sure you want to delete this file?</div> \
<div.ellipsis style=\"font-weight: bold;\" .text>" + name + "</div> \

View File

@ -24,6 +24,11 @@ class Grid: Behavior {
{
return this.$(tbody>tr:current);
}
function getCurrentRows()
{
return this.$$(tbody>tr:checked);
}
function getCurrentColumn()
{

View File

@ -40,9 +40,7 @@ class ConnectStatus: Reactor.Component {
}
event click $(.connect-status .link) () {
var options = handler.get_options();
options["stop-service"] = "";
handler.set_options(options);
handler.set_option("stop-service", "");
}
}
@ -138,7 +136,6 @@ function createNewConnect(id, type) {
var myIdMenu;
var audioInputMenu;
var configOptions = {};
class AudioInputs: Reactor.Component {
function this() {
audioInputMenu = this;
@ -167,7 +164,7 @@ class AudioInputs: Reactor.Component {
}
function get_value() {
return configOptions["audio-input"] || this.get_default();
return handler.get_option("audio-input") || this.get_default();
}
function toggleMenuState() {
@ -182,8 +179,7 @@ class AudioInputs: Reactor.Component {
var v = me.id;
if (v == this.get_value()) return;
if (v == this.get_default()) v = "";
configOptions["audio-input"] = v;
handler.set_options(configOptions);
handler.set_option("audio-input", v);
this.toggleMenuState();
}
}
@ -223,7 +219,6 @@ class MyIdMenu: Reactor.Component {
event click $(svg#menu) (_, me) {
audioInputMenu.update({ show: true });
configOptions = handler.get_options();
this.toggleMenuState();
var menu = $(menu#config-options);
me.popup(menu);
@ -232,7 +227,7 @@ class MyIdMenu: Reactor.Component {
function toggleMenuState() {
for (var el in $$(menu#config-options>li)) {
if (el.id && el.id.indexOf("enable-") == 0) {
var enabled = configOptions[el.id] != "N";
var enabled = handler.get_option(el.id) != "N";
el.attributes.toggleClass("selected", enabled);
el.attributes.toggleClass("line-through", !enabled);
}
@ -241,11 +236,10 @@ class MyIdMenu: Reactor.Component {
event click $(menu#config-options>li) (_, me) {
if (me.id && me.id.indexOf("enable-") == 0) {
configOptions[me.id] = configOptions[me.id] == "N" ? "" : "N";
handler.set_options(configOptions);
handler.set_option(me.id, handler.get_option(me.id) == "N" ? "" : "N");
}
if (me.id == "whitelist") {
var old_value = (configOptions["whitelist"] || "").split(",").join("\n");
var old_value = handler.get_option("whitelist").split(",").join("\n");
handler.msgbox("custom-whitelist", "IP Whitelisting", "<div .form> \
<textarea spellcheck=\"false\" name=\"text\" novalue=\"0.0.0.0\" style=\"overflow: scroll-indicator; height: 160px; font-size: 1.2em; padding: 0.5em;\">" + old_value + "</textarea>\
</div> \
@ -253,7 +247,7 @@ class MyIdMenu: Reactor.Component {
if (!res) return;
var value = (res.text || "").trim();
if (value) {
var values = value.split(/[\s,;]+/g);
var values = value.split(/[\s,;\n]+/g);
for (var ip in values) {
if (!ip.match(/^\d+\.\d+\.\d+\.\d+$/)) {
return "Invalid ip: " + ip;
@ -262,11 +256,11 @@ class MyIdMenu: Reactor.Component {
value = values.join("\n");
}
if (value == old_value) return;
configOptions["whitelist"] = value.replace("\n", ",");
stdout.println("whitelist updated");
handler.set_options(configOptions);
handler.set_option("whitelist", value.replace("\n", ","));
}, 300);
} else if (me.id == "custom-server") {
var configOptions = handler.get_options();
var old_relay = configOptions["relay-server"] || "";
var old_id = configOptions["custom-rendezvous-server"] || "";
handler.msgbox("custom-server", "ID/Relay Server", "<div .form> \
@ -293,8 +287,7 @@ class MyIdMenu: Reactor.Component {
} else if (me.id == "forum") {
handler.open_url("https:://forum.rustdesk.com");
} else if (me.id == "stop-service") {
configOptions["stop-service"] = service_stopped ? "" : "Y";
handler.set_options(configOptions);
handler.set_option("stop-service", service_stopped ? "" : "Y");
} else if (me.id == "about") {
var name = handler.get_app_name();
handler.msgbox("custom-nocancel-nook-hasclose", "About " + name, "<div style='line-height: 2em'> \