From 0f72af652973f1c65c2476a24f3d9db5b62d81d3 Mon Sep 17 00:00:00 2001 From: asur4s Date: Wed, 29 Mar 2023 00:40:05 -0700 Subject: [PATCH 001/112] Release modifier when multi conn --- src/keyboard.rs | 25 ++++++++++++++++++++++ src/platform/mod.rs | 2 +- src/server/connection.rs | 42 ++++++++++++++++++++++++++++++++++++- src/server/input_service.rs | 2 +- 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 294e058c5..0e271a0b6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -365,6 +365,21 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { } } +#[inline] +pub fn is_modifier(key: &rdev::Key) -> bool { + matches!( + key, + Key::ShiftLeft + | Key::ShiftRight + | Key::ControlLeft + | Key::ControlRight + | Key::MetaLeft + | Key::MetaRight + | Key::Alt + | Key::AltGr + ) +} + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_numpad_key(event: &Event) -> bool { @@ -984,3 +999,13 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } events } + +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn keycode_to_rdev_key(keycode: u32) -> Key { + #[cfg(target_os = "windows")] + return rdev::win_key_from_scancode(keycode); + #[cfg(target_os = "linux")] + return rdev::linux_key_from_code(keycode); + #[cfg(target_os = "macos")] + return rdev::macos_key_from_code(keycode.try_into().unwrap_or_default()); +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f8fe7a6dd..777de3b01 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -37,7 +37,7 @@ pub fn breakdown_callback() { #[cfg(target_os = "linux")] crate::input_service::clear_remapped_keycode(); #[cfg(not(any(target_os = "android", target_os = "ios")))] - crate::input_service::release_modifiers(); + crate::input_service::release_device_modifiers(); } // Android diff --git a/src/server/connection.rs b/src/server/connection.rs index 0c17fd176..dbda219e0 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -10,6 +10,7 @@ use crate::{ new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender, }, common::{get_default_sound_input, set_sound_input}, + keyboard::{is_modifier, keycode_to_rdev_key}, video_service, }; #[cfg(any(target_os = "android", target_os = "ios"))] @@ -39,6 +40,7 @@ use sha2::{Digest, Sha256}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::sync::atomic::Ordering; use std::{ + collections::HashSet, num::NonZeroI64, sync::{atomic::AtomicI64, mpsc as std_mpsc}, }; @@ -132,6 +134,7 @@ pub struct Connection { voice_call_request_timestamp: Option, audio_input_device_before_voice_call: Option, options_in_login: Option, + pressed_modifiers: HashSet, } impl ConnInner { @@ -243,6 +246,7 @@ impl Connection { voice_call_request_timestamp: None, audio_input_device_before_voice_call: None, options_in_login: None, + pressed_modifiers: Default::default(), }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -618,7 +622,6 @@ impl Connection { } #[cfg(target_os = "linux")] clear_remapped_keycode(); - release_modifiers(); log::info!("Input thread exited"); } @@ -1366,6 +1369,28 @@ impl Connection { } else { me.press }; + + let key = match me.mode.unwrap() { + KeyboardMode::Map => Some(keycode_to_rdev_key(me.chr())), + KeyboardMode::Translate => { + if let Some(key_event::Union::Chr(code)) = me.union.clone() { + Some(keycode_to_rdev_key(code & 0x0000FFFF)) + } else { + None + } + } + _ => None, + } + .filter(is_modifier); + + if let Some(key) = key { + if is_press { + self.pressed_modifiers.insert(key); + } else { + self.pressed_modifiers.remove(&key); + } + } + if is_press { match me.union { Some(key_event::Union::Unicode(_)) @@ -2023,6 +2048,14 @@ impl Connection { }) .count(); } + + #[cfg(not(any(target_os = "android", target_os = "ios")))] + fn release_pressed_modifiers(&mut self) { + for modifier in self.pressed_modifiers.iter() { + rdev::simulate(&rdev::EventType::KeyRelease(*modifier)).ok(); + } + self.pressed_modifiers.clear(); + } } pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) { @@ -2220,3 +2253,10 @@ impl Default for PortableState { } } } + +impl Drop for Connection { + fn drop(&mut self) { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + self.release_pressed_modifiers(); + } +} diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 25b41d0ef..5bc486516 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -551,7 +551,7 @@ fn record_key_to_key(record_key: u64) -> Option { } } -pub fn release_modifiers() { +pub fn release_device_modifiers() { let mut en = ENIGO.lock().unwrap(); for modifier in [ Key::Shift, From 0312e734c06aba56ddc09bd2128da05710d0509c Mon Sep 17 00:00:00 2001 From: chiehw Date: Fri, 31 Mar 2023 14:18:55 +0800 Subject: [PATCH 002/112] fix build --- src/lang.rs | 2 +- src/server/connection.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lang.rs b/src/lang.rs index 972510325..4776eb89e 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -5,12 +5,12 @@ mod cn; mod cs; mod da; mod de; +mod el; mod en; mod eo; mod es; mod fa; mod fr; -mod el; mod hu; mod id; mod it; diff --git a/src/server/connection.rs b/src/server/connection.rs index dbda219e0..eecfaa20c 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -10,7 +10,6 @@ use crate::{ new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender, }, common::{get_default_sound_input, set_sound_input}, - keyboard::{is_modifier, keycode_to_rdev_key}, video_service, }; #[cfg(any(target_os = "android", target_os = "ios"))] @@ -1371,17 +1370,17 @@ impl Connection { }; let key = match me.mode.unwrap() { - KeyboardMode::Map => Some(keycode_to_rdev_key(me.chr())), + KeyboardMode::Map => Some(crate::keyboard::keycode_to_rdev_key(me.chr())), KeyboardMode::Translate => { if let Some(key_event::Union::Chr(code)) = me.union.clone() { - Some(keycode_to_rdev_key(code & 0x0000FFFF)) + Some(crate::keyboard::keycode_to_rdev_key(code & 0x0000FFFF)) } else { None } } _ => None, } - .filter(is_modifier); + .filter(crate::keyboard::is_modifier); if let Some(key) = key { if is_press { From 4c2169d92985ec66eb1cde76c84dbd3b52a11345 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 30 Mar 2023 10:08:05 +0800 Subject: [PATCH 003/112] feat: add dll setup/uninstall script --- src/platform/windows.rs | 91 +++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 379cd29fb..83f5af2dd 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -10,15 +10,16 @@ use hbb_common::{ sleep, timeout, tokio, }; use std::io::prelude::*; +use std::path::Path; use std::ptr::null_mut; use std::{ + collections::HashMap, ffi::OsString, fs, io, mem, os::windows::process::CommandExt, path::PathBuf, sync::{Arc, Mutex}, time::{Duration, Instant}, - collections::HashMap }; use winapi::{ ctypes::c_void, @@ -717,7 +718,7 @@ pub fn is_share_rdp() -> bool { } pub fn set_share_rdp(enable: bool) { - let (subkey, _, _, _) = get_install_info(); + let (subkey, _, _, _, _) = get_install_info(); let cmd = format!( "reg add {} /f /v share_rdp /t REG_SZ /d \"{}\"", subkey, @@ -810,11 +811,11 @@ fn get_valid_subkey() -> String { return get_subkey(&app_name, false); } -pub fn get_install_info() -> (String, String, String, String) { +pub fn get_install_info() -> (String, String, String, String, String) { get_install_info_with_subkey(get_valid_subkey()) } -fn get_default_install_info() -> (String, String, String, String) { +fn get_default_install_info() -> (String, String, String, String, String) { get_install_info_with_subkey(get_subkey(&crate::get_app_name(), false)) } @@ -883,7 +884,7 @@ pub fn check_update_broker_process() -> ResultType<()> { Ok(()) } -fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String) { +fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String, String) { let mut path = get_reg_of(&subkey, "InstallLocation"); if path.is_empty() { path = get_default_install_path(); @@ -894,26 +895,32 @@ fn get_install_info_with_subkey(subkey: String) -> (String, String, String, Stri crate::get_app_name() ); let exe = format!("{}\\{}.exe", path, crate::get_app_name()); - (subkey, path, start_menu, exe) + let dll = format!("{}\\sciter.dll", path); + (subkey, path, start_menu, exe, dll) } -pub fn copy_exe_cmd(src_exe: &str, _exe: &str, path: &str) -> String { +pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String { #[cfg(feature = "flutter")] - let main_exe = format!( + let main_raw = format!( "XCOPY \"{}\" \"{}\" /Y /E /H /C /I /K /R /Z", - PathBuf::from(src_exe) + PathBuf::from(src_raw) .parent() .unwrap() .to_string_lossy() .to_string(), - path + _path ); #[cfg(not(feature = "flutter"))] - let main_exe = format!( - "copy /Y \"{src_exe}\" \"{exe}\"", - src_exe = src_exe, - exe = _exe + let main_raw = format!( + "copy /Y \"{src_raw}\" \"{raw}\"", + src_raw = src_raw, + raw = _raw ); + return main_raw; +} + +pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { + let main_exe = copy_raw_cmd(src_exe, exe, path); return format!( " @@ -929,8 +936,19 @@ pub fn copy_exe_cmd(src_exe: &str, _exe: &str, path: &str) -> String { } pub fn update_me() -> ResultType<()> { - let (_, path, _, exe) = get_install_info(); + let (_, path, _, exe, dll) = get_install_info(); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned(); + let src_dll = std::env::current_exe()? + .parent() + .unwrap_or(&Path::new(&get_default_install_path())) + .join("sciter.dll") + .to_str() + .unwrap_or("") + .to_owned(); + #[cfg(feature = "flutter")] + let copy_dll = "".to_string(); + #[cfg(not(feature = "flutter"))] + let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); let cmds = format!( " chcp 65001 @@ -938,10 +956,12 @@ pub fn update_me() -> ResultType<()> { taskkill /F /IM {broker_exe} taskkill /F /IM {app_name}.exe /FI \"PID ne {cur_pid}\" {copy_exe} + {copy_dll} sc start {app_name} {lic} ", copy_exe = copy_exe_cmd(&src_exe, &exe, &path), + copy_dll = copy_dll, broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, app_name = crate::get_app_name(), lic = register_licence(), @@ -980,12 +1000,14 @@ fn get_after_install(exe: &str) -> String { pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> ResultType<()> { let uninstall_str = get_uninstall(false); let mut path = path.trim_end_matches('\\').to_owned(); - let (subkey, _path, start_menu, exe) = get_default_install_info(); + let (subkey, _path, start_menu, exe, dll) = get_default_install_info(); let mut exe = exe; + let mut dll = dll; if path.is_empty() { path = _path; } else { exe = exe.replace(&_path, &path); + dll = dll.replace(&_path, &path); } let mut version_major = "0"; let mut version_minor = "0"; @@ -1109,6 +1131,18 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} app_name = crate::get_app_name(), ); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string(); + let src_dll = std::env::current_exe()? + .parent() + .unwrap_or(&Path::new(&get_default_install_path())) + .join("sciter.dll") + .to_str() + .unwrap_or("") + .to_owned(); + + #[cfg(feature = "flutter")] + let copy_dll = "".to_string(); + #[cfg(not(feature = "flutter"))] + let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); let install_cert = if options.contains("driverCert") { format!("\"{}\" --install-cert \"RustDeskIddDriver.cer\"", src_exe) @@ -1122,6 +1156,7 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} chcp 65001 md \"{path}\" {copy_exe} +{copy_dll} 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}\" @@ -1181,6 +1216,7 @@ sc delete {app_name} &dels }, copy_exe = copy_exe_cmd(&src_exe, &exe, &path), + copy_dll = copy_dll ); run_cmds(cmds, debug, "install")?; std::thread::sleep(std::time::Duration::from_millis(2000)); @@ -1193,7 +1229,7 @@ sc delete {app_name} } pub fn run_after_install() -> ResultType<()> { - let (_, _, _, exe) = get_install_info(); + let (_, _, _, exe,_) = get_install_info(); run_cmds(get_after_install(&exe), true, "after_install") } @@ -1227,7 +1263,7 @@ fn get_before_uninstall(kill_self: bool) -> String { } fn get_uninstall(kill_self: bool) -> String { - let (subkey, path, start_menu, _) = get_install_info(); + let (subkey, path, start_menu, _, _) = get_install_info(); format!( " {before_uninstall} @@ -1331,7 +1367,7 @@ pub fn is_installed() -> bool { service::ServiceAccess, service_manager::{ServiceManager, ServiceManagerAccess}, }; - let (_, _, _, exe) = get_install_info(); + let (_, _, _, exe, _) = get_install_info(); if !std::fs::metadata(exe).is_ok() { return false; } @@ -1347,7 +1383,7 @@ pub fn is_installed() -> bool { } pub fn get_installed_version() -> String { - let (_, _, _, exe) = get_install_info(); + let (_, _, _, exe, _) = get_install_info(); if let Ok(output) = std::process::Command::new(exe).arg("--version").output() { for line in String::from_utf8_lossy(&output.stdout).lines() { return line.to_owned(); @@ -1357,7 +1393,7 @@ pub fn get_installed_version() -> String { } fn get_reg(name: &str) -> String { - let (subkey, _, _, _) = get_install_info(); + let (subkey, _, _, _, _) = get_install_info(); get_reg_of(&subkey, name) } @@ -1408,7 +1444,7 @@ pub fn bootstrap() { } fn register_licence() -> String { - let (subkey, _, _, _) = get_install_info(); + let (subkey, _, _, _, _) = get_install_info(); if let Ok(lic) = get_license_from_exe_name() { format!( " @@ -1765,14 +1801,17 @@ pub fn send_message_to_hnwd( pub fn create_process_with_logon(user: &str, pwd: &str, exe: &str, arg: &str) -> ResultType<()> { let last_error_table = HashMap::from([ - (ERROR_LOGON_FAILURE, "The user name or password is incorrect."), - (ERROR_ACCESS_DENIED, "Access is denied.") + ( + ERROR_LOGON_FAILURE, + "The user name or password is incorrect.", + ), + (ERROR_ACCESS_DENIED, "Access is denied."), ]); unsafe { let user_split = user.split("\\").collect::>(); let wuser = wide_string(user_split.get(1).unwrap_or(&user)); - let wpc = wide_string(user_split.get(0).unwrap_or(&"")); + let wpc = wide_string(user_split.get(0).unwrap_or(&"")); let wpwd = wide_string(pwd); let cmd = if arg.is_empty() { format!("\"{}\"", exe) @@ -1804,7 +1843,7 @@ pub fn create_process_with_logon(user: &str, pwd: &str, exe: &str, arg: &str) -> { let last_error = GetLastError(); bail!( - "CreateProcessWithLogonW failed : \"{}\", errno={}", + "CreateProcessWithLogonW failed : \"{}\", errno={}", last_error_table .get(&last_error) .unwrap_or(&"Unknown error"), From a015a9b16489bfe2854c83546454f5d8c9ef5a6f Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 31 Mar 2023 17:12:48 +0800 Subject: [PATCH 004/112] fix: remove bracket for and expression in nightly ci --- .github/workflows/flutter-nightly.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 06f6f8150..30bf10605 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -1003,7 +1003,7 @@ jobs: done - name: Publish debian package - if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1176,7 +1176,7 @@ jobs: done - name: Publish debian package - if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1199,7 +1199,7 @@ jobs: sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml - name: Publish appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1273,7 +1273,7 @@ jobs: # res/rustdesk*.zst - name: Publish fedora28/centos8 package - if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1396,7 +1396,7 @@ jobs: done - name: Publish debian package - if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1452,7 +1452,7 @@ jobs: cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f - name: Publish archlinux package - if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1461,7 +1461,7 @@ jobs: res/rustdesk*.zst - name: Build appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' shell: bash run: | # set-up appimage-builder @@ -1475,7 +1475,7 @@ jobs: sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml - name: Publish appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1484,7 +1484,7 @@ jobs: ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage - name: Publish fedora28/centos8 package - if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true From a06ce7e1834e846cb22b0031d9f03c5fcd611064 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 30 Mar 2023 10:54:43 +0800 Subject: [PATCH 005/112] add: x86 windows --- .github/workflows/flutter-nightly.yml | 10 ++++------ Cargo.lock | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 30bf10605..b6020460c 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -154,7 +154,7 @@ jobs: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} # Temporarily disable this action due to additional test is needed. - if: false + # if: false strategy: fail-fast: false matrix: @@ -175,14 +175,10 @@ jobs: - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: - toolchain: stable-${{ matrix.job.target }} + toolchain: nightly-${{ matrix.job.target }} target: ${{ matrix.job.target }} override: true profile: minimal # minimal component installation (ie, no documentation) - - - name: Set Rust toolchain to the target - run: | - rustup default stable-${{ matrix.job.target }} - uses: Swatinem/rust-cache@v2 with: @@ -204,6 +200,8 @@ jobs: shell: bash run: | python3 res/inline-sciter.py + # Patch sciter x86 + sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml # Replace the link for the ico. rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico cargo build --features inline --release --bins diff --git a/Cargo.lock b/Cargo.lock index a0815f8e0..5a4cad9f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6343,7 +6343,7 @@ dependencies = [ [[package]] name = "trayicon" version = "0.1.3-1" -source = "git+https://github.com/open-trade/trayicon-rs#8d9c4489287752cc5be4a35c103198f7111112f9" +source = "git+https://github.com/open-trade/trayicon-rs#35bd01963271b45a0b6a0f65f1ce03a5f35bc691" dependencies = [ "winapi 0.3.9", "winit", From 78748271ac329a63fea837d772a7f7da4f462f89 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 31 Mar 2023 16:10:52 +0800 Subject: [PATCH 006/112] vp8 Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 42 ++- .../lib/desktop/widgets/remote_toolbar.dart | 26 +- flutter/lib/mobile/pages/remote_page.dart | 5 +- libs/hbb_common/protos/message.proto | 16 +- libs/hbb_common/src/config.rs | 5 +- libs/scrap/examples/benchmark.rs | 46 ++- libs/scrap/src/common/android.rs | 1 + libs/scrap/src/common/codec.rs | 319 +++++++++--------- libs/scrap/src/common/hwcodec.rs | 17 +- libs/scrap/src/common/mod.rs | 53 +++ libs/scrap/src/common/record.rs | 58 ++-- libs/scrap/src/common/vpxcodec.rs | 46 +-- src/client.rs | 37 +- src/client/helper.rs | 33 +- src/client/io_loop.rs | 9 +- src/flutter_ffi.rs | 13 +- src/server/connection.rs | 34 +- src/server/video_service.rs | 85 +++-- src/ui/header.tis | 9 +- src/ui/remote.rs | 22 +- src/ui_interface.rs | 7 + src/ui_session_interface.rs | 28 +- 22 files changed, 470 insertions(+), 441 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 66ef83d31..74d51407c 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1258,9 +1258,6 @@ class _DisplayState extends State<_Display> { } Widget codec(BuildContext context) { - if (!bind.mainHasHwcodec()) { - return Offstage(); - } final key = 'codec-preference'; onChanged(String value) async { await bind.mainSetUserDefaultOption(key: key, value: value); @@ -1268,28 +1265,45 @@ class _DisplayState extends State<_Display> { } final groupValue = bind.mainGetUserDefaultOption(key: key); - + var hwRadios = []; + try { + final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings()); + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + if (h264) { + hwRadios.add(_Radio(context, + value: 'h264', + groupValue: groupValue, + label: 'H264', + onChanged: onChanged)); + } + if (h265) { + hwRadios.add(_Radio(context, + value: 'h265', + groupValue: groupValue, + label: 'H265', + onChanged: onChanged)); + } + } catch (e) { + debugPrint("failed to parse supported hwdecodings, err=$e"); + } return _Card(title: 'Default Codec', children: [ _Radio(context, value: 'auto', groupValue: groupValue, label: 'Auto', onChanged: onChanged), + _Radio(context, + value: 'vp8', + groupValue: groupValue, + label: 'VP8', + onChanged: onChanged), _Radio(context, value: 'vp9', groupValue: groupValue, label: 'VP9', onChanged: onChanged), - _Radio(context, - value: 'h264', - groupValue: groupValue, - label: 'H264', - onChanged: onChanged), - _Radio(context, - value: 'h265', - groupValue: groupValue, - label: 'H265', - onChanged: onChanged), + ...hwRadios, ]); } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 0ef8674ef..cb4b6d644 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1349,29 +1349,30 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec() { return futureBuilder(future: () async { - final supportedHwcodec = - await bind.sessionSupportedHwcodec(id: widget.id); + final alternativeCodecs = + await bind.sessionAlternativeCodecs(id: widget.id); final codecPreference = await bind.sessionGetOption(id: widget.id, arg: 'codec-preference') ?? ''; return { - 'supportedHwcodec': supportedHwcodec, + 'alternativeCodecs': alternativeCodecs, 'codecPreference': codecPreference }; }(), hasData: (data) { final List codecs = []; try { - final Map codecsJson = jsonDecode(data['supportedHwcodec']); + final Map codecsJson = jsonDecode(data['alternativeCodecs']); + final vp8 = codecsJson['vp8'] ?? false; final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { debugPrint("Show Codec Preference err=$e"); } - final visible = bind.mainHasHwcodec() && - codecs.length == 2 && - (codecs[0] || codecs[1]); + final visible = + codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); if (!visible) return Offstage(); final groupValue = data['codecPreference'] as String; onChanged(String? value) async { @@ -1392,6 +1393,13 @@ class _DisplayMenuState extends State<_DisplayMenu> { onChanged: onChanged, ffi: widget.ffi, ), + _RadioMenuButton( + child: Text(translate('VP8')), + value: 'vp8', + groupValue: groupValue, + onChanged: codecs[0] ? onChanged : null, + ffi: widget.ffi, + ), _RadioMenuButton( child: Text(translate('VP9')), value: 'vp9', @@ -1403,14 +1411,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate('H264')), value: 'h264', groupValue: groupValue, - onChanged: codecs[0] ? onChanged : null, + onChanged: codecs[1] ? onChanged : null, ffi: widget.ffi, ), _RadioMenuButton( child: Text(translate('H265')), value: 'h265', groupValue: groupValue, - onChanged: codecs[1] ? onChanged : null, + onChanged: codecs[2] ? onChanged : null, ffi: widget.ffi, ), ]); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 083cdcd1c..74a327c27 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -973,9 +973,11 @@ void showOptions( if (hasHwcodec) { try { final Map codecsJson = - jsonDecode(await bind.sessionSupportedHwcodec(id: id)); + jsonDecode(await bind.sessionAlternativeCodecs(id: id)); + final vp8 = codecsJson['vp8'] ?? false; final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { @@ -1044,6 +1046,7 @@ void showOptions( if (hasHwcodec && codecs.length == 2 && (codecs[0] || codecs[1])) { radios.addAll([ getRadio(translate('Auto'), 'auto', codec, setCodec), + getRadio('VP8', 'vp8', codec, setCodec), getRadio('VP9', 'vp9', codec, setCodec), ]); if (codecs[0]) { diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 6295c160b..321747e3a 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -24,6 +24,7 @@ message VideoFrame { YUV yuv = 8; EncodedVideoFrames h264s = 10; EncodedVideoFrames h265s = 11; + EncodedVideoFrames vp8s = 12; } } @@ -76,6 +77,7 @@ message Features { message SupportedEncoding { bool h264 = 1; bool h265 = 2; + bool vp8 = 3; } message PeerInfo { @@ -457,18 +459,20 @@ enum ImageQuality { Best = 4; } -message VideoCodecState { +message SupportedDecoding { enum PreferCodec { Auto = 0; - VPX = 1; + VP9 = 1; H264 = 2; H265 = 3; + VP8 = 4; } - int32 score_vpx = 1; - int32 score_h264 = 2; - int32 score_h265 = 3; + int32 ability_vp9 = 1; + int32 ability_h264 = 2; + int32 ability_h265 = 3; PreferCodec prefer = 4; + int32 ability_vp8 = 5; } message OptionMessage { @@ -486,7 +490,7 @@ message OptionMessage { BoolOption disable_audio = 7; BoolOption disable_clipboard = 8; BoolOption enable_file_transfer = 9; - VideoCodecState video_codec_state = 10; + SupportedDecoding supported_decoding = 10; int32 custom_fps = 11; BoolOption disable_keyboard = 12; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6a823c7b7..960074a8f 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -917,7 +917,8 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = + decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } @@ -1356,7 +1357,7 @@ impl UserDefaultConfig { "view_style" => self.get_string(key, "original", vec!["adaptive"]), "scroll_style" => self.get_string(key, "scrollauto", vec!["scrollbar"]), "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), - "codec-preference" => self.get_string(key, "auto", vec!["vp9", "h264", "h265"]), + "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), _ => self diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index 003830f95..ba8dec9f2 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -3,7 +3,8 @@ use hbb_common::env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use scrap::{ codec::{EncoderApi, EncoderCfg}, Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, - VpxVideoCodecId, STRIDE_ALIGN, + VpxVideoCodecId::{self, *}, + STRIDE_ALIGN, }; use std::{io::Write, time::Instant}; @@ -49,7 +50,7 @@ fn main() { "benchmark {}x{} bitrate:{}k hw_pixfmt:{:?}", width, height, bitrate_k, args.flag_hw_pixfmt ); - test_vp9(&yuvs, width, height, bitrate_k, yuv_count); + [VP8, VP9].map(|c| test_vpx(c, &yuvs, width, height, bitrate_k, yuv_count)); #[cfg(feature = "hwcodec")] { use hwcodec::AVPixelFormat; @@ -57,7 +58,7 @@ fn main() { Pixfmt::I420 => AVPixelFormat::AV_PIX_FMT_YUV420P, Pixfmt::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, }; - let yuvs = hw::vp9_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); + let yuvs = hw::vpx_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); hw::test(&yuvs, width, height, bitrate_k, yuv_count, hw_pixfmt); } } @@ -87,13 +88,20 @@ fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { } } -fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, yuv_count: usize) { +fn test_vpx( + codec_id: VpxVideoCodecId, + yuvs: &Vec>, + width: usize, + height: usize, + bitrate_k: usize, + yuv_count: usize, +) { let config = EncoderCfg::VPX(VpxEncoderConfig { width: width as _, height: height as _, timebase: [1, 1000], bitrate: bitrate_k as _, - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }); let mut encoder = VpxEncoder::new(config).unwrap(); @@ -104,35 +112,43 @@ fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, .unwrap(); let _ = encoder.flush().unwrap(); } - println!("vp9 encode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} encode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); // prepare data separately - let mut vp9s = vec![]; + let mut vpxs = vec![]; let start = Instant::now(); for yuv in yuvs { for ref frame in encoder .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) .unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } for ref frame in encoder.flush().unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } } - assert_eq!(vp9s.len(), yuv_count); + assert_eq!(vpxs.len(), yuv_count); let mut decoder = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }) .unwrap(); let start = Instant::now(); - for vp9 in vp9s { - let _ = decoder.decode(&vp9); + for vpx in vpxs { + let _ = decoder.decode(&vpx); let _ = decoder.flush(); } - println!("vp9 decode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} decode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); } #[cfg(feature = "hwcodec")] @@ -267,7 +283,7 @@ mod hw { Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265 } - pub fn vp9_yuv_to_hw_yuv( + pub fn vpx_yuv_to_hw_yuv( yuvs: Vec>, width: usize, height: usize, diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 8daf8e4bb..36d6a8a9b 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -50,6 +50,7 @@ impl crate::TraitCapturer for Capturer { pub enum Frame<'a> { RAW(&'a [u8]), + VP8(&'a [u8]), VP9(&'a [u8]), Empty, } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 9e4b6fce4..3209933b4 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -1,7 +1,6 @@ -use std::ops::{Deref, DerefMut}; -#[cfg(feature = "hwcodec")] use std::{ collections::HashMap, + ops::{Deref, DerefMut}, sync::{Arc, Mutex}, }; @@ -11,30 +10,31 @@ use crate::hwcodec::*; use crate::mediacodec::{ MediaCodecDecoder, MediaCodecDecoders, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT, }; -use crate::{vpxcodec::*, ImageFormat}; +use crate::{vpxcodec::*, CodecName, ImageFormat}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::sysinfo::{System, SystemExt}; use hbb_common::{ anyhow::anyhow, + config::PeerConfig, log, - message_proto::{video_frame, EncodedVideoFrames, Message, VideoCodecState}, + message_proto::{ + supported_decoding::PreferCodec, video_frame, EncodedVideoFrames, Message, + SupportedDecoding, SupportedEncoding, + }, ResultType, }; #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] -use hbb_common::{ - config::{Config2, PeerConfig}, - lazy_static, - message_proto::video_codec_state::PreferCodec, -}; +use hbb_common::{config::Config2, lazy_static}; -#[cfg(feature = "hwcodec")] lazy_static::lazy_static! { - static ref PEER_DECODER_STATES: Arc>> = Default::default(); + static ref PEER_DECODINGS: Arc>> = Default::default(); + static ref CODEC_NAME: Arc> = Arc::new(Mutex::new(CodecName::VP9)); } -const SCORE_VPX: i32 = 90; #[derive(Debug, Clone)] pub struct HwEncoderConfig { - pub codec_name: String, + pub name: String, pub width: usize, pub height: usize, pub bitrate: i32, @@ -58,10 +58,6 @@ pub trait EncoderApi { fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()>; } -pub struct DecoderCfg { - pub vpx: VpxDecoderConfig, -} - pub struct Encoder { pub codec: Box, } @@ -81,7 +77,8 @@ impl DerefMut for Encoder { } pub struct Decoder { - vpx: VpxDecoder, + vp8: VpxDecoder, + vp9: VpxDecoder, #[cfg(feature = "hwcodec")] hw: HwDecoders, #[cfg(feature = "hwcodec")] @@ -91,10 +88,10 @@ pub struct Decoder { } #[derive(Debug, Clone)] -pub enum EncoderUpdate { - State(VideoCodecState), +pub enum EncodingUpdate { + New(SupportedDecoding), Remove, - DisableHwIfNotExist, + NewOnlyVP9, } impl Encoder { @@ -120,172 +117,156 @@ impl Encoder { } } - // TODO - pub fn update_video_encoder(id: i32, update: EncoderUpdate) { + pub fn update(id: i32, update: EncodingUpdate) { + let mut decodings = PEER_DECODINGS.lock().unwrap(); + match update { + EncodingUpdate::New(decoding) => { + decodings.insert(id, decoding); + } + EncodingUpdate::Remove => { + decodings.remove(&id); + } + EncodingUpdate::NewOnlyVP9 => { + decodings.insert( + id, + SupportedDecoding { + ability_vp9: 1, + ..Default::default() + }, + ); + } + } + + let vp8_useable = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_vp8 > 0); + #[allow(unused_mut)] + let mut h264_name = None; + #[allow(unused_mut)] + let mut h265_name = None; #[cfg(feature = "hwcodec")] { - let mut states = PEER_DECODER_STATES.lock().unwrap(); - match update { - EncoderUpdate::State(state) => { - states.insert(id, state); - } - EncoderUpdate::Remove => { - states.remove(&id); - } - EncoderUpdate::DisableHwIfNotExist => { - if !states.contains_key(&id) { - states.insert(id, VideoCodecState::default()); - } - } + let best = HwEncoder::best(); + let h264_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); + let h265_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); + if h264_useable { + h264_name = best.h264.map_or(None, |c| Some(c.name)); } - let name = HwEncoder::current_name(); - if states.len() > 0 { - let best = HwEncoder::best(); - let enabled_h264 = best.h264.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h264 > 0); - let enabled_h265 = best.h265.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h265 > 0); - - // Preference first - let mut preference = PreferCodec::Auto; - let preferences: Vec<_> = states - .iter() - .filter(|(_, s)| { - s.prefer == PreferCodec::VPX.into() - || s.prefer == PreferCodec::H264.into() && enabled_h264 - || s.prefer == PreferCodec::H265.into() && enabled_h265 - }) - .map(|(_, s)| s.prefer) - .collect(); - if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { - preference = preferences[0].enum_value_or(PreferCodec::Auto); - } - - match preference { - PreferCodec::VPX => *name.lock().unwrap() = None, - PreferCodec::H264 => { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)) - } - PreferCodec::H265 => { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)) - } - PreferCodec::Auto => { - // score encoder - let mut score_vpx = SCORE_VPX; - let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); - let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); - - // score decoder - score_vpx += states.iter().map(|s| s.1.score_vpx).sum::(); - if enabled_h264 { - score_h264 += states.iter().map(|s| s.1.score_h264).sum::(); - } - if enabled_h265 { - score_h265 += states.iter().map(|s| s.1.score_h265).sum::(); - } - - if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)); - } else if enabled_h264 - && score_h264 >= score_vpx - && score_h264 >= score_h265 - { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)); - } else { - *name.lock().unwrap() = None; - } - } - } - - log::info!( - "connection count:{}, used preference:{:?}, encoder:{:?}", - states.len(), - preference, - name.lock().unwrap() - ) - } else { - *name.lock().unwrap() = None; + if h265_useable { + h265_name = best.h265.map_or(None, |c| Some(c.name)); } } - #[cfg(not(feature = "hwcodec"))] - { - let _ = id; - let _ = update; + + let mut name = CODEC_NAME.lock().unwrap(); + let mut preference = PreferCodec::Auto; + let preferences: Vec<_> = decodings + .iter() + .filter(|(_, s)| { + s.prefer == PreferCodec::VP9.into() + || s.prefer == PreferCodec::VP8.into() && vp8_useable + || s.prefer == PreferCodec::H264.into() && h264_name.is_some() + || s.prefer == PreferCodec::H265.into() && h265_name.is_some() + }) + .map(|(_, s)| s.prefer) + .collect(); + if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { + preference = preferences[0].enum_value_or(PreferCodec::Auto); } + + #[allow(unused_mut)] + let mut auto_codec = CodecName::VP9; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if vp8_useable && System::new_all().total_memory() <= 4 * 1024 * 1024 * 1024 { + // 4 Gb + auto_codec = CodecName::VP8 + } + + match preference { + PreferCodec::VP8 => *name = CodecName::VP8, + PreferCodec::VP9 => *name = CodecName::VP9, + PreferCodec::H264 => *name = h264_name.map_or(auto_codec, |c| CodecName::H264(c)), + PreferCodec::H265 => *name = h265_name.map_or(auto_codec, |c| CodecName::H265(c)), + PreferCodec::Auto => *name = auto_codec, + } + + log::info!( + "connection count:{}, used preference:{:?}, encoder:{:?}", + decodings.len(), + preference, + *name + ) } + #[inline] - pub fn current_hw_encoder_name() -> Option { - #[cfg(feature = "hwcodec")] - if enable_hwcodec_option() { - return HwEncoder::current_name().lock().unwrap().clone(); - } else { - return None; - } - #[cfg(not(feature = "hwcodec"))] - return None; + pub fn negotiated_codec() -> CodecName { + CODEC_NAME.lock().unwrap().clone() } - pub fn supported_encoding() -> (bool, bool) { + pub fn supported_encoding() -> SupportedEncoding { + #[allow(unused_mut)] + let mut encoding = SupportedEncoding { + vp8: true, + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwEncoder::best(); - ( - best.h264.as_ref().map_or(false, |c| c.score > 0), - best.h265.as_ref().map_or(false, |c| c.score > 0), - ) - } else { - (false, false) + encoding.h264 = best.h264.is_some(); + encoding.h265 = best.h265.is_some(); } - #[cfg(not(feature = "hwcodec"))] - (false, false) + encoding } } impl Decoder { - pub fn video_codec_state(_id: &str) -> VideoCodecState { + pub fn supported_decodings(id_for_perfer: Option<&str>) -> SupportedDecoding { + #[allow(unused_mut)] + let mut decoding = SupportedDecoding { + ability_vp8: 1, + ability_vp9: 1, + prefer: id_for_perfer + .map_or(PreferCodec::Auto, |id| Self::codec_preference(id)) + .into(), + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwDecoder::best(); - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264: best.h264.map_or(0, |c| c.score), - score_h265: best.h265.map_or(0, |c| c.score), - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; + decoding.ability_h264 = if best.h264.is_some() { 1 } else { 0 }; + decoding.ability_h265 = if best.h265.is_some() { 1 } else { 0 }; } #[cfg(feature = "mediacodec")] if enable_hwcodec_option() { - let score_h264 = if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 92 - } else { - 0 - }; - let score_h265 = if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 94 - } else { - 0 - }; - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264, - score_h265, - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; - } - VideoCodecState { - score_vpx: SCORE_VPX, - ..Default::default() + decoding.ability_h264 = + if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; + decoding.ability_h265 = + if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; } + decoding } - pub fn new(config: DecoderCfg) -> Decoder { - let vpx = VpxDecoder::new(config.vpx).unwrap(); + pub fn new() -> Decoder { + let vp8 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP8, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); + let vp9 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); Decoder { - vpx, + vp8, + vp9, #[cfg(feature = "hwcodec")] hw: if enable_hwcodec_option() { HwDecoder::new_decoders() @@ -310,8 +291,11 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { + video_frame::Union::Vp8s(vp8s) => { + Decoder::handle_vpxs_video_frame(&mut self.vp8, vp8s, fmt, rgb) + } video_frame::Union::Vp9s(vp9s) => { - Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, fmt, rgb) + Decoder::handle_vpxs_video_frame(&mut self.vp9, vp9s, fmt, rgb) } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { @@ -349,15 +333,15 @@ impl Decoder { } } - fn handle_vp9s_video_frame( + fn handle_vpxs_video_frame( decoder: &mut VpxDecoder, - vp9s: &EncodedVideoFrames, + vpxs: &EncodedVideoFrames, fmt: (ImageFormat, usize), rgb: &mut Vec, ) -> ResultType { let mut last_frame = Image::new(); - for vp9 in vp9s.frames.iter() { - for frame in decoder.decode(&vp9.data)? { + for vpx in vpxs.frames.iter() { + for frame in decoder.decode(&vpx.data)? { drop(last_frame); last_frame = frame; } @@ -408,14 +392,15 @@ impl Decoder { return Ok(false); } - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] fn codec_preference(id: &str) -> PreferCodec { let codec = PeerConfig::load(id) .options .get("codec-preference") .map_or("".to_owned(), |c| c.to_owned()); - if codec == "vp9" { - PreferCodec::VPX + if codec == "vp8" { + PreferCodec::VP8 + } else if codec == "vp9" { + PreferCodec::VP9 } else if codec == "h264" { PreferCodec::H264 } else if codec == "h265" { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 2c69774fb..f4de4bf84 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -7,7 +7,7 @@ use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, - lazy_static, log, + log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, ResultType, }; @@ -19,11 +19,6 @@ use hwcodec::{ Quality::{self, *}, RateControl::{self, *}, }; -use std::sync::{Arc, Mutex}; - -lazy_static::lazy_static! { - static ref HW_ENCODER_NAME: Arc>> = Default::default(); -} const CFG_KEY_ENCODER: &str = "bestHwEncoders"; const CFG_KEY_DECODER: &str = "bestHwDecoders"; @@ -49,7 +44,7 @@ impl EncoderApi for HwEncoder { match cfg { EncoderCfg::HW(config) => { let ctx = EncodeContext { - name: config.codec_name.clone(), + name: config.name.clone(), width: config.width as _, height: config.height as _, pixfmt: DEFAULT_PIXFMT, @@ -60,12 +55,12 @@ impl EncoderApi for HwEncoder { quality: DEFAULT_HW_QUALITY, rc: DEFAULT_RC, }; - let format = match Encoder::format_from_name(config.codec_name.clone()) { + let format = match Encoder::format_from_name(config.name.clone()) { Ok(format) => format, Err(_) => { return Err(anyhow!(format!( "failed to get format from name:{}", - config.codec_name + config.name ))) } }; @@ -133,10 +128,6 @@ impl HwEncoder { }) } - pub fn current_name() -> Arc>> { - HW_ENCODER_NAME.clone() - } - pub fn encode(&mut self, bgra: &[u8]) -> ResultType> { match self.pixfmt { AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420( diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 0ad158cca..26b946401 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -1,4 +1,5 @@ pub use self::vpxcodec::*; +use hbb_common::message_proto::{video_frame, VideoFrame}; cfg_if! { if #[cfg(quartz)] { @@ -92,3 +93,55 @@ pub fn is_cursor_embedded() -> bool { pub fn is_cursor_embedded() -> bool { false } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CodecName { + VP8, + VP9, + H264(String), + H265(String), +} + +#[derive(PartialEq, Debug, Clone)] +pub enum CodecFormat { + VP8, + VP9, + H264, + H265, + Unknown, +} + +impl From<&VideoFrame> for CodecFormat { + fn from(it: &VideoFrame) -> Self { + match it.union { + Some(video_frame::Union::Vp8s(_)) => CodecFormat::VP8, + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, + _ => CodecFormat::Unknown, + } + } +} + +impl From<&CodecName> for CodecFormat { + fn from(value: &CodecName) -> Self { + match value { + CodecName::VP8 => Self::VP8, + CodecName::VP9 => Self::VP9, + CodecName::H264(_) => Self::H264, + CodecName::H265(_) => Self::H265, + } + } +} + +impl ToString for CodecFormat { + fn to_string(&self) -> String { + match self { + CodecFormat::VP8 => "VP8".into(), + CodecFormat::VP9 => "VP9".into(), + CodecFormat::H264 => "H264".into(), + CodecFormat::H265 => "H265".into(), + CodecFormat::Unknown => "Unknow".into(), + } + } +} diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 9f38f2d6a..9de70ae14 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -1,3 +1,4 @@ +use crate::CodecFormat; #[cfg(feature = "hwcodec")] use hbb_common::anyhow::anyhow; use hbb_common::{ @@ -21,13 +22,6 @@ use webm::mux::{self, Segment, Track, VideoTrack, Writer}; const MIN_SECS: u64 = 1; -#[derive(Debug, Clone, PartialEq)] -pub enum RecordCodecID { - VP9, - H264, - H265, -} - #[derive(Debug, Clone)] pub struct RecorderContext { pub server: bool, @@ -36,7 +30,7 @@ pub struct RecorderContext { pub filename: String, pub width: usize, pub height: usize, - pub codec_id: RecordCodecID, + pub format: CodecFormat, pub tx: Option>, } @@ -55,8 +49,9 @@ impl RecorderContext { } let file = if self.server { "s" } else { "c" }.to_string() + &self.id.clone() - + &chrono::Local::now().format("_%Y%m%d%H%M%S").to_string() - + if self.codec_id == RecordCodecID::VP9 { + + &chrono::Local::now().format("_%Y%m%d%H%M%S_").to_string() + + &self.format.to_string() + + if self.format == CodecFormat::VP9 || self.format == CodecFormat::VP8 { ".webm" } else { ".mp4" @@ -107,8 +102,8 @@ impl DerefMut for Recorder { impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { ctx.set_filename()?; - let recorder = match ctx.codec_id { - RecordCodecID::VP9 => Recorder { + let recorder = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Recorder { inner: Box::new(WebmRecorder::new(ctx.clone())?), ctx, }, @@ -126,8 +121,8 @@ impl Recorder { fn change(&mut self, mut ctx: RecorderContext) -> ResultType<()> { ctx.set_filename()?; - self.inner = match ctx.codec_id { - RecordCodecID::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), + self.inner = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), #[cfg(feature = "hwcodec")] _ => Box::new(HwRecorder::new(ctx.clone())?), #[cfg(not(feature = "hwcodec"))] @@ -148,10 +143,19 @@ impl Recorder { pub fn write_frame(&mut self, frame: &video_frame::Union) -> ResultType<()> { match frame { - video_frame::Union::Vp9s(vp9s) => { - if self.ctx.codec_id != RecordCodecID::VP9 { + video_frame::Union::Vp8s(vp8s) => { + if self.ctx.format != CodecFormat::VP8 { self.change(RecorderContext { - codec_id: RecordCodecID::VP9, + format: CodecFormat::VP8, + ..self.ctx.clone() + })?; + } + vp8s.frames.iter().map(|f| self.write_video(f)).count(); + } + video_frame::Union::Vp9s(vp9s) => { + if self.ctx.format != CodecFormat::VP9 { + self.change(RecorderContext { + format: CodecFormat::VP9, ..self.ctx.clone() })?; } @@ -159,25 +163,25 @@ impl Recorder { } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { - if self.ctx.codec_id != RecordCodecID::H264 { + if self.ctx.format != CodecFormat::H264 { self.change(RecorderContext { - codec_id: RecordCodecID::H264, + format: CodecFormat::H264, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H264 { + if self.ctx.format == CodecFormat::H264 { h264s.frames.iter().map(|f| self.write_video(f)).count(); } } #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { - if self.ctx.codec_id != RecordCodecID::H265 { + if self.ctx.format != CodecFormat::H265 { self.change(RecorderContext { - codec_id: RecordCodecID::H265, + format: CodecFormat::H265, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H265 { + if self.ctx.format == CodecFormat::H265 { h265s.frames.iter().map(|f| self.write_video(f)).count(); } } @@ -221,7 +225,11 @@ impl RecorderApi for WebmRecorder { ctx.width as _, ctx.height as _, None, - mux::VideoCodecId::VP9, + if ctx.format == CodecFormat::VP9 { + mux::VideoCodecId::VP9 + } else { + mux::VideoCodecId::VP8 + }, ); Ok(WebmRecorder { vt, @@ -279,7 +287,7 @@ impl RecorderApi for HwRecorder { filename: ctx.filename.clone(), width: ctx.width, height: ctx.height, - is265: ctx.codec_id == RecordCodecID::H265, + is265: ctx.format == CodecFormat::H265, framerate: crate::hwcodec::DEFAULT_TIME_BASE[1] as _, }) .map_err(|_| anyhow!("Failed to create hardware muxer"))?; diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 3df9c0461..4820ea171 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -30,6 +30,7 @@ pub struct VpxEncoder { ctx: vpx_codec_ctx_t, width: usize, height: usize, + id: VpxVideoCodecId, } pub struct VpxDecoder { @@ -97,15 +98,10 @@ impl EncoderApi for VpxEncoder { { match cfg { crate::codec::EncoderCfg::VPX(config) => { - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_cx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), + }; let mut c = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; call_vpx!(vpx_codec_enc_config_default(i, &mut c, 0)); @@ -187,12 +183,17 @@ impl EncoderApi for VpxEncoder { VP9E_SET_TILE_COLUMNS as _, 4 as c_int )); + } else if config.codec == VpxVideoCodecId::VP8 { + // https://github.com/webmproject/libvpx/blob/972149cafeb71d6f08df89e91a0130d6a38c4b15/vpx/vp8cx.h#L172 + // https://groups.google.com/a/webmproject.org/g/webm-discuss/c/DJhSrmfQ61M + call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, 12,)); } Ok(Self { ctx, width: config.width as _, height: config.height as _, + id: config.codec, }) } _ => Err(anyhow!("encoder type mismatch")), @@ -213,7 +214,7 @@ impl EncoderApi for VpxEncoder { // to-do: flush periodically, e.g. 1 second if frames.len() > 0 { - Ok(VpxEncoder::create_msg(frames)) + Ok(VpxEncoder::create_msg(self.id, frames)) } else { Err(anyhow!("no valid frame")) } @@ -280,13 +281,17 @@ impl VpxEncoder { } #[inline] - fn create_msg(vp9s: Vec) -> Message { + pub fn create_msg(codec_id: VpxVideoCodecId, frames: Vec) -> Message { let mut msg_out = Message::new(); let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), + let vpxs = EncodedVideoFrames { + frames: frames.into(), ..Default::default() - }); + }; + match codec_id { + VpxVideoCodecId::VP8 => vf.set_vp8s(vpxs), + VpxVideoCodecId::VP9 => vf.set_vp9s(vpxs), + } msg_out.set_video_frame(vf); msg_out } @@ -382,15 +387,10 @@ impl VpxDecoder { pub fn new(config: VpxDecoderConfig) -> Result { // This is sound because `vpx_codec_ctx` is a repr(C) struct without any field that can // cause UB if uninitialized. - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_dx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), + }; let mut ctx = Default::default(); let cfg = vpx_codec_dec_cfg_t { threads: if config.num_threads == 0 { diff --git a/src/client.rs b/src/client.rs index 2f745b70c..ae93ccfcf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -44,9 +44,9 @@ use hbb_common::{ }; pub use helper::*; use scrap::{ - codec::{Decoder, DecoderCfg}, + codec::Decoder, record::{Recorder, RecorderContext}, - ImageFormat, VpxDecoderConfig, VpxVideoCodecId, + ImageFormat, }; use crate::{ @@ -917,12 +917,7 @@ impl VideoHandler { /// Create a new video handler. pub fn new() -> Self { VideoHandler { - decoder: Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }, - }), + decoder: Decoder::new(), rgb: Default::default(), recorder: Default::default(), record: false, @@ -954,12 +949,7 @@ impl VideoHandler { /// Reset the decoder. pub fn reset(&mut self) { - self.decoder = Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: 1, - }, - }); + self.decoder = Decoder::new(); } /// Start or stop screen record. @@ -973,7 +963,7 @@ impl VideoHandler { filename: "".to_owned(), width: w as _, height: h as _, - codec_id: scrap::record::RecordCodecID::VP9, + format: scrap::CodecFormat::VP9, tx: None, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); @@ -999,7 +989,7 @@ pub struct LoginConfigHandler { pub conn_id: i32, features: Option, session_id: u64, - pub supported_encoding: Option<(bool, bool)>, + pub supported_encoding: SupportedEncoding, pub restarting_remote_device: bool, pub force_relay: bool, pub direct: Option, @@ -1047,7 +1037,7 @@ impl LoginConfigHandler { self.remember = !config.password.is_empty(); self.config = config; self.session_id = rand::random(); - self.supported_encoding = None; + self.supported_encoding = Default::default(); self.restarting_remote_device = false; self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay; self.direct = None; @@ -1331,8 +1321,8 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - let state = Decoder::video_codec_state(&self.id); - msg.video_codec_state = hbb_common::protobuf::MessageField::some(state); + msg.supported_decoding = + hbb_common::protobuf::MessageField::some(Decoder::supported_decodings(Some(&self.id))); n += 1; if n > 0 { @@ -1565,10 +1555,7 @@ impl LoginConfigHandler { self.conn_id = pi.conn_id; // no matter if change, for update file time self.save_config(config); - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - self.supported_encoding = Some((pi.encoding.h264, pi.encoding.h265)); - } + self.supported_encoding = pi.encoding.clone().unwrap_or_default(); } pub fn get_remote_dir(&self) -> String { @@ -1626,10 +1613,10 @@ impl LoginConfigHandler { } pub fn change_prefer_codec(&self) -> Message { - let state = scrap::codec::Decoder::video_codec_state(&self.id); + let decoding = scrap::codec::Decoder::supported_decodings(Some(&self.id)); let mut misc = Misc::new(); misc.set_option(OptionMessage { - video_codec_state: hbb_common::protobuf::MessageField::some(state), + supported_decoding: hbb_common::protobuf::MessageField::some(decoding), ..Default::default() }); let mut msg_out = Message::new(); diff --git a/src/client/helper.rs b/src/client/helper.rs index a9696a8e8..61844d908 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,37 +1,8 @@ use hbb_common::{ get_time, - message_proto::{video_frame, Message, VideoFrame, VoiceCallRequest, VoiceCallResponse}, + message_proto::{Message, VoiceCallRequest, VoiceCallResponse}, }; - -#[derive(PartialEq, Debug, Clone)] -pub enum CodecFormat { - VP9, - H264, - H265, - Unknown, -} - -impl From<&VideoFrame> for CodecFormat { - fn from(it: &VideoFrame) -> Self { - match it.union { - Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::H264s(_)) => CodecFormat::H264, - Some(video_frame::Union::H265s(_)) => CodecFormat::H265, - _ => CodecFormat::Unknown, - } - } -} - -impl ToString for CodecFormat { - fn to_string(&self) -> String { - match self { - CodecFormat::VP9 => "VP9".into(), - CodecFormat::H264 => "H264".into(), - CodecFormat::H265 => "H265".into(), - CodecFormat::Unknown => "Unknow".into(), - } - } -} +use scrap::CodecFormat; #[derive(Debug, Default)] pub struct QualityStatus { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b0bddc82e..0f662e015 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -29,10 +29,10 @@ use hbb_common::tokio::{ time::{self, Duration, Instant, Interval}, }; use hbb_common::{allow_err, fs, get_time, log, message_proto::*, Stream}; +use scrap::CodecFormat; use crate::client::{ - new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, - SEC30, + new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{self, update_clipboard}; @@ -817,11 +817,10 @@ impl Remote { } fn contains_key_frame(vf: &VideoFrame) -> bool { + use video_frame::Union::*; match &vf.union { Some(vf) => match vf { - video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key), + Vp8s(f) | Vp9s(f) | H264s(f) | H265s(f) => f.frames.iter().any(|e| e.key), _ => false, }, None => false, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index aee486b94..1d965ebc5 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -969,6 +969,13 @@ pub fn main_has_hwcodec() -> SyncReturn { SyncReturn(has_hwcodec()) } +pub fn main_supported_hwdecodings() -> SyncReturn { + let decoding = supported_hwdecodings(); + let msg = HashMap::from([("h264", decoding.0), ("h265", decoding.1)]); + + SyncReturn(serde_json::ser::to_string(&msg).unwrap_or("".to_owned())) +} + pub fn main_is_root() -> bool { is_root() } @@ -1054,10 +1061,10 @@ pub fn session_send_note(id: String, note: String) { } } -pub fn session_supported_hwcodec(id: String) -> String { +pub fn session_alternative_codecs(id: String) -> String { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - let (h264, h265) = session.supported_hwcodec(); - let msg = HashMap::from([("h264", h264), ("h265", h265)]); + let (vp8, h264, h265) = session.alternative_codecs(); + let msg = HashMap::from([("vp8", vp8), ("h264", h264), ("h265", h265)]); serde_json::ser::to_string(&msg).unwrap_or("".to_owned()) } else { String::new() diff --git a/src/server/connection.rs b/src/server/connection.rs index 725ff43ee..539d96ddd 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -533,7 +533,7 @@ impl Connection { let _ = privacy_mode::turn_off_privacy(0); } video_service::notify_video_frame_fetched(id, None); - scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); + scrap::codec::Encoder::update(id, scrap::codec::EncodingUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); if conn.authorized { password::update_temporary_password(); @@ -860,16 +860,7 @@ impl Connection { } } - #[cfg(feature = "hwcodec")] - { - let (h264, h265) = scrap::codec::Encoder::supported_encoding(); - pi.encoding = Some(SupportedEncoding { - h264, - h265, - ..Default::default() - }) - .into(); - } + pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into(); if self.port_forward_socket.is_some() { let mut msg_out = Message::new(); @@ -1145,21 +1136,21 @@ impl Connection { self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.options_in_login = Some(o.clone()); - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::State(q), + scrap::codec::EncodingUpdate::New(q), ); } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } self.video_ack_required = lr.video_ack_required; @@ -1758,11 +1749,8 @@ impl Connection { .unwrap() .update_user_fps(o.custom_fps as _); } - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( - self.inner.id(), - scrap::codec::EncoderUpdate::State(q), - ); + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update(self.inner.id(), scrap::codec::EncodingUpdate::New(q)); } if let Ok(q) = o.lock_after_session_end.enum_value() { if q != BoolOption::NotSet { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 205d0584c..691ca4abe 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,7 +31,7 @@ use scrap::{ codec::{Encoder, EncoderCfg, HwEncoderConfig}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - Display, TraitCapturer, + CodecName, Display, TraitCapturer, }; #[cfg(windows)] use std::sync::Once; @@ -468,21 +468,29 @@ fn run(sp: GenericService) -> ResultType<()> { drop(video_qos); log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - let encoder_cfg = match Encoder::current_hw_encoder_name() { - Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { - codec_name, - width: c.width, - height: c.height, - bitrate: bitrate as _, - }), - None => EncoderCfg::VPX(VpxEncoderConfig { - width: c.width as _, - height: c.height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }), + let encoder_cfg = match Encoder::negotiated_codec() { + scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => { + EncoderCfg::HW(HwEncoderConfig { + name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }) + } + name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => { + EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: if name == scrap::CodecName::VP8 { + VpxVideoCodecId::VP8 + } else { + VpxVideoCodecId::VP9 + }, + num_threads: (num_cpus::get() / 2) as _, + }) + } }; let mut encoder; @@ -526,7 +534,7 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); - let codec_name = Encoder::current_hw_encoder_name(); + let codec_name = Encoder::negotiated_codec(); let recorder = get_recorder(c.width, c.height, &codec_name); #[cfg(windows)] start_uac_elevation_check(); @@ -557,7 +565,7 @@ fn run(sp: GenericService) -> ResultType<()> { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - if codec_name != Encoder::current_hw_encoder_name() { + if codec_name != Encoder::negotiated_codec() { bail!("SWITCH"); } #[cfg(windows)] @@ -603,8 +611,14 @@ fn run(sp: GenericService) -> ResultType<()> { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; match frame { + scrap::Frame::VP8(data) => { + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP8, &sp, data, ms)?; + frame_controller.set_send(now, send_conn_ids); + } scrap::Frame::VP9(data) => { - let send_conn_ids = handle_one_frame_encoded(&sp, data, ms)?; + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP9, &sp, data, ms)?; frame_controller.set_send(now, send_conn_ids); } scrap::Frame::RAW(data) => { @@ -717,12 +731,11 @@ fn run(sp: GenericService) -> ResultType<()> { fn get_recorder( width: usize, height: usize, - codec_name: &Option, + codec_name: &CodecName, ) -> Arc>> { #[cfg(not(target_os = "ios"))] let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { use crate::hbbs_http::record_upload; - use scrap::record::RecordCodecID::*; let tx = if record_upload::is_enable() { let (tx, rx) = std::sync::mpsc::channel(); @@ -731,16 +744,6 @@ fn get_recorder( } else { None }; - let codec_id = match codec_name { - Some(name) => { - if name.contains("264") { - H264 - } else { - H265 - } - } - None => VP9, - }; Recorder::new(RecorderContext { server: true, id: Config::get_id(), @@ -748,7 +751,7 @@ fn get_recorder( filename: "".to_owned(), width, height, - codec_id, + format: codec_name.into(), tx, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) @@ -775,19 +778,6 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu Ok(()) } -#[inline] -#[cfg(any(target_os = "android", target_os = "ios"))] -fn create_msg(vp9s: Vec) -> Message { - let mut msg_out = Message::new(); - let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), - ..Default::default() - }); - msg_out.set_video_frame(vf); - msg_out -} - #[inline] fn handle_one_frame( sp: &GenericService, @@ -820,6 +810,7 @@ fn handle_one_frame( #[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub fn handle_one_frame_encoded( + codec: VpxVideoCodecId, sp: &GenericService, frame: &[u8], ms: i64, @@ -831,13 +822,13 @@ pub fn handle_one_frame_encoded( } Ok(()) })?; - let vp9_frame = EncodedVideoFrame { + let vpx_frame = EncodedVideoFrame { data: frame.to_vec().into(), key: true, pts: ms, ..Default::default() }; - let send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); + let send_conn_ids = sp.send_video_frame(scrap::VpxEncoder::create_msg(codec, vec![vpx_frame])); Ok(send_conn_ids) } diff --git a/src/ui/header.tis b/src/ui/header.tis index 257ba417e..af4f1e349 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -161,8 +161,8 @@ class Header: Reactor.Component { } function renderDisplayPop() { - var codecs = handler.supported_hwcodec(); - var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]); + var codecs = handler.alternative_codecs(); + var show_codec = codecs[0] || codecs[1] || codecs[2]; var cursor_embedded = false; if ((pi.displays || []).length > 0) { @@ -186,9 +186,10 @@ class Header: Reactor.Component { {show_codec ?
  • {svg_checkmark}Auto
  • + {codecs[0] ?
  • {svg_checkmark}VP8
  • : ""}
  • {svg_checkmark}VP9
  • - {codecs[0] ?
  • {svg_checkmark}H264
  • : ""} - {codecs[1] ?
  • {svg_checkmark}H265
  • : ""} + {codecs[1] ?
  • {svg_checkmark}H264
  • : ""} + {codecs[2] ?
  • {svg_checkmark}H265
  • : ""}
    : ""}
    {!cursor_embedded &&
  • {svg_checkmark}{translate('Show remote cursor')}
  • } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 68decf955..fc878cf1f 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -20,7 +20,6 @@ use hbb_common::{ use crate::{ client::*, - ui_interface::has_hwcodec, ui_session_interface::{InvokeUiSession, Session}, }; @@ -197,7 +196,14 @@ impl InvokeUiSession for SciterHandler { self.call("confirmDeleteFiles", &make_args!(id, i, name)); } - fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { + fn override_file_confirm( + &self, + id: i32, + file_num: i32, + to: String, + is_upload: bool, + is_identical: bool, + ) { self.call( "overrideFileConfirm", &make_args!(id, file_num, to, is_upload, is_identical), @@ -451,8 +457,7 @@ impl sciter::EventHandler for SciterSession { fn set_write_override(i32, i32, bool, bool, bool); fn get_keyboard_mode(); fn save_keyboard_mode(String); - fn has_hwcodec(); - fn supported_hwcodec(); + fn alternative_codecs(); fn change_prefer_codec(); fn restart_remote_device(); fn request_voice_call(); @@ -504,10 +509,6 @@ impl SciterSession { v } - fn has_hwcodec(&self) -> bool { - has_hwcodec() - } - pub fn t(&self, name: String) -> String { crate::client::translate(name) } @@ -516,9 +517,10 @@ impl SciterSession { super::get_icon() } - fn supported_hwcodec(&self) -> Value { - let (h264, h265) = self.0.supported_hwcodec(); + fn alternative_codecs(&self) -> Value { + let (vp8, h264, h265) = self.0.alternative_codecs(); let mut v = Value::array(0); + v.push(vp8); v.push(h264); v.push(h265); v diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 11357be4a..26d470fe0 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -752,6 +752,13 @@ pub fn has_hwcodec() -> bool { return true; } +#[cfg(feature = "flutter")] +#[inline] +pub fn supported_hwdecodings() -> (bool, bool) { + let decoding = scrap::codec::Decoder::supported_decodings(None); + (decoding.ability_h264 > 0, decoding.ability_h265 > 0) +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_root() -> bool { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f89be4879..3dee89a6e 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -216,24 +216,16 @@ impl Session { true } - pub fn supported_hwcodec(&self) -> (bool, bool) { - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - let decoder = scrap::codec::Decoder::video_codec_state(&self.id); - let mut h264 = decoder.score_h264 > 0; - let mut h265 = decoder.score_h265 > 0; - let (encoding_264, encoding_265) = self - .lc - .read() - .unwrap() - .supported_encoding - .unwrap_or_default(); - h264 = h264 && encoding_264; - h265 = h265 && encoding_265; - return (h264, h265); - } - #[allow(unreachable_code)] - (false, false) + pub fn alternative_codecs(&self) -> (bool, bool, bool) { + let decoder = scrap::codec::Decoder::supported_decodings(None); + let mut vp8 = decoder.ability_vp8 > 0; + let mut h264 = decoder.ability_h264 > 0; + let mut h265 = decoder.ability_h265 > 0; + let enc = &self.lc.read().unwrap().supported_encoding; + vp8 = vp8 && enc.vp8; + h264 = h264 && enc.h264; + h265 = h265 && enc.h265; + (vp8, h264, h265) } pub fn change_prefer_codec(&self) { From 3b289f54c94afc6eb49b6c3c4c489c6ed2edd796 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 31 Mar 2023 23:13:27 +0800 Subject: [PATCH 007/112] opt: use our nightly vcpkg --- .github/workflows/flutter-nightly.yml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index b6020460c..e5c2c2752 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -87,18 +87,14 @@ jobs: Push-Location flutter ; flutter pub get ; Pop-Location ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - name: Install vcpkg dependencies run: | - $VCPKG_ROOT/vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static - shell: bash - + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 + - name: Build rustdesk + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver - name: Sign rustdesk files @@ -192,12 +188,14 @@ jobs: - name: Install vcpkg dependencies run: | - $VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static - shell: bash + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 - name: Build rustdesk id: build shell: bash + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg run: | python3 res/inline-sciter.py # Patch sciter x86 From 726884f1f3890cc6072da5c6d4611b56f6e8ca48 Mon Sep 17 00:00:00 2001 From: chiehw Date: Sat, 1 Apr 2023 01:12:41 +0800 Subject: [PATCH 008/112] Fix review --- src/server/connection.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index eecfaa20c..b25c5b1e7 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1369,10 +1369,12 @@ impl Connection { me.press }; - let key = match me.mode.unwrap() { - KeyboardMode::Map => Some(crate::keyboard::keycode_to_rdev_key(me.chr())), + let key = match me.mode.enum_value_or_default() { + KeyboardMode::Map => { + Some(crate::keyboard::keycode_to_rdev_key(me.chr())) + } KeyboardMode::Translate => { - if let Some(key_event::Union::Chr(code)) = me.union.clone() { + if let Some(key_event::Union::Chr(code)) = me.union { Some(crate::keyboard::keycode_to_rdev_key(code & 0x0000FFFF)) } else { None From c0aa343757f49ed97467d4d677f548b61a1defb4 Mon Sep 17 00:00:00 2001 From: jimmyGALLAND <64364019+jimmyGALLAND@users.noreply.github.com> Date: Fri, 31 Mar 2023 21:57:05 +0200 Subject: [PATCH 009/112] Update fr.rs --- src/lang/fr.rs | 79 +++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index bf3853d96..3ec3f08d2 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -213,7 +213,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Closed manually by the peer", "Fermé manuellement par le pair"), ("Enable remote configuration modification", "Autoriser la modification de la configuration à distance"), ("Run without install", "Exécuter sans installer"), - ("Connect via relay", ""), + ("Connect via relay", "Connexion via relais"), ("Always connect via relay", "Forcer la connexion relais"), ("whitelist_tip", "Seule une IP de la liste blanche peut accéder à mon appareil"), ("Login", "Connexion"), @@ -288,8 +288,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "L'activation de la capture d'écran démarrera automatiquement le service, permettant à d'autres appareils de demander une connexion à partir de cet appareil."), ("android_stop_service_tip", "La fermeture du service fermera automatiquement toutes les connexions établies."), ("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", ""), - ("android_permission_may_not_change_tip", ""), + ("android_start_service_tip", "Appuyez sur [Démarrer le service] ou activez l'autorisation [Capture d'écran] pour démarrer le service de partage d'écran."), + ("android_permission_may_not_change_tip", "Les autorisations pour les connexions établies peuvent ne pas être prisent en compte instantanément ou avant la reconnection."), ("Account", "Compte"), ("Overwrite", "Écraser"), ("This file exists, skip or overwrite this file?", "Ce fichier existe, ignorer ou écraser ce fichier ?"), @@ -311,8 +311,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Gardez le service RustDesk en arrière plan"), ("Ignore Battery Optimizations", "Ignorer les optimisations batterie"), ("android_open_battery_optimizations_tip", "Conseil android d'optimisation de batterie"), - ("Start on Boot", ""), - ("Start the screen sharing service on boot, requires special permissions", ""), + ("Start on Boot", "Lancer au démarrage"), + ("Start the screen sharing service on boot, requires special permissions", "Lancer le service de partage d'écran au démarrage, nécessite des autorisations spéciales"), ("Connection not allowed", "Connexion non autorisée"), ("Legacy mode", "Mode hérité"), ("Map mode", ""), @@ -348,7 +348,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Security", "Sécurité"), ("Theme", "Thème"), ("Dark Theme", "Thème somble"), - ("Light Theme", ""), + ("Light Theme", "Thème clair"), ("Dark", "Sombre"), ("Light", "Clair"), ("Follow System", "Suivi système"), @@ -421,7 +421,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("software_render_tip", "Si vous avez une carte graphique NVIDIA et que la fenêtre distante se ferme immédiatement après la connexion, l'installation du pilote Nouveau et le choix d'utiliser le rendu du logiciel peuvent aider. Un redémarrage du logiciel est requis."), ("Always use software rendering", "Utiliser toujours le rendu logiciel"), ("config_input", "Afin de contrôler le bureau à distance avec le clavier, vous devez accorder à RustDesk l'autorisation \"Surveillance de l’entrée\"."), - ("config_microphone", ""), + ("config_microphone", "Pour discuter à distance, vous devez accorder à RustDesk les autorisations « Enregistrer l'audio »."), ("request_elevation_tip", "Vous pouvez également demander une augmentation des privilèges s'il y a quelqu'un du côté distant."), ("Wait", "En cours"), ("Elevation Error", "Erreur d'augmentation des privilèges"), @@ -451,37 +451,38 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("FPS", "FPS"), ("Auto", "Auto"), ("Other Default Options", "Autres options par défaut"), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), - ("relay_hint_tip", ""), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), - ("No transfers in progress", ""), - ("Set one-time password length", ""), - ("idd_driver_tip", ""), - ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), - ("empty_recent_tip", ""), - ("empty_favorite_tip", ""), - ("empty_lan_tip", ""), - ("empty_address_book_tip", ""), - ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), - ("Me", ""), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("Voice call", "Appel voix"), + ("Text chat", "Conversation textuelfle"), + ("Stop voice call", "Stopper l'appel voix"), + ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\ » à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\ » dans la fiche pair."), + ("Reconnect", "Se reconnecter"), + ("Codec", "Codec"), + ("Resolution", "Résolution"), + ("No transfers in progress", "Pas de transfert en cours"), + ("Set one-time password length", "Définir la longueur du mot de passe à usage unique"), + ("idd_driver_tip", "Installez le pilote d'affichage virtuel pour être utilisé lorsque vous n'avez pas d'affichage physique."), + ("confirm_idd_driver_tip", "L'option d'installation du pilote d'affichage virtuel est cochée. Notez qu'un certificat de test sera installé pour faire confiance au pilote d'affichage virtuel. Ce certificat de test ne sera utilisé que pour faire confiance aux pilotes Rustdesk."), + ("RDP Settings", "Configuration RDP"), + ("Sort by", "Trier par"), + ("New Connection", "Nouvelle connexion"), + ("Restore", "Restaurer"), + ("Minimize", "Minimiser"), + ("Maximize", "Maximiser"), + ("Your Device", "Votre appareil"), + ("empty_recent_tip", "Oups, pas de sessions récentes!\nIl est temps d'en prévoir une nouvelle."), + ("empty_favorite_tip", "Vous n'avez pas encore de pairs favoris?\nTrouvons quelqu'un avec qui vous connecter et ajoutez-le à vos favoris!"), + ("empty_lan_tip", "Oh non, il semble que nous n'ayons pas encore de pairs découverts."), + ("empty_address_book_tip", "Ouh là là! il semble qu'il n'y ait actuellement aucun pair répertorié dans votre carnet d'adresses."), + ("eg: admin", "ex: admin"), + ("Empty Username", "Nom d'utilisation non spécifié"), + ("Empty Password", "Mot de passe non spécifié"), + ("Me", "Moi"), + ("identical_file_tip", "Ce fichier est identique à celui du pair."), + ("show_monitors_tip", "Afficher les moniteurs dans la barre d'outils."), + ("View Mode", "Mode vue"), + ("enter_rustdesk_passwd_tip", "Saisissez le mot de passe RustDesk."), + ("remember_rustdesk_passwd_tip", "Se rappeler du mot de passe RustDesk."), + ("login_linux_tip", "Se connecter au compte Linux distant"), + ("login_linux_tooltip_tip", "Vous devez vous connecter à un compte Linux distant pour activer une session de bureau X."), ].iter().cloned().collect(); } From e4fc46a8f3a2256575de7b6e37baea275849243f Mon Sep 17 00:00:00 2001 From: jimmyGALLAND <64364019+jimmyGALLAND@users.noreply.github.com> Date: Fri, 31 Mar 2023 22:32:39 +0200 Subject: [PATCH 010/112] Update fr.rs --- src/lang/fr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 3ec3f08d2..3279b9175 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -454,7 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Voice call", "Appel voix"), ("Text chat", "Conversation textuelfle"), ("Stop voice call", "Stopper l'appel voix"), - ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\ » à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\ » dans la fiche pair."), + ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\" à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\" dans la fiche pair."), ("Reconnect", "Se reconnecter"), ("Codec", "Codec"), ("Resolution", "Résolution"), From b542acff2353e7c6e4a0a312d2322f32633d80b4 Mon Sep 17 00:00:00 2001 From: Tomasz Boguszewski Date: Sat, 1 Apr 2023 03:39:34 +0200 Subject: [PATCH 011/112] Use match statement in msgbox function Signed-off-by: Tomasz Boguszewski --- src/cli.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 40ab21188..454eec1ee 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -48,18 +48,24 @@ impl Interface for Session { } fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str) { - if msgtype == "input-password" { - self.sender - .send(Data::Login((self.password.clone(), true))) - .ok(); - } else if msgtype == "re-input-password" { - log::error!("{}: {}", title, text); - let pass = rpassword::prompt_password("Enter password: ").unwrap(); - self.sender.send(Data::Login((pass, true))).ok(); - } else if msgtype.contains("error") { - log::error!("{}: {}: {}", msgtype, title, text); - } else { - log::info!("{}: {}: {}", msgtype, title, text); + match msgtype { + "input-password" => { + self.sender + .send(Data::Login((self.password.clone(), true))) + .ok(); + } + "re-input-password" => { + log::error!("{}: {}", title, text); + let password = rpassword::prompt_password("Enter password: ").unwrap(); + let login_data = Data::Login((password, true)); + self.sender.send(login_data).ok(); + } + msg if msg.contains("error") => { + log::error!("{}: {}: {}", msgtype, title, text); + } + _ => { + log::info!("{}: {}: {}", msgtype, title, text); + } } } From 6372c96722b5415e379515302040a624af6ae553 Mon Sep 17 00:00:00 2001 From: Tomasz Boguszewski Date: Sat, 1 Apr 2023 04:09:35 +0200 Subject: [PATCH 012/112] Simplify gen_name function Signed-off-by: Tomasz Boguszewski --- src/naming.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/naming.rs b/src/naming.rs index 53e675d92..d805ee462 100644 --- a/src/naming.rs +++ b/src/naming.rs @@ -4,10 +4,8 @@ use hbb_common::ResultType; use license::*; fn gen_name(lic: &License) -> ResultType { - let tmp = serde_json::to_vec::(lic)?; - let tmp = URL_SAFE_NO_PAD.encode(&tmp); - let tmp: String = tmp.chars().rev().collect(); - Ok(tmp) + let tmp = URL_SAFE_NO_PAD.encode(&serde_json::to_vec(lic)?); + Ok(tmp.chars().rev().collect()) } fn main() { From 83b7518897e6979ce25dd3f84ac48090a76b6036 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 1 Apr 2023 10:13:39 +0800 Subject: [PATCH 013/112] Revert "vp8" --- .../desktop/pages/desktop_setting_page.dart | 42 +-- .../lib/desktop/widgets/remote_toolbar.dart | 26 +- flutter/lib/mobile/pages/remote_page.dart | 5 +- libs/hbb_common/protos/message.proto | 16 +- libs/hbb_common/src/config.rs | 5 +- libs/scrap/examples/benchmark.rs | 46 +-- libs/scrap/src/common/android.rs | 1 - libs/scrap/src/common/codec.rs | 319 +++++++++--------- libs/scrap/src/common/hwcodec.rs | 17 +- libs/scrap/src/common/mod.rs | 53 --- libs/scrap/src/common/record.rs | 56 ++- libs/scrap/src/common/vpxcodec.rs | 46 +-- src/client.rs | 37 +- src/client/helper.rs | 33 +- src/client/io_loop.rs | 9 +- src/flutter_ffi.rs | 13 +- src/server/connection.rs | 34 +- src/server/video_service.rs | 85 ++--- src/ui/header.tis | 9 +- src/ui/remote.rs | 22 +- src/ui_interface.rs | 7 - src/ui_session_interface.rs | 28 +- 22 files changed, 440 insertions(+), 469 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 74d51407c..66ef83d31 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1258,6 +1258,9 @@ class _DisplayState extends State<_Display> { } Widget codec(BuildContext context) { + if (!bind.mainHasHwcodec()) { + return Offstage(); + } final key = 'codec-preference'; onChanged(String value) async { await bind.mainSetUserDefaultOption(key: key, value: value); @@ -1265,45 +1268,28 @@ class _DisplayState extends State<_Display> { } final groupValue = bind.mainGetUserDefaultOption(key: key); - var hwRadios = []; - try { - final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings()); - final h264 = codecsJson['h264'] ?? false; - final h265 = codecsJson['h265'] ?? false; - if (h264) { - hwRadios.add(_Radio(context, - value: 'h264', - groupValue: groupValue, - label: 'H264', - onChanged: onChanged)); - } - if (h265) { - hwRadios.add(_Radio(context, - value: 'h265', - groupValue: groupValue, - label: 'H265', - onChanged: onChanged)); - } - } catch (e) { - debugPrint("failed to parse supported hwdecodings, err=$e"); - } + return _Card(title: 'Default Codec', children: [ _Radio(context, value: 'auto', groupValue: groupValue, label: 'Auto', onChanged: onChanged), - _Radio(context, - value: 'vp8', - groupValue: groupValue, - label: 'VP8', - onChanged: onChanged), _Radio(context, value: 'vp9', groupValue: groupValue, label: 'VP9', onChanged: onChanged), - ...hwRadios, + _Radio(context, + value: 'h264', + groupValue: groupValue, + label: 'H264', + onChanged: onChanged), + _Radio(context, + value: 'h265', + groupValue: groupValue, + label: 'H265', + onChanged: onChanged), ]); } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index cb4b6d644..0ef8674ef 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1349,30 +1349,29 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec() { return futureBuilder(future: () async { - final alternativeCodecs = - await bind.sessionAlternativeCodecs(id: widget.id); + final supportedHwcodec = + await bind.sessionSupportedHwcodec(id: widget.id); final codecPreference = await bind.sessionGetOption(id: widget.id, arg: 'codec-preference') ?? ''; return { - 'alternativeCodecs': alternativeCodecs, + 'supportedHwcodec': supportedHwcodec, 'codecPreference': codecPreference }; }(), hasData: (data) { final List codecs = []; try { - final Map codecsJson = jsonDecode(data['alternativeCodecs']); - final vp8 = codecsJson['vp8'] ?? false; + final Map codecsJson = jsonDecode(data['supportedHwcodec']); final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; - codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { debugPrint("Show Codec Preference err=$e"); } - final visible = - codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); + final visible = bind.mainHasHwcodec() && + codecs.length == 2 && + (codecs[0] || codecs[1]); if (!visible) return Offstage(); final groupValue = data['codecPreference'] as String; onChanged(String? value) async { @@ -1393,13 +1392,6 @@ class _DisplayMenuState extends State<_DisplayMenu> { onChanged: onChanged, ffi: widget.ffi, ), - _RadioMenuButton( - child: Text(translate('VP8')), - value: 'vp8', - groupValue: groupValue, - onChanged: codecs[0] ? onChanged : null, - ffi: widget.ffi, - ), _RadioMenuButton( child: Text(translate('VP9')), value: 'vp9', @@ -1411,14 +1403,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate('H264')), value: 'h264', groupValue: groupValue, - onChanged: codecs[1] ? onChanged : null, + onChanged: codecs[0] ? onChanged : null, ffi: widget.ffi, ), _RadioMenuButton( child: Text(translate('H265')), value: 'h265', groupValue: groupValue, - onChanged: codecs[2] ? onChanged : null, + onChanged: codecs[1] ? onChanged : null, ffi: widget.ffi, ), ]); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 74a327c27..083cdcd1c 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -973,11 +973,9 @@ void showOptions( if (hasHwcodec) { try { final Map codecsJson = - jsonDecode(await bind.sessionAlternativeCodecs(id: id)); - final vp8 = codecsJson['vp8'] ?? false; + jsonDecode(await bind.sessionSupportedHwcodec(id: id)); final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; - codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { @@ -1046,7 +1044,6 @@ void showOptions( if (hasHwcodec && codecs.length == 2 && (codecs[0] || codecs[1])) { radios.addAll([ getRadio(translate('Auto'), 'auto', codec, setCodec), - getRadio('VP8', 'vp8', codec, setCodec), getRadio('VP9', 'vp9', codec, setCodec), ]); if (codecs[0]) { diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 321747e3a..6295c160b 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -24,7 +24,6 @@ message VideoFrame { YUV yuv = 8; EncodedVideoFrames h264s = 10; EncodedVideoFrames h265s = 11; - EncodedVideoFrames vp8s = 12; } } @@ -77,7 +76,6 @@ message Features { message SupportedEncoding { bool h264 = 1; bool h265 = 2; - bool vp8 = 3; } message PeerInfo { @@ -459,20 +457,18 @@ enum ImageQuality { Best = 4; } -message SupportedDecoding { +message VideoCodecState { enum PreferCodec { Auto = 0; - VP9 = 1; + VPX = 1; H264 = 2; H265 = 3; - VP8 = 4; } - int32 ability_vp9 = 1; - int32 ability_h264 = 2; - int32 ability_h265 = 3; + int32 score_vpx = 1; + int32 score_h264 = 2; + int32 score_h265 = 3; PreferCodec prefer = 4; - int32 ability_vp8 = 5; } message OptionMessage { @@ -490,7 +486,7 @@ message OptionMessage { BoolOption disable_audio = 7; BoolOption disable_clipboard = 8; BoolOption enable_file_transfer = 9; - SupportedDecoding supported_decoding = 10; + VideoCodecState video_codec_state = 10; int32 custom_fps = 11; BoolOption disable_keyboard = 12; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 960074a8f..6a823c7b7 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -917,8 +917,7 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = - decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } @@ -1357,7 +1356,7 @@ impl UserDefaultConfig { "view_style" => self.get_string(key, "original", vec!["adaptive"]), "scroll_style" => self.get_string(key, "scrollauto", vec!["scrollbar"]), "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), - "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), + "codec-preference" => self.get_string(key, "auto", vec!["vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), _ => self diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index ba8dec9f2..003830f95 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -3,8 +3,7 @@ use hbb_common::env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use scrap::{ codec::{EncoderApi, EncoderCfg}, Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, - VpxVideoCodecId::{self, *}, - STRIDE_ALIGN, + VpxVideoCodecId, STRIDE_ALIGN, }; use std::{io::Write, time::Instant}; @@ -50,7 +49,7 @@ fn main() { "benchmark {}x{} bitrate:{}k hw_pixfmt:{:?}", width, height, bitrate_k, args.flag_hw_pixfmt ); - [VP8, VP9].map(|c| test_vpx(c, &yuvs, width, height, bitrate_k, yuv_count)); + test_vp9(&yuvs, width, height, bitrate_k, yuv_count); #[cfg(feature = "hwcodec")] { use hwcodec::AVPixelFormat; @@ -58,7 +57,7 @@ fn main() { Pixfmt::I420 => AVPixelFormat::AV_PIX_FMT_YUV420P, Pixfmt::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, }; - let yuvs = hw::vpx_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); + let yuvs = hw::vp9_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); hw::test(&yuvs, width, height, bitrate_k, yuv_count, hw_pixfmt); } } @@ -88,20 +87,13 @@ fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { } } -fn test_vpx( - codec_id: VpxVideoCodecId, - yuvs: &Vec>, - width: usize, - height: usize, - bitrate_k: usize, - yuv_count: usize, -) { +fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, yuv_count: usize) { let config = EncoderCfg::VPX(VpxEncoderConfig { width: width as _, height: height as _, timebase: [1, 1000], bitrate: bitrate_k as _, - codec: codec_id, + codec: VpxVideoCodecId::VP9, num_threads: (num_cpus::get() / 2) as _, }); let mut encoder = VpxEncoder::new(config).unwrap(); @@ -112,43 +104,35 @@ fn test_vpx( .unwrap(); let _ = encoder.flush().unwrap(); } - println!( - "{:?} encode: {:?}", - codec_id, - start.elapsed() / yuv_count as _ - ); + println!("vp9 encode: {:?}", start.elapsed() / yuv_count as _); // prepare data separately - let mut vpxs = vec![]; + let mut vp9s = vec![]; let start = Instant::now(); for yuv in yuvs { for ref frame in encoder .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) .unwrap() { - vpxs.push(frame.data.to_vec()); + vp9s.push(frame.data.to_vec()); } for ref frame in encoder.flush().unwrap() { - vpxs.push(frame.data.to_vec()); + vp9s.push(frame.data.to_vec()); } } - assert_eq!(vpxs.len(), yuv_count); + assert_eq!(vp9s.len(), yuv_count); let mut decoder = VpxDecoder::new(VpxDecoderConfig { - codec: codec_id, + codec: VpxVideoCodecId::VP9, num_threads: (num_cpus::get() / 2) as _, }) .unwrap(); let start = Instant::now(); - for vpx in vpxs { - let _ = decoder.decode(&vpx); + for vp9 in vp9s { + let _ = decoder.decode(&vp9); let _ = decoder.flush(); } - println!( - "{:?} decode: {:?}", - codec_id, - start.elapsed() / yuv_count as _ - ); + println!("vp9 decode: {:?}", start.elapsed() / yuv_count as _); } #[cfg(feature = "hwcodec")] @@ -283,7 +267,7 @@ mod hw { Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265 } - pub fn vpx_yuv_to_hw_yuv( + pub fn vp9_yuv_to_hw_yuv( yuvs: Vec>, width: usize, height: usize, diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 36d6a8a9b..8daf8e4bb 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -50,7 +50,6 @@ impl crate::TraitCapturer for Capturer { pub enum Frame<'a> { RAW(&'a [u8]), - VP8(&'a [u8]), VP9(&'a [u8]), Empty, } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 3209933b4..9e4b6fce4 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -1,6 +1,7 @@ +use std::ops::{Deref, DerefMut}; +#[cfg(feature = "hwcodec")] use std::{ collections::HashMap, - ops::{Deref, DerefMut}, sync::{Arc, Mutex}, }; @@ -10,31 +11,30 @@ use crate::hwcodec::*; use crate::mediacodec::{ MediaCodecDecoder, MediaCodecDecoders, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT, }; -use crate::{vpxcodec::*, CodecName, ImageFormat}; +use crate::{vpxcodec::*, ImageFormat}; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use hbb_common::sysinfo::{System, SystemExt}; use hbb_common::{ anyhow::anyhow, - config::PeerConfig, log, - message_proto::{ - supported_decoding::PreferCodec, video_frame, EncodedVideoFrames, Message, - SupportedDecoding, SupportedEncoding, - }, + message_proto::{video_frame, EncodedVideoFrames, Message, VideoCodecState}, ResultType, }; #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] -use hbb_common::{config::Config2, lazy_static}; +use hbb_common::{ + config::{Config2, PeerConfig}, + lazy_static, + message_proto::video_codec_state::PreferCodec, +}; +#[cfg(feature = "hwcodec")] lazy_static::lazy_static! { - static ref PEER_DECODINGS: Arc>> = Default::default(); - static ref CODEC_NAME: Arc> = Arc::new(Mutex::new(CodecName::VP9)); + static ref PEER_DECODER_STATES: Arc>> = Default::default(); } +const SCORE_VPX: i32 = 90; #[derive(Debug, Clone)] pub struct HwEncoderConfig { - pub name: String, + pub codec_name: String, pub width: usize, pub height: usize, pub bitrate: i32, @@ -58,6 +58,10 @@ pub trait EncoderApi { fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()>; } +pub struct DecoderCfg { + pub vpx: VpxDecoderConfig, +} + pub struct Encoder { pub codec: Box, } @@ -77,8 +81,7 @@ impl DerefMut for Encoder { } pub struct Decoder { - vp8: VpxDecoder, - vp9: VpxDecoder, + vpx: VpxDecoder, #[cfg(feature = "hwcodec")] hw: HwDecoders, #[cfg(feature = "hwcodec")] @@ -88,10 +91,10 @@ pub struct Decoder { } #[derive(Debug, Clone)] -pub enum EncodingUpdate { - New(SupportedDecoding), +pub enum EncoderUpdate { + State(VideoCodecState), Remove, - NewOnlyVP9, + DisableHwIfNotExist, } impl Encoder { @@ -117,156 +120,172 @@ impl Encoder { } } - pub fn update(id: i32, update: EncodingUpdate) { - let mut decodings = PEER_DECODINGS.lock().unwrap(); - match update { - EncodingUpdate::New(decoding) => { - decodings.insert(id, decoding); - } - EncodingUpdate::Remove => { - decodings.remove(&id); - } - EncodingUpdate::NewOnlyVP9 => { - decodings.insert( - id, - SupportedDecoding { - ability_vp9: 1, - ..Default::default() - }, - ); - } - } - - let vp8_useable = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_vp8 > 0); - #[allow(unused_mut)] - let mut h264_name = None; - #[allow(unused_mut)] - let mut h265_name = None; + // TODO + pub fn update_video_encoder(id: i32, update: EncoderUpdate) { #[cfg(feature = "hwcodec")] { - let best = HwEncoder::best(); - let h264_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); - let h265_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); - if h264_useable { - h264_name = best.h264.map_or(None, |c| Some(c.name)); + let mut states = PEER_DECODER_STATES.lock().unwrap(); + match update { + EncoderUpdate::State(state) => { + states.insert(id, state); + } + EncoderUpdate::Remove => { + states.remove(&id); + } + EncoderUpdate::DisableHwIfNotExist => { + if !states.contains_key(&id) { + states.insert(id, VideoCodecState::default()); + } + } } - if h265_useable { - h265_name = best.h265.map_or(None, |c| Some(c.name)); + let name = HwEncoder::current_name(); + if states.len() > 0 { + let best = HwEncoder::best(); + let enabled_h264 = best.h264.is_some() + && states.len() > 0 + && states.iter().all(|(_, s)| s.score_h264 > 0); + let enabled_h265 = best.h265.is_some() + && states.len() > 0 + && states.iter().all(|(_, s)| s.score_h265 > 0); + + // Preference first + let mut preference = PreferCodec::Auto; + let preferences: Vec<_> = states + .iter() + .filter(|(_, s)| { + s.prefer == PreferCodec::VPX.into() + || s.prefer == PreferCodec::H264.into() && enabled_h264 + || s.prefer == PreferCodec::H265.into() && enabled_h265 + }) + .map(|(_, s)| s.prefer) + .collect(); + if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { + preference = preferences[0].enum_value_or(PreferCodec::Auto); + } + + match preference { + PreferCodec::VPX => *name.lock().unwrap() = None, + PreferCodec::H264 => { + *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)) + } + PreferCodec::H265 => { + *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)) + } + PreferCodec::Auto => { + // score encoder + let mut score_vpx = SCORE_VPX; + let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); + let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); + + // score decoder + score_vpx += states.iter().map(|s| s.1.score_vpx).sum::(); + if enabled_h264 { + score_h264 += states.iter().map(|s| s.1.score_h264).sum::(); + } + if enabled_h265 { + score_h265 += states.iter().map(|s| s.1.score_h265).sum::(); + } + + if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { + *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)); + } else if enabled_h264 + && score_h264 >= score_vpx + && score_h264 >= score_h265 + { + *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)); + } else { + *name.lock().unwrap() = None; + } + } + } + + log::info!( + "connection count:{}, used preference:{:?}, encoder:{:?}", + states.len(), + preference, + name.lock().unwrap() + ) + } else { + *name.lock().unwrap() = None; } } - - let mut name = CODEC_NAME.lock().unwrap(); - let mut preference = PreferCodec::Auto; - let preferences: Vec<_> = decodings - .iter() - .filter(|(_, s)| { - s.prefer == PreferCodec::VP9.into() - || s.prefer == PreferCodec::VP8.into() && vp8_useable - || s.prefer == PreferCodec::H264.into() && h264_name.is_some() - || s.prefer == PreferCodec::H265.into() && h265_name.is_some() - }) - .map(|(_, s)| s.prefer) - .collect(); - if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { - preference = preferences[0].enum_value_or(PreferCodec::Auto); + #[cfg(not(feature = "hwcodec"))] + { + let _ = id; + let _ = update; } - - #[allow(unused_mut)] - let mut auto_codec = CodecName::VP9; - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if vp8_useable && System::new_all().total_memory() <= 4 * 1024 * 1024 * 1024 { - // 4 Gb - auto_codec = CodecName::VP8 - } - - match preference { - PreferCodec::VP8 => *name = CodecName::VP8, - PreferCodec::VP9 => *name = CodecName::VP9, - PreferCodec::H264 => *name = h264_name.map_or(auto_codec, |c| CodecName::H264(c)), - PreferCodec::H265 => *name = h265_name.map_or(auto_codec, |c| CodecName::H265(c)), - PreferCodec::Auto => *name = auto_codec, - } - - log::info!( - "connection count:{}, used preference:{:?}, encoder:{:?}", - decodings.len(), - preference, - *name - ) } - #[inline] - pub fn negotiated_codec() -> CodecName { - CODEC_NAME.lock().unwrap().clone() + pub fn current_hw_encoder_name() -> Option { + #[cfg(feature = "hwcodec")] + if enable_hwcodec_option() { + return HwEncoder::current_name().lock().unwrap().clone(); + } else { + return None; + } + #[cfg(not(feature = "hwcodec"))] + return None; } - pub fn supported_encoding() -> SupportedEncoding { - #[allow(unused_mut)] - let mut encoding = SupportedEncoding { - vp8: true, - ..Default::default() - }; + pub fn supported_encoding() -> (bool, bool) { #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwEncoder::best(); - encoding.h264 = best.h264.is_some(); - encoding.h265 = best.h265.is_some(); + ( + best.h264.as_ref().map_or(false, |c| c.score > 0), + best.h265.as_ref().map_or(false, |c| c.score > 0), + ) + } else { + (false, false) } - encoding + #[cfg(not(feature = "hwcodec"))] + (false, false) } } impl Decoder { - pub fn supported_decodings(id_for_perfer: Option<&str>) -> SupportedDecoding { - #[allow(unused_mut)] - let mut decoding = SupportedDecoding { - ability_vp8: 1, - ability_vp9: 1, - prefer: id_for_perfer - .map_or(PreferCodec::Auto, |id| Self::codec_preference(id)) - .into(), - ..Default::default() - }; + pub fn video_codec_state(_id: &str) -> VideoCodecState { #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwDecoder::best(); - decoding.ability_h264 = if best.h264.is_some() { 1 } else { 0 }; - decoding.ability_h265 = if best.h265.is_some() { 1 } else { 0 }; + return VideoCodecState { + score_vpx: SCORE_VPX, + score_h264: best.h264.map_or(0, |c| c.score), + score_h265: best.h265.map_or(0, |c| c.score), + prefer: Self::codec_preference(_id).into(), + ..Default::default() + }; } #[cfg(feature = "mediacodec")] if enable_hwcodec_option() { - decoding.ability_h264 = - if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 1 - } else { - 0 - }; - decoding.ability_h265 = - if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 1 - } else { - 0 - }; + let score_h264 = if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 92 + } else { + 0 + }; + let score_h265 = if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 94 + } else { + 0 + }; + return VideoCodecState { + score_vpx: SCORE_VPX, + score_h264, + score_h265, + prefer: Self::codec_preference(_id).into(), + ..Default::default() + }; + } + VideoCodecState { + score_vpx: SCORE_VPX, + ..Default::default() } - decoding } - pub fn new() -> Decoder { - let vp8 = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP8, - num_threads: (num_cpus::get() / 2) as _, - }) - .unwrap(); - let vp9 = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }) - .unwrap(); + pub fn new(config: DecoderCfg) -> Decoder { + let vpx = VpxDecoder::new(config.vpx).unwrap(); Decoder { - vp8, - vp9, + vpx, #[cfg(feature = "hwcodec")] hw: if enable_hwcodec_option() { HwDecoder::new_decoders() @@ -291,11 +310,8 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { - video_frame::Union::Vp8s(vp8s) => { - Decoder::handle_vpxs_video_frame(&mut self.vp8, vp8s, fmt, rgb) - } video_frame::Union::Vp9s(vp9s) => { - Decoder::handle_vpxs_video_frame(&mut self.vp9, vp9s, fmt, rgb) + Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, fmt, rgb) } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { @@ -333,15 +349,15 @@ impl Decoder { } } - fn handle_vpxs_video_frame( + fn handle_vp9s_video_frame( decoder: &mut VpxDecoder, - vpxs: &EncodedVideoFrames, + vp9s: &EncodedVideoFrames, fmt: (ImageFormat, usize), rgb: &mut Vec, ) -> ResultType { let mut last_frame = Image::new(); - for vpx in vpxs.frames.iter() { - for frame in decoder.decode(&vpx.data)? { + for vp9 in vp9s.frames.iter() { + for frame in decoder.decode(&vp9.data)? { drop(last_frame); last_frame = frame; } @@ -392,15 +408,14 @@ impl Decoder { return Ok(false); } + #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] fn codec_preference(id: &str) -> PreferCodec { let codec = PeerConfig::load(id) .options .get("codec-preference") .map_or("".to_owned(), |c| c.to_owned()); - if codec == "vp8" { - PreferCodec::VP8 - } else if codec == "vp9" { - PreferCodec::VP9 + if codec == "vp9" { + PreferCodec::VPX } else if codec == "h264" { PreferCodec::H264 } else if codec == "h265" { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index f4de4bf84..2c69774fb 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -7,7 +7,7 @@ use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, - log, + lazy_static, log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, ResultType, }; @@ -19,6 +19,11 @@ use hwcodec::{ Quality::{self, *}, RateControl::{self, *}, }; +use std::sync::{Arc, Mutex}; + +lazy_static::lazy_static! { + static ref HW_ENCODER_NAME: Arc>> = Default::default(); +} const CFG_KEY_ENCODER: &str = "bestHwEncoders"; const CFG_KEY_DECODER: &str = "bestHwDecoders"; @@ -44,7 +49,7 @@ impl EncoderApi for HwEncoder { match cfg { EncoderCfg::HW(config) => { let ctx = EncodeContext { - name: config.name.clone(), + name: config.codec_name.clone(), width: config.width as _, height: config.height as _, pixfmt: DEFAULT_PIXFMT, @@ -55,12 +60,12 @@ impl EncoderApi for HwEncoder { quality: DEFAULT_HW_QUALITY, rc: DEFAULT_RC, }; - let format = match Encoder::format_from_name(config.name.clone()) { + let format = match Encoder::format_from_name(config.codec_name.clone()) { Ok(format) => format, Err(_) => { return Err(anyhow!(format!( "failed to get format from name:{}", - config.name + config.codec_name ))) } }; @@ -128,6 +133,10 @@ impl HwEncoder { }) } + pub fn current_name() -> Arc>> { + HW_ENCODER_NAME.clone() + } + pub fn encode(&mut self, bgra: &[u8]) -> ResultType> { match self.pixfmt { AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420( diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 26b946401..0ad158cca 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -1,5 +1,4 @@ pub use self::vpxcodec::*; -use hbb_common::message_proto::{video_frame, VideoFrame}; cfg_if! { if #[cfg(quartz)] { @@ -93,55 +92,3 @@ pub fn is_cursor_embedded() -> bool { pub fn is_cursor_embedded() -> bool { false } - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CodecName { - VP8, - VP9, - H264(String), - H265(String), -} - -#[derive(PartialEq, Debug, Clone)] -pub enum CodecFormat { - VP8, - VP9, - H264, - H265, - Unknown, -} - -impl From<&VideoFrame> for CodecFormat { - fn from(it: &VideoFrame) -> Self { - match it.union { - Some(video_frame::Union::Vp8s(_)) => CodecFormat::VP8, - Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::H264s(_)) => CodecFormat::H264, - Some(video_frame::Union::H265s(_)) => CodecFormat::H265, - _ => CodecFormat::Unknown, - } - } -} - -impl From<&CodecName> for CodecFormat { - fn from(value: &CodecName) -> Self { - match value { - CodecName::VP8 => Self::VP8, - CodecName::VP9 => Self::VP9, - CodecName::H264(_) => Self::H264, - CodecName::H265(_) => Self::H265, - } - } -} - -impl ToString for CodecFormat { - fn to_string(&self) -> String { - match self { - CodecFormat::VP8 => "VP8".into(), - CodecFormat::VP9 => "VP9".into(), - CodecFormat::H264 => "H264".into(), - CodecFormat::H265 => "H265".into(), - CodecFormat::Unknown => "Unknow".into(), - } - } -} diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 9de70ae14..9f38f2d6a 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -1,4 +1,3 @@ -use crate::CodecFormat; #[cfg(feature = "hwcodec")] use hbb_common::anyhow::anyhow; use hbb_common::{ @@ -22,6 +21,13 @@ use webm::mux::{self, Segment, Track, VideoTrack, Writer}; const MIN_SECS: u64 = 1; +#[derive(Debug, Clone, PartialEq)] +pub enum RecordCodecID { + VP9, + H264, + H265, +} + #[derive(Debug, Clone)] pub struct RecorderContext { pub server: bool, @@ -30,7 +36,7 @@ pub struct RecorderContext { pub filename: String, pub width: usize, pub height: usize, - pub format: CodecFormat, + pub codec_id: RecordCodecID, pub tx: Option>, } @@ -49,9 +55,8 @@ impl RecorderContext { } let file = if self.server { "s" } else { "c" }.to_string() + &self.id.clone() - + &chrono::Local::now().format("_%Y%m%d%H%M%S_").to_string() - + &self.format.to_string() - + if self.format == CodecFormat::VP9 || self.format == CodecFormat::VP8 { + + &chrono::Local::now().format("_%Y%m%d%H%M%S").to_string() + + if self.codec_id == RecordCodecID::VP9 { ".webm" } else { ".mp4" @@ -102,8 +107,8 @@ impl DerefMut for Recorder { impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { ctx.set_filename()?; - let recorder = match ctx.format { - CodecFormat::VP8 | CodecFormat::VP9 => Recorder { + let recorder = match ctx.codec_id { + RecordCodecID::VP9 => Recorder { inner: Box::new(WebmRecorder::new(ctx.clone())?), ctx, }, @@ -121,8 +126,8 @@ impl Recorder { fn change(&mut self, mut ctx: RecorderContext) -> ResultType<()> { ctx.set_filename()?; - self.inner = match ctx.format { - CodecFormat::VP8 | CodecFormat::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), + self.inner = match ctx.codec_id { + RecordCodecID::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), #[cfg(feature = "hwcodec")] _ => Box::new(HwRecorder::new(ctx.clone())?), #[cfg(not(feature = "hwcodec"))] @@ -143,19 +148,10 @@ impl Recorder { pub fn write_frame(&mut self, frame: &video_frame::Union) -> ResultType<()> { match frame { - video_frame::Union::Vp8s(vp8s) => { - if self.ctx.format != CodecFormat::VP8 { - self.change(RecorderContext { - format: CodecFormat::VP8, - ..self.ctx.clone() - })?; - } - vp8s.frames.iter().map(|f| self.write_video(f)).count(); - } video_frame::Union::Vp9s(vp9s) => { - if self.ctx.format != CodecFormat::VP9 { + if self.ctx.codec_id != RecordCodecID::VP9 { self.change(RecorderContext { - format: CodecFormat::VP9, + codec_id: RecordCodecID::VP9, ..self.ctx.clone() })?; } @@ -163,25 +159,25 @@ impl Recorder { } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { - if self.ctx.format != CodecFormat::H264 { + if self.ctx.codec_id != RecordCodecID::H264 { self.change(RecorderContext { - format: CodecFormat::H264, + codec_id: RecordCodecID::H264, ..self.ctx.clone() })?; } - if self.ctx.format == CodecFormat::H264 { + if self.ctx.codec_id == RecordCodecID::H264 { h264s.frames.iter().map(|f| self.write_video(f)).count(); } } #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { - if self.ctx.format != CodecFormat::H265 { + if self.ctx.codec_id != RecordCodecID::H265 { self.change(RecorderContext { - format: CodecFormat::H265, + codec_id: RecordCodecID::H265, ..self.ctx.clone() })?; } - if self.ctx.format == CodecFormat::H265 { + if self.ctx.codec_id == RecordCodecID::H265 { h265s.frames.iter().map(|f| self.write_video(f)).count(); } } @@ -225,11 +221,7 @@ impl RecorderApi for WebmRecorder { ctx.width as _, ctx.height as _, None, - if ctx.format == CodecFormat::VP9 { - mux::VideoCodecId::VP9 - } else { - mux::VideoCodecId::VP8 - }, + mux::VideoCodecId::VP9, ); Ok(WebmRecorder { vt, @@ -287,7 +279,7 @@ impl RecorderApi for HwRecorder { filename: ctx.filename.clone(), width: ctx.width, height: ctx.height, - is265: ctx.format == CodecFormat::H265, + is265: ctx.codec_id == RecordCodecID::H265, framerate: crate::hwcodec::DEFAULT_TIME_BASE[1] as _, }) .map_err(|_| anyhow!("Failed to create hardware muxer"))?; diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 4820ea171..3df9c0461 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -30,7 +30,6 @@ pub struct VpxEncoder { ctx: vpx_codec_ctx_t, width: usize, height: usize, - id: VpxVideoCodecId, } pub struct VpxDecoder { @@ -98,10 +97,15 @@ impl EncoderApi for VpxEncoder { { match cfg { crate::codec::EncoderCfg::VPX(config) => { - let i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), - }; + let i; + if cfg!(feature = "VP8") { + i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), + }; + } else { + i = call_vpx_ptr!(vpx_codec_vp9_cx()); + } let mut c = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; call_vpx!(vpx_codec_enc_config_default(i, &mut c, 0)); @@ -183,17 +187,12 @@ impl EncoderApi for VpxEncoder { VP9E_SET_TILE_COLUMNS as _, 4 as c_int )); - } else if config.codec == VpxVideoCodecId::VP8 { - // https://github.com/webmproject/libvpx/blob/972149cafeb71d6f08df89e91a0130d6a38c4b15/vpx/vp8cx.h#L172 - // https://groups.google.com/a/webmproject.org/g/webm-discuss/c/DJhSrmfQ61M - call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, 12,)); } Ok(Self { ctx, width: config.width as _, height: config.height as _, - id: config.codec, }) } _ => Err(anyhow!("encoder type mismatch")), @@ -214,7 +213,7 @@ impl EncoderApi for VpxEncoder { // to-do: flush periodically, e.g. 1 second if frames.len() > 0 { - Ok(VpxEncoder::create_msg(self.id, frames)) + Ok(VpxEncoder::create_msg(frames)) } else { Err(anyhow!("no valid frame")) } @@ -281,17 +280,13 @@ impl VpxEncoder { } #[inline] - pub fn create_msg(codec_id: VpxVideoCodecId, frames: Vec) -> Message { + fn create_msg(vp9s: Vec) -> Message { let mut msg_out = Message::new(); let mut vf = VideoFrame::new(); - let vpxs = EncodedVideoFrames { - frames: frames.into(), + vf.set_vp9s(EncodedVideoFrames { + frames: vp9s.into(), ..Default::default() - }; - match codec_id { - VpxVideoCodecId::VP8 => vf.set_vp8s(vpxs), - VpxVideoCodecId::VP9 => vf.set_vp9s(vpxs), - } + }); msg_out.set_video_frame(vf); msg_out } @@ -387,10 +382,15 @@ impl VpxDecoder { pub fn new(config: VpxDecoderConfig) -> Result { // This is sound because `vpx_codec_ctx` is a repr(C) struct without any field that can // cause UB if uninitialized. - let i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), - }; + let i; + if cfg!(feature = "VP8") { + i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), + }; + } else { + i = call_vpx_ptr!(vpx_codec_vp9_dx()); + } let mut ctx = Default::default(); let cfg = vpx_codec_dec_cfg_t { threads: if config.num_threads == 0 { diff --git a/src/client.rs b/src/client.rs index ae93ccfcf..2f745b70c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -44,9 +44,9 @@ use hbb_common::{ }; pub use helper::*; use scrap::{ - codec::Decoder, + codec::{Decoder, DecoderCfg}, record::{Recorder, RecorderContext}, - ImageFormat, + ImageFormat, VpxDecoderConfig, VpxVideoCodecId, }; use crate::{ @@ -917,7 +917,12 @@ impl VideoHandler { /// Create a new video handler. pub fn new() -> Self { VideoHandler { - decoder: Decoder::new(), + decoder: Decoder::new(DecoderCfg { + vpx: VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }, + }), rgb: Default::default(), recorder: Default::default(), record: false, @@ -949,7 +954,12 @@ impl VideoHandler { /// Reset the decoder. pub fn reset(&mut self) { - self.decoder = Decoder::new(); + self.decoder = Decoder::new(DecoderCfg { + vpx: VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: 1, + }, + }); } /// Start or stop screen record. @@ -963,7 +973,7 @@ impl VideoHandler { filename: "".to_owned(), width: w as _, height: h as _, - format: scrap::CodecFormat::VP9, + codec_id: scrap::record::RecordCodecID::VP9, tx: None, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); @@ -989,7 +999,7 @@ pub struct LoginConfigHandler { pub conn_id: i32, features: Option, session_id: u64, - pub supported_encoding: SupportedEncoding, + pub supported_encoding: Option<(bool, bool)>, pub restarting_remote_device: bool, pub force_relay: bool, pub direct: Option, @@ -1037,7 +1047,7 @@ impl LoginConfigHandler { self.remember = !config.password.is_empty(); self.config = config; self.session_id = rand::random(); - self.supported_encoding = Default::default(); + self.supported_encoding = None; self.restarting_remote_device = false; self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay; self.direct = None; @@ -1321,8 +1331,8 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - msg.supported_decoding = - hbb_common::protobuf::MessageField::some(Decoder::supported_decodings(Some(&self.id))); + let state = Decoder::video_codec_state(&self.id); + msg.video_codec_state = hbb_common::protobuf::MessageField::some(state); n += 1; if n > 0 { @@ -1555,7 +1565,10 @@ impl LoginConfigHandler { self.conn_id = pi.conn_id; // no matter if change, for update file time self.save_config(config); - self.supported_encoding = pi.encoding.clone().unwrap_or_default(); + #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] + { + self.supported_encoding = Some((pi.encoding.h264, pi.encoding.h265)); + } } pub fn get_remote_dir(&self) -> String { @@ -1613,10 +1626,10 @@ impl LoginConfigHandler { } pub fn change_prefer_codec(&self) -> Message { - let decoding = scrap::codec::Decoder::supported_decodings(Some(&self.id)); + let state = scrap::codec::Decoder::video_codec_state(&self.id); let mut misc = Misc::new(); misc.set_option(OptionMessage { - supported_decoding: hbb_common::protobuf::MessageField::some(decoding), + video_codec_state: hbb_common::protobuf::MessageField::some(state), ..Default::default() }); let mut msg_out = Message::new(); diff --git a/src/client/helper.rs b/src/client/helper.rs index 61844d908..a9696a8e8 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,8 +1,37 @@ use hbb_common::{ get_time, - message_proto::{Message, VoiceCallRequest, VoiceCallResponse}, + message_proto::{video_frame, Message, VideoFrame, VoiceCallRequest, VoiceCallResponse}, }; -use scrap::CodecFormat; + +#[derive(PartialEq, Debug, Clone)] +pub enum CodecFormat { + VP9, + H264, + H265, + Unknown, +} + +impl From<&VideoFrame> for CodecFormat { + fn from(it: &VideoFrame) -> Self { + match it.union { + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, + _ => CodecFormat::Unknown, + } + } +} + +impl ToString for CodecFormat { + fn to_string(&self) -> String { + match self { + CodecFormat::VP9 => "VP9".into(), + CodecFormat::H264 => "H264".into(), + CodecFormat::H265 => "H265".into(), + CodecFormat::Unknown => "Unknow".into(), + } + } +} #[derive(Debug, Default)] pub struct QualityStatus { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 0f662e015..b0bddc82e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -29,10 +29,10 @@ use hbb_common::tokio::{ time::{self, Duration, Instant, Interval}, }; use hbb_common::{allow_err, fs, get_time, log, message_proto::*, Stream}; -use scrap::CodecFormat; use crate::client::{ - new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, + new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, + SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{self, update_clipboard}; @@ -817,10 +817,11 @@ impl Remote { } fn contains_key_frame(vf: &VideoFrame) -> bool { - use video_frame::Union::*; match &vf.union { Some(vf) => match vf { - Vp8s(f) | Vp9s(f) | H264s(f) | H265s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key), _ => false, }, None => false, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 1d965ebc5..aee486b94 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -969,13 +969,6 @@ pub fn main_has_hwcodec() -> SyncReturn { SyncReturn(has_hwcodec()) } -pub fn main_supported_hwdecodings() -> SyncReturn { - let decoding = supported_hwdecodings(); - let msg = HashMap::from([("h264", decoding.0), ("h265", decoding.1)]); - - SyncReturn(serde_json::ser::to_string(&msg).unwrap_or("".to_owned())) -} - pub fn main_is_root() -> bool { is_root() } @@ -1061,10 +1054,10 @@ pub fn session_send_note(id: String, note: String) { } } -pub fn session_alternative_codecs(id: String) -> String { +pub fn session_supported_hwcodec(id: String) -> String { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - let (vp8, h264, h265) = session.alternative_codecs(); - let msg = HashMap::from([("vp8", vp8), ("h264", h264), ("h265", h265)]); + let (h264, h265) = session.supported_hwcodec(); + let msg = HashMap::from([("h264", h264), ("h265", h265)]); serde_json::ser::to_string(&msg).unwrap_or("".to_owned()) } else { String::new() diff --git a/src/server/connection.rs b/src/server/connection.rs index d1ae4d6a3..23e166fcf 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -536,7 +536,7 @@ impl Connection { let _ = privacy_mode::turn_off_privacy(0); } video_service::notify_video_frame_fetched(id, None); - scrap::codec::Encoder::update(id, scrap::codec::EncodingUpdate::Remove); + scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); if conn.authorized { password::update_temporary_password(); @@ -862,7 +862,16 @@ impl Connection { } } - pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into(); + #[cfg(feature = "hwcodec")] + { + let (h264, h265) = scrap::codec::Encoder::supported_encoding(); + pi.encoding = Some(SupportedEncoding { + h264, + h265, + ..Default::default() + }) + .into(); + } if self.port_forward_socket.is_some() { let mut msg_out = Message::new(); @@ -1138,21 +1147,21 @@ impl Connection { self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.options_in_login = Some(o.clone()); - if let Some(q) = o.supported_decoding.clone().take() { - scrap::codec::Encoder::update( + if let Some(q) = o.video_codec_state.clone().take() { + scrap::codec::Encoder::update_video_encoder( self.inner.id(), - scrap::codec::EncodingUpdate::New(q), + scrap::codec::EncoderUpdate::State(q), ); } else { - scrap::codec::Encoder::update( + scrap::codec::Encoder::update_video_encoder( self.inner.id(), - scrap::codec::EncodingUpdate::NewOnlyVP9, + scrap::codec::EncoderUpdate::DisableHwIfNotExist, ); } } else { - scrap::codec::Encoder::update( + scrap::codec::Encoder::update_video_encoder( self.inner.id(), - scrap::codec::EncodingUpdate::NewOnlyVP9, + scrap::codec::EncoderUpdate::DisableHwIfNotExist, ); } self.video_ack_required = lr.video_ack_required; @@ -1775,8 +1784,11 @@ impl Connection { .unwrap() .update_user_fps(o.custom_fps as _); } - if let Some(q) = o.supported_decoding.clone().take() { - scrap::codec::Encoder::update(self.inner.id(), scrap::codec::EncodingUpdate::New(q)); + if let Some(q) = o.video_codec_state.clone().take() { + scrap::codec::Encoder::update_video_encoder( + self.inner.id(), + scrap::codec::EncoderUpdate::State(q), + ); } if let Ok(q) = o.lock_after_session_end.enum_value() { if q != BoolOption::NotSet { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 691ca4abe..205d0584c 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,7 +31,7 @@ use scrap::{ codec::{Encoder, EncoderCfg, HwEncoderConfig}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - CodecName, Display, TraitCapturer, + Display, TraitCapturer, }; #[cfg(windows)] use std::sync::Once; @@ -468,29 +468,21 @@ fn run(sp: GenericService) -> ResultType<()> { drop(video_qos); log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - let encoder_cfg = match Encoder::negotiated_codec() { - scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => { - EncoderCfg::HW(HwEncoderConfig { - name, - width: c.width, - height: c.height, - bitrate: bitrate as _, - }) - } - name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => { - EncoderCfg::VPX(VpxEncoderConfig { - width: c.width as _, - height: c.height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: if name == scrap::CodecName::VP8 { - VpxVideoCodecId::VP8 - } else { - VpxVideoCodecId::VP9 - }, - num_threads: (num_cpus::get() / 2) as _, - }) - } + let encoder_cfg = match Encoder::current_hw_encoder_name() { + Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { + codec_name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }), + None => EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }), }; let mut encoder; @@ -534,7 +526,7 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); - let codec_name = Encoder::negotiated_codec(); + let codec_name = Encoder::current_hw_encoder_name(); let recorder = get_recorder(c.width, c.height, &codec_name); #[cfg(windows)] start_uac_elevation_check(); @@ -565,7 +557,7 @@ fn run(sp: GenericService) -> ResultType<()> { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - if codec_name != Encoder::negotiated_codec() { + if codec_name != Encoder::current_hw_encoder_name() { bail!("SWITCH"); } #[cfg(windows)] @@ -611,14 +603,8 @@ fn run(sp: GenericService) -> ResultType<()> { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; match frame { - scrap::Frame::VP8(data) => { - let send_conn_ids = - handle_one_frame_encoded(VpxVideoCodecId::VP8, &sp, data, ms)?; - frame_controller.set_send(now, send_conn_ids); - } scrap::Frame::VP9(data) => { - let send_conn_ids = - handle_one_frame_encoded(VpxVideoCodecId::VP9, &sp, data, ms)?; + let send_conn_ids = handle_one_frame_encoded(&sp, data, ms)?; frame_controller.set_send(now, send_conn_ids); } scrap::Frame::RAW(data) => { @@ -731,11 +717,12 @@ fn run(sp: GenericService) -> ResultType<()> { fn get_recorder( width: usize, height: usize, - codec_name: &CodecName, + codec_name: &Option, ) -> Arc>> { #[cfg(not(target_os = "ios"))] let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { use crate::hbbs_http::record_upload; + use scrap::record::RecordCodecID::*; let tx = if record_upload::is_enable() { let (tx, rx) = std::sync::mpsc::channel(); @@ -744,6 +731,16 @@ fn get_recorder( } else { None }; + let codec_id = match codec_name { + Some(name) => { + if name.contains("264") { + H264 + } else { + H265 + } + } + None => VP9, + }; Recorder::new(RecorderContext { server: true, id: Config::get_id(), @@ -751,7 +748,7 @@ fn get_recorder( filename: "".to_owned(), width, height, - format: codec_name.into(), + codec_id, tx, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) @@ -778,6 +775,19 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu Ok(()) } +#[inline] +#[cfg(any(target_os = "android", target_os = "ios"))] +fn create_msg(vp9s: Vec) -> Message { + let mut msg_out = Message::new(); + let mut vf = VideoFrame::new(); + vf.set_vp9s(EncodedVideoFrames { + frames: vp9s.into(), + ..Default::default() + }); + msg_out.set_video_frame(vf); + msg_out +} + #[inline] fn handle_one_frame( sp: &GenericService, @@ -810,7 +820,6 @@ fn handle_one_frame( #[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub fn handle_one_frame_encoded( - codec: VpxVideoCodecId, sp: &GenericService, frame: &[u8], ms: i64, @@ -822,13 +831,13 @@ pub fn handle_one_frame_encoded( } Ok(()) })?; - let vpx_frame = EncodedVideoFrame { + let vp9_frame = EncodedVideoFrame { data: frame.to_vec().into(), key: true, pts: ms, ..Default::default() }; - let send_conn_ids = sp.send_video_frame(scrap::VpxEncoder::create_msg(codec, vec![vpx_frame])); + let send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); Ok(send_conn_ids) } diff --git a/src/ui/header.tis b/src/ui/header.tis index af4f1e349..257ba417e 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -161,8 +161,8 @@ class Header: Reactor.Component { } function renderDisplayPop() { - var codecs = handler.alternative_codecs(); - var show_codec = codecs[0] || codecs[1] || codecs[2]; + var codecs = handler.supported_hwcodec(); + var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]); var cursor_embedded = false; if ((pi.displays || []).length > 0) { @@ -186,10 +186,9 @@ class Header: Reactor.Component { {show_codec ?
  • {svg_checkmark}Auto
  • - {codecs[0] ?
  • {svg_checkmark}VP8
  • : ""}
  • {svg_checkmark}VP9
  • - {codecs[1] ?
  • {svg_checkmark}H264
  • : ""} - {codecs[2] ?
  • {svg_checkmark}H265
  • : ""} + {codecs[0] ?
  • {svg_checkmark}H264
  • : ""} + {codecs[1] ?
  • {svg_checkmark}H265
  • : ""}
    : ""}
    {!cursor_embedded &&
  • {svg_checkmark}{translate('Show remote cursor')}
  • } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index fc878cf1f..68decf955 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -20,6 +20,7 @@ use hbb_common::{ use crate::{ client::*, + ui_interface::has_hwcodec, ui_session_interface::{InvokeUiSession, Session}, }; @@ -196,14 +197,7 @@ impl InvokeUiSession for SciterHandler { self.call("confirmDeleteFiles", &make_args!(id, i, name)); } - fn override_file_confirm( - &self, - id: i32, - file_num: i32, - to: String, - is_upload: bool, - is_identical: bool, - ) { + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { self.call( "overrideFileConfirm", &make_args!(id, file_num, to, is_upload, is_identical), @@ -457,7 +451,8 @@ impl sciter::EventHandler for SciterSession { fn set_write_override(i32, i32, bool, bool, bool); fn get_keyboard_mode(); fn save_keyboard_mode(String); - fn alternative_codecs(); + fn has_hwcodec(); + fn supported_hwcodec(); fn change_prefer_codec(); fn restart_remote_device(); fn request_voice_call(); @@ -509,6 +504,10 @@ impl SciterSession { v } + fn has_hwcodec(&self) -> bool { + has_hwcodec() + } + pub fn t(&self, name: String) -> String { crate::client::translate(name) } @@ -517,10 +516,9 @@ impl SciterSession { super::get_icon() } - fn alternative_codecs(&self) -> Value { - let (vp8, h264, h265) = self.0.alternative_codecs(); + fn supported_hwcodec(&self) -> Value { + let (h264, h265) = self.0.supported_hwcodec(); let mut v = Value::array(0); - v.push(vp8); v.push(h264); v.push(h265); v diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 26d470fe0..11357be4a 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -752,13 +752,6 @@ pub fn has_hwcodec() -> bool { return true; } -#[cfg(feature = "flutter")] -#[inline] -pub fn supported_hwdecodings() -> (bool, bool) { - let decoding = scrap::codec::Decoder::supported_decodings(None); - (decoding.ability_h264 > 0, decoding.ability_h265 > 0) -} - #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_root() -> bool { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 3dee89a6e..f89be4879 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -216,16 +216,24 @@ impl Session { true } - pub fn alternative_codecs(&self) -> (bool, bool, bool) { - let decoder = scrap::codec::Decoder::supported_decodings(None); - let mut vp8 = decoder.ability_vp8 > 0; - let mut h264 = decoder.ability_h264 > 0; - let mut h265 = decoder.ability_h265 > 0; - let enc = &self.lc.read().unwrap().supported_encoding; - vp8 = vp8 && enc.vp8; - h264 = h264 && enc.h264; - h265 = h265 && enc.h265; - (vp8, h264, h265) + pub fn supported_hwcodec(&self) -> (bool, bool) { + #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] + { + let decoder = scrap::codec::Decoder::video_codec_state(&self.id); + let mut h264 = decoder.score_h264 > 0; + let mut h265 = decoder.score_h265 > 0; + let (encoding_264, encoding_265) = self + .lc + .read() + .unwrap() + .supported_encoding + .unwrap_or_default(); + h264 = h264 && encoding_264; + h265 = h265 && encoding_265; + return (h264, h265); + } + #[allow(unreachable_code)] + (false, false) } pub fn change_prefer_codec(&self) { From afa00df941fb4eb333138649ac2c56e99bb8ab57 Mon Sep 17 00:00:00 2001 From: Tomasz Boguszewski Date: Sat, 1 Apr 2023 04:23:25 +0200 Subject: [PATCH 014/112] Improve argument parsing Signed-off-by: Tomasz Boguszewski --- src/naming.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/naming.rs b/src/naming.rs index d805ee462..7a8d0cecc 100644 --- a/src/naming.rs +++ b/src/naming.rs @@ -9,19 +9,8 @@ fn gen_name(lic: &License) -> ResultType { } fn main() { - let mut args = Vec::new(); - let mut i = 0; - for arg in std::env::args() { - if i > 0 { - args.push(arg); - } - i += 1; - } - let api = if args.len() < 3 { - "".to_owned() - } else { - args[2].clone() - }; + let args: Vec<_> = std::env::args().skip(1).collect(); + let api = args.get(2).cloned().unwrap_or_default(); if args.len() >= 2 { println!( "rustdesk-licensed-{}.exe", From 3ab73bdf361d4df4621a87f8e87d21704e2c602a Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 10:26:55 +0800 Subject: [PATCH 015/112] fix x11 login screen Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 6 ++++++ src/server/service.rs | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 89c96799d..4da6d7436 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -40,6 +40,12 @@ pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND } +#[inline] +pub fn is_x11_wayland() -> bool { + let ds = get_display_server(); + ds == DISPLAY_SERVER_X11 || ds == DISPLAY_SERVER_WAYLAND +} + #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() diff --git a/src/server/service.rs b/src/server/service.rs index 9857889cc..fbe21105d 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -190,12 +190,15 @@ impl> ServiceTmpl { } #[inline] - fn wait_prelogin(&self) { + fn wait_prelogin_or_x11gdm(&self) { #[cfg(target_os = "linux")] while self.active() { if crate::platform::linux::is_prelogin() { break; } + if crate::platform::linux::is_x11_wayland() { + break; + } thread::sleep(time::Duration::from_millis(300)); } } @@ -209,7 +212,7 @@ impl> ServiceTmpl { let mut callback = callback; let sp = self.clone(); let thread = thread::spawn(move || { - sp.wait_prelogin(); + sp.wait_prelogin_or_x11gdm(); let mut state = S::default(); let mut may_reset = false; @@ -245,7 +248,7 @@ impl> ServiceTmpl { let sp = self.clone(); let mut callback = callback; let thread = thread::spawn(move || { - sp.wait_prelogin(); + sp.wait_prelogin_or_x11gdm(); let mut error_timeout = HIBERNATE_TIMEOUT; while sp.active() { From de485ca3a494bdc905555384cc40e894a884e79a Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 10:57:40 +0800 Subject: [PATCH 016/112] remove wait prelogin in service Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 6 ------ src/server/service.rs | 18 ------------------ 2 files changed, 24 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 4da6d7436..89c96799d 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -40,12 +40,6 @@ pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND } -#[inline] -pub fn is_x11_wayland() -> bool { - let ds = get_display_server(); - ds == DISPLAY_SERVER_X11 || ds == DISPLAY_SERVER_WAYLAND -} - #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() diff --git a/src/server/service.rs b/src/server/service.rs index fbe21105d..9cc1e860c 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -189,20 +189,6 @@ impl> ServiceTmpl { } } - #[inline] - fn wait_prelogin_or_x11gdm(&self) { - #[cfg(target_os = "linux")] - while self.active() { - if crate::platform::linux::is_prelogin() { - break; - } - if crate::platform::linux::is_x11_wayland() { - break; - } - thread::sleep(time::Duration::from_millis(300)); - } - } - pub fn repeat(&self, interval_ms: u64, callback: F) where F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send, @@ -212,8 +198,6 @@ impl> ServiceTmpl { let mut callback = callback; let sp = self.clone(); let thread = thread::spawn(move || { - sp.wait_prelogin_or_x11gdm(); - let mut state = S::default(); let mut may_reset = false; while sp.active() { @@ -248,8 +232,6 @@ impl> ServiceTmpl { let sp = self.clone(); let mut callback = callback; let thread = thread::spawn(move || { - sp.wait_prelogin_or_x11gdm(); - let mut error_timeout = HIBERNATE_TIMEOUT; while sp.active() { if sp.has_subscribes() { From 820b01ab41ba45ed71276c7052f7c263397013e1 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 11:42:10 +0800 Subject: [PATCH 017/112] add translate mode ref link Signed-off-by: fufesou --- src/keyboard.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/keyboard.rs b/src/keyboard.rs index cdedc4fdb..86735b8f6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -942,6 +942,7 @@ fn is_press(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(_)) } +// https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); if let Some(unicode_info) = &event.unicode { From 40204077db1a025815ebe233c4cacea5b933ee0d Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 13:18:01 +0800 Subject: [PATCH 018/112] revert linux is_prelogin Signed-off-by: fufesou --- src/platform/linux.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 1fdfd316d..dda4b115b 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -455,8 +455,8 @@ pub fn get_env_var(k: &str) -> String { } pub fn is_prelogin() -> bool { - let (uid, uname) = get_active_user_id_name(); - uid.len() >= 4 || uname == "root" + let n = get_active_userid().len(); + n < 4 && n > 1 } pub fn is_root() -> bool { From ba8ab75a8080732c26f86fafadbaa0387f2867d5 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 15:51:42 +0800 Subject: [PATCH 019/112] fix remote toolbar autohide Signed-off-by: fufesou --- flutter/lib/desktop/widgets/remote_toolbar.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 0ef8674ef..703ed6db9 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -320,6 +320,8 @@ class _RemoteMenubarState extends State { PeerInfo get pi => widget.ffi.ffiModel.pi; FfiModel get ffiModel => widget.ffi.ffiModel; + triggerAutoHide() => _debouncerHide.value = _debouncerHide.value + 1; + @override initState() { super.initState(); @@ -332,7 +334,7 @@ class _RemoteMenubarState extends State { widget.onEnterOrLeaveImageSetter((enter) { if (enter) { - _debouncerHide.value = 0; + triggerAutoHide(); _isCursorOverImage = true; } else { _isCursorOverImage = false; @@ -367,7 +369,7 @@ class _RemoteMenubarState extends State { Widget _buildDraggableShowHide(BuildContext context) { return Obx(() { if (show.isTrue && _dragging.isFalse) { - _debouncerHide.value = 1; + triggerAutoHide(); } return Align( alignment: FractionalOffset(_fractionX.value, 0), From ae53ec877b7da292725eff8d891621bd0c8d9ebb Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 18:09:53 +0800 Subject: [PATCH 020/112] translate mode, win2win, Send both unicode and virtual keycode to remote side Signed-off-by: fufesou --- libs/hbb_common/protos/message.proto | 6 ++- src/keyboard.rs | 57 +++++++++++++++++----------- src/platform/windows.rs | 42 +++++++++++--------- src/server/input_service.rs | 43 +++++++++++++++++++++ 4 files changed, 105 insertions(+), 43 deletions(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 6295c160b..a481ae6c4 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -203,11 +203,13 @@ message KeyEvent { bool press = 2; oneof union { ControlKey control_key = 3; - // high word, sym key code. win: virtual-key code, linux: keysym ?, macos: - // low word, position key code. win: scancode, linux: key code, macos: key code + // position key code. win: scancode, linux: key code, macos: key code uint32 chr = 4; uint32 unicode = 5; string seq = 6; + // high word. virtual keycode + // low word. unicode + uint32 win2win_hotkey = 7; } repeated ControlKey modifiers = 8; KeyboardMode mode = 9; diff --git a/src/keyboard.rs b/src/keyboard.rs index 86735b8f6..a2d17bc14 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -4,7 +4,7 @@ use crate::common::GrabState; #[cfg(feature = "flutter")] use crate::flutter::{CUR_SESSION_ID, SESSIONS}; #[cfg(target_os = "windows")] -use crate::platform::windows::get_char_by_vk; +use crate::platform::windows::{get_char_from_vk, get_unicode_from_vk}; #[cfg(not(any(feature = "flutter", feature = "cli")))] use crate::ui::CUR_SESSION; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -874,7 +874,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m #[cfg(target_os = "windows")] if _peer == OS_LOWER_LINUX { if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { - if let Some(chr) = get_char_by_vk(event.platform_code as u32) { + if let Some(chr) = get_char_from_vk(event.platform_code as u32) { let mut evt = key_event.clone(); evt.set_seq(chr.to_string()); events.push(evt); @@ -885,6 +885,33 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m } } +#[cfg(target_os = "windows")] +fn try_file_win2win_hotkey( + peer: &str, + event: &Event, + key_event: &KeyEvent, + events: &mut Vec, +) { + if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { + let win2win_hotkey = match event.event_type { + EventType::KeyPress(..) => { + if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) { + Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16)) + } else { + None + } + } + EventType::KeyRelease(..) => Some(event.platform_code << 16), + _ => None, + }; + if let Some(code) = win2win_hotkey { + let mut evt = key_event.clone(); + evt.set_win2win_hotkey(code); + events.push(evt); + } + } +} + #[cfg(target_os = "windows")] fn is_hot_key_modifiers_down() -> bool { if rdev::get_modifier(Key::ControlLeft) || rdev::get_modifier(Key::ControlRight) { @@ -899,25 +926,6 @@ fn is_hot_key_modifiers_down() -> bool { return false; } -#[inline] -#[cfg(target_os = "windows")] -pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { - let mut key_event = map_keyboard_mode(peer, event, key_event)?; - let chr = if peer == OS_LOWER_WINDOWS { - (key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16) - } else { - key_event.chr() - }; - key_event.set_chr(chr); - Some(key_event) -} - -#[inline] -#[cfg(not(target_os = "windows"))] -pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { - map_keyboard_mode(peer, event, key_event) -} - #[inline] #[cfg(any(target_os = "linux", target_os = "windows"))] fn is_altgr(event: &Event) -> bool { @@ -961,7 +969,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(not(any(target_os = "android", target_os = "ios")))] if is_numpad_key(&event) { - if let Some(evt) = translate_key_code(peer, event, key_event) { + if let Some(evt) = map_keyboard_mode(peer, event, key_event) { events.push(evt); } return events; @@ -995,13 +1003,16 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } + #[cfg(target_os = "windows")] + try_file_win2win_hotkey(peer, event, &key_event, &mut events); + #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { try_fill_unicode(peer, event, &key_event, &mut events); } if events.is_empty() { - if let Some(evt) = translate_key_code(peer, event, key_event) { + if let Some(evt) = map_keyboard_mode(peer, event, key_event) { events.push(evt); } } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 83f5af2dd..76b06c490 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1229,7 +1229,7 @@ sc delete {app_name} } pub fn run_after_install() -> ResultType<()> { - let (_, _, _, exe,_) = get_install_info(); + let (_, _, _, exe, _) = get_install_info(); run_cmds(get_after_install(&exe), true, "after_install") } @@ -2130,7 +2130,25 @@ mod cert { } } -pub fn get_char_by_vk(vk: u32) -> Option { +#[inline] +pub fn get_char_from_vk(vk: u32) -> Option { + get_char_from_unicode(get_unicode_from_vk(vk)?) +} + +pub fn get_char_from_unicode(unicode: u16) -> Option { + let buff = [unicode]; + if let Some(chr) = String::from_utf16(&buff[..1]).ok()?.chars().next() { + if chr.is_control() { + return None; + } else { + Some(chr) + } + } else { + None + } +} + +pub fn get_unicode_from_vk(vk: u32) -> Option { const BUF_LEN: i32 = 32; let mut buff = [0_u16; BUF_LEN as usize]; let buff_ptr = buff.as_mut_ptr(); @@ -2155,19 +2173,7 @@ pub fn get_char_by_vk(vk: u32) -> Option { ToUnicodeEx(vk, 0x00, &state as _, buff_ptr, BUF_LEN, 0, layout) }; if len == 1 { - if let Some(chr) = String::from_utf16(&buff[..len as usize]) - .ok()? - .chars() - .next() - { - if chr.is_control() { - return None; - } else { - Some(chr) - } - } else { - None - } + Some(buff[0]) } else { None } @@ -2190,10 +2196,10 @@ mod tests { } #[test] - fn test_get_char_by_vk() { - let chr = get_char_by_vk(0x41); // VK_A + fn test_get_unicode_char_by_vk() { + let chr = get_char_from_vk(0x41); // VK_A assert_eq!(chr, Some('a')); - let chr = get_char_by_vk(VK_ESCAPE as u32); // VK_ESC + let chr = get_char_from_vk(VK_ESCAPE as u32); // VK_ESC assert_eq!(chr, None) } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 5bc486516..38467fa32 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1,6 +1,8 @@ use super::*; #[cfg(target_os = "linux")] use crate::common::IS_X11; +#[cfg(target_os = "windows")] +use crate::platform::windows::get_char_from_unicode; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; @@ -1280,12 +1282,53 @@ fn translate_keyboard_mode(evt: &KeyEvent) { Some(key_event::Union::Unicode(..)) => { // Do not handle unicode for now. } + #[cfg(target_os = "windows")] + Some(key_event::Union::Win2winHotkey(code)) => { + simulate_win2win_hotkey(*code, evt.down); + } _ => { log::debug!("Unreachable. Unexpected key event {:?}", &evt); } } } +#[cfg(target_os = "windows")] +fn simulate_win2win_hotkey(code: u32, down: bool) { + let mut simulated = false; + + let unicode: u16 = (code & 0x0000FFFF) as u16; + if unicode != 0 { + // Try convert unicode to virtual keycode first. + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw + let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + if res as u16 != 0xFFFF { + let vk = res & 0x00FF; + let flag = res >> 8; + let modifiers = [rdev::Key::ShiftLeft, rdev::Key::ControlLeft, rdev::Key::Alt]; + let mod_len = modifiers.len(); + for pos in 0..mod_len { + if flag & (0x0001 << pos) != 0 { + allow_err!(rdev::simulate(&EventType::KeyPress(modifiers[pos]))); + } + } + allow_err!(rdev::simulate_code(Some(vk as _), None, true)); + allow_err!(rdev::simulate_code(Some(vk as _), None, false)); + for pos in 0..mod_len { + let rpos = mod_len - 1 - pos; + if flag & (0x0001 << rpos) != 0 { + allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos]))); + } + } + simulated = true; + } + } + + if simulated { + let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; + allow_err!(rdev::simulate_code(Some(keycode), None, down)); + } +} + pub fn handle_key_(evt: &KeyEvent) { if EXITING.load(Ordering::SeqCst) { return; From 021939a6a6c16b2ec62aefb5bf2d6eddb4dd22bd Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 19:30:22 +0800 Subject: [PATCH 021/112] tmp commit, for debug Signed-off-by: fufesou --- src/keyboard.rs | 8 ++++++++ src/server/input_service.rs | 16 ++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index a2d17bc14..e8186ad45 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -517,6 +517,11 @@ pub fn event_to_key_events( } }; + println!( + "REMOVE ME ==================================== key_events {:?}", + &key_events + ); + #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate { let is_numpad_key = is_numpad_key(&event); @@ -893,8 +898,10 @@ fn try_file_win2win_hotkey( events: &mut Vec, ) { if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { + let mut down = false; let win2win_hotkey = match event.event_type { EventType::KeyPress(..) => { + down = true; if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) { Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16)) } else { @@ -907,6 +914,7 @@ fn try_file_win2win_hotkey( if let Some(code) = win2win_hotkey { let mut evt = key_event.clone(); evt.set_win2win_hotkey(code); + evt.down = down; events.push(evt); } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 38467fa32..d85848314 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1,8 +1,6 @@ use super::*; #[cfg(target_os = "linux")] use crate::common::IS_X11; -#[cfg(target_os = "windows")] -use crate::platform::windows::get_char_from_unicode; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; @@ -1294,13 +1292,12 @@ fn translate_keyboard_mode(evt: &KeyEvent) { #[cfg(target_os = "windows")] fn simulate_win2win_hotkey(code: u32, down: bool) { - let mut simulated = false; - let unicode: u16 = (code & 0x0000FFFF) as u16; - if unicode != 0 { + if down { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + println!("REMOVE ME =============================== VkKeyScanW {} {}", unicode, res); if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; @@ -1319,14 +1316,13 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos]))); } } - simulated = true; + return; } } - if simulated { - let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; - allow_err!(rdev::simulate_code(Some(keycode), None, down)); - } + let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; + println!("REMOVE ME =============================== simulate_win2win_hotkey down {} {},{}", down, unicode, keycode); + allow_err!(rdev::simulate_code(Some(keycode), None, down)); } pub fn handle_key_(evt: &KeyEvent) { From ed4016a77aa62fb6bb41730ae8a28906b0b35d2b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 20:56:03 +0800 Subject: [PATCH 022/112] debug done Signed-off-by: fufesou --- src/keyboard.rs | 5 ----- src/server/input_service.rs | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index e8186ad45..b0eb68aa5 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -517,11 +517,6 @@ pub fn event_to_key_events( } }; - println!( - "REMOVE ME ==================================== key_events {:?}", - &key_events - ); - #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate { let is_numpad_key = is_numpad_key(&event); diff --git a/src/server/input_service.rs b/src/server/input_service.rs index d85848314..0def3aa90 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1297,7 +1297,6 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; - println!("REMOVE ME =============================== VkKeyScanW {} {}", unicode, res); if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; @@ -1321,7 +1320,6 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; - println!("REMOVE ME =============================== simulate_win2win_hotkey down {} {},{}", down, unicode, keycode); allow_err!(rdev::simulate_code(Some(keycode), None, down)); } From 83249f0f957770c5757386aefe0c9f132be7542a Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 21:35:59 +0800 Subject: [PATCH 023/112] debug done Signed-off-by: fufesou --- src/keyboard.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index b0eb68aa5..7f1f7e5f8 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -865,6 +865,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m if name.len() > 0 { let mut evt = key_event.clone(); evt.set_seq(name.to_string()); + evt.down = true; events.push(evt); } } @@ -877,6 +878,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m if let Some(chr) = get_char_from_vk(event.platform_code as u32) { let mut evt = key_event.clone(); evt.set_seq(chr.to_string()); + evt.down = true; events.push(evt); } } @@ -956,6 +958,7 @@ fn is_press(event: &Event) -> bool { // https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); + if let Some(unicode_info) = &event.unicode { if unicode_info.is_dead { #[cfg(target_os = "macos")] @@ -994,11 +997,15 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - return events; } + #[cfg(target_os = "windows")] + try_file_win2win_hotkey(peer, event, &key_event, &mut events); + #[cfg(any(target_os = "linux", target_os = "windows"))] - if is_press(event) { + if events.is_empty() && is_press(event) { try_fill_unicode(peer, event, &key_event, &mut events); } + // If AltGr is down, no need to send events other than unicode. #[cfg(target_os = "windows")] unsafe { if IS_0X021D_DOWN { @@ -1006,9 +1013,6 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } - #[cfg(target_os = "windows")] - try_file_win2win_hotkey(peer, event, &key_event, &mut events); - #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { try_fill_unicode(peer, event, &key_event, &mut events); From 51ff2d35b55e8a9742caeacb3713afb28b868120 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 2 Apr 2023 14:31:30 +0800 Subject: [PATCH 024/112] translate mode win, update input layout, just a temporary workaround Signed-off-by: fufesou --- src/flutter.rs | 2 +- src/server/input_service.rs | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index fde9ce7cd..b293059d0 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -218,7 +218,7 @@ impl VideoRenderer { } pub fn on_rgba(&self, rgba: &Vec) { - if self.ptr == usize::default() { + if self.ptr == usize::default() || self.width == 0 || self.height == 0 { return; } if let Some(func) = &self.on_rgba_func { diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 0def3aa90..c5011a856 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -16,9 +16,19 @@ use std::{ thread, time::{self, Instant}, }; +#[cfg(target_os = "windows")] +use winapi::um::winuser::{ + ActivateKeyboardLayout, GetForegroundWindow, GetKeyboardLayout, GetWindowThreadProcessId, + VkKeyScanW, +}; const INVALID_CURSOR_POS: i32 = i32::MIN; +#[cfg(target_os = "windows")] +lazy_static::lazy_static! { + static ref LAST_HKL: Arc> = Arc::new(Mutex::new(0)); +} + #[derive(Default)] struct StateCursor { hcursor: u64, @@ -1242,7 +1252,32 @@ fn translate_process_code(code: u32, down: bool) { }; } +#[cfg(target_os = "windows")] +fn check_update_input_layout() { + unsafe { + let foreground_thread_id = + GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()); + let layout = GetKeyboardLayout(foreground_thread_id); + let layout_u32 = layout as u32; + let mut last_layout_lock = LAST_HKL.lock().unwrap(); + if *last_layout_lock == 0 || *last_layout_lock != layout_u32 { + let res = ActivateKeyboardLayout(layout, 0); + if res == layout { + *last_layout_lock = layout_u32; + } else { + log::error!("Failed to call ActivateKeyboardLayout, {}", layout_u32); + } + } + } +} + fn translate_keyboard_mode(evt: &KeyEvent) { + // --server could not detect the input layout change. + // This is a temporary workaround. + // There may be a better way to detect and handle the input layout change. + #[cfg(target_os = "windows")] + check_update_input_layout(); + match &evt.union { Some(key_event::Union::Seq(seq)) => { // Fr -> US @@ -1296,7 +1331,7 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { if down { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw - let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + let res = unsafe { VkKeyScanW(unicode) }; if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; From e0667833d5394be7f66da63440c15708818802d3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 2 Apr 2023 14:39:54 +0800 Subject: [PATCH 025/112] comment Signed-off-by: fufesou --- src/server/input_service.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index c5011a856..7d3229c12 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1274,7 +1274,20 @@ fn check_update_input_layout() { fn translate_keyboard_mode(evt: &KeyEvent) { // --server could not detect the input layout change. // This is a temporary workaround. + // // There may be a better way to detect and handle the input layout change. + // while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) + // { + // ... + // if (msg.message == WM_INPUTLANGCHANGE) + // { + // // handle WM_INPUTLANGCHANGE message here + // check_update_input_layout(); + // } + // TranslateMessage(&msg); + // DispatchMessage(&msg); + // ... + // } #[cfg(target_os = "windows")] check_update_input_layout(); From 156ee78ebb4ad81c5e26d337f2e6c303730787be Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 3 Apr 2023 00:16:09 +0800 Subject: [PATCH 026/112] remove compile warn --- src/platform/windows.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 76b06c490..383259a32 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -9,15 +9,15 @@ use hbb_common::{ message_proto::Resolution, sleep, timeout, tokio, }; -use std::io::prelude::*; -use std::path::Path; -use std::ptr::null_mut; use std::{ collections::HashMap, ffi::OsString, - fs, io, mem, + fs, io, + io::prelude::*, + mem, os::windows::process::CommandExt, - path::PathBuf, + path::*, + ptr::null_mut, sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -936,8 +936,9 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { } pub fn update_me() -> ResultType<()> { - let (_, path, _, exe, dll) = get_install_info(); + let (_, path, _, exe, _dll) = get_install_info(); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned(); + #[cfg(not(feature = "flutter"))] let src_dll = std::env::current_exe()? .parent() .unwrap_or(&Path::new(&get_default_install_path())) @@ -948,7 +949,7 @@ pub fn update_me() -> ResultType<()> { #[cfg(feature = "flutter")] let copy_dll = "".to_string(); #[cfg(not(feature = "flutter"))] - let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); + let copy_dll = copy_raw_cmd(&src_dll, &_dll, &path); let cmds = format!( " chcp 65001 @@ -1002,12 +1003,12 @@ pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> Res let mut path = path.trim_end_matches('\\').to_owned(); let (subkey, _path, start_menu, exe, dll) = get_default_install_info(); let mut exe = exe; - let mut dll = dll; + let mut _dll = dll; if path.is_empty() { path = _path; } else { exe = exe.replace(&_path, &path); - dll = dll.replace(&_path, &path); + _dll = _dll.replace(&_path, &path); } let mut version_major = "0"; let mut version_minor = "0"; @@ -1131,6 +1132,7 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} app_name = crate::get_app_name(), ); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string(); + #[cfg(not(feature = "flutter"))] let src_dll = std::env::current_exe()? .parent() .unwrap_or(&Path::new(&get_default_install_path())) @@ -1138,11 +1140,10 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} .to_str() .unwrap_or("") .to_owned(); - #[cfg(feature = "flutter")] let copy_dll = "".to_string(); #[cfg(not(feature = "flutter"))] - let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); + let copy_dll = copy_raw_cmd(&src_dll, &_dll, &path); let install_cert = if options.contains("driverCert") { format!("\"{}\" --install-cert \"RustDeskIddDriver.cer\"", src_exe) From 8b1fb742b421c182c7d14efd69fb1eb633191e98 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 3 Apr 2023 00:24:39 +0800 Subject: [PATCH 027/112] Icons.list_rounded looks bad on Windows. https://github.com/rustdesk/rustdesk/issues/3885#issuecomment-1493356168 --- flutter/lib/common/widgets/peer_tab_page.dart | 2 +- flutter/pubspec.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 2d36d9150..0c8ac9329 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -261,7 +261,7 @@ class _PeerTabPageState extends State }, child: Icon( peerCardUiType.value == PeerUiType.grid - ? Icons.list_rounded + ? Icons.list : Icons.grid_view_rounded, size: 18, color: textColor, diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 6bd19d01a..639ee04de 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -1236,10 +1236,10 @@ packages: dependency: "direct main" description: name: texture_rgba_renderer - sha256: "52bc9f217b7b07a760ee837d5a17329ad1f78ae8ed1e3fa612c6f1bed3c77f79" + sha256: cb048abdd800468ca40749ca10d1db9d1e6a055d1cde6234c05191293f0c7d61 url: "https://pub.dev" source: hosted - version: "0.0.13" + version: "0.0.16" timing: dependency: transitive description: From 88d82749979eba69147a35f717da0dbc6fc1668e Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sun, 2 Apr 2023 22:41:16 +0800 Subject: [PATCH 028/112] fix: nightly build needs to upload artifact --- .github/workflows/flutter-build.yml | 1608 ++++++++++++++++++++++++ .github/workflows/flutter-ci.yml | 2 +- .github/workflows/flutter-nightly.yml | 1613 +------------------------ 3 files changed, 1614 insertions(+), 1609 deletions(-) create mode 100644 .github/workflows/flutter-build.yml diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml new file mode 100644 index 000000000..97a4073af --- /dev/null +++ b/.github/workflows/flutter-build.yml @@ -0,0 +1,1608 @@ +name: Build the flutter version of the RustDesk + +on: + workflow_call: + inputs: + upload-artifact: + type: boolean + default: true + +env: + LLVM_VERSION: "15.0.6" + FLUTTER_VERSION: "3.7.0" + TAG_NAME: "nightly" + # vcpkg version: 2022.05.10 + # for multiarch gcc compatibility + VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44" + VERSION: "1.2.0" + NDK_VERSION: "r23" + #signing keys env variable checks + ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}' + MACOS_P12_BASE64: '${{ secrets.MACOS_P12_BASE64 }}' + # To make a custom build with your own servers set the below secret values + RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}' + RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}' + UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}" + +jobs: + build-for-windows-flutter: + name: ${{ matrix.job.target }} (${{ matrix.job.os }}) + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-pc-windows-msvc , os: windows-2019 } + # - { target: x86_64-pc-windows-gnu , os: windows-2019 } + - { target: x86_64-pc-windows-msvc, os: windows-2019 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Install LLVM and Clang + uses: KyleMayes/install-llvm-action@v1 + with: + version: ${{ env.LLVM_VERSION }} + + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + cache: true + + - name: Replace engine with rustdesk custom flutter engine + run: | + flutter doctor -v + flutter precache --windows + Invoke-WebRequest -Uri https://github.com/Kingtous/engine/releases/download/v3.7.0-rustdesk/windows-x64-release-flutter.zip -OutFile windows-x64-flutter-release.zip + Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine + mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/ + + - 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) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }} + + - name: Install flutter rust bridge deps + run: | + cargo install flutter_rust_bridge_codegen + Push-Location flutter ; flutter pub get ; Pop-Location + ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart + + - name: Install vcpkg dependencies + run: | + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 + + - name: Build rustdesk + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg + run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver + + - name: Sign rustdesk files + uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.CERTNAME }}' + folder: './flutter/build/windows/runner/Release/' + recursive: true + + - name: Build self-extracted executable + shell: bash + if: env.UPLOAD_ARTIFACT == 'true' + run: | + pushd ./libs/portable + python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe + popd + mkdir -p ./SignOutput + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe + + # - name: Rename rustdesk + # shell: bash + # run: | + # for name in rustdesk*??-install.exe; do + # mv "$name" ./SignOutput/"${name%%-install.exe}-${{ matrix.job.target }}.exe" + # done + + - name: Sign rustdesk self-extracted file + uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' + folder: './SignOutput' + recursive: false + + - name: Publish Release + uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./SignOutput/rustdesk-*.exe + + # The fallback for the flutter version, we use Sciter for 32bit Windows. + build-for-windows-sciter: + name: ${{ matrix.job.target }} (${{ matrix.job.os }}) + runs-on: ${{ matrix.job.os }} + # Temporarily disable this action due to additional test is needed. + # if: false + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-pc-windows-msvc , os: windows-2019 } + # - { target: x86_64-pc-windows-gnu , os: windows-2019 } + - { target: i686-pc-windows-msvc, os: windows-2019 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Install LLVM and Clang + uses: Kingtous/install-llvm-action-32bit@master + with: + version: ${{ env.LLVM_VERSION }} + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-${{ matrix.job.target }} + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }}-sciter + + - name: Restore from cache and install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 + + - name: Build rustdesk + id: build + shell: bash + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg + run: | + python3 res/inline-sciter.py + # Patch sciter x86 + sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml + # Replace the link for the ico. + rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico + cargo build --features inline --release --bins + mkdir -p ./Release + mv ./target/release/rustdesk.exe ./Release/rustdesk.exe + curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll + echo "output_folder=./Release" >> $GITHUB_OUTPUT + + - name: Sign rustdesk files + uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.CERTNAME }}' + folder: './Release/' + recursive: true + + - name: Build self-extracted executable + shell: bash + run: | + pushd ./libs/portable + pip3 install -r requirements.txt + python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe + popd + mkdir -p ./SignOutput + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe + + - name: Sign rustdesk self-extracted file + uses: GermanBluefox/code-sign-action@v7 + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' + folder: './SignOutput' + recursive: false + + - name: Publish Release + uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./SignOutput/rustdesk-*.exe + + build-for-macOS: + name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { + target: x86_64-apple-darwin, + os: macos-latest, + extra-build-args: "", + } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Import the codesign cert + if: env.MACOS_P12_BASE64 != null + uses: apple-actions/import-codesign-certs@v1 + with: + p12-file-base64: ${{ secrets.MACOS_P12_BASE64 }} + p12-password: ${{ secrets.MACOS_P12_PASSWORD }} + keychain: rustdesk + + - name: Check sign and import sign key + if: env.MACOS_P12_BASE64 != null + run: | + security default-keychain -s rustdesk.keychain + security find-identity -v + + - name: Import notarize key + if: env.MACOS_P12_BASE64 != null + uses: timheuer/base64-to-file@v1.2 + with: + # https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_rcodesign.html#notarizing-and-stapling + fileName: rustdesk.json + fileDir: ${{ github.workspace }} + encodedString: ${{ secrets.MACOS_NOTARIZE_JSON }} + + - name: Install rcodesign tool + if: env.MACOS_P12_BASE64 != null + shell: bash + run: | + pushd /tmp + wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz + tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz + mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin + popd + + - name: Install build runtime + run: | + brew install llvm create-dmg nasm yasm cmake gcc wget ninja pkg-config + + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + + - 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) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }} + + - name: Install flutter rust bridge deps + shell: bash + run: | + cargo install flutter_rust_bridge_codegen + pushd flutter && flutter pub get && popd + ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart + + - name: Restore from cache and install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + $VCPKG_ROOT/vcpkg install libvpx libyuv opus + + - name: Show version information (Rust, cargo, Clang) + shell: bash + run: | + clang --version || true + rustup -V + rustup toolchain list + rustup default + cargo -V + rustc -V + + - name: Build rustdesk + run: | + # --hwcodec not supported on macos yet + ./build.py --flutter ${{ matrix.job.extra-build-args }} + + - name: Codesign app and create signed dmg + if: env.MACOS_P12_BASE64 != null + run: | + security default-keychain -s rustdesk.keychain + security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain + # start sign the rustdesk.app and dmg + rm rustdesk-${{ env.VERSION }}.dmg || true + codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict ./flutter/build/macos/Build/Products/Release/RustDesk.app -vvv + create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app + codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict rustdesk-${{ env.VERSION }}.dmg -vvv + # notarize the rustdesk-${{ env.VERSION }}.dmg + rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json --staple rustdesk-${{ env.VERSION }}.dmg + + - name: Rename rustdesk + run: | + for name in rustdesk*??.dmg; do + mv "$name" "${name%%.dmg}-${{ matrix.job.target }}.dmg" + done + + - name: Publish DMG package + if: env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk*-${{ matrix.job.target }}.dmg + + build-vcpkg-deps-linux: + uses: ./.github/workflows/vcpkg-deps-linux.yml + + generate-bridge-linux: + uses: ./.github/workflows/bridge.yml + + build-rustdesk-android: + needs: [generate-bridge-linux] + name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { + arch: x86_64, + target: aarch64-linux-android, + os: ubuntu-20.04, + extra-build-features: "", + openssl-arch: android-arm64 + } + - { + arch: x86_64, + target: armv7-linux-androideabi, + os: ubuntu-18.04, + extra-build-features: "", + openssl-arch: android-arm + } + steps: + - name: Install dependencies + run: | + sudo apt update + sudo apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless + - name: Checkout source code + uses: actions/checkout@v3 + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: ${{ env.NDK_VERSION }} + add-to-path: true + + - name: Clone deps + shell: bash + run: | + pushd /opt + git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + + - name: Disable rust bridge build + run: | + sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + + - name: Build rustdesk lib + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg + run: | + rustup target add ${{ matrix.job.target }} + cargo install cargo-ndk + case ${{ matrix.job.target }} in + aarch64-linux-android) + ./flutter/ndk_arm64.sh + mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so + ;; + armv7-linux-androideabi) + ./flutter/ndk_arm.sh + mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so + ;; + esac + + - name: Build rustdesk + shell: bash + env: + JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64 + run: | + export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH + # temporary use debug sign config + sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle + case ${{ matrix.job.target }} in + aarch64-linux-android) + mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a + cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so + # build flutter + pushd flutter + flutter build apk --release --target-platform android-arm64 --split-per-abi + mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + ;; + armv7-linux-androideabi) + mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a + cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so + # build flutter + pushd flutter + flutter build apk --release --target-platform android-arm --split-per-abi + mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + ;; + esac + popd + mkdir -p signed-apk; pushd signed-apk + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk . + + - uses: r0adkll/sign-android-release@v1 + name: Sign app APK + if: env.ANDROID_SIGNING_KEY != null + id: sign-rustdesk + with: + releaseDirectory: ./signed-apk + signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }} + alias: ${{ secrets.ANDROID_ALIAS }} + keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }} + env: + # override default build-tools version (29.0.3) -- optional + BUILD_TOOLS_VERSION: "30.0.2" + + - name: Upload Artifacts + if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + uses: actions/upload-artifact@master + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk + path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} + + - name: Publish signed apk package + if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ${{steps.sign-rustdesk.outputs.signedReleaseFile}} + + - name: Publish unsigned apk package + if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + + build-rustdesk-lib-linux-amd64: + needs: [generate-bridge-linux, build-vcpkg-deps-linux] + name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + # use a high level qemu-user-static + job: + # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } + # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-20.04, + extra-build-features: "", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-20.04, + extra-build-features: "flatpak", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-20.04, + extra-build-features: "appimage", + } + # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + steps: + - name: Maximize build space + run: | + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo apt update -y + sudo apt install qemu-user-static + + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 12 + + - name: Free Space + run: | + df + + - 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) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + cache-directories: "/opt/rust-registry" + + - name: Install local registry + run: | + mkdir -p /opt/rust-registry + cargo install cargo-local-registry + + - name: Build local registry + uses: nick-fields/retry@v2 + id: build-local-registry + continue-on-error: true + with: + max_attempts: 3 + timeout_minutes: 15 + retry_on: error + command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry + + - name: Disable rust bridge build + run: | + sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + # only build cdylib + sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Restore vcpkg files + uses: actions/download-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: /opt/artifacts/vcpkg/installed + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk library for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04 + # not ready yet + # distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + ls -l /opt/artifacts/vcpkg/installed + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/rust-registry:/opt/rust-registry" + shell: /bin/bash + install: | + apt update -y + echo -e "installing deps" + apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + # we have libopus compiled by us. + apt remove -y libopus-dev || true + # output devs + ls -l ./ + tree -L 3 /opt/artifacts/vcpkg/installed + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # rust + pushd /opt + wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz + tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz + cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh + rm -rf rust-1.64.0-${{ matrix.job.target }} + # edit config + mkdir -p ~/.cargo/ + echo """ + [source.crates-io] + registry = 'https://github.com/rust-lang/crates.io-index' + replace-with = 'local-registry' + + [source.local-registry] + local-registry = '/opt/rust-registry/' + """ > ~/.cargo/config + cat ~/.cargo/config + # start build + pushd /workspace + # mock + case "${{ matrix.job.arch }}" in + x86_64) + # no need mock on x86_64 + export VCPKG_ROOT=/opt/artifacts/vcpkg + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release + ;; + esac + + - name: Upload Artifacts + uses: actions/upload-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: target/release/liblibrustdesk.so + + build-rustdesk-lib-linux-arm: + needs: [generate-bridge-linux, build-vcpkg-deps-linux] + name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + # use a high level qemu-user-static + job: + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-20.04, + use-cross: true, + extra-build-features: "", + } + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-18.04, # just for naming package, not running host + use-cross: true, + extra-build-features: "appimage", + } + # - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } + # - { + # arch: armv7, + # target: armv7-unknown-linux-gnueabihf, + # os: ubuntu-20.04, + # use-cross: true, + # extra-build-features: "", + # } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } + steps: + - name: Maximize build space + run: | + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo apt update -y + sudo apt install qemu-user-static + + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 12 + + - name: Free Space + run: | + df + + - 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) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + cache-directories: "/opt/rust-registry" + + - name: Install local registry + run: | + mkdir -p /opt/rust-registry + cargo install cargo-local-registry + + - name: Build local registry + uses: nick-fields/retry@v2 + id: build-local-registry + continue-on-error: true + with: + max_attempts: 3 + timeout_minutes: 15 + retry_on: error + command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry + + - name: Disable rust bridge build + run: | + sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + # only build cdylib + sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Restore vcpkg files + uses: actions/download-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: /opt/artifacts/vcpkg/installed + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk library for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + ls -l /opt/artifacts/vcpkg/installed + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/rust-registry:/opt/rust-registry" + shell: /bin/bash + install: | + apt update -y + echo -e "installing deps" + apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + # we have libopus compiled by us. + apt remove -y libopus-dev || true + # output devs + ls -l ./ + tree -L 3 /opt/artifacts/vcpkg/installed + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # rust + pushd /opt + wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz + tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz + cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh + rm -rf rust-1.64.0-${{ matrix.job.target }} + # edit config + mkdir -p ~/.cargo/ + echo """ + [source.crates-io] + registry = 'https://github.com/rust-lang/crates.io-index' + replace-with = 'local-registry' + + [source.local-registry] + local-registry = '/opt/rust-registry/' + """ > ~/.cargo/config + cat ~/.cargo/config + # start build + pushd /workspace + export VCPKG_ROOT=/opt/artifacts/vcpkg + cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release + + - name: Upload Artifacts + uses: actions/upload-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: target/release/liblibrustdesk.so + + build-rustdesk-sciter-arm: + needs: [build-vcpkg-deps-linux] + name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + # use a high level qemu-user-static + job: + - { + arch: armv7, + target: armv7-unknown-linux-gnueabihf, + deb-arch: armhf, + os: ubuntu-latest, + use-cross: true, + extra-build-features: "", + } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } + steps: + + - name: Maximize build space + run: | + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo apt update -y + sudo apt install qemu-user-static + + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 12 + + - name: Free Space + run: | + df + + - 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) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + cache-directories: "/opt/rust-registry" + + - name: Install local registry + run: | + mkdir -p /opt/rust-registry + cargo install cargo-local-registry + + - name: Build local registry + uses: nick-fields/retry@v2 + id: build-local-registry + continue-on-error: true + with: + max_attempts: 3 + timeout_minutes: 15 + retry_on: error + command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry + + - name: Restore vcpkg files + uses: actions/download-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: /opt/artifacts/vcpkg/installed + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk sciter binary for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/rust-registry:/opt/rust-registry" + shell: /bin/bash + install: | + apt update -y + apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev + apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev + apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # rust + pushd /opt + wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz + tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz + cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh + rm -rf rust-1.64.0-${{ matrix.job.target }} + # edit config + mkdir -p ~/.cargo/ + echo """ + [source.crates-io] + registry = 'https://github.com/rust-lang/crates.io-index' + replace-with = 'local-registry' + + [source.local-registry] + local-registry = '/opt/rust-registry/' + """ > ~/.cargo/config + cat ~/.cargo/config + + # build + pushd /workspace + python3 ./res/inline-sciter.py + export VCPKG_ROOT=/opt/artifacts/vcpkg + export ARCH=armhf + cargo build --features inline --release --bins + # package + mkdir -p ./Release + mv ./target/release/rustdesk ./Release/rustdesk + wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/arm32/libsciter-gtk.so + ./build.py --package ./Release + + - name: Rename rustdesk + shell: bash + run: | + for name in rustdesk*??.deb; do + # use cp to duplicate deb files to fit other packages. + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb" + done + + - name: Publish debian package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + + - name: Upload Artifact + uses: actions/upload-artifact@master + if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + + build-rustdesk-linux-arm: + needs: [build-rustdesk-lib-linux-arm] + name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ubuntu-20.04 # 20.04 has more performance on arm build + strategy: + fail-fast: false + matrix: + job: + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-18.04, # just for naming package, not running host + use-cross: true, + extra-build-features: "", + } + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-18.04, # just for naming package, not running host + use-cross: true, + extra-build-features: "appimage", + } + # - { + # arch: aarch64, + # target: aarch64-unknown-linux-gnu, + # os: ubuntu-18.04, # just for naming package, not running host + # use-cross: true, + # extra-build-features: "flatpak", + # } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Prepare env + run: | + sudo apt update -y + sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools + mkdir -p ./target/release/ + + - name: Restore the rustdesk lib file + uses: actions/download-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: ./target/release/ + + - name: Download Flutter + shell: bash + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + pushd /opt + # clone repo and reset to flutter 3.7.0 + git clone https://github.com/sony/flutter-elinux.git || true + pushd flutter-elinux + # reset to flutter 3.7.0 + git fetch + git reset --hard 51a1d685901f79fbac51665a967c3a1a789ecee5 + popd + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk binary for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/flutter-elinux:/opt/flutter-elinux" + shell: /bin/bash + install: | + apt update -y + apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + pushd /workspace + # we use flutter-elinux to build our rustdesk + export PATH=/opt/flutter-elinux/bin:$PATH + sed -i "s/flutter build linux --release/flutter-elinux build linux/g" ./build.py + # Setup flutter-elinux. Run doctor to check if issues here. + flutter-elinux doctor -v + # Patch arm64 engine for flutter 3.6.0+ + flutter-elinux precache --linux + pushd /tmp + curl -O https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.7.0-stable.tar.xz + tar -xvf flutter_linux_3.7.0-stable.tar.xz flutter/bin/cache/artifacts/engine/linux-x64/shader_lib + cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib /opt/flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64 + popd + # edit to corresponding arch + case ${{ matrix.job.arch }} in + aarch64) + sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py + sed -i "s/x64\/release/arm64\/release/g" ./build.py + ;; + armv7) + sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py + sed -i "s/x64\/release/arm\/release/g" ./build.py + ;; + esac + python3 ./build.py --flutter --hwcodec --skip-cargo + # rpm package + echo -e "start packaging fedora package" + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec + sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter.spec + ;; + aarch64) + sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" + done + # rpm suse package + echo -e "start packaging suse package" + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec + sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter-suse.spec + ;; + aarch64) + sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter-suse.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" + done + + - name: Rename rustdesk + shell: bash + run: | + for name in rustdesk*??.deb; do + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + done + + - name: Publish debian package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Build appimage package + if: ${{ matrix.job.extra-build-features == 'appimage' }} + shell: bash + run: | + # set-up appimage-builder + pushd /tmp + wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage + chmod +x appimage-builder-x86_64.AppImage + sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder + popd + # run appimage-builder + pushd appimage + sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml + + - name: Publish appimage package + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage + + - name: Upload Artifact + uses: actions/upload-artifact@master + if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Patch archlinux PKGBUILD + if: ${{ matrix.job.extra-build-features == '' }} + run: | + sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/linux\/x64/linux\/arm/g" ./res/PKGBUILD + ;; + aarch64) + sed -i "s/linux\/x64/linux\/arm64/g" ./res/PKGBUILD + ;; + esac + + # Temporary disable for there is no many archlinux arm hosts + # - name: Build archlinux package + # if: ${{ matrix.job.extra-build-features == '' }} + # uses: vufa/arch-makepkg-action@master + # with: + # packages: > + # llvm + # clang + # libva + # libvdpau + # rust + # gstreamer + # unzip + # git + # cmake + # gcc + # curl + # wget + # yasm + # nasm + # zip + # make + # pkg-config + # clang + # gtk3 + # xdotool + # libxcb + # libxfixes + # alsa-lib + # pipewire + # python + # ttf-arphic-uming + # libappindicator-gtk3 + # scripts: | + # cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f + + # - name: Publish archlinux package + # if: ${{ matrix.job.extra-build-features == '' }} + # uses: softprops/action-gh-release@v1 + # with: + # prerelease: true + # tag_name: ${{ env.TAG_NAME }} + # files: | + # res/rustdesk*.zst + + - name: Publish fedora28/centos8 package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + /opt/artifacts/rpm/*.rpm + + build-rustdesk-linux-amd64: + needs: [build-rustdesk-lib-linux-amd64] + name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } + # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-18.04, + extra-build-features: "", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-18.04, + extra-build-features: "flatpak", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-18.04, + extra-build-features: "appimage", + } + # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Prepare env + run: | + sudo apt update -y + sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools + mkdir -p ./target/release/ + + - name: Restore the rustdesk lib file + uses: actions/download-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: ./target/release/ + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk binary for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04 + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + shell: /bin/bash + install: | + apt update -y + apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # Setup Flutter + pushd /opt + wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz + tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz + ls -l . + export PATH=/opt/flutter/bin:$PATH + flutter doctor -v + pushd /workspace + python3 ./build.py --flutter --hwcodec --skip-cargo + # rpm package + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" + done + # rpm suse package + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" + done + + - name: Rename rustdesk + shell: bash + run: | + for name in rustdesk*??.deb; do + # use cp to duplicate deb files to fit other packages. + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + done + + - name: Publish debian package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Upload Artifact + uses: actions/upload-artifact@master + if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Patch archlinux PKGBUILD + if: ${{ matrix.job.extra-build-features == '' }} + run: | + sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD + + - name: Build archlinux package + if: ${{ matrix.job.extra-build-features == '' }} + uses: vufa/arch-makepkg-action@master + with: + packages: > + llvm + clang + libva + libvdpau + rust + gstreamer + unzip + git + cmake + gcc + curl + wget + yasm + nasm + zip + make + pkg-config + clang + gtk3 + xdotool + libxcb + libxfixes + alsa-lib + pipewire + python + ttf-arphic-uming + libappindicator-gtk3 + scripts: | + cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f + + - name: Publish archlinux package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + res/rustdesk*.zst + + - name: Build appimage package + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' + shell: bash + run: | + # set-up appimage-builder + pushd /tmp + wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage + chmod +x appimage-builder-x86_64.AppImage + sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder + popd + # run appimage-builder + pushd appimage + sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml + + - name: Publish appimage package + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage + + - name: Publish fedora28/centos8 package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + /opt/artifacts/rpm/*.rpm + + # Temporary disable flatpak arm build + # + # build-flatpak-arm: + # name: Build Flatpak + # needs: [build-rustdesk-linux-arm] + # runs-on: ${{ matrix.job.os }} + # strategy: + # fail-fast: false + # matrix: + # job: + # # - { target: aarch64-unknown-linux-gnu , os: ubuntu-18.04, arch: arm64 } + # - { target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, arch: arm64 } + # steps: + # - name: Checkout source code + # uses: actions/checkout@v3 + + # - name: Download Binary + # uses: actions/download-artifact@master + # with: + # name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + # path: . + + # - name: Rename Binary + # run: | + # mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb + + # - uses: Kingtous/run-on-arch-action@amd64-support + # name: Build rustdesk flatpak package for ${{ matrix.job.arch }} + # id: rpm + # with: + # arch: ${{ matrix.job.arch }} + # distro: ubuntu18.04 + # githubToken: ${{ github.token }} + # setup: | + # ls -l "${PWD}" + # dockerRunArgs: | + # --volume "${PWD}:/workspace" + # shell: /bin/bash + # install: | + # apt update -y + # apt install -y rpm + # run: | + # pushd /workspace + # # install + # apt update -y + # apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git + # # flatpak deps + # flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + # flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 + # flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 + # # package + # pushd flatpak + # git clone https://github.com/flathub/shared-modules.git --depth=1 + # flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json + # flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak org.rustdesk.rustdesk + + # - name: Publish flatpak package + # uses: softprops/action-gh-release@v1 + # with: + # prerelease: true + # tag_name: ${{ env.TAG_NAME }} + # files: | + # flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak + + build-flatpak-amd64: + name: Build Flatpak + needs: [build-rustdesk-linux-amd64] + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + job: + - { target: x86_64-unknown-linux-gnu, os: ubuntu-18.04, arch: x86_64 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Download Binary + uses: actions/download-artifact@master + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + path: . + + - name: Rename Binary + run: | + mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk flatpak package for ${{ matrix.job.arch }} + id: rpm + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04 + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + shell: /bin/bash + install: | + apt update -y + apt install -y rpm git wget curl + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + pushd /workspace + # install + apt update -y + apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git + # flatpak deps + flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 + flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 + # package + pushd flatpak + git clone https://github.com/flathub/shared-modules.git --depth=1 + flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json + flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak com.rustdesk.RustDesk + + - name: Publish flatpak package + uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index 14027f9f8..d40d6f736 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -18,7 +18,7 @@ on: jobs: run-ci: - uses: ./.github/workflows/flutter-nightly.yml + uses: ./.github/workflows/flutter-build.yml with: upload-artifact: false \ No newline at end of file diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index e5c2c2752..ab625e431 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -5,1613 +5,10 @@ on: # schedule build every night - cron: "0 0 * * *" workflow_dispatch: - inputs: - upload-artifact: - description: "Upload the artifact produced by this workflow to the Release page." - type: boolean - default: true - workflow_call: - inputs: - upload-artifact: - type: boolean - default: true - -env: - LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.7.0" - TAG_NAME: "nightly" - # vcpkg version: 2022.05.10 - # for multiarch gcc compatibility - VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44" - VERSION: "1.2.0" - NDK_VERSION: "r23" - #signing keys env variable checks - ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}' - MACOS_P12_BASE64: '${{ secrets.MACOS_P12_BASE64 }}' - # To make a custom build with your own servers set the below secret values - RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}' - RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}' - UPLOAD_ARTIFACT: '${{ inputs.upload-artifact }}' jobs: - build-for-windows-flutter: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - # - { target: i686-pc-windows-msvc , os: windows-2019 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: x86_64-pc-windows-msvc, os: windows-2019 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 - with: - version: ${{ env.LLVM_VERSION }} - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - name: Replace engine with rustdesk custom flutter engine - run: | - flutter doctor -v - flutter precache --windows - Invoke-WebRequest -Uri https://github.com/Kingtous/engine/releases/download/v3.7.0-rustdesk/windows-x64-release-flutter.zip -OutFile windows-x64-flutter-release.zip - Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine - mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/ - - - 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) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }} - - - name: Install flutter rust bridge deps - run: | - cargo install flutter_rust_bridge_codegen - Push-Location flutter ; flutter pub get ; Pop-Location - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Install vcpkg dependencies - run: | - cd C:\ - git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 - - - name: Build rustdesk - env: - VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg - run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver - - - name: Sign rustdesk files - uses: GermanBluefox/code-sign-action@v7 - if: env.UPLOAD_ARTIFACT == 'true' - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.CERTNAME }}' - folder: './flutter/build/windows/runner/Release/' - recursive: true - - - name: Build self-extracted executable - shell: bash - if: env.UPLOAD_ARTIFACT == 'true' - run: | - pushd ./libs/portable - python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe - popd - mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe - - # - name: Rename rustdesk - # shell: bash - # run: | - # for name in rustdesk*??-install.exe; do - # mv "$name" ./SignOutput/"${name%%-install.exe}-${{ matrix.job.target }}.exe" - # done - - - name: Sign rustdesk self-extracted file - uses: GermanBluefox/code-sign-action@v7 - if: env.UPLOAD_ARTIFACT == 'true' - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' - folder: './SignOutput' - recursive: false - - - name: Publish Release - uses: softprops/action-gh-release@v1 - if: env.UPLOAD_ARTIFACT == 'true' - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./SignOutput/rustdesk-*.exe - - # The fallback for the flutter version, we use Sciter for 32bit Windows. - build-for-windows-sciter: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) - runs-on: ${{ matrix.job.os }} - # Temporarily disable this action due to additional test is needed. - # if: false - strategy: - fail-fast: false - matrix: - job: - # - { target: i686-pc-windows-msvc , os: windows-2019 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: i686-pc-windows-msvc, os: windows-2019 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install LLVM and Clang - uses: Kingtous/install-llvm-action-32bit@master - with: - version: ${{ env.LLVM_VERSION }} - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-${{ matrix.job.target }} - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }}-sciter - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - cd C:\ - git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 - - - name: Build rustdesk - id: build - shell: bash - env: - VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg - run: | - python3 res/inline-sciter.py - # Patch sciter x86 - sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml - # Replace the link for the ico. - rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico - cargo build --features inline --release --bins - mkdir -p ./Release - mv ./target/release/rustdesk.exe ./Release/rustdesk.exe - curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll - echo "output_folder=./Release" >> $GITHUB_OUTPUT - - - name: Sign rustdesk files - uses: GermanBluefox/code-sign-action@v7 - if: env.UPLOAD_ARTIFACT == 'true' - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.CERTNAME }}' - folder: './Release/' - recursive: true - - - name: Build self-extracted executable - shell: bash - run: | - pushd ./libs/portable - pip3 install -r requirements.txt - python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe - popd - mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe - - - name: Sign rustdesk self-extracted file - uses: GermanBluefox/code-sign-action@v7 - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' - folder: './SignOutput' - recursive: false - - - name: Publish Release - uses: softprops/action-gh-release@v1 - if: env.UPLOAD_ARTIFACT == 'true' - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./SignOutput/rustdesk-*.exe - - build-for-macOS: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { - target: x86_64-apple-darwin, - os: macos-latest, - extra-build-args: "", - } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Import the codesign cert - if: env.MACOS_P12_BASE64 != null - uses: apple-actions/import-codesign-certs@v1 - with: - p12-file-base64: ${{ secrets.MACOS_P12_BASE64 }} - p12-password: ${{ secrets.MACOS_P12_PASSWORD }} - keychain: rustdesk - - - name: Check sign and import sign key - if: env.MACOS_P12_BASE64 != null - run: | - security default-keychain -s rustdesk.keychain - security find-identity -v - - - name: Import notarize key - if: env.MACOS_P12_BASE64 != null - uses: timheuer/base64-to-file@v1.2 - with: - # https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_rcodesign.html#notarizing-and-stapling - fileName: rustdesk.json - fileDir: ${{ github.workspace }} - encodedString: ${{ secrets.MACOS_NOTARIZE_JSON }} - - - name: Install rcodesign tool - if: env.MACOS_P12_BASE64 != null - shell: bash - run: | - pushd /tmp - wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz - tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz - mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin - popd - - - name: Install build runtime - run: | - brew install llvm create-dmg nasm yasm cmake gcc wget ninja pkg-config - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - - - 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) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }} - - - name: Install flutter rust bridge deps - shell: bash - run: | - cargo install flutter_rust_bridge_codegen - pushd flutter && flutter pub get && popd - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - $VCPKG_ROOT/vcpkg install libvpx libyuv opus - - - name: Show version information (Rust, cargo, Clang) - shell: bash - run: | - clang --version || true - rustup -V - rustup toolchain list - rustup default - cargo -V - rustc -V - - - name: Build rustdesk - run: | - # --hwcodec not supported on macos yet - ./build.py --flutter ${{ matrix.job.extra-build-args }} - - - name: Codesign app and create signed dmg - if: env.MACOS_P12_BASE64 != null - run: | - security default-keychain -s rustdesk.keychain - security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain - # start sign the rustdesk.app and dmg - rm rustdesk-${{ env.VERSION }}.dmg || true - codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict ./flutter/build/macos/Build/Products/Release/RustDesk.app -vvv - create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app - codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict rustdesk-${{ env.VERSION }}.dmg -vvv - # notarize the rustdesk-${{ env.VERSION }}.dmg - rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json --staple rustdesk-${{ env.VERSION }}.dmg - - - name: Rename rustdesk - run: | - for name in rustdesk*??.dmg; do - mv "$name" "${name%%.dmg}-${{ matrix.job.target }}.dmg" - done - - - name: Publish DMG package - if: env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk*-${{ matrix.job.target }}.dmg - - build-vcpkg-deps-linux: - uses: ./.github/workflows/vcpkg-deps-linux.yml - - generate-bridge-linux: - uses: ./.github/workflows/bridge.yml - - build-rustdesk-android: - needs: [generate-bridge-linux] - name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { - arch: x86_64, - target: aarch64-linux-android, - os: ubuntu-20.04, - extra-build-features: "", - openssl-arch: android-arm64 - } - - { - arch: x86_64, - target: armv7-linux-androideabi, - os: ubuntu-18.04, - extra-build-features: "", - openssl-arch: android-arm - } - steps: - - name: Install dependencies - run: | - sudo apt update - sudo apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless - - name: Checkout source code - uses: actions/checkout@v3 - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - - uses: nttld/setup-ndk@v1 - id: setup-ndk - with: - ndk-version: ${{ env.NDK_VERSION }} - add-to-path: true - - - name: Clone deps - shell: bash - run: | - pushd /opt - git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - - - name: Build rustdesk lib - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} - VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg - run: | - rustup target add ${{ matrix.job.target }} - cargo install cargo-ndk - case ${{ matrix.job.target }} in - aarch64-linux-android) - ./flutter/ndk_arm64.sh - mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - ;; - armv7-linux-androideabi) - ./flutter/ndk_arm.sh - mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - ;; - esac - - - name: Build rustdesk - shell: bash - env: - JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64 - run: | - export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH - # temporary use debug sign config - sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle - case ${{ matrix.job.target }} in - aarch64-linux-android) - mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a - cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - # build flutter - pushd flutter - flutter build apk --release --target-platform android-arm64 --split-per-abi - mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - ;; - armv7-linux-androideabi) - mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a - cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - # build flutter - pushd flutter - flutter build apk --release --target-platform android-arm --split-per-abi - mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - ;; - esac - popd - mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk . - - - uses: r0adkll/sign-android-release@v1 - name: Sign app APK - if: env.ANDROID_SIGNING_KEY != null - id: sign-rustdesk - with: - releaseDirectory: ./signed-apk - signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }} - alias: ${{ secrets.ANDROID_ALIAS }} - keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} - keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }} - env: - # override default build-tools version (29.0.3) -- optional - BUILD_TOOLS_VERSION: "30.0.2" - - - name: Upload Artifacts - if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' - uses: actions/upload-artifact@master - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk - path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - - - name: Publish signed apk package - if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - - - name: Publish unsigned apk package - if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - - build-rustdesk-lib-linux-amd64: - needs: [generate-bridge-linux, build-vcpkg-deps-linux] - name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - # use a high level qemu-user-static - job: - # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "flatpak", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "appimage", - } - # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - steps: - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - 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) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - # only build cdylib - sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk library for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - # not ready yet - # distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - ls -l /opt/artifacts/vcpkg/installed - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - echo -e "installing deps" - apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null - # we have libopus compiled by us. - apt remove -y libopus-dev || true - # output devs - ls -l ./ - tree -L 3 /opt/artifacts/vcpkg/installed - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - # start build - pushd /workspace - # mock - case "${{ matrix.job.arch }}" in - x86_64) - # no need mock on x86_64 - export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release - ;; - esac - - - name: Upload Artifacts - uses: actions/upload-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: target/release/liblibrustdesk.so - - build-rustdesk-lib-linux-arm: - needs: [generate-bridge-linux, build-vcpkg-deps-linux] - name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - # use a high level qemu-user-static - job: - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-20.04, - use-cross: true, - extra-build-features: "", - } - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "appimage", - } - # - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } - # - { - # arch: armv7, - # target: armv7-unknown-linux-gnueabihf, - # os: ubuntu-20.04, - # use-cross: true, - # extra-build-features: "", - # } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - 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) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - # only build cdylib - sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk library for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - ls -l /opt/artifacts/vcpkg/installed - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - echo -e "installing deps" - apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null - # we have libopus compiled by us. - apt remove -y libopus-dev || true - # output devs - ls -l ./ - tree -L 3 /opt/artifacts/vcpkg/installed - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - # start build - pushd /workspace - export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release - - - name: Upload Artifacts - uses: actions/upload-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: target/release/liblibrustdesk.so - - build-rustdesk-sciter-arm: - needs: [build-vcpkg-deps-linux] - name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - # use a high level qemu-user-static - job: - - { - arch: armv7, - target: armv7-unknown-linux-gnueabihf, - deb-arch: armhf, - os: ubuntu-latest, - use-cross: true, - extra-build-features: "", - } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - 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) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk sciter binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev - apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev - apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - - # build - pushd /workspace - python3 ./res/inline-sciter.py - export VCPKG_ROOT=/opt/artifacts/vcpkg - export ARCH=armhf - cargo build --features inline --release --bins - # package - mkdir -p ./Release - mv ./target/release/rustdesk ./Release/rustdesk - wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/arm32/libsciter-gtk.so - ./build.py --package ./Release - - - name: Rename rustdesk - shell: bash - run: | - for name in rustdesk*??.deb; do - # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb" - done - - - name: Publish debian package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - - - name: Upload Artifact - uses: actions/upload-artifact@master - if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - - build-rustdesk-linux-arm: - needs: [build-rustdesk-lib-linux-arm] - name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ubuntu-20.04 # 20.04 has more performance on arm build - strategy: - fail-fast: false - matrix: - job: - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "", - } - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "appimage", - } - # - { - # arch: aarch64, - # target: aarch64-unknown-linux-gnu, - # os: ubuntu-18.04, # just for naming package, not running host - # use-cross: true, - # extra-build-features: "flatpak", - # } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Prepare env - run: | - sudo apt update -y - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools - mkdir -p ./target/release/ - - - name: Restore the rustdesk lib file - uses: actions/download-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: ./target/release/ - - - name: Download Flutter - shell: bash - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /opt - # clone repo and reset to flutter 3.7.0 - git clone https://github.com/sony/flutter-elinux.git || true - pushd flutter-elinux - # reset to flutter 3.7.0 - git fetch - git reset --hard 51a1d685901f79fbac51665a967c3a1a789ecee5 - popd - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/flutter-elinux:/opt/flutter-elinux" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /workspace - # we use flutter-elinux to build our rustdesk - export PATH=/opt/flutter-elinux/bin:$PATH - sed -i "s/flutter build linux --release/flutter-elinux build linux/g" ./build.py - # Setup flutter-elinux. Run doctor to check if issues here. - flutter-elinux doctor -v - # Patch arm64 engine for flutter 3.6.0+ - flutter-elinux precache --linux - pushd /tmp - curl -O https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.7.0-stable.tar.xz - tar -xvf flutter_linux_3.7.0-stable.tar.xz flutter/bin/cache/artifacts/engine/linux-x64/shader_lib - cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib /opt/flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64 - popd - # edit to corresponding arch - case ${{ matrix.job.arch }} in - aarch64) - sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py - sed -i "s/x64\/release/arm64\/release/g" ./build.py - ;; - armv7) - sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py - sed -i "s/x64\/release/arm\/release/g" ./build.py - ;; - esac - python3 ./build.py --flutter --hwcodec --skip-cargo - # rpm package - echo -e "start packaging fedora package" - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec - sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter.spec - ;; - aarch64) - sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" - done - # rpm suse package - echo -e "start packaging suse package" - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec - sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter-suse.spec - ;; - aarch64) - sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter-suse.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" - done - - - name: Rename rustdesk - shell: bash - run: | - for name in rustdesk*??.deb; do - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" - done - - - name: Publish debian package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Build appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} - shell: bash - run: | - # set-up appimage-builder - pushd /tmp - wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage - chmod +x appimage-builder-x86_64.AppImage - sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder - popd - # run appimage-builder - pushd appimage - sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml - - - name: Publish appimage package - if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage - - - name: Upload Artifact - uses: actions/upload-artifact@master - if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Patch archlinux PKGBUILD - if: ${{ matrix.job.extra-build-features == '' }} - run: | - sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/linux\/x64/linux\/arm/g" ./res/PKGBUILD - ;; - aarch64) - sed -i "s/linux\/x64/linux\/arm64/g" ./res/PKGBUILD - ;; - esac - - # Temporary disable for there is no many archlinux arm hosts - # - name: Build archlinux package - # if: ${{ matrix.job.extra-build-features == '' }} - # uses: vufa/arch-makepkg-action@master - # with: - # packages: > - # llvm - # clang - # libva - # libvdpau - # rust - # gstreamer - # unzip - # git - # cmake - # gcc - # curl - # wget - # yasm - # nasm - # zip - # make - # pkg-config - # clang - # gtk3 - # xdotool - # libxcb - # libxfixes - # alsa-lib - # pipewire - # python - # ttf-arphic-uming - # libappindicator-gtk3 - # scripts: | - # cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f - - # - name: Publish archlinux package - # if: ${{ matrix.job.extra-build-features == '' }} - # uses: softprops/action-gh-release@v1 - # with: - # prerelease: true - # tag_name: ${{ env.TAG_NAME }} - # files: | - # res/rustdesk*.zst - - - name: Publish fedora28/centos8 package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - /opt/artifacts/rpm/*.rpm - - build-rustdesk-linux-amd64: - needs: [build-rustdesk-lib-linux-amd64] - name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - job: - # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, - extra-build-features: "", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, - extra-build-features: "flatpak", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, - extra-build-features: "appimage", - } - # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Prepare env - run: | - sudo apt update -y - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools - mkdir -p ./target/release/ - - - name: Restore the rustdesk lib file - uses: actions/download-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: ./target/release/ - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # Setup Flutter - pushd /opt - wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz - tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz - ls -l . - export PATH=/opt/flutter/bin:$PATH - flutter doctor -v - pushd /workspace - python3 ./build.py --flutter --hwcodec --skip-cargo - # rpm package - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" - done - # rpm suse package - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" - done - - - name: Rename rustdesk - shell: bash - run: | - for name in rustdesk*??.deb; do - # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" - done - - - name: Publish debian package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Upload Artifact - uses: actions/upload-artifact@master - if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Patch archlinux PKGBUILD - if: ${{ matrix.job.extra-build-features == '' }} - run: | - sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD - - - name: Build archlinux package - if: ${{ matrix.job.extra-build-features == '' }} - uses: vufa/arch-makepkg-action@master - with: - packages: > - llvm - clang - libva - libvdpau - rust - gstreamer - unzip - git - cmake - gcc - curl - wget - yasm - nasm - zip - make - pkg-config - clang - gtk3 - xdotool - libxcb - libxfixes - alsa-lib - pipewire - python - ttf-arphic-uming - libappindicator-gtk3 - scripts: | - cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f - - - name: Publish archlinux package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - res/rustdesk*.zst - - - name: Build appimage package - if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' - shell: bash - run: | - # set-up appimage-builder - pushd /tmp - wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage - chmod +x appimage-builder-x86_64.AppImage - sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder - popd - # run appimage-builder - pushd appimage - sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml - - - name: Publish appimage package - if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage - - - name: Publish fedora28/centos8 package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - /opt/artifacts/rpm/*.rpm - - # Temporary disable flatpak arm build - # - # build-flatpak-arm: - # name: Build Flatpak - # needs: [build-rustdesk-linux-arm] - # runs-on: ${{ matrix.job.os }} - # strategy: - # fail-fast: false - # matrix: - # job: - # # - { target: aarch64-unknown-linux-gnu , os: ubuntu-18.04, arch: arm64 } - # - { target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, arch: arm64 } - # steps: - # - name: Checkout source code - # uses: actions/checkout@v3 - - # - name: Download Binary - # uses: actions/download-artifact@master - # with: - # name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - # path: . - - # - name: Rename Binary - # run: | - # mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb - - # - uses: Kingtous/run-on-arch-action@amd64-support - # name: Build rustdesk flatpak package for ${{ matrix.job.arch }} - # id: rpm - # with: - # arch: ${{ matrix.job.arch }} - # distro: ubuntu18.04 - # githubToken: ${{ github.token }} - # setup: | - # ls -l "${PWD}" - # dockerRunArgs: | - # --volume "${PWD}:/workspace" - # shell: /bin/bash - # install: | - # apt update -y - # apt install -y rpm - # run: | - # pushd /workspace - # # install - # apt update -y - # apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git - # # flatpak deps - # flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - # flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 - # flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 - # # package - # pushd flatpak - # git clone https://github.com/flathub/shared-modules.git --depth=1 - # flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json - # flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak org.rustdesk.rustdesk - - # - name: Publish flatpak package - # uses: softprops/action-gh-release@v1 - # with: - # prerelease: true - # tag_name: ${{ env.TAG_NAME }} - # files: | - # flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak - - build-flatpak-amd64: - name: Build Flatpak - needs: [build-rustdesk-linux-amd64] - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - job: - - { target: x86_64-unknown-linux-gnu, os: ubuntu-18.04, arch: x86_64 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Download Binary - uses: actions/download-artifact@master - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: . - - - name: Rename Binary - run: | - mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk flatpak package for ${{ matrix.job.arch }} - id: rpm - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - shell: /bin/bash - install: | - apt update -y - apt install -y rpm git wget curl - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /workspace - # install - apt update -y - apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git - # flatpak deps - flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 - flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 - # package - pushd flatpak - git clone https://github.com/flathub/shared-modules.git --depth=1 - flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json - flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak com.rustdesk.RustDesk - - - name: Publish flatpak package - uses: softprops/action-gh-release@v1 - if: env.UPLOAD_ARTIFACT == 'true' - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak + run-flutter-nightly-build: + uses: ./.github/workflows/flutter-build.yml + with: + upload-artifact: true + \ No newline at end of file From ddd0d6eafc56611be4c45e3e4a60b9ee0fbc9482 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 3 Apr 2023 11:03:21 +0800 Subject: [PATCH 029/112] remove disable-vp8 --- flutter/build_android_deps.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flutter/build_android_deps.sh b/flutter/build_android_deps.sh index a30abd154..2f876ca6e 100755 --- a/flutter/build_android_deps.sh +++ b/flutter/build_android_deps.sh @@ -58,13 +58,12 @@ function build { fi make clean ./configure --target=$LIBVPX_TARGET \ - --enable-pic --disable-vp8 \ + --enable-pic --disable-webm-io \ --disable-unit-tests \ --disable-examples \ --disable-libyuv \ --disable-postproc \ - --disable-vp8 \ --disable-tools \ --disable-docs \ --prefix=$PREFIX @@ -122,4 +121,4 @@ build arm64-v8a arm64-android aarch64-linux-android arm64-android-gcc build armeabi-v7a arm-android arm-linux-androideabi armv7-android-gcc # rm -rf build/libvpx -# rm -rf build/oboe \ No newline at end of file +# rm -rf build/oboe From c842650c11b04dbd8eedeaff5bcc9ffea92b8b31 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 31 Mar 2023 16:10:52 +0800 Subject: [PATCH 030/112] vp8 Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 42 ++- .../lib/desktop/widgets/remote_toolbar.dart | 26 +- flutter/lib/mobile/pages/remote_page.dart | 35 +- libs/hbb_common/protos/message.proto | 16 +- libs/hbb_common/src/config.rs | 5 +- libs/scrap/examples/benchmark.rs | 46 ++- libs/scrap/src/common/android.rs | 1 + libs/scrap/src/common/codec.rs | 319 +++++++++--------- libs/scrap/src/common/hwcodec.rs | 17 +- libs/scrap/src/common/mod.rs | 53 +++ libs/scrap/src/common/record.rs | 58 ++-- libs/scrap/src/common/vpxcodec.rs | 46 +-- src/client.rs | 37 +- src/client/helper.rs | 33 +- src/client/io_loop.rs | 9 +- src/flutter_ffi.rs | 13 +- src/server/connection.rs | 34 +- src/server/video_service.rs | 85 +++-- src/ui/header.tis | 9 +- src/ui/remote.rs | 22 +- src/ui_interface.rs | 7 + src/ui_session_interface.rs | 28 +- 22 files changed, 484 insertions(+), 457 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 66ef83d31..74d51407c 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1258,9 +1258,6 @@ class _DisplayState extends State<_Display> { } Widget codec(BuildContext context) { - if (!bind.mainHasHwcodec()) { - return Offstage(); - } final key = 'codec-preference'; onChanged(String value) async { await bind.mainSetUserDefaultOption(key: key, value: value); @@ -1268,28 +1265,45 @@ class _DisplayState extends State<_Display> { } final groupValue = bind.mainGetUserDefaultOption(key: key); - + var hwRadios = []; + try { + final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings()); + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + if (h264) { + hwRadios.add(_Radio(context, + value: 'h264', + groupValue: groupValue, + label: 'H264', + onChanged: onChanged)); + } + if (h265) { + hwRadios.add(_Radio(context, + value: 'h265', + groupValue: groupValue, + label: 'H265', + onChanged: onChanged)); + } + } catch (e) { + debugPrint("failed to parse supported hwdecodings, err=$e"); + } return _Card(title: 'Default Codec', children: [ _Radio(context, value: 'auto', groupValue: groupValue, label: 'Auto', onChanged: onChanged), + _Radio(context, + value: 'vp8', + groupValue: groupValue, + label: 'VP8', + onChanged: onChanged), _Radio(context, value: 'vp9', groupValue: groupValue, label: 'VP9', onChanged: onChanged), - _Radio(context, - value: 'h264', - groupValue: groupValue, - label: 'H264', - onChanged: onChanged), - _Radio(context, - value: 'h265', - groupValue: groupValue, - label: 'H265', - onChanged: onChanged), + ...hwRadios, ]); } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 703ed6db9..cdbf8be27 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1351,29 +1351,30 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec() { return futureBuilder(future: () async { - final supportedHwcodec = - await bind.sessionSupportedHwcodec(id: widget.id); + final alternativeCodecs = + await bind.sessionAlternativeCodecs(id: widget.id); final codecPreference = await bind.sessionGetOption(id: widget.id, arg: 'codec-preference') ?? ''; return { - 'supportedHwcodec': supportedHwcodec, + 'alternativeCodecs': alternativeCodecs, 'codecPreference': codecPreference }; }(), hasData: (data) { final List codecs = []; try { - final Map codecsJson = jsonDecode(data['supportedHwcodec']); + final Map codecsJson = jsonDecode(data['alternativeCodecs']); + final vp8 = codecsJson['vp8'] ?? false; final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { debugPrint("Show Codec Preference err=$e"); } - final visible = bind.mainHasHwcodec() && - codecs.length == 2 && - (codecs[0] || codecs[1]); + final visible = + codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); if (!visible) return Offstage(); final groupValue = data['codecPreference'] as String; onChanged(String? value) async { @@ -1394,6 +1395,13 @@ class _DisplayMenuState extends State<_DisplayMenu> { onChanged: onChanged, ffi: widget.ffi, ), + _RadioMenuButton( + child: Text(translate('VP8')), + value: 'vp8', + groupValue: groupValue, + onChanged: codecs[0] ? onChanged : null, + ffi: widget.ffi, + ), _RadioMenuButton( child: Text(translate('VP9')), value: 'vp9', @@ -1405,14 +1413,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate('H264')), value: 'h264', groupValue: groupValue, - onChanged: codecs[0] ? onChanged : null, + onChanged: codecs[1] ? onChanged : null, ffi: widget.ffi, ), _RadioMenuButton( child: Text(translate('H265')), value: 'h265', groupValue: groupValue, - onChanged: codecs[1] ? onChanged : null, + onChanged: codecs[2] ? onChanged : null, ffi: widget.ffi, ), ]); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 083cdcd1c..35959f407 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -970,17 +970,17 @@ void showOptions( final perms = gFFI.ffiModel.permissions; final hasHwcodec = bind.mainHasHwcodec(); final List codecs = []; - if (hasHwcodec) { - try { - final Map codecsJson = - jsonDecode(await bind.sessionSupportedHwcodec(id: id)); - final h264 = codecsJson['h264'] ?? false; - final h265 = codecsJson['h265'] ?? false; - codecs.add(h264); - codecs.add(h265); - } catch (e) { - debugPrint("Show Codec Preference err=$e"); - } + try { + final Map codecsJson = + jsonDecode(await bind.sessionAlternativeCodecs(id: id)); + final vp8 = codecsJson['vp8'] ?? false; + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); + codecs.add(h264); + codecs.add(h265); + } catch (e) { + debugPrint("Show Codec Preference err=$e"); } dialogManager.show((setState, close) { @@ -1041,15 +1041,16 @@ void showOptions( const Divider(color: MyTheme.border) ]; - if (hasHwcodec && codecs.length == 2 && (codecs[0] || codecs[1])) { - radios.addAll([ - getRadio(translate('Auto'), 'auto', codec, setCodec), - getRadio('VP9', 'vp9', codec, setCodec), - ]); + if (codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2])) { + radios.add(getRadio(translate('Auto'), 'auto', codec, setCodec)); if (codecs[0]) { + radios.add(getRadio('VP8', 'vp8', codec, setCodec)); + } + radios.add(getRadio('VP9', 'vp9', codec, setCodec)); + if (codecs[1]) { radios.add(getRadio('H264', 'h264', codec, setCodec)); } - if (codecs[1]) { + if (codecs[2]) { radios.add(getRadio('H265', 'h265', codec, setCodec)); } radios.add(const Divider(color: MyTheme.border)); diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index a481ae6c4..cd86111ec 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -24,6 +24,7 @@ message VideoFrame { YUV yuv = 8; EncodedVideoFrames h264s = 10; EncodedVideoFrames h265s = 11; + EncodedVideoFrames vp8s = 12; } } @@ -76,6 +77,7 @@ message Features { message SupportedEncoding { bool h264 = 1; bool h265 = 2; + bool vp8 = 3; } message PeerInfo { @@ -459,18 +461,20 @@ enum ImageQuality { Best = 4; } -message VideoCodecState { +message SupportedDecoding { enum PreferCodec { Auto = 0; - VPX = 1; + VP9 = 1; H264 = 2; H265 = 3; + VP8 = 4; } - int32 score_vpx = 1; - int32 score_h264 = 2; - int32 score_h265 = 3; + int32 ability_vp9 = 1; + int32 ability_h264 = 2; + int32 ability_h265 = 3; PreferCodec prefer = 4; + int32 ability_vp8 = 5; } message OptionMessage { @@ -488,7 +492,7 @@ message OptionMessage { BoolOption disable_audio = 7; BoolOption disable_clipboard = 8; BoolOption enable_file_transfer = 9; - VideoCodecState video_codec_state = 10; + SupportedDecoding supported_decoding = 10; int32 custom_fps = 11; BoolOption disable_keyboard = 12; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6a823c7b7..960074a8f 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -917,7 +917,8 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = + decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } @@ -1356,7 +1357,7 @@ impl UserDefaultConfig { "view_style" => self.get_string(key, "original", vec!["adaptive"]), "scroll_style" => self.get_string(key, "scrollauto", vec!["scrollbar"]), "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), - "codec-preference" => self.get_string(key, "auto", vec!["vp9", "h264", "h265"]), + "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), _ => self diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index 003830f95..ba8dec9f2 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -3,7 +3,8 @@ use hbb_common::env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use scrap::{ codec::{EncoderApi, EncoderCfg}, Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, - VpxVideoCodecId, STRIDE_ALIGN, + VpxVideoCodecId::{self, *}, + STRIDE_ALIGN, }; use std::{io::Write, time::Instant}; @@ -49,7 +50,7 @@ fn main() { "benchmark {}x{} bitrate:{}k hw_pixfmt:{:?}", width, height, bitrate_k, args.flag_hw_pixfmt ); - test_vp9(&yuvs, width, height, bitrate_k, yuv_count); + [VP8, VP9].map(|c| test_vpx(c, &yuvs, width, height, bitrate_k, yuv_count)); #[cfg(feature = "hwcodec")] { use hwcodec::AVPixelFormat; @@ -57,7 +58,7 @@ fn main() { Pixfmt::I420 => AVPixelFormat::AV_PIX_FMT_YUV420P, Pixfmt::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, }; - let yuvs = hw::vp9_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); + let yuvs = hw::vpx_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); hw::test(&yuvs, width, height, bitrate_k, yuv_count, hw_pixfmt); } } @@ -87,13 +88,20 @@ fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { } } -fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, yuv_count: usize) { +fn test_vpx( + codec_id: VpxVideoCodecId, + yuvs: &Vec>, + width: usize, + height: usize, + bitrate_k: usize, + yuv_count: usize, +) { let config = EncoderCfg::VPX(VpxEncoderConfig { width: width as _, height: height as _, timebase: [1, 1000], bitrate: bitrate_k as _, - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }); let mut encoder = VpxEncoder::new(config).unwrap(); @@ -104,35 +112,43 @@ fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, .unwrap(); let _ = encoder.flush().unwrap(); } - println!("vp9 encode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} encode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); // prepare data separately - let mut vp9s = vec![]; + let mut vpxs = vec![]; let start = Instant::now(); for yuv in yuvs { for ref frame in encoder .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) .unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } for ref frame in encoder.flush().unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } } - assert_eq!(vp9s.len(), yuv_count); + assert_eq!(vpxs.len(), yuv_count); let mut decoder = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }) .unwrap(); let start = Instant::now(); - for vp9 in vp9s { - let _ = decoder.decode(&vp9); + for vpx in vpxs { + let _ = decoder.decode(&vpx); let _ = decoder.flush(); } - println!("vp9 decode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} decode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); } #[cfg(feature = "hwcodec")] @@ -267,7 +283,7 @@ mod hw { Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265 } - pub fn vp9_yuv_to_hw_yuv( + pub fn vpx_yuv_to_hw_yuv( yuvs: Vec>, width: usize, height: usize, diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 8daf8e4bb..36d6a8a9b 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -50,6 +50,7 @@ impl crate::TraitCapturer for Capturer { pub enum Frame<'a> { RAW(&'a [u8]), + VP8(&'a [u8]), VP9(&'a [u8]), Empty, } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 9e4b6fce4..3209933b4 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -1,7 +1,6 @@ -use std::ops::{Deref, DerefMut}; -#[cfg(feature = "hwcodec")] use std::{ collections::HashMap, + ops::{Deref, DerefMut}, sync::{Arc, Mutex}, }; @@ -11,30 +10,31 @@ use crate::hwcodec::*; use crate::mediacodec::{ MediaCodecDecoder, MediaCodecDecoders, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT, }; -use crate::{vpxcodec::*, ImageFormat}; +use crate::{vpxcodec::*, CodecName, ImageFormat}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::sysinfo::{System, SystemExt}; use hbb_common::{ anyhow::anyhow, + config::PeerConfig, log, - message_proto::{video_frame, EncodedVideoFrames, Message, VideoCodecState}, + message_proto::{ + supported_decoding::PreferCodec, video_frame, EncodedVideoFrames, Message, + SupportedDecoding, SupportedEncoding, + }, ResultType, }; #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] -use hbb_common::{ - config::{Config2, PeerConfig}, - lazy_static, - message_proto::video_codec_state::PreferCodec, -}; +use hbb_common::{config::Config2, lazy_static}; -#[cfg(feature = "hwcodec")] lazy_static::lazy_static! { - static ref PEER_DECODER_STATES: Arc>> = Default::default(); + static ref PEER_DECODINGS: Arc>> = Default::default(); + static ref CODEC_NAME: Arc> = Arc::new(Mutex::new(CodecName::VP9)); } -const SCORE_VPX: i32 = 90; #[derive(Debug, Clone)] pub struct HwEncoderConfig { - pub codec_name: String, + pub name: String, pub width: usize, pub height: usize, pub bitrate: i32, @@ -58,10 +58,6 @@ pub trait EncoderApi { fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()>; } -pub struct DecoderCfg { - pub vpx: VpxDecoderConfig, -} - pub struct Encoder { pub codec: Box, } @@ -81,7 +77,8 @@ impl DerefMut for Encoder { } pub struct Decoder { - vpx: VpxDecoder, + vp8: VpxDecoder, + vp9: VpxDecoder, #[cfg(feature = "hwcodec")] hw: HwDecoders, #[cfg(feature = "hwcodec")] @@ -91,10 +88,10 @@ pub struct Decoder { } #[derive(Debug, Clone)] -pub enum EncoderUpdate { - State(VideoCodecState), +pub enum EncodingUpdate { + New(SupportedDecoding), Remove, - DisableHwIfNotExist, + NewOnlyVP9, } impl Encoder { @@ -120,172 +117,156 @@ impl Encoder { } } - // TODO - pub fn update_video_encoder(id: i32, update: EncoderUpdate) { + pub fn update(id: i32, update: EncodingUpdate) { + let mut decodings = PEER_DECODINGS.lock().unwrap(); + match update { + EncodingUpdate::New(decoding) => { + decodings.insert(id, decoding); + } + EncodingUpdate::Remove => { + decodings.remove(&id); + } + EncodingUpdate::NewOnlyVP9 => { + decodings.insert( + id, + SupportedDecoding { + ability_vp9: 1, + ..Default::default() + }, + ); + } + } + + let vp8_useable = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_vp8 > 0); + #[allow(unused_mut)] + let mut h264_name = None; + #[allow(unused_mut)] + let mut h265_name = None; #[cfg(feature = "hwcodec")] { - let mut states = PEER_DECODER_STATES.lock().unwrap(); - match update { - EncoderUpdate::State(state) => { - states.insert(id, state); - } - EncoderUpdate::Remove => { - states.remove(&id); - } - EncoderUpdate::DisableHwIfNotExist => { - if !states.contains_key(&id) { - states.insert(id, VideoCodecState::default()); - } - } + let best = HwEncoder::best(); + let h264_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); + let h265_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); + if h264_useable { + h264_name = best.h264.map_or(None, |c| Some(c.name)); } - let name = HwEncoder::current_name(); - if states.len() > 0 { - let best = HwEncoder::best(); - let enabled_h264 = best.h264.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h264 > 0); - let enabled_h265 = best.h265.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h265 > 0); - - // Preference first - let mut preference = PreferCodec::Auto; - let preferences: Vec<_> = states - .iter() - .filter(|(_, s)| { - s.prefer == PreferCodec::VPX.into() - || s.prefer == PreferCodec::H264.into() && enabled_h264 - || s.prefer == PreferCodec::H265.into() && enabled_h265 - }) - .map(|(_, s)| s.prefer) - .collect(); - if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { - preference = preferences[0].enum_value_or(PreferCodec::Auto); - } - - match preference { - PreferCodec::VPX => *name.lock().unwrap() = None, - PreferCodec::H264 => { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)) - } - PreferCodec::H265 => { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)) - } - PreferCodec::Auto => { - // score encoder - let mut score_vpx = SCORE_VPX; - let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); - let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); - - // score decoder - score_vpx += states.iter().map(|s| s.1.score_vpx).sum::(); - if enabled_h264 { - score_h264 += states.iter().map(|s| s.1.score_h264).sum::(); - } - if enabled_h265 { - score_h265 += states.iter().map(|s| s.1.score_h265).sum::(); - } - - if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)); - } else if enabled_h264 - && score_h264 >= score_vpx - && score_h264 >= score_h265 - { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)); - } else { - *name.lock().unwrap() = None; - } - } - } - - log::info!( - "connection count:{}, used preference:{:?}, encoder:{:?}", - states.len(), - preference, - name.lock().unwrap() - ) - } else { - *name.lock().unwrap() = None; + if h265_useable { + h265_name = best.h265.map_or(None, |c| Some(c.name)); } } - #[cfg(not(feature = "hwcodec"))] - { - let _ = id; - let _ = update; + + let mut name = CODEC_NAME.lock().unwrap(); + let mut preference = PreferCodec::Auto; + let preferences: Vec<_> = decodings + .iter() + .filter(|(_, s)| { + s.prefer == PreferCodec::VP9.into() + || s.prefer == PreferCodec::VP8.into() && vp8_useable + || s.prefer == PreferCodec::H264.into() && h264_name.is_some() + || s.prefer == PreferCodec::H265.into() && h265_name.is_some() + }) + .map(|(_, s)| s.prefer) + .collect(); + if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { + preference = preferences[0].enum_value_or(PreferCodec::Auto); } + + #[allow(unused_mut)] + let mut auto_codec = CodecName::VP9; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if vp8_useable && System::new_all().total_memory() <= 4 * 1024 * 1024 * 1024 { + // 4 Gb + auto_codec = CodecName::VP8 + } + + match preference { + PreferCodec::VP8 => *name = CodecName::VP8, + PreferCodec::VP9 => *name = CodecName::VP9, + PreferCodec::H264 => *name = h264_name.map_or(auto_codec, |c| CodecName::H264(c)), + PreferCodec::H265 => *name = h265_name.map_or(auto_codec, |c| CodecName::H265(c)), + PreferCodec::Auto => *name = auto_codec, + } + + log::info!( + "connection count:{}, used preference:{:?}, encoder:{:?}", + decodings.len(), + preference, + *name + ) } + #[inline] - pub fn current_hw_encoder_name() -> Option { - #[cfg(feature = "hwcodec")] - if enable_hwcodec_option() { - return HwEncoder::current_name().lock().unwrap().clone(); - } else { - return None; - } - #[cfg(not(feature = "hwcodec"))] - return None; + pub fn negotiated_codec() -> CodecName { + CODEC_NAME.lock().unwrap().clone() } - pub fn supported_encoding() -> (bool, bool) { + pub fn supported_encoding() -> SupportedEncoding { + #[allow(unused_mut)] + let mut encoding = SupportedEncoding { + vp8: true, + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwEncoder::best(); - ( - best.h264.as_ref().map_or(false, |c| c.score > 0), - best.h265.as_ref().map_or(false, |c| c.score > 0), - ) - } else { - (false, false) + encoding.h264 = best.h264.is_some(); + encoding.h265 = best.h265.is_some(); } - #[cfg(not(feature = "hwcodec"))] - (false, false) + encoding } } impl Decoder { - pub fn video_codec_state(_id: &str) -> VideoCodecState { + pub fn supported_decodings(id_for_perfer: Option<&str>) -> SupportedDecoding { + #[allow(unused_mut)] + let mut decoding = SupportedDecoding { + ability_vp8: 1, + ability_vp9: 1, + prefer: id_for_perfer + .map_or(PreferCodec::Auto, |id| Self::codec_preference(id)) + .into(), + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwDecoder::best(); - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264: best.h264.map_or(0, |c| c.score), - score_h265: best.h265.map_or(0, |c| c.score), - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; + decoding.ability_h264 = if best.h264.is_some() { 1 } else { 0 }; + decoding.ability_h265 = if best.h265.is_some() { 1 } else { 0 }; } #[cfg(feature = "mediacodec")] if enable_hwcodec_option() { - let score_h264 = if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 92 - } else { - 0 - }; - let score_h265 = if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 94 - } else { - 0 - }; - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264, - score_h265, - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; - } - VideoCodecState { - score_vpx: SCORE_VPX, - ..Default::default() + decoding.ability_h264 = + if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; + decoding.ability_h265 = + if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; } + decoding } - pub fn new(config: DecoderCfg) -> Decoder { - let vpx = VpxDecoder::new(config.vpx).unwrap(); + pub fn new() -> Decoder { + let vp8 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP8, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); + let vp9 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); Decoder { - vpx, + vp8, + vp9, #[cfg(feature = "hwcodec")] hw: if enable_hwcodec_option() { HwDecoder::new_decoders() @@ -310,8 +291,11 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { + video_frame::Union::Vp8s(vp8s) => { + Decoder::handle_vpxs_video_frame(&mut self.vp8, vp8s, fmt, rgb) + } video_frame::Union::Vp9s(vp9s) => { - Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, fmt, rgb) + Decoder::handle_vpxs_video_frame(&mut self.vp9, vp9s, fmt, rgb) } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { @@ -349,15 +333,15 @@ impl Decoder { } } - fn handle_vp9s_video_frame( + fn handle_vpxs_video_frame( decoder: &mut VpxDecoder, - vp9s: &EncodedVideoFrames, + vpxs: &EncodedVideoFrames, fmt: (ImageFormat, usize), rgb: &mut Vec, ) -> ResultType { let mut last_frame = Image::new(); - for vp9 in vp9s.frames.iter() { - for frame in decoder.decode(&vp9.data)? { + for vpx in vpxs.frames.iter() { + for frame in decoder.decode(&vpx.data)? { drop(last_frame); last_frame = frame; } @@ -408,14 +392,15 @@ impl Decoder { return Ok(false); } - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] fn codec_preference(id: &str) -> PreferCodec { let codec = PeerConfig::load(id) .options .get("codec-preference") .map_or("".to_owned(), |c| c.to_owned()); - if codec == "vp9" { - PreferCodec::VPX + if codec == "vp8" { + PreferCodec::VP8 + } else if codec == "vp9" { + PreferCodec::VP9 } else if codec == "h264" { PreferCodec::H264 } else if codec == "h265" { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 2c69774fb..f4de4bf84 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -7,7 +7,7 @@ use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, - lazy_static, log, + log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, ResultType, }; @@ -19,11 +19,6 @@ use hwcodec::{ Quality::{self, *}, RateControl::{self, *}, }; -use std::sync::{Arc, Mutex}; - -lazy_static::lazy_static! { - static ref HW_ENCODER_NAME: Arc>> = Default::default(); -} const CFG_KEY_ENCODER: &str = "bestHwEncoders"; const CFG_KEY_DECODER: &str = "bestHwDecoders"; @@ -49,7 +44,7 @@ impl EncoderApi for HwEncoder { match cfg { EncoderCfg::HW(config) => { let ctx = EncodeContext { - name: config.codec_name.clone(), + name: config.name.clone(), width: config.width as _, height: config.height as _, pixfmt: DEFAULT_PIXFMT, @@ -60,12 +55,12 @@ impl EncoderApi for HwEncoder { quality: DEFAULT_HW_QUALITY, rc: DEFAULT_RC, }; - let format = match Encoder::format_from_name(config.codec_name.clone()) { + let format = match Encoder::format_from_name(config.name.clone()) { Ok(format) => format, Err(_) => { return Err(anyhow!(format!( "failed to get format from name:{}", - config.codec_name + config.name ))) } }; @@ -133,10 +128,6 @@ impl HwEncoder { }) } - pub fn current_name() -> Arc>> { - HW_ENCODER_NAME.clone() - } - pub fn encode(&mut self, bgra: &[u8]) -> ResultType> { match self.pixfmt { AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420( diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 0ad158cca..26b946401 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -1,4 +1,5 @@ pub use self::vpxcodec::*; +use hbb_common::message_proto::{video_frame, VideoFrame}; cfg_if! { if #[cfg(quartz)] { @@ -92,3 +93,55 @@ pub fn is_cursor_embedded() -> bool { pub fn is_cursor_embedded() -> bool { false } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CodecName { + VP8, + VP9, + H264(String), + H265(String), +} + +#[derive(PartialEq, Debug, Clone)] +pub enum CodecFormat { + VP8, + VP9, + H264, + H265, + Unknown, +} + +impl From<&VideoFrame> for CodecFormat { + fn from(it: &VideoFrame) -> Self { + match it.union { + Some(video_frame::Union::Vp8s(_)) => CodecFormat::VP8, + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, + _ => CodecFormat::Unknown, + } + } +} + +impl From<&CodecName> for CodecFormat { + fn from(value: &CodecName) -> Self { + match value { + CodecName::VP8 => Self::VP8, + CodecName::VP9 => Self::VP9, + CodecName::H264(_) => Self::H264, + CodecName::H265(_) => Self::H265, + } + } +} + +impl ToString for CodecFormat { + fn to_string(&self) -> String { + match self { + CodecFormat::VP8 => "VP8".into(), + CodecFormat::VP9 => "VP9".into(), + CodecFormat::H264 => "H264".into(), + CodecFormat::H265 => "H265".into(), + CodecFormat::Unknown => "Unknow".into(), + } + } +} diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 9f38f2d6a..9de70ae14 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -1,3 +1,4 @@ +use crate::CodecFormat; #[cfg(feature = "hwcodec")] use hbb_common::anyhow::anyhow; use hbb_common::{ @@ -21,13 +22,6 @@ use webm::mux::{self, Segment, Track, VideoTrack, Writer}; const MIN_SECS: u64 = 1; -#[derive(Debug, Clone, PartialEq)] -pub enum RecordCodecID { - VP9, - H264, - H265, -} - #[derive(Debug, Clone)] pub struct RecorderContext { pub server: bool, @@ -36,7 +30,7 @@ pub struct RecorderContext { pub filename: String, pub width: usize, pub height: usize, - pub codec_id: RecordCodecID, + pub format: CodecFormat, pub tx: Option>, } @@ -55,8 +49,9 @@ impl RecorderContext { } let file = if self.server { "s" } else { "c" }.to_string() + &self.id.clone() - + &chrono::Local::now().format("_%Y%m%d%H%M%S").to_string() - + if self.codec_id == RecordCodecID::VP9 { + + &chrono::Local::now().format("_%Y%m%d%H%M%S_").to_string() + + &self.format.to_string() + + if self.format == CodecFormat::VP9 || self.format == CodecFormat::VP8 { ".webm" } else { ".mp4" @@ -107,8 +102,8 @@ impl DerefMut for Recorder { impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { ctx.set_filename()?; - let recorder = match ctx.codec_id { - RecordCodecID::VP9 => Recorder { + let recorder = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Recorder { inner: Box::new(WebmRecorder::new(ctx.clone())?), ctx, }, @@ -126,8 +121,8 @@ impl Recorder { fn change(&mut self, mut ctx: RecorderContext) -> ResultType<()> { ctx.set_filename()?; - self.inner = match ctx.codec_id { - RecordCodecID::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), + self.inner = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), #[cfg(feature = "hwcodec")] _ => Box::new(HwRecorder::new(ctx.clone())?), #[cfg(not(feature = "hwcodec"))] @@ -148,10 +143,19 @@ impl Recorder { pub fn write_frame(&mut self, frame: &video_frame::Union) -> ResultType<()> { match frame { - video_frame::Union::Vp9s(vp9s) => { - if self.ctx.codec_id != RecordCodecID::VP9 { + video_frame::Union::Vp8s(vp8s) => { + if self.ctx.format != CodecFormat::VP8 { self.change(RecorderContext { - codec_id: RecordCodecID::VP9, + format: CodecFormat::VP8, + ..self.ctx.clone() + })?; + } + vp8s.frames.iter().map(|f| self.write_video(f)).count(); + } + video_frame::Union::Vp9s(vp9s) => { + if self.ctx.format != CodecFormat::VP9 { + self.change(RecorderContext { + format: CodecFormat::VP9, ..self.ctx.clone() })?; } @@ -159,25 +163,25 @@ impl Recorder { } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { - if self.ctx.codec_id != RecordCodecID::H264 { + if self.ctx.format != CodecFormat::H264 { self.change(RecorderContext { - codec_id: RecordCodecID::H264, + format: CodecFormat::H264, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H264 { + if self.ctx.format == CodecFormat::H264 { h264s.frames.iter().map(|f| self.write_video(f)).count(); } } #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { - if self.ctx.codec_id != RecordCodecID::H265 { + if self.ctx.format != CodecFormat::H265 { self.change(RecorderContext { - codec_id: RecordCodecID::H265, + format: CodecFormat::H265, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H265 { + if self.ctx.format == CodecFormat::H265 { h265s.frames.iter().map(|f| self.write_video(f)).count(); } } @@ -221,7 +225,11 @@ impl RecorderApi for WebmRecorder { ctx.width as _, ctx.height as _, None, - mux::VideoCodecId::VP9, + if ctx.format == CodecFormat::VP9 { + mux::VideoCodecId::VP9 + } else { + mux::VideoCodecId::VP8 + }, ); Ok(WebmRecorder { vt, @@ -279,7 +287,7 @@ impl RecorderApi for HwRecorder { filename: ctx.filename.clone(), width: ctx.width, height: ctx.height, - is265: ctx.codec_id == RecordCodecID::H265, + is265: ctx.format == CodecFormat::H265, framerate: crate::hwcodec::DEFAULT_TIME_BASE[1] as _, }) .map_err(|_| anyhow!("Failed to create hardware muxer"))?; diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 3df9c0461..4820ea171 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -30,6 +30,7 @@ pub struct VpxEncoder { ctx: vpx_codec_ctx_t, width: usize, height: usize, + id: VpxVideoCodecId, } pub struct VpxDecoder { @@ -97,15 +98,10 @@ impl EncoderApi for VpxEncoder { { match cfg { crate::codec::EncoderCfg::VPX(config) => { - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_cx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), + }; let mut c = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; call_vpx!(vpx_codec_enc_config_default(i, &mut c, 0)); @@ -187,12 +183,17 @@ impl EncoderApi for VpxEncoder { VP9E_SET_TILE_COLUMNS as _, 4 as c_int )); + } else if config.codec == VpxVideoCodecId::VP8 { + // https://github.com/webmproject/libvpx/blob/972149cafeb71d6f08df89e91a0130d6a38c4b15/vpx/vp8cx.h#L172 + // https://groups.google.com/a/webmproject.org/g/webm-discuss/c/DJhSrmfQ61M + call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, 12,)); } Ok(Self { ctx, width: config.width as _, height: config.height as _, + id: config.codec, }) } _ => Err(anyhow!("encoder type mismatch")), @@ -213,7 +214,7 @@ impl EncoderApi for VpxEncoder { // to-do: flush periodically, e.g. 1 second if frames.len() > 0 { - Ok(VpxEncoder::create_msg(frames)) + Ok(VpxEncoder::create_msg(self.id, frames)) } else { Err(anyhow!("no valid frame")) } @@ -280,13 +281,17 @@ impl VpxEncoder { } #[inline] - fn create_msg(vp9s: Vec) -> Message { + pub fn create_msg(codec_id: VpxVideoCodecId, frames: Vec) -> Message { let mut msg_out = Message::new(); let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), + let vpxs = EncodedVideoFrames { + frames: frames.into(), ..Default::default() - }); + }; + match codec_id { + VpxVideoCodecId::VP8 => vf.set_vp8s(vpxs), + VpxVideoCodecId::VP9 => vf.set_vp9s(vpxs), + } msg_out.set_video_frame(vf); msg_out } @@ -382,15 +387,10 @@ impl VpxDecoder { pub fn new(config: VpxDecoderConfig) -> Result { // This is sound because `vpx_codec_ctx` is a repr(C) struct without any field that can // cause UB if uninitialized. - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_dx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), + }; let mut ctx = Default::default(); let cfg = vpx_codec_dec_cfg_t { threads: if config.num_threads == 0 { diff --git a/src/client.rs b/src/client.rs index 2f745b70c..ae93ccfcf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -44,9 +44,9 @@ use hbb_common::{ }; pub use helper::*; use scrap::{ - codec::{Decoder, DecoderCfg}, + codec::Decoder, record::{Recorder, RecorderContext}, - ImageFormat, VpxDecoderConfig, VpxVideoCodecId, + ImageFormat, }; use crate::{ @@ -917,12 +917,7 @@ impl VideoHandler { /// Create a new video handler. pub fn new() -> Self { VideoHandler { - decoder: Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }, - }), + decoder: Decoder::new(), rgb: Default::default(), recorder: Default::default(), record: false, @@ -954,12 +949,7 @@ impl VideoHandler { /// Reset the decoder. pub fn reset(&mut self) { - self.decoder = Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: 1, - }, - }); + self.decoder = Decoder::new(); } /// Start or stop screen record. @@ -973,7 +963,7 @@ impl VideoHandler { filename: "".to_owned(), width: w as _, height: h as _, - codec_id: scrap::record::RecordCodecID::VP9, + format: scrap::CodecFormat::VP9, tx: None, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); @@ -999,7 +989,7 @@ pub struct LoginConfigHandler { pub conn_id: i32, features: Option, session_id: u64, - pub supported_encoding: Option<(bool, bool)>, + pub supported_encoding: SupportedEncoding, pub restarting_remote_device: bool, pub force_relay: bool, pub direct: Option, @@ -1047,7 +1037,7 @@ impl LoginConfigHandler { self.remember = !config.password.is_empty(); self.config = config; self.session_id = rand::random(); - self.supported_encoding = None; + self.supported_encoding = Default::default(); self.restarting_remote_device = false; self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay; self.direct = None; @@ -1331,8 +1321,8 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - let state = Decoder::video_codec_state(&self.id); - msg.video_codec_state = hbb_common::protobuf::MessageField::some(state); + msg.supported_decoding = + hbb_common::protobuf::MessageField::some(Decoder::supported_decodings(Some(&self.id))); n += 1; if n > 0 { @@ -1565,10 +1555,7 @@ impl LoginConfigHandler { self.conn_id = pi.conn_id; // no matter if change, for update file time self.save_config(config); - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - self.supported_encoding = Some((pi.encoding.h264, pi.encoding.h265)); - } + self.supported_encoding = pi.encoding.clone().unwrap_or_default(); } pub fn get_remote_dir(&self) -> String { @@ -1626,10 +1613,10 @@ impl LoginConfigHandler { } pub fn change_prefer_codec(&self) -> Message { - let state = scrap::codec::Decoder::video_codec_state(&self.id); + let decoding = scrap::codec::Decoder::supported_decodings(Some(&self.id)); let mut misc = Misc::new(); misc.set_option(OptionMessage { - video_codec_state: hbb_common::protobuf::MessageField::some(state), + supported_decoding: hbb_common::protobuf::MessageField::some(decoding), ..Default::default() }); let mut msg_out = Message::new(); diff --git a/src/client/helper.rs b/src/client/helper.rs index a9696a8e8..61844d908 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,37 +1,8 @@ use hbb_common::{ get_time, - message_proto::{video_frame, Message, VideoFrame, VoiceCallRequest, VoiceCallResponse}, + message_proto::{Message, VoiceCallRequest, VoiceCallResponse}, }; - -#[derive(PartialEq, Debug, Clone)] -pub enum CodecFormat { - VP9, - H264, - H265, - Unknown, -} - -impl From<&VideoFrame> for CodecFormat { - fn from(it: &VideoFrame) -> Self { - match it.union { - Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::H264s(_)) => CodecFormat::H264, - Some(video_frame::Union::H265s(_)) => CodecFormat::H265, - _ => CodecFormat::Unknown, - } - } -} - -impl ToString for CodecFormat { - fn to_string(&self) -> String { - match self { - CodecFormat::VP9 => "VP9".into(), - CodecFormat::H264 => "H264".into(), - CodecFormat::H265 => "H265".into(), - CodecFormat::Unknown => "Unknow".into(), - } - } -} +use scrap::CodecFormat; #[derive(Debug, Default)] pub struct QualityStatus { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b0bddc82e..0f662e015 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -29,10 +29,10 @@ use hbb_common::tokio::{ time::{self, Duration, Instant, Interval}, }; use hbb_common::{allow_err, fs, get_time, log, message_proto::*, Stream}; +use scrap::CodecFormat; use crate::client::{ - new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, - SEC30, + new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{self, update_clipboard}; @@ -817,11 +817,10 @@ impl Remote { } fn contains_key_frame(vf: &VideoFrame) -> bool { + use video_frame::Union::*; match &vf.union { Some(vf) => match vf { - video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key), + Vp8s(f) | Vp9s(f) | H264s(f) | H265s(f) => f.frames.iter().any(|e| e.key), _ => false, }, None => false, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index aee486b94..1d965ebc5 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -969,6 +969,13 @@ pub fn main_has_hwcodec() -> SyncReturn { SyncReturn(has_hwcodec()) } +pub fn main_supported_hwdecodings() -> SyncReturn { + let decoding = supported_hwdecodings(); + let msg = HashMap::from([("h264", decoding.0), ("h265", decoding.1)]); + + SyncReturn(serde_json::ser::to_string(&msg).unwrap_or("".to_owned())) +} + pub fn main_is_root() -> bool { is_root() } @@ -1054,10 +1061,10 @@ pub fn session_send_note(id: String, note: String) { } } -pub fn session_supported_hwcodec(id: String) -> String { +pub fn session_alternative_codecs(id: String) -> String { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - let (h264, h265) = session.supported_hwcodec(); - let msg = HashMap::from([("h264", h264), ("h265", h265)]); + let (vp8, h264, h265) = session.alternative_codecs(); + let msg = HashMap::from([("vp8", vp8), ("h264", h264), ("h265", h265)]); serde_json::ser::to_string(&msg).unwrap_or("".to_owned()) } else { String::new() diff --git a/src/server/connection.rs b/src/server/connection.rs index 23e166fcf..d1ae4d6a3 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -536,7 +536,7 @@ impl Connection { let _ = privacy_mode::turn_off_privacy(0); } video_service::notify_video_frame_fetched(id, None); - scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); + scrap::codec::Encoder::update(id, scrap::codec::EncodingUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); if conn.authorized { password::update_temporary_password(); @@ -862,16 +862,7 @@ impl Connection { } } - #[cfg(feature = "hwcodec")] - { - let (h264, h265) = scrap::codec::Encoder::supported_encoding(); - pi.encoding = Some(SupportedEncoding { - h264, - h265, - ..Default::default() - }) - .into(); - } + pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into(); if self.port_forward_socket.is_some() { let mut msg_out = Message::new(); @@ -1147,21 +1138,21 @@ impl Connection { self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.options_in_login = Some(o.clone()); - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::State(q), + scrap::codec::EncodingUpdate::New(q), ); } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } self.video_ack_required = lr.video_ack_required; @@ -1784,11 +1775,8 @@ impl Connection { .unwrap() .update_user_fps(o.custom_fps as _); } - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( - self.inner.id(), - scrap::codec::EncoderUpdate::State(q), - ); + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update(self.inner.id(), scrap::codec::EncodingUpdate::New(q)); } if let Ok(q) = o.lock_after_session_end.enum_value() { if q != BoolOption::NotSet { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 205d0584c..691ca4abe 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,7 +31,7 @@ use scrap::{ codec::{Encoder, EncoderCfg, HwEncoderConfig}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - Display, TraitCapturer, + CodecName, Display, TraitCapturer, }; #[cfg(windows)] use std::sync::Once; @@ -468,21 +468,29 @@ fn run(sp: GenericService) -> ResultType<()> { drop(video_qos); log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - let encoder_cfg = match Encoder::current_hw_encoder_name() { - Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { - codec_name, - width: c.width, - height: c.height, - bitrate: bitrate as _, - }), - None => EncoderCfg::VPX(VpxEncoderConfig { - width: c.width as _, - height: c.height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }), + let encoder_cfg = match Encoder::negotiated_codec() { + scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => { + EncoderCfg::HW(HwEncoderConfig { + name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }) + } + name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => { + EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: if name == scrap::CodecName::VP8 { + VpxVideoCodecId::VP8 + } else { + VpxVideoCodecId::VP9 + }, + num_threads: (num_cpus::get() / 2) as _, + }) + } }; let mut encoder; @@ -526,7 +534,7 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); - let codec_name = Encoder::current_hw_encoder_name(); + let codec_name = Encoder::negotiated_codec(); let recorder = get_recorder(c.width, c.height, &codec_name); #[cfg(windows)] start_uac_elevation_check(); @@ -557,7 +565,7 @@ fn run(sp: GenericService) -> ResultType<()> { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - if codec_name != Encoder::current_hw_encoder_name() { + if codec_name != Encoder::negotiated_codec() { bail!("SWITCH"); } #[cfg(windows)] @@ -603,8 +611,14 @@ fn run(sp: GenericService) -> ResultType<()> { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; match frame { + scrap::Frame::VP8(data) => { + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP8, &sp, data, ms)?; + frame_controller.set_send(now, send_conn_ids); + } scrap::Frame::VP9(data) => { - let send_conn_ids = handle_one_frame_encoded(&sp, data, ms)?; + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP9, &sp, data, ms)?; frame_controller.set_send(now, send_conn_ids); } scrap::Frame::RAW(data) => { @@ -717,12 +731,11 @@ fn run(sp: GenericService) -> ResultType<()> { fn get_recorder( width: usize, height: usize, - codec_name: &Option, + codec_name: &CodecName, ) -> Arc>> { #[cfg(not(target_os = "ios"))] let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { use crate::hbbs_http::record_upload; - use scrap::record::RecordCodecID::*; let tx = if record_upload::is_enable() { let (tx, rx) = std::sync::mpsc::channel(); @@ -731,16 +744,6 @@ fn get_recorder( } else { None }; - let codec_id = match codec_name { - Some(name) => { - if name.contains("264") { - H264 - } else { - H265 - } - } - None => VP9, - }; Recorder::new(RecorderContext { server: true, id: Config::get_id(), @@ -748,7 +751,7 @@ fn get_recorder( filename: "".to_owned(), width, height, - codec_id, + format: codec_name.into(), tx, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) @@ -775,19 +778,6 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu Ok(()) } -#[inline] -#[cfg(any(target_os = "android", target_os = "ios"))] -fn create_msg(vp9s: Vec) -> Message { - let mut msg_out = Message::new(); - let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), - ..Default::default() - }); - msg_out.set_video_frame(vf); - msg_out -} - #[inline] fn handle_one_frame( sp: &GenericService, @@ -820,6 +810,7 @@ fn handle_one_frame( #[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub fn handle_one_frame_encoded( + codec: VpxVideoCodecId, sp: &GenericService, frame: &[u8], ms: i64, @@ -831,13 +822,13 @@ pub fn handle_one_frame_encoded( } Ok(()) })?; - let vp9_frame = EncodedVideoFrame { + let vpx_frame = EncodedVideoFrame { data: frame.to_vec().into(), key: true, pts: ms, ..Default::default() }; - let send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); + let send_conn_ids = sp.send_video_frame(scrap::VpxEncoder::create_msg(codec, vec![vpx_frame])); Ok(send_conn_ids) } diff --git a/src/ui/header.tis b/src/ui/header.tis index 257ba417e..af4f1e349 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -161,8 +161,8 @@ class Header: Reactor.Component { } function renderDisplayPop() { - var codecs = handler.supported_hwcodec(); - var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]); + var codecs = handler.alternative_codecs(); + var show_codec = codecs[0] || codecs[1] || codecs[2]; var cursor_embedded = false; if ((pi.displays || []).length > 0) { @@ -186,9 +186,10 @@ class Header: Reactor.Component { {show_codec ?
  • {svg_checkmark}Auto
  • + {codecs[0] ?
  • {svg_checkmark}VP8
  • : ""}
  • {svg_checkmark}VP9
  • - {codecs[0] ?
  • {svg_checkmark}H264
  • : ""} - {codecs[1] ?
  • {svg_checkmark}H265
  • : ""} + {codecs[1] ?
  • {svg_checkmark}H264
  • : ""} + {codecs[2] ?
  • {svg_checkmark}H265
  • : ""}
    : ""}
    {!cursor_embedded &&
  • {svg_checkmark}{translate('Show remote cursor')}
  • } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 68decf955..fc878cf1f 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -20,7 +20,6 @@ use hbb_common::{ use crate::{ client::*, - ui_interface::has_hwcodec, ui_session_interface::{InvokeUiSession, Session}, }; @@ -197,7 +196,14 @@ impl InvokeUiSession for SciterHandler { self.call("confirmDeleteFiles", &make_args!(id, i, name)); } - fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { + fn override_file_confirm( + &self, + id: i32, + file_num: i32, + to: String, + is_upload: bool, + is_identical: bool, + ) { self.call( "overrideFileConfirm", &make_args!(id, file_num, to, is_upload, is_identical), @@ -451,8 +457,7 @@ impl sciter::EventHandler for SciterSession { fn set_write_override(i32, i32, bool, bool, bool); fn get_keyboard_mode(); fn save_keyboard_mode(String); - fn has_hwcodec(); - fn supported_hwcodec(); + fn alternative_codecs(); fn change_prefer_codec(); fn restart_remote_device(); fn request_voice_call(); @@ -504,10 +509,6 @@ impl SciterSession { v } - fn has_hwcodec(&self) -> bool { - has_hwcodec() - } - pub fn t(&self, name: String) -> String { crate::client::translate(name) } @@ -516,9 +517,10 @@ impl SciterSession { super::get_icon() } - fn supported_hwcodec(&self) -> Value { - let (h264, h265) = self.0.supported_hwcodec(); + fn alternative_codecs(&self) -> Value { + let (vp8, h264, h265) = self.0.alternative_codecs(); let mut v = Value::array(0); + v.push(vp8); v.push(h264); v.push(h265); v diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 11357be4a..26d470fe0 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -752,6 +752,13 @@ pub fn has_hwcodec() -> bool { return true; } +#[cfg(feature = "flutter")] +#[inline] +pub fn supported_hwdecodings() -> (bool, bool) { + let decoding = scrap::codec::Decoder::supported_decodings(None); + (decoding.ability_h264 > 0, decoding.ability_h265 > 0) +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_root() -> bool { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f89be4879..3dee89a6e 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -216,24 +216,16 @@ impl Session { true } - pub fn supported_hwcodec(&self) -> (bool, bool) { - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - let decoder = scrap::codec::Decoder::video_codec_state(&self.id); - let mut h264 = decoder.score_h264 > 0; - let mut h265 = decoder.score_h265 > 0; - let (encoding_264, encoding_265) = self - .lc - .read() - .unwrap() - .supported_encoding - .unwrap_or_default(); - h264 = h264 && encoding_264; - h265 = h265 && encoding_265; - return (h264, h265); - } - #[allow(unreachable_code)] - (false, false) + pub fn alternative_codecs(&self) -> (bool, bool, bool) { + let decoder = scrap::codec::Decoder::supported_decodings(None); + let mut vp8 = decoder.ability_vp8 > 0; + let mut h264 = decoder.ability_h264 > 0; + let mut h265 = decoder.ability_h265 > 0; + let enc = &self.lc.read().unwrap().supported_encoding; + vp8 = vp8 && enc.vp8; + h264 = h264 && enc.h264; + h265 = h265 && enc.h265; + (vp8, h264, h265) } pub fn change_prefer_codec(&self) { From 10eddc139c3254d9183ede0c945b6ad8cf7af5f6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 22 Mar 2023 17:01:11 +0800 Subject: [PATCH 031/112] linux virtual display, init commit Signed-off-by: fufesou --- Cargo.lock | 54 ++ Cargo.toml | 2 + libs/hbb_common/protos/message.proto | 6 + libs/hbb_common/src/platform/linux.rs | 4 +- res/pam.d/rustdesk.debian | 5 + res/pam.d/rustdesk.suse | 5 + res/startwm.sh | 130 ++++ res/xorg.conf | 30 + src/cli.rs | 4 +- src/client.rs | 124 +++- src/client/io_loop.rs | 4 +- src/platform/linux.rs | 1 + src/platform/linux_desktop.rs | 951 ++++++++++++++++++++++++++ src/platform/mod.rs | 3 + src/port_forward.rs | 4 +- src/rendezvous_mediator.rs | 9 +- src/server.rs | 34 + src/server/connection.rs | 103 ++- src/server/service.rs | 2 + src/ui/common.css | 4 + src/ui/common.tis | 26 +- src/ui/msgbox.tis | 62 +- src/ui/remote.rs | 2 +- src/ui_session_interface.rs | 29 +- 24 files changed, 1544 insertions(+), 54 deletions(-) create mode 100644 res/pam.d/rustdesk.debian create mode 100644 res/pam.d/rustdesk.suse create mode 100755 res/startwm.sh create mode 100644 res/xorg.conf create mode 100644 src/platform/linux_desktop.rs diff --git a/Cargo.lock b/Cargo.lock index 5a4cad9f8..9d2984abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4159,6 +4159,38 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "pam" +version = "0.7.0" +source = "git+https://github.com/fufesou/pam#10da2cbbabe32cbc9de22a66abe44738e7ec0ea0" +dependencies = [ + "libc", + "pam-macros", + "pam-sys", + "users 0.10.0", +] + +[[package]] +name = "pam-macros" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94f3b9b97df3c6d4e51a14916639b24e02c7d15d1dba686ce9b1118277cb811" +dependencies = [ + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "pam-sys" +version = "1.0.0-alpha4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9dfd42858f6a6bb1081079fd9dc259ca3e2aaece6cb689fd36b1058046c969" +dependencies = [ + "bindgen 0.59.2", + "libc", +] + [[package]] name = "pango" version = "0.16.5" @@ -5124,6 +5156,7 @@ dependencies = [ "objc", "objc_id", "os-version", + "pam", "parity-tokio-ipc", "rdev", "repng", @@ -5149,6 +5182,7 @@ dependencies = [ "tray-icon", "trayicon", "url", + "users 0.11.0", "uuid", "virtual_display", "whoami", @@ -6443,6 +6477,26 @@ dependencies = [ "serde 1.0.159", ] +[[package]] +name = "users" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "users" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" +dependencies = [ + "libc", + "log", +] + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index a01415837..10f90f387 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,6 +122,8 @@ mouce = { git="https://github.com/fufesou/mouce.git" } evdev = { git="https://github.com/fufesou/evdev" } dbus = "0.9" dbus-crossroads = "0.5" +pam = { git="https://github.com/fufesou/pam" } +users = "0.11.0" [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index a481ae6c4..c49f163a8 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -52,6 +52,11 @@ message FileTransfer { bool show_hidden = 2; } +message OSLogin { + string username = 1; + string password = 2; +} + message LoginRequest { string username = 1; bytes password = 2; @@ -65,6 +70,7 @@ message LoginRequest { bool video_ack_required = 9; uint64 session_id = 10; string version = 11; + OSLogin os_login = 12; } message ChatMessage { string text = 1; } diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 89c96799d..1d826ea97 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -191,7 +191,7 @@ pub fn run_cmds(cmds: &str) -> ResultType { } #[cfg(not(feature = "flatpak"))] -fn run_loginctl(args: Option>) -> std::io::Result { +pub(super) fn run_loginctl(args: Option>) -> std::io::Result { let mut cmd = std::process::Command::new("loginctl"); if let Some(a) = args { return cmd.args(a).output(); @@ -200,7 +200,7 @@ fn run_loginctl(args: Option>) -> std::io::Result>) -> std::io::Result { +pub(super) fn run_loginctl(args: Option>) -> std::io::Result { let mut l_args = String::from("loginctl"); if let Some(a) = args { l_args = format!("{} {}", l_args, a.join(" ")); diff --git a/res/pam.d/rustdesk.debian b/res/pam.d/rustdesk.debian new file mode 100644 index 000000000..789ce8f7c --- /dev/null +++ b/res/pam.d/rustdesk.debian @@ -0,0 +1,5 @@ +#%PAM-1.0 +@include common-auth +@include common-account +@include common-session +@include common-password diff --git a/res/pam.d/rustdesk.suse b/res/pam.d/rustdesk.suse new file mode 100644 index 000000000..a7c7836ce --- /dev/null +++ b/res/pam.d/rustdesk.suse @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth include common-auth +account include common-account +session include common-session +password include common-password diff --git a/res/startwm.sh b/res/startwm.sh new file mode 100755 index 000000000..7cdaf07ce --- /dev/null +++ b/res/startwm.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +# This script is derived from https://github.com/neutrinolabs/xrdp/sesman/startwm.sh. + +# +# This script is an example. You might need to edit this script +# depending on your distro if it doesn't work for you. +# +# Uncomment the following line for debug: +# exec xterm + + +# Execution sequence for interactive login shell - pseudocode +# +# IF /etc/profile is readable THEN +# execute ~/.bash_profile +# END IF +# IF ~/.bash_profile is readable THEN +# execute ~/.bash_profile +# ELSE +# IF ~/.bash_login is readable THEN +# execute ~/.bash_login +# ELSE +# IF ~/.profile is readable THEN +# execute ~/.profile +# END IF +# END IF +# END IF +pre_start() +{ + if [ -r /etc/profile ]; then + . /etc/profile + fi + if [ -r ~/.bash_profile ]; then + . ~/.bash_profile + else + if [ -r ~/.bash_login ]; then + . ~/.bash_login + else + if [ -r ~/.profile ]; then + . ~/.profile + fi + fi + fi + return 0 +} + +# When loging out from the interactive shell, the execution sequence is: +# +# IF ~/.bash_logout exists THEN +# execute ~/.bash_logout +# END IF +post_start() +{ + if [ -r ~/.bash_logout ]; then + . ~/.bash_logout + fi + return 0 +} + +#start the window manager +wm_start() +{ + if [ -r /etc/default/locale ]; then + . /etc/default/locale + export LANG LANGUAGE + fi + + # debian + if [ -r /etc/X11/Xsession ]; then + pre_start + . /etc/X11/Xsession + post_start + exit 0 + fi + + # alpine + # Don't use /etc/X11/xinit/Xsession - it doesn't work + if [ -f /etc/alpine-release ]; then + if [ -f /etc/X11/xinit/xinitrc ]; then + pre_start + /etc/X11/xinit/xinitrc + post_start + else + echo "** xinit package isn't installed" >&2 + exit 1 + fi + fi + + # el + if [ -r /etc/X11/xinit/Xsession ]; then + pre_start + . /etc/X11/xinit/Xsession + post_start + exit 0 + fi + + # suse + if [ -r /etc/X11/xdm/Xsession ]; then + # since the following script run a user login shell, + # do not execute the pseudo login shell scripts + . /etc/X11/xdm/Xsession + exit 0 + elif [ -r /usr/etc/X11/xdm/Xsession ]; then + . /usr/etc/X11/xdm/Xsession + exit 0 + fi + + pre_start + xterm + post_start +} + +#. /etc/environment +#export PATH=$PATH +#export LANG=$LANG + +# change PATH to be what your environment needs usually what is in +# /etc/environment +#PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games" +#export PATH=$PATH + +# for PATH and LANG from /etc/environment +# pam will auto process the environment file if /etc/pam.d/xrdp-sesman +# includes +# auth required pam_env.so readenv=1 + +wm_start + +exit 1 diff --git a/res/xorg.conf b/res/xorg.conf new file mode 100644 index 000000000..fe1539995 --- /dev/null +++ b/res/xorg.conf @@ -0,0 +1,30 @@ +Section "Monitor" + Identifier "Dummy Monitor" + + # Default HorizSync 31.50 - 48.00 kHz + HorizSync 5.0 - 150.0 + # Default VertRefresh 50.00 - 70.00 Hz + VertRefresh 5.0 - 100.0 + + # Taken from https://www.xpra.org/xorg.conf + Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135 + Modeline "1280x720" 27.41 1280 1312 1416 1448 720 737 740 757 +EndSection + +Section "Device" + Identifier "Dummy VideoCard" + Driver "dummy" + # Default VideoRam 4096 + # (1920 * 1080 * 4) / 1024 = 8100 + VideoRam 8100 +EndSection + +Section "Screen" + Identifier "Dummy Screen" + Device "Dummy VideoCard" + Monitor "Dummy Monitor" + SubSection "Display" + Depth 24 + Modes "1920x1080" "1280x720" + EndSubSection +EndSection \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 454eec1ee..13e70987b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -85,8 +85,8 @@ impl Interface for Session { handle_hash(self.lc.clone(), &pass, hash, self, peer).await; } - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { - handle_login_from_ui(self.lc.clone(), password, remember, peer).await; + async fn handle_login_from_ui(&mut self, os_username: String, os_password: String, password: String, remember: bool, peer: &mut Stream) { + handle_login_from_ui(self.lc.clone(), os_username, os_password, password, remember, peer).await; } async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { diff --git a/src/client.rs b/src/client.rs index 2f745b70c..f03cb0e55 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1591,7 +1591,12 @@ impl LoginConfigHandler { } /// Create a [`Message`] for login. - fn create_login_msg(&self, password: Vec) -> Message { + fn create_login_msg( + &self, + os_username: String, + os_password: String, + password: Vec, + ) -> Message { #[cfg(any(target_os = "android", target_os = "ios"))] let my_id = Config::get_id_or(crate::common::DEVICE_ID.lock().unwrap().clone()); #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -1604,6 +1609,12 @@ impl LoginConfigHandler { option: self.get_option_message(true).into(), session_id: self.session_id, version: crate::VERSION.to_string(), + os_login: Some(OSLogin { + username: os_username, + password: os_password, + ..Default::default() + }) + .into(), ..Default::default() }; match self.conn_type { @@ -1908,10 +1919,46 @@ pub fn handle_login_error( err: &str, interface: &impl Interface, ) -> bool { - if err == "Wrong Password" { + if err == crate::server::LOGIN_MSG_PASSWORD_EMPTY { + lc.write().unwrap().password = Default::default(); + interface.msgbox("input-password", "Password Required", "", ""); + true + } else if err == crate::server::LOGIN_MSG_PASSWORD_WRONG { lc.write().unwrap().password = Default::default(); interface.msgbox("re-input-password", err, "Do you want to enter again?", ""); true + } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY { + interface.msgbox( + "xsession-login", + "xsession is unready", + "Input linux user/password", + "", + ); + true + } else if err == crate::server::LOGIN_MSG_XSESSION_FAILED { + interface.msgbox( + "xsession-re-login", + "xsession username/password is wrong", + "Do you want to enter again?", + "", + ); + true + } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY { + interface.msgbox( + "xsession-login-password", + "xsession is unready", + "Input connection password and linux user/password", + "", + ); + true + } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG { + interface.msgbox( + "xsession-login-re-password", + "xsession is unready and password is wrong", + "Do you want to enter again?", + "", + ); + true } else if err == "No Password Access" { lc.write().unwrap().password = Default::default(); interface.msgbox( @@ -1944,7 +1991,7 @@ pub async fn handle_hash( lc: Arc>, password_preset: &str, hash: Hash, - interface: &impl Interface, + _interface: &impl Interface, peer: &mut Stream, ) { lc.write().unwrap().hash = hash.clone(); @@ -1970,13 +2017,19 @@ pub async fn handle_hash( } if password.is_empty() { // login without password, the remote side can click accept - send_login(lc.clone(), Vec::new(), peer).await; - interface.msgbox("input-password", "Password Required", "", ""); + send_login(lc.clone(), "".to_owned(), "".to_owned(), Vec::new(), peer).await; } else { let mut hasher = Sha256::new(); hasher.update(&password); hasher.update(&hash.challenge); - send_login(lc.clone(), hasher.finalize()[..].into(), peer).await; + send_login( + lc.clone(), + "".to_owned(), + "".to_owned(), + hasher.finalize()[..].into(), + peer, + ) + .await; } lc.write().unwrap().hash = hash; } @@ -1986,10 +2039,21 @@ pub async fn handle_hash( /// # Arguments /// /// * `lc` - Login config. +/// * `os_username` - OS username. +/// * `os_password` - OS password. /// * `password` - Password. /// * `peer` - [`Stream`] for communicating with peer. -async fn send_login(lc: Arc>, password: Vec, peer: &mut Stream) { - let msg_out = lc.read().unwrap().create_login_msg(password); +async fn send_login( + lc: Arc>, + os_username: String, + os_password: String, + password: Vec, + peer: &mut Stream, +) { + let msg_out = lc + .read() + .unwrap() + .create_login_msg(os_username, os_password, password); allow_err!(peer.send(&msg_out).await); } @@ -1998,25 +2062,40 @@ async fn send_login(lc: Arc>, password: Vec, peer /// # Arguments /// /// * `lc` - Login config. +/// * `os_username` - OS username. +/// * `os_password` - OS password. /// * `password` - Password. /// * `remember` - Whether to remember password. /// * `peer` - [`Stream`] for communicating with peer. pub async fn handle_login_from_ui( lc: Arc>, + os_username: String, + os_password: String, password: String, remember: bool, peer: &mut Stream, ) { - let mut hasher = Sha256::new(); - hasher.update(password); - hasher.update(&lc.read().unwrap().hash.salt); - let res = hasher.finalize(); - lc.write().unwrap().remember = remember; - lc.write().unwrap().password = res[..].into(); + let mut hash_password = if password.is_empty() { + let mut password2 = lc.read().unwrap().password.clone(); + if password2.is_empty() { + password2 = lc.read().unwrap().config.password.clone(); + } + password2 + } else { + let mut hasher = Sha256::new(); + hasher.update(password); + hasher.update(&lc.read().unwrap().hash.salt); + let res = hasher.finalize(); + lc.write().unwrap().remember = remember; + lc.write().unwrap().password = res[..].into(); + res[..].into() + }; let mut hasher2 = Sha256::new(); - hasher2.update(&res[..]); + hasher2.update(&hash_password[..]); hasher2.update(&lc.read().unwrap().hash.challenge); - send_login(lc.clone(), hasher2.finalize()[..].into(), peer).await; + hash_password = hasher2.finalize()[..].to_vec(); + + send_login(lc.clone(), os_username, os_password, hash_password, peer).await; } async fn send_switch_login_request( @@ -2030,7 +2109,7 @@ async fn send_switch_login_request( lr: hbb_common::protobuf::MessageField::some( lc.read() .unwrap() - .create_login_msg(vec![]) + .create_login_msg("".to_owned(), "".to_owned(), vec![]) .login_request() .to_owned(), ), @@ -2051,7 +2130,14 @@ pub trait Interface: Send + Clone + 'static + Sized { self.msgbox("error", "Error", err, ""); } async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream); - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream); + async fn handle_login_from_ui( + &mut self, + os_username: String, + os_password: String, + password: String, + remember: bool, + peer: &mut Stream, + ); async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream); fn get_login_config_handler(&self) -> Arc>; @@ -2071,7 +2157,7 @@ pub trait Interface: Send + Clone + 'static + Sized { #[derive(Clone)] pub enum Data { Close, - Login((String, bool)), + Login((String, String, String, bool)), Message(Message), SendFiles((i32, String, String, i32, bool, bool)), RemoveDirAll((i32, String, bool, bool)), diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b0bddc82e..c1c38af40 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -360,9 +360,9 @@ impl Remote { allow_err!(peer.send(&msg).await); return false; } - Data::Login((password, remember)) => { + Data::Login((os_username, os_password, password, remember)) => { self.handler - .handle_login_from_ui(password, remember, peer) + .handle_login_from_ui(os_username, os_password, password, remember, peer) .await; } Data::ToggleClipboardFile => { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index dda4b115b..c1da431a4 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,3 +1,4 @@ +use super::linux_desktop::GNOME_SESSION_BINARY; use super::{CursorData, ResultType}; use desktop::Desktop; pub use hbb_common::platform::linux::*; diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop.rs new file mode 100644 index 000000000..bf9f465d0 --- /dev/null +++ b/src/platform/linux_desktop.rs @@ -0,0 +1,951 @@ +use super::{linux::*, ResultType}; +use hbb_common::{allow_err, bail, log, rand::prelude::*, tokio::time}; +use pam; +use std::{ + collections::HashMap, + os::unix::process::CommandExt, + path::Path, + process::{Child, Command}, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{sync_channel, SyncSender}, + Arc, Mutex, + }, + time::{Duration, Instant}, +}; +use users::{get_user_by_name, os::unix::UserExt, User}; + +lazy_static::lazy_static! { + static ref DESKTOP_RUNNING: Arc = Arc::new(AtomicBool::new(false)); + static ref DESKTOP_INST: Arc>> = Arc::new(Mutex::new(None)); +} + +pub const VIRTUAL_X11_DESKTOP: &str = "xfce4"; +pub const VIRTUAL_X11_DESKTOP_START: &str = "startxfce4"; +pub const XFCE4_PANEL: &str = "xfce4-panel"; +pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary"; +pub const ENV_DESKTOP_PROTOCAL: &str = "RUSTDESK_PROTOCAL"; +pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; +pub const ENV_DESKTOP_PROTOCAL__X11: &str = "x11"; +pub const ENV_DESKTOP_PROTOCAL_UNKNOWN: &str = "unknown"; + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Protocal { + Wayland, + X11, // Xorg + Unknown, +} + +#[derive(Debug, Clone)] +pub struct DesktopEnv { + pub protocal: Protocal, + pub username: String, + pub uid: String, + pub display: String, + pub xauth: String, +} + +#[derive(Debug)] +pub struct Desktop { + env: DesktopEnv, + child_exit: Arc, + is_child_running: Arc, +} + +fn check_update_env() { + let mut inst = DESKTOP_INST.lock().unwrap(); + if let Some(inst) = &mut (*inst) { + if !inst.is_child_running.load(Ordering::SeqCst) { + inst.child_exit.store(true, Ordering::SeqCst); + let old_env = inst.env.clone(); + allow_err!(inst.env.refresh()); + if !inst.env.is_same_env(&old_env) { + inst.env.update_env(); + log::debug!("desktop env changed, {:?}", &inst.env); + } + } + } +} + +pub fn start_xdesktop() { + std::thread::spawn(|| { + if wait_xdesktop(20) { + log::info!("Wait desktop: default"); + } else { + log::info!("Wait desktop: none"); + } + *DESKTOP_INST.lock().unwrap() = Some(Desktop::new()); + + let interval = time::Duration::from_millis(super::SERVICE_INTERVAL); + DESKTOP_RUNNING.store(true, Ordering::SeqCst); + while DESKTOP_RUNNING.load(Ordering::SeqCst) { + check_update_env(); + std::thread::sleep(interval); + } + log::info!("xdesktop update thread exit"); + }); +} + +pub fn stop_xdesktop() { + DESKTOP_RUNNING.store(false, Ordering::SeqCst); +} + +pub fn get_desktop_env() -> Option { + match &*DESKTOP_INST.lock().unwrap() { + Some(inst) => Some(inst.env.clone()), + None => None, + } +} + +pub fn try_start_x_session(username: &str, password: &str) -> ResultType { + let mut inst = DESKTOP_INST.lock().unwrap(); + if let Some(inst) = &mut (*inst) { + let _ = inst.try_start_x_session(username, password)?; + log::debug!("try_start_x_session, username: {}, {:?}", &username, &inst); + Ok(inst.env.clone()) + } else { + bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); + } +} + +fn wait_xdesktop(timeout_secs: u64) -> bool { + let wait_begin = Instant::now(); + while wait_begin.elapsed().as_secs() < timeout_secs { + let seat0 = get_values_of_seat0(&[0]); + if !seat0[0].is_empty() { + return true; + } + + if let Ok(output) = run_cmds(format!( + "ps -ef | grep -v 'grep' | grep -E 'gnome-session-binary|{}'", + XFCE4_PANEL + )) { + if !output.is_empty() { + log::info!("wait xdesktop: find xclient {}", &output); + return true; + } + } + + std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + } + + false +} + +impl DesktopEnv { + pub fn new() -> Self { + let xauth = get_env_var("XAUTHORITY"); + + Self { + protocal: Protocal::Unknown, + username: "".to_owned(), + uid: "".to_owned(), + display: "".to_owned(), + xauth: if xauth.is_empty() { + "/tmp/.Xauthority".to_owned() + } else { + xauth + }, + } + } + + fn update_env(&self) { + if self.is_ready() { + std::env::set_var("DISPLAY", &self.display); + std::env::set_var("XAUTHORITY", &self.xauth); + std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); + } else { + std::env::set_var("DISPLAY", ""); + std::env::set_var("XAUTHORITY", ""); + std::env::set_var(ENV_DESKTOP_PROTOCAL, &Protocal::Unknown.to_string()); + } + } + + pub fn is_same_env(&self, other: &Self) -> bool { + self.protocal == other.protocal + && self.uid == other.uid + && self.display == other.display + && self.xauth == other.xauth + } + + #[inline(always)] + pub fn is_ready(&self) -> bool { + self.protocal == Protocal::X11 + } + + // The logic mainly fron https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334. + fn get_avail_display() -> ResultType { + let display_range = 0..51; + for i in display_range.clone() { + if Self::is_x_server_running(i) { + continue; + } + return Ok(i); + } + bail!("No avaliable display found in range {:?}", display_range) + } + + fn is_x_server_running(display: u32) -> bool { + Path::new(&format!("/tmp/.X11-unix/X{}", display)).exists() + || Path::new(&format!("/tmp/.X{}-lock", display)).exists() + } + + fn get_display(&mut self) { + self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10); + if self.display.is_empty() { + self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10); + } + if self.display.is_empty() { + self.display = Self::get_display_by_user(&self.username); + } + if self.display.is_empty() { + self.display = ":0".to_owned(); + } + self.display = self + .display + .replace(&whoami::hostname(), "") + .replace("localhost", ""); + } + + fn get_xauth(&mut self) { + self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10); + if self.xauth.is_empty() { + get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10); + } + + let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid); + if self.xauth.is_empty() { + self.xauth = if std::path::Path::new(&gdm).exists() { + gdm + } else { + let username = &self.username; + if username == "root" { + format!("/{}/.Xauthority", username) + } else { + let tmp = format!("/home/{}/.Xauthority", username); + if std::path::Path::new(&tmp).exists() { + tmp + } else { + format!("/var/lib/{}/.Xauthority", username) + } + } + }; + } + } + + // fixme: reduce loginctl + fn get_env_seat0(&mut self) -> ResultType { + let output = Command::new("loginctl").output()?; + for line in String::from_utf8_lossy(&output.stdout).lines() { + if line.contains("seat0") { + if let Some(sid) = line.split_whitespace().nth(0) { + if Self::is_active(sid)? { + if let Some(uid) = line.split_whitespace().nth(1) { + self.uid = uid.to_owned(); + } + if let Some(u) = line.split_whitespace().nth(2) { + self.username = u.to_owned(); + } + + self.protocal = Protocal::Unknown; + let type_output = Command::new("loginctl") + .args(vec!["show-session", "-p", "Type", sid]) + .output()?; + let type_stdout = String::from_utf8_lossy(&type_output.stdout); + + if type_stdout.contains("x11") { + self.protocal = Protocal::X11; + break; + } else if type_stdout.contains("wayland") { + self.protocal = Protocal::Wayland; + } + } + } + } + } + Ok(self.is_ready()) + } + + // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 + fn get_env_active(&mut self) -> ResultType { + let output = Command::new("loginctl").output()?; + + // set active Xorg session + for line in String::from_utf8_lossy(&output.stdout).lines() { + if line.contains("sessions listed.") { + continue; + } + if let Some(sid) = line.split_whitespace().nth(0) { + if Self::is_active(sid)? { + if Self::get_display_server_of_session(sid) == ENV_DESKTOP_PROTOCAL__X11 { + if let Some(uid) = line.split_whitespace().nth(1) { + self.uid = uid.to_owned(); + } + if let Some(u) = line.split_whitespace().nth(2) { + self.username = u.to_owned(); + } + + self.protocal = Protocal::X11; + } + } + } + } + // // set active xfce4 session + // for line in String::from_utf8_lossy(&output.stdout).lines() { + // if let Some(sid) = line.split_whitespace().nth(0) { + // if Self::is_active(sid)? { + // let tty_output = Command::new("loginctl") + // .args(vec!["show-session", "-p", "TTY", sid]) + // .output()?; + // let tty: String = String::from_utf8_lossy(&tty_output.stdout) + // .replace("TTY=", "") + // .trim_end() + // .into(); + + // let xfce_panel_info = + // run_cmds(format!("ps -e | grep \"{}.\\\\+{}\"", tty, XFCE4_PANEL))?; + // if xfce_panel_info.trim_end().to_string() != "" { + // if let Some(uid) = line.split_whitespace().nth(1) { + // self.uid = uid.to_owned(); + // } + // if let Some(u) = line.split_whitespace().nth(2) { + // self.username = u.to_owned(); + // } + // } + // } + // } + // } + Ok(self.is_ready()) + } + + // fixme: dup + fn get_display_server_of_session(session: &str) -> String { + if let Ok(output) = Command::new("loginctl") + .args(vec!["show-session", "-p", "Type", session]) + .output() + // Check session type of the session + { + let display_server = String::from_utf8_lossy(&output.stdout) + .replace("Type=", "") + .trim_end() + .into(); + if display_server == "tty" { + // If the type is tty... + if let Ok(output) = Command::new("loginctl") + .args(vec!["show-session", "-p", "TTY", session]) + .output() + // Get the tty number + { + let tty: String = String::from_utf8_lossy(&output.stdout) + .replace("TTY=", "") + .trim_end() + .into(); + 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() != "" { + // If it is, manually return "x11", otherwise return tty + ENV_DESKTOP_PROTOCAL__X11.to_owned() + } else { + display_server + } + } else { + // If any of these commands fail just fall back to the display server + display_server + } + } else { + display_server + } + } else { + // If the session is not a tty, then just return the type as usual + display_server + } + } else { + "".to_owned() + } + } + + // fixme: remove + fn is_active(sid: &str) -> ResultType { + let output = Command::new("loginctl") + .args(vec!["show-session", "-p", "State", sid]) + .output()?; + + Ok(String::from_utf8_lossy(&output.stdout).contains("active")) + } + + fn get_display_by_user(user: &str) -> String { + // log::debug!("w {}", &user); + if let Ok(output) = std::process::Command::new("w").arg(&user).output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + let mut iter = line.split_whitespace(); + let b = iter.nth(2); + if let Some(b) = b { + if b.starts_with(":") { + return b.to_owned(); + } + } + } + } + // above not work for gdm user + //log::debug!("ls -l /tmp/.X11-unix/"); + let mut last = "".to_owned(); + if let Ok(output) = std::process::Command::new("ls") + .args(vec!["-l", "/tmp/.X11-unix/"]) + .output() + { + for line in String::from_utf8_lossy(&output.stdout).lines() { + let mut iter = line.split_whitespace(); + let user_field = iter.nth(2); + if let Some(x) = iter.last() { + if x.starts_with("X") { + last = x.replace("X", ":").to_owned(); + if user_field == Some(&user) { + return last; + } + } + } + } + } + last + } + + fn add_xauth_cookie( + file: &str, + display: &str, + uid: u32, + gid: u32, + envs: &HashMap<&str, String>, + ) -> ResultType<()> { + let randstr = (0..16) + .map(|_| format!("{:02x}", random::())) + .collect::(); + let output = Command::new("xauth") + .uid(uid) + .gid(gid) + .envs(envs) + .args(vec!["-q", "-f", file, "add", display, ".", &randstr]) + .output()?; + // xauth run success, even the following error occurs. + // Ok(Output { status: ExitStatus(unix_wait_status(0)), stdout: "", stderr: "xauth: file .Xauthority does not exist\n" }) + let errmsg = String::from_utf8_lossy(&output.stderr).to_string(); + if !errmsg.is_empty() { + if !errmsg.contains("does not exist") { + bail!("Failed to launch xauth, {}", errmsg) + } + } + Ok(()) + } + + fn wait_x_server_running(pid: u32, display_num: u32, max_wait_secs: u64) -> ResultType<()> { + let wait_begin = Instant::now(); + loop { + if run_cmds(format!("ls /proc/{}", pid))?.is_empty() { + bail!("X server exit"); + } + + if Self::is_x_server_running(display_num) { + return Ok(()); + } + if wait_begin.elapsed().as_secs() > max_wait_secs { + bail!("Failed to wait xserver after {} seconds", max_wait_secs); + } + std::thread::sleep(Duration::from_millis(300)); + } + } + + fn refresh(&mut self) -> ResultType { + *self = Self::new(); + if self.get_env_seat0()? || self.get_env_active()? { + self.get_display(); + self.get_xauth(); + Ok(true) + } else { + Ok(false) + } + } +} + +impl Drop for Desktop { + fn drop(&mut self) { + self.stop_children(); + } +} + +impl Desktop { + fn fatal_exit() { + std::process::exit(0); + } + + pub fn new() -> Self { + Self { + env: DesktopEnv::new(), + child_exit: Arc::new(AtomicBool::new(true)), + is_child_running: Arc::new(AtomicBool::new(false)), + } + } + + fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> { + match get_user_by_name(username) { + Some(userinfo) => { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(username, password); + match client.authenticate() { + Ok(_) => { + if self.env.is_ready() && self.env.username == username { + return Ok(()); + } + + self.env.username = username.to_string(); + self.env.uid = userinfo.uid().to_string(); + self.env.protocal = Protocal::Unknown; + match self.start_x_session(&userinfo, password) { + Ok(_) => { + log::info!("Succeeded to start x11, update env {:?}", &self.env); + self.env.update_env(); + Ok(()) + } + Err(e) => { + self.env = DesktopEnv::new(); + self.env.update_env(); + bail!("failed to start x session, {}", e); + } + } + } + Err(e) => { + bail!("failed to check user pass for {}, {}", username, e); + } + } + } + None => { + bail!("failed to get userinfo of {}", username); + } + } + } + + fn start_x_session(&mut self, userinfo: &User, password: &str) -> ResultType<()> { + self.stop_children(); + + let display_num = DesktopEnv::get_avail_display()?; + // "xServer_ip:display_num.screen_num" + self.env.display = format!(":{}", display_num); + + let uid = userinfo.uid(); + let gid = userinfo.primary_group_id(); + let envs = HashMap::from([ + ("SHELL", userinfo.shell().to_string_lossy().to_string()), + ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), + ("USER", self.env.username.clone()), + ("UID", userinfo.uid().to_string()), + ("HOME", userinfo.home_dir().to_string_lossy().to_string()), + ( + "XDG_RUNTIME_DIR", + format!("/run/user/{}", userinfo.uid().to_string()), + ), + // ("DISPLAY", self.display.clone()), + // ("XAUTHORITY", self.xauth.clone()), + // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), + ]); + let env = self.env.clone(); + self.child_exit.store(false, Ordering::SeqCst); + let is_child_running = self.is_child_running.clone(); + + let (tx_res, rx_res) = sync_channel(1); + let password = password.to_string(); + // start x11 + std::thread::spawn(move || { + match Self::start_x_session_thread( + tx_res.clone(), + is_child_running, + env, + uid, + gid, + display_num, + password, + envs, + ) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to start x session thread"); + allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e))); + } + } + }); + + // wait x11 + match rx_res.recv_timeout(Duration::from_millis(10_000)) { + Ok(res) => { + if res == "" { + self.env.protocal = Protocal::X11; + Ok(()) + } else { + bail!(res) + } + } + Err(e) => { + bail!("Failed to recv x11 result {}", e) + } + } + } + + fn start_x_session_thread( + tx_res: SyncSender, + is_child_running: Arc, + env: DesktopEnv, + uid: u32, + gid: u32, + display_num: u32, + password: String, + envs: HashMap<&str, String>, + ) -> ResultType<()> { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(&env.username, &password); + client.authenticate()?; + + client.set_item(pam::PamItemType::TTY, &env.display)?; + client.open_session()?; + + // fixme: FreeBSD kernel needs to login here. + // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 + + let (child_xorg, child_wm) = Self::start_x11(&env, uid, gid, display_num, &envs)?; + is_child_running.store(true, Ordering::SeqCst); + + log::info!("Start xorg and wm done, notify and wait xtop x11"); + allow_err!(tx_res.send("".to_owned())); + + Self::wait_stop_x11(child_xorg, child_wm); + log::info!("Wait x11 stop done"); + Ok(()) + } + + fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType { + if let Ok(_) = child_xorg.kill() { + for _ in 0..3 { + match child_xorg.try_wait() { + Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)), + Ok(None) => {} + Err(e) => { + // fatal error + log::error!("Failed to wait xorg process, {}", e); + bail!("Failed to wait xorg process, {}", e) + } + } + std::thread::sleep(std::time::Duration::from_millis(1_000)); + } + log::error!("Failed to wait xorg process, not exit"); + bail!("Failed to wait xorg process, not exit") + } else { + Ok("Xorg is already exited".to_owned()) + } + } + + fn start_x11( + env: &DesktopEnv, + uid: u32, + gid: u32, + display_num: u32, + envs: &HashMap<&str, String>, + ) -> ResultType<(Child, Child)> { + log::debug!("envs of user {}: {:?}", &env.username, &envs); + + DesktopEnv::add_xauth_cookie(&env.xauth, &env.display, uid, gid, &envs)?; + + // Start Xorg + let mut child_xorg = Self::start_x_server(&env.xauth, &env.display, uid, gid, &envs)?; + + log::info!("xorg started, wait 10 secs to ensuer x server is running"); + + let max_wait_secs = 10; + // wait x server running + if let Err(e) = + DesktopEnv::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) + { + match Self::wait_xorg_exit(&mut child_xorg) { + Ok(msg) => log::info!("{}", msg), + Err(e) => { + log::error!("{}", e); + Self::fatal_exit(); + } + } + bail!(e) + } + + log::info!( + "xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}", + &env.display, + &env.xauth + ); + + std::env::set_var("DISPLAY", &env.display); + std::env::set_var("XAUTHORITY", &env.xauth); + // start window manager (startwm.sh) + let child_wm = match Self::start_x_window_manager(uid, gid, &envs) { + Ok(c) => c, + Err(e) => { + match Self::wait_xorg_exit(&mut child_xorg) { + Ok(msg) => log::info!("{}", msg), + Err(e) => { + log::error!("{}", e); + Self::fatal_exit(); + } + } + bail!(e) + } + }; + log::info!("x window manager is started"); + + Ok((child_xorg, child_wm)) + } + + fn try_wait_x11_child_exit(child_xorg: &mut Child, child_wm: &mut Child) -> bool { + match child_xorg.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG Xorg exit with {}", + status + ); + log::info!("Xorg exit with {}", status); + return true; + } + Ok(None) => {} + Err(e) => log::error!("Failed to wait xorg process, {}", e), + } + + match child_wm.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG: wm exit with {}", + status + ); + log::info!("wm exit with {}", status); + return true; + } + Ok(None) => {} + Err(e) => log::error!("Failed to wait xorg process, {}", e), + } + false + } + + fn wait_x11_children_exit(child_xorg: &mut Child, child_wm: &mut Child) { + log::debug!("Try kill child process xorg"); + if let Ok(_) = child_xorg.kill() { + let mut exited = false; + for _ in 0..2 { + match child_xorg.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG Xorg exit with {}", + status + ); + log::info!("Xorg exit with {}", status); + exited = true; + break; + } + Ok(None) => {} + Err(e) => { + println!( + "=============================MYDEBUG Failed to wait xorg process, {}", + &e + ); + log::error!("Failed to wait xorg process, {}", e); + Self::fatal_exit(); + } + } + std::thread::sleep(std::time::Duration::from_millis(1_000)); + } + if !exited { + println!( + "=============================MYDEBUG Failed to wait child xorg, after kill()" + ); + log::error!("Failed to wait child xorg, after kill()"); + // try kill -9? + } + } + log::debug!("Try kill child process wm"); + if let Ok(_) = child_wm.kill() { + let mut exited = false; + for _ in 0..2 { + match child_wm.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG wm exit with {}", + status + ); + log::info!("wm exit with {}", status); + exited = true; + } + Ok(None) => {} + Err(e) => { + println!( + "=============================MYDEBUG Failed to wait wm process, {}", + &e + ); + log::error!("Failed to wait wm process, {}", e); + Self::fatal_exit(); + } + } + std::thread::sleep(std::time::Duration::from_millis(1_000)); + } + if !exited { + println!( + "=============================MYDEBUG Failed to wait child xorg, after kill()" + ); + log::error!("Failed to wait child xorg, after kill()"); + // try kill -9? + } + } + } + + fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool { + let mut inst = DESKTOP_INST.lock().unwrap(); + let mut exited = true; + if let Some(inst) = &mut (*inst) { + if inst.child_exit.load(Ordering::SeqCst) { + exited = true; + } else { + exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); + } + if exited { + println!("=============================MYDEBUG begin to wait x11 children exit"); + Self::wait_x11_children_exit(child_xorg, child_wm); + inst.is_child_running.store(false, Ordering::SeqCst); + inst.child_exit.store(true, Ordering::SeqCst); + } + } + exited + } + + fn wait_stop_x11(mut child_xorg: Child, mut child_wm: Child) { + loop { + if Self::try_wait_stop_x11(&mut child_xorg, &mut child_wm) { + break; + } + std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + } + } + + fn get_xorg() -> ResultType<&'static str> { + // Fedora 26 or later + let xorg = "/usr/libexec/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Debian 9 or later + let xorg = "/usr/lib/xorg/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Ubuntu 16.04 or later + let xorg = "/usr/lib/xorg/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Arch Linux + let xorg = "/usr/lib/xorg-server/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Arch Linux + let xorg = "/usr/lib/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // CentOS 7 /usr/bin/Xorg or param=Xorg + + log::warn!("Failed to find xorg, use default Xorg.\n Please add \"allowed_users=anybody\" to \"/etc/X11/Xwrapper.config\"."); + Ok("Xorg") + } + + fn start_x_server( + xauth: &str, + display: &str, + uid: u32, + gid: u32, + envs: &HashMap<&str, String>, + ) -> ResultType { + let xorg = Self::get_xorg()?; + log::info!("Use xorg: {}", &xorg); + match Command::new(xorg) + .envs(envs) + .uid(uid) + .gid(gid) + .args(vec![ + "-noreset", + "+extension", + "GLX", + "+extension", + "RANDR", + "+extension", + "RENDER", + //"-logfile", + //"/tmp/RustDesk_xorg.log", + "-config", + "rustdesk/xorg.conf", + "-auth", + xauth, + display, + ]) + .spawn() + { + Ok(c) => Ok(c), + Err(e) => { + bail!("Failed to start Xorg with display {}, {}", display, e); + } + } + } + + fn start_x_window_manager( + uid: u32, + gid: u32, + envs: &HashMap<&str, String>, + ) -> ResultType { + match Command::new("/etc/rustdesk/startwm.sh") + .envs(envs) + .uid(uid) + .gid(gid) + .spawn() + { + Ok(c) => Ok(c), + Err(e) => { + bail!("Failed to start window manager, {}", e); + } + } + } + + fn stop_children(&mut self) { + self.child_exit.store(true, Ordering::SeqCst); + for _i in 1..10 { + if !self.is_child_running.load(Ordering::SeqCst) { + break; + } + std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + } + if self.is_child_running.load(Ordering::SeqCst) { + log::warn!("xdesktop child is still running!"); + } + } +} + +fn pam_get_service_name() -> &'static str { + if Path::new("/etc/pam.d/rustdesk").is_file() { + "rustdesk" + } else { + "gdm" + } +} + +impl ToString for Protocal { + fn to_string(&self) -> String { + match self { + Protocal::X11 => ENV_DESKTOP_PROTOCAL__X11.to_owned(), + Protocal::Wayland => ENV_DESKTOP_PROTOCAL_WAYLAND.to_owned(), + Protocal::Unknown => ENV_DESKTOP_PROTOCAL_UNKNOWN.to_owned(), + } + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 777de3b01..16bcc775a 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -17,6 +17,9 @@ pub mod delegate; #[cfg(target_os = "linux")] pub mod linux; +#[cfg(target_os = "linux")] +pub mod linux_desktop; + #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::{message_proto::CursorData, ResultType}; #[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))] diff --git a/src/port_forward.rs b/src/port_forward.rs index f50f40db8..4e05ad92f 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -199,8 +199,8 @@ async fn connect_and_login_2( }, d = ui_receiver.recv() => { match d { - Some(Data::Login((password, remember))) => { - interface.handle_login_from_ui(password, remember, &mut stream).await; + Some(Data::Login((os_username, os_password, password, remember))) => { + interface.handle_login_from_ui(os_username, os_password, password, remember, &mut stream).await; } _ => {} } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 8b7dae1ba..339a45bf3 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -54,6 +54,8 @@ impl RendezvousMediator { pub async fn start_all() { let mut nat_tested = false; check_zombie(); + #[cfg(target_os = "linux")] + crate::server::check_xdesktop(); let server = new_server(); if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 { crate::test_nat_type(); @@ -72,7 +74,10 @@ impl RendezvousMediator { allow_err!(super::lan::start_listening()); }); } - loop { + #[cfg(target_os = "linux")] + crate::platform::linux_desktop::start_xdesktop(); + SHOULD_EXIT.store(false, Ordering::SeqCst); + while !SHOULD_EXIT.load(Ordering::SeqCst) { Config::reset_online(); if Config::get_option("stop-service").is_empty() { if !nat_tested { @@ -96,6 +101,8 @@ impl RendezvousMediator { } sleep(1.).await; } + #[cfg(target_os = "linux")] + crate::platform::linux_desktop::stop_xdesktop(); } pub async fn start(server: ServerPtr, host: String) -> ResultType<()> { diff --git a/src/server.rs b/src/server.rs index 681e7bed1..81c967ba1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -356,6 +356,40 @@ pub fn check_zombie() { }); } +#[cfg(target_os = "linux")] +pub fn check_xdesktop() { + std::thread::spawn(|| { + use crate::platform::linux_desktop::{get_desktop_env, DesktopEnv}; + let mut desktop_env = DesktopEnv::new(); + loop { + let new_env = match get_desktop_env() { + Some(env) => env, + None => DesktopEnv::new(), + }; + + if new_env.is_ready() { + if !desktop_env.is_ready() { + desktop_env = new_env.clone(); + } else { + if !desktop_env.is_same_env(&new_env) { + log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); + break; + } + } + } else { + if desktop_env.is_ready() { + log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); + break; + } + } + + std::thread::sleep(Duration::from_millis(300)); + } + + crate::RendezvousMediator::restart(); + }); +} + /// Start the host server that allows the remote peer to control the current machine. /// /// # Arguments diff --git a/src/server/connection.rs b/src/server/connection.rs index 23e166fcf..36190485e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -57,6 +57,16 @@ lazy_static::lazy_static! { pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); +pub const LOGIN_MSG_XDESKTOP_NOT_INITED: &str = "xdesktop env is not inited"; +pub const LOGIN_MSG_XSESSION_NOT_READY: &str = "xsession unready"; +pub const LOGIN_MSG_XSESSION_FAILED: &str = "xsession failed"; +pub const LOGIN_MSG_XSESSION_ANOTHER_USER_READTY: &str = "xsession another user login"; +pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY: &str = "xsession unready, password empty"; +pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG: &str = "xsession unready, password wrong"; +pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; +pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; +pub const LOGIN_MSG_OFFLINE: &str = "Offline"; + #[derive(Clone, Default)] pub struct ConnInner { id: i32, @@ -902,7 +912,7 @@ impl Connection { } #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.file_transfer.is_some() { - if crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { + if !crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { username = "".to_owned(); } } @@ -1061,6 +1071,47 @@ impl Connection { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } + fn try_start_desktop(_username: &str, _passsword: &str) -> String { + #[cfg(target_os = "linux")] + { + if _username.is_empty() { + match crate::platform::linux_desktop::get_desktop_env() { + Some(desktop_env) => { + if desktop_env.is_ready() { + "" + } else { + LOGIN_MSG_XSESSION_NOT_READY + } + } + None => LOGIN_MSG_XDESKTOP_NOT_INITED, + } + .to_owned() + } else { + match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { + Ok(desktop_env) => { + if desktop_env.is_ready() { + if _username != desktop_env.username { + LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() + } else { + "".to_owned() + } + } else { + LOGIN_MSG_XSESSION_NOT_READY.to_owned() + } + } + Err(e) => { + log::error!("Failed to start xsession {}", e); + LOGIN_MSG_XSESSION_FAILED.to_owned() + } + } + } + } + #[cfg(not(target_os = "linux"))] + { + "".to_owned() + } + } + fn validate_one_password(&self, password: String) -> bool { if password.len() == 0 { return false; @@ -1225,16 +1276,31 @@ impl Connection { } _ => {} } + + let desktop_err = match lr.os_login.as_ref() { + Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), + None => Self::try_start_desktop("", ""), + }; + if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY { + self.send_login_error(desktop_err).await; + return true; + } + if !hbb_common::is_ipv4_str(&lr.username) && lr.username != Config::get_id() { - self.send_login_error("Offline").await; + self.send_login_error(LOGIN_MSG_OFFLINE).await; } else if password::approve_mode() == ApproveMode::Click || password::approve_mode() == ApproveMode::Both && !password::has_valid_password() { - self.try_start_cm(lr.my_id, lr.my_name, false); - if hbb_common::get_version_number(&lr.version) - >= hbb_common::get_version_number("1.2.0") - { - self.send_login_error("No Password Access").await; + if desktop_err.is_empty() { + self.try_start_cm(lr.my_id, lr.my_name, false); + if hbb_common::get_version_number(&lr.version) + >= hbb_common::get_version_number("1.2.0") + { + self.send_login_error("No Password Access").await; + } + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) + .await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1290,16 +1356,25 @@ impl Connection { .lock() .unwrap() .insert(self.ip.clone(), failure); - self.send_login_error("Wrong Password").await; - self.try_start_cm(lr.my_id, lr.my_name, false); + if desktop_err.is_empty() { + self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.try_start_cm(lr.my_id, lr.my_name, false); + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) + .await; + } } else { if failure.0 != 0 { LOGIN_FAILURES.lock().unwrap().remove(&self.ip); } - self.try_start_cm(lr.my_id, lr.my_name, true); - self.send_logon_response().await; - if self.port_forward_socket.is_some() { - return false; + if desktop_err.is_empty() { + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; + } + } else { + self.send_login_error(desktop_err).await; } } } @@ -2072,7 +2147,7 @@ async fn start_ipc( tx_from_cm: mpsc::UnboundedSender, ) -> ResultType<()> { loop { - if !crate::platform::is_prelogin() { + if crate::platform::is_prelogin() { break; } sleep(1.).await; diff --git a/src/server/service.rs b/src/server/service.rs index 9cc1e860c..fe038f3c0 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -221,6 +221,7 @@ impl> ServiceTmpl { thread::sleep(interval - elapsed); } } + log::info!("Service {} exit", sp.name()); }); self.0.write().unwrap().handle = Some(thread); } @@ -256,6 +257,7 @@ impl> ServiceTmpl { } thread::sleep(time::Duration::from_millis(HIBERNATE_TIMEOUT)); } + log::info!("Service {} exit", sp.name()); }); self.0.write().unwrap().handle = Some(thread); } diff --git a/src/ui/common.css b/src/ui/common.css index 0fb9afcb1..9845ff104 100644 --- a/src/ui/common.css +++ b/src/ui/common.css @@ -142,6 +142,10 @@ div.password input { font-size: 1.2em; } +div.username input { + font-size: 1.2em; +} + svg { background: none; } diff --git a/src/ui/common.tis b/src/ui/common.tis index b6723b131..ef6d215aa 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -252,7 +252,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= view.close(); return; } - handler.login(res.password, res.remember); + handler.login("", "", res.password, res.remember); if (!is_port_forward) { // Specially handling file transfer for no permission hanging issue (including 60ms // timer in setPermission. @@ -262,6 +262,30 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= else msgbox("connecting", "Connecting...", "Logging in..."); } }; + } else if (type == "xsession-login" || type == "xsession-re-login") { + callback = function (res) { + if (!res) { + view.close(); + return; + } + handler.login(res.osusername, res.ospassword, "", false); + if (!is_port_forward) { + if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in..."); + else msgbox("connecting", "Connecting...", "Logging in..."); + } + }; + } else if (type.indexOf("xsession-login") >= 0) { + callback = function (res) { + if (!res) { + view.close(); + return; + } + handler.login(res.osusername, res.ospassword, res.password, res.remember); + if (!is_port_forward) { + if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in..."); + else msgbox("connecting", "Connecting...", "Logging in..."); + } + }; } else if (type.indexOf("custom") < 0 && !is_port_forward && !callback) { callback = function() { view.close(); } } else if (type == 'wait-remote-accept-nook') { diff --git a/src/ui/msgbox.tis b/src/ui/msgbox.tis index d5c60d95c..d9a311452 100644 --- a/src/ui/msgbox.tis +++ b/src/ui/msgbox.tis @@ -32,7 +32,7 @@ class MsgboxComponent: Reactor.Component { } function getIcon(color) { - if (this.type == "input-password") { + if (this.type == "input-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { return ; } if (this.type == "connecting") { @@ -41,7 +41,7 @@ class MsgboxComponent: Reactor.Component { if (this.type == "success") { return ; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { return ; } return null; @@ -56,11 +56,36 @@ class MsgboxComponent: Reactor.Component {
    ; } + function getInputUserPasswordContent() { + return
    +
    {translate("OS Username")}
    +
    +
    {translate("OS Password")}
    + +
    +
    ; + } + + function getXsessionPasswordContent() { + return
    +
    {translate("OS Username")}
    +
    +
    {translate("OS Password")}
    + +
    {translate('Please enter your password')}
    + +
    {translate('Remember password')}
    +
    ; + } + function getContent() { if (this.type == "input-password") { return this.getInputPasswordContent(); - } - if (this.type == "custom-os-password") { + } else if (this.type == "xsession-login") { + return this.getInputUserPasswordContent(); + } else if (this.type == "xsession-login-password") { + return this.getXsessionPasswordContent(); + } else if (this.type == "custom-os-password") { var ts = this.auto_login ? { checked: true } : {}; return
    @@ -71,13 +96,13 @@ class MsgboxComponent: Reactor.Component { } function getColor() { - if (this.type == "input-password" || this.type == "custom-os-password") { + if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { return "#AD448E"; } if (this.type == "success") { return "#32bea6"; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { return "#e04f5f"; } return "#2C8CFF"; @@ -177,6 +202,16 @@ class MsgboxComponent: Reactor.Component { this.update(); return; } + if (this.type == "xsession-re-login") { + this.type = "xsession-login"; + this.update(); + return; + } + if (this.type == "xsession-login-re-password") { + this.type = "xsession-login-password"; + this.update(); + return; + } var values = this.getValues(); if (this.callback) { var self = this; @@ -238,6 +273,21 @@ class MsgboxComponent: Reactor.Component { return; } } + if (this.type == "xsession-login") { + values.osusername = (values.osusername || "").trim(); + values.ospassword = (values.ospassword || "").trim(); + if (!values.osusername || !values.ospassword) { + return; + } + } + if (this.type == "xsession-login-password") { + values.password = (values.password || "").trim(); + values.osusername = (values.osusername || "").trim(); + values.ospassword = (values.ospassword || "").trim(); + if (!values.osusername || !values.ospassword || !values.password) { + return; + } + } return values; } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 68decf955..966315a23 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -398,7 +398,7 @@ impl sciter::EventHandler for SciterSession { fn is_file_transfer(); fn is_port_forward(); fn is_rdp(); - fn login(String, bool); + fn login(String, String, String, bool); fn new_rdp(); fn send_mouse(i32, i32, i32, bool, bool, bool, bool); fn enter(); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f89be4879..2a0776668 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -711,8 +711,14 @@ impl Session { fs::get_string(&path) } - pub fn login(&self, password: String, remember: bool) { - self.send(Data::Login((password, remember))); + pub fn login( + &mut self, + os_username: String, + os_password: String, + password: String, + remember: bool, + ) { + self.send(Data::Login((os_username, os_password, password, remember))); } pub fn new_rdp(&self) { @@ -1005,8 +1011,23 @@ impl Interface for Session { handle_hash(self.lc.clone(), pass, hash, self, peer).await; } - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { - handle_login_from_ui(self.lc.clone(), password, remember, peer).await; + async fn handle_login_from_ui( + &mut self, + os_username: String, + os_password: String, + password: String, + remember: bool, + peer: &mut Stream, + ) { + handle_login_from_ui( + self.lc.clone(), + os_username, + os_password, + password, + remember, + peer, + ) + .await; } async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { From a94052a24a15ac112d7a91ff0e6cbffc385c8080 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 18:01:27 +0800 Subject: [PATCH 032/112] ignore seat0 on gdm Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 2 +- src/platform/linux_desktop.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1d826ea97..cf1cf6da5 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { + if !line.contains("gdm") && line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop.rs index bf9f465d0..0d25b3743 100644 --- a/src/platform/linux_desktop.rs +++ b/src/platform/linux_desktop.rs @@ -237,7 +237,7 @@ impl DesktopEnv { fn get_env_seat0(&mut self) -> ResultType { let output = Command::new("loginctl").output()?; for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { + if !line.contains("gdm") && line.contains("seat0") { if let Some(sid) = line.split_whitespace().nth(0) { if Self::is_active(sid)? { if let Some(uid) = line.split_whitespace().nth(1) { From c65a3b2489c76b22fb70897bad3e8632d312b660 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 18:40:25 +0800 Subject: [PATCH 033/112] virtual display, update build script Signed-off-by: fufesou --- build.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index 7acf97a7d..3a06c4161 100755 --- a/build.py +++ b/build.py @@ -287,6 +287,8 @@ def build_flutter_deb(version, features): system2('flutter build linux --release') system2('mkdir -p tmpdeb/usr/bin/') system2('mkdir -p tmpdeb/usr/lib/rustdesk') + system2('mkdir -p tmpdeb/etc/rustdesk/') + system2('mkdir -p tmpdeb/etc/pam.d/') system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') system2('mkdir -p tmpdeb/usr/share/applications/') system2('mkdir -p tmpdeb/usr/share/polkit-1/actions') @@ -303,6 +305,12 @@ def build_flutter_deb(version, features): 'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop') system2( 'cp ../res/com.rustdesk.RustDesk.policy tmpdeb/usr/share/polkit-1/actions/') + system2( + 'cp ../res/startwm.sh tmpdeb/etc/rustdesk/') + system2( + 'cp ../res/xorg.conf tmpdeb/etc/rustdesk/') + system2( + 'cp ../res/pam.d/rustdesk.debian tmpdeb/etc/pam.d/rustdesk') system2( "echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit") @@ -553,12 +561,21 @@ def main(): 'cp res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop') system2( 'cp res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop') - system2('cp -a res/DEBIAN/* tmpdeb/DEBIAN/') + os.system('mkdir -p tmpdeb/etc/rustdesk/') + os.system('cp -a res/startwm.sh tmpdeb/etc/rustdesk/') + os.system('mkdir -p tmpdeb/etc/X11/rustdesk/') + os.system('cp res/xorg.conf tmpdeb/etc/X11/rustdesk/') + os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/') + os.system('mkdir -p tmpdeb/etc/pam.d/') + os.system('cp pam.d/rustdesk.debian tmpdeb/etc/pam.d/rustdesk') system2('strip tmpdeb/usr/bin/rustdesk') system2('mkdir -p tmpdeb/usr/lib/rustdesk') system2('mv tmpdeb/usr/bin/rustdesk tmpdeb/usr/lib/rustdesk/') system2('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/') md5_file('usr/share/rustdesk/files/systemd/rustdesk.service') + md5_file('etc/rustdesk/startwm.sh') + md5_file('etc/X11/rustdesk/xorg.conf') + md5_file('etc/pam.d/rustdesk') md5_file('usr/lib/rustdesk/libsciter-gtk.so') system2('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/') os.rename('rustdesk.deb', 'rustdesk-%s.deb' % version) From e24e05ba5c31db9b1051bcbb2d01bafda888b900 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 19:57:58 +0800 Subject: [PATCH 034/112] tmp commit Signed-off-by: fufesou --- flutter/lib/models/model.dart | 10 ++++++++-- src/flutter_ffi.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 06bfeec45..20999f1bb 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1631,8 +1631,14 @@ class FFI { } /// Login with [password], choose if the client should [remember] it. - void login(String id, String password, bool remember) { - bind.sessionLogin(id: id, password: password, remember: remember); + void login(String osUsername, String osPassword, String id, String password, + bool remember) { + bind.sessionLogin( + id: id, + os_username: osUsername, + os_password: osPassword, + password: password, + remember: remember); } /// Close the remote session. diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index aee486b94..c810437fb 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -136,9 +136,15 @@ pub fn session_get_option(id: String, arg: String) -> Option { } } -pub fn session_login(id: String, password: String, remember: bool) { +pub fn session_login( + id: String, + os_username: String, + os_password: String, + password: String, + remember: bool, +) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.login(password, remember); + session.login(os_username, os_password, password, remember); } } From 988d8dc32c093ca5e1d67d29622602b67570bc50 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 24 Mar 2023 11:34:00 +0800 Subject: [PATCH 035/112] test build Signed-off-by: fufesou --- src/ui_session_interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 2a0776668..39e614230 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -712,7 +712,7 @@ impl Session { } pub fn login( - &mut self, + &self, os_username: String, os_password: String, password: String, From 461aa622f8861322b55205db8c4d4bf0e8799c3a Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 08:57:19 +0800 Subject: [PATCH 036/112] fix build Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 4 ++-- flutter/lib/models/model.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index cc815c5a3..4562e519c 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -506,8 +506,8 @@ _connectDialog( final peerPassword = peerPasswordController?.text.trim() ?? ''; if (peerPasswordController != null && peerPassword.isEmpty) return; gFFI.login( - // username, - // password, + username, + password, id, peerPassword, remember, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 20999f1bb..155956c74 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1635,8 +1635,8 @@ class FFI { bool remember) { bind.sessionLogin( id: id, - os_username: osUsername, - os_password: osPassword, + osUsername: osUsername, + osPassword: osPassword, password: password, remember: remember); } From 5e794818601ba0a7bab881b83f19aa4a2250a442 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 20:20:45 +0800 Subject: [PATCH 037/112] linux, refact desktop env Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 2 +- src/platform/linux.rs | 10 +- src/platform/linux_desktop.rs | 358 ++++++++++---------------- src/rendezvous_mediator.rs | 2 - src/server.rs | 34 --- src/server/connection.rs | 50 ++-- 6 files changed, 163 insertions(+), 293 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index cf1cf6da5..1d826ea97 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if !line.contains("gdm") && line.contains("seat0") { + if line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index c1da431a4..857372bd7 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,4 +1,4 @@ -use super::linux_desktop::GNOME_SESSION_BINARY; +use super::linux_desktop::{get_desktop_env, Desktop}; use super::{CursorData, ResultType}; use desktop::Desktop; pub use hbb_common::platform::linux::*; @@ -382,13 +382,13 @@ pub fn start_os_service() { #[inline] pub fn get_active_user_id_name() -> (String, String) { - let vec_id_name = get_values_of_seat0(&[1, 2]); - (vec_id_name[0].clone(), vec_id_name[1].clone()) + let desktop = get_desktop_env(); + (desktop.uid.clone(), desktop.username.clone()) } #[inline] pub fn get_active_userid() -> String { - get_values_of_seat0(&[1])[0].clone() + get_desktop_env().uid.clone() } fn get_cm() -> bool { @@ -434,7 +434,7 @@ fn _get_display_manager() -> String { #[inline] pub fn get_active_username() -> String { - get_values_of_seat0(&[2])[0].clone() + get_desktop_env().username.clone() } pub fn get_active_user_home() -> Option { diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop.rs index 0d25b3743..0cd66512a 100644 --- a/src/platform/linux_desktop.rs +++ b/src/platform/linux_desktop.rs @@ -17,7 +17,8 @@ use users::{get_user_by_name, os::unix::UserExt, User}; lazy_static::lazy_static! { static ref DESKTOP_RUNNING: Arc = Arc::new(AtomicBool::new(false)); - static ref DESKTOP_INST: Arc>> = Arc::new(Mutex::new(None)); + static ref DESKTOP_ENV: Arc> = Arc::new(Mutex::new(Desktop::new())); + static ref CHILD_FLAGS: Arc>> = Arc::new(Mutex::new(None)); } pub const VIRTUAL_X11_DESKTOP: &str = "xfce4"; @@ -37,7 +38,8 @@ pub enum Protocal { } #[derive(Debug, Clone)] -pub struct DesktopEnv { +pub struct Desktop { + pub sid: String, pub protocal: Protocal, pub username: String, pub uid: String, @@ -46,24 +48,31 @@ pub struct DesktopEnv { } #[derive(Debug)] -pub struct Desktop { - env: DesktopEnv, +struct ChildFlags { child_exit: Arc, is_child_running: Arc, } fn check_update_env() { - let mut inst = DESKTOP_INST.lock().unwrap(); - if let Some(inst) = &mut (*inst) { - if !inst.is_child_running.load(Ordering::SeqCst) { - inst.child_exit.store(true, Ordering::SeqCst); - let old_env = inst.env.clone(); - allow_err!(inst.env.refresh()); - if !inst.env.is_same_env(&old_env) { - inst.env.update_env(); - log::debug!("desktop env changed, {:?}", &inst.env); - } + let mut child_flags = CHILD_FLAGS.lock().unwrap(); + let mut desktop = DESKTOP_ENV.lock().unwrap(); + + if let Some(child_flags) = &mut (*child_flags) { + if child_flags.is_child_running.load(Ordering::SeqCst) { + return; } + child_flags.child_exit.store(true, Ordering::SeqCst); + } + + if !desktop.sid.is_empty() && is_active(&desktop.sid) { + return; + } + + let old_desktop = desktop.clone(); + desktop.refresh(); + if !desktop.is_same_env(&old_desktop) { + desktop.update_env(); + log::debug!("desktop env changed, {:?}", &desktop); } } @@ -74,7 +83,7 @@ pub fn start_xdesktop() { } else { log::info!("Wait desktop: none"); } - *DESKTOP_INST.lock().unwrap() = Some(Desktop::new()); + *CHILD_FLAGS.lock().unwrap() = Some(ChildFlags::new()); let interval = time::Duration::from_millis(super::SERVICE_INTERVAL); DESKTOP_RUNNING.store(true, Ordering::SeqCst); @@ -90,19 +99,24 @@ pub fn stop_xdesktop() { DESKTOP_RUNNING.store(false, Ordering::SeqCst); } -pub fn get_desktop_env() -> Option { - match &*DESKTOP_INST.lock().unwrap() { - Some(inst) => Some(inst.env.clone()), - None => None, - } +#[inline] +pub fn get_desktop_env() -> Desktop { + DESKTOP_ENV.lock().unwrap().clone() } -pub fn try_start_x_session(username: &str, password: &str) -> ResultType { - let mut inst = DESKTOP_INST.lock().unwrap(); - if let Some(inst) = &mut (*inst) { - let _ = inst.try_start_x_session(username, password)?; - log::debug!("try_start_x_session, username: {}, {:?}", &username, &inst); - Ok(inst.env.clone()) +pub fn try_start_x_session(username: &str, password: &str) -> ResultType { + let mut child_flags = CHILD_FLAGS.lock().unwrap(); + let mut desktop_lock = DESKTOP_ENV.lock().unwrap(); + let mut desktop = Desktop::new(); + if let Some(child_flags) = &mut (*child_flags) { + let _ = child_flags.try_start_x_session(&mut desktop, username, password)?; + log::debug!( + "try_start_x_session, username: {}, {:?}", + &username, + &child_flags + ); + *desktop_lock = desktop.clone(); + Ok(desktop) } else { bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); } @@ -111,8 +125,8 @@ pub fn try_start_x_session(username: &str, password: &str) -> ResultType bool { let wait_begin = Instant::now(); while wait_begin.elapsed().as_secs() < timeout_secs { - let seat0 = get_values_of_seat0(&[0]); - if !seat0[0].is_empty() { + let uid = &get_values_of_seat0(&[1])[0]; + if !uid.is_empty() { return true; } @@ -132,11 +146,12 @@ fn wait_xdesktop(timeout_secs: u64) -> bool { false } -impl DesktopEnv { +impl Desktop { pub fn new() -> Self { let xauth = get_env_var("XAUTHORITY"); Self { + sid: "".to_owned(), protocal: Protocal::Unknown, username: "".to_owned(), uid: "".to_owned(), @@ -150,29 +165,43 @@ impl DesktopEnv { } fn update_env(&self) { - if self.is_ready() { + if self.is_x11() { std::env::set_var("DISPLAY", &self.display); std::env::set_var("XAUTHORITY", &self.xauth); std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); } else { std::env::set_var("DISPLAY", ""); std::env::set_var("XAUTHORITY", ""); - std::env::set_var(ENV_DESKTOP_PROTOCAL, &Protocal::Unknown.to_string()); + std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); } } pub fn is_same_env(&self, other: &Self) -> bool { - self.protocal == other.protocal + self.sid == other.sid + && self.protocal == other.protocal && self.uid == other.uid && self.display == other.display && self.xauth == other.xauth } - #[inline(always)] - pub fn is_ready(&self) -> bool { + #[inline] + pub fn is_x11(&self) -> bool { self.protocal == Protocal::X11 } + #[inline] + pub fn is_wayland(&self) -> bool { + self.protocal == Protocal::Wayland + } + + #[inline] + pub fn is_ready(&self) -> bool { + match self.protocal { + Protocal::X11 | Protocal::Wayland => true, + _ => false, + } + } + // The logic mainly fron https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334. fn get_avail_display() -> ResultType { let display_range = 0..51; @@ -185,6 +214,7 @@ impl DesktopEnv { bail!("No avaliable display found in range {:?}", display_range) } + #[inline] fn is_x_server_running(display: u32) -> bool { Path::new(&format!("/tmp/.X11-unix/X{}", display)).exists() || Path::new(&format!("/tmp/.X{}-lock", display)).exists() @@ -233,148 +263,6 @@ impl DesktopEnv { } } - // fixme: reduce loginctl - fn get_env_seat0(&mut self) -> ResultType { - let output = Command::new("loginctl").output()?; - for line in String::from_utf8_lossy(&output.stdout).lines() { - if !line.contains("gdm") && line.contains("seat0") { - if let Some(sid) = line.split_whitespace().nth(0) { - if Self::is_active(sid)? { - if let Some(uid) = line.split_whitespace().nth(1) { - self.uid = uid.to_owned(); - } - if let Some(u) = line.split_whitespace().nth(2) { - self.username = u.to_owned(); - } - - self.protocal = Protocal::Unknown; - let type_output = Command::new("loginctl") - .args(vec!["show-session", "-p", "Type", sid]) - .output()?; - let type_stdout = String::from_utf8_lossy(&type_output.stdout); - - if type_stdout.contains("x11") { - self.protocal = Protocal::X11; - break; - } else if type_stdout.contains("wayland") { - self.protocal = Protocal::Wayland; - } - } - } - } - } - Ok(self.is_ready()) - } - - // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 - fn get_env_active(&mut self) -> ResultType { - let output = Command::new("loginctl").output()?; - - // set active Xorg session - for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("sessions listed.") { - continue; - } - if let Some(sid) = line.split_whitespace().nth(0) { - if Self::is_active(sid)? { - if Self::get_display_server_of_session(sid) == ENV_DESKTOP_PROTOCAL__X11 { - if let Some(uid) = line.split_whitespace().nth(1) { - self.uid = uid.to_owned(); - } - if let Some(u) = line.split_whitespace().nth(2) { - self.username = u.to_owned(); - } - - self.protocal = Protocal::X11; - } - } - } - } - // // set active xfce4 session - // for line in String::from_utf8_lossy(&output.stdout).lines() { - // if let Some(sid) = line.split_whitespace().nth(0) { - // if Self::is_active(sid)? { - // let tty_output = Command::new("loginctl") - // .args(vec!["show-session", "-p", "TTY", sid]) - // .output()?; - // let tty: String = String::from_utf8_lossy(&tty_output.stdout) - // .replace("TTY=", "") - // .trim_end() - // .into(); - - // let xfce_panel_info = - // run_cmds(format!("ps -e | grep \"{}.\\\\+{}\"", tty, XFCE4_PANEL))?; - // if xfce_panel_info.trim_end().to_string() != "" { - // if let Some(uid) = line.split_whitespace().nth(1) { - // self.uid = uid.to_owned(); - // } - // if let Some(u) = line.split_whitespace().nth(2) { - // self.username = u.to_owned(); - // } - // } - // } - // } - // } - Ok(self.is_ready()) - } - - // fixme: dup - fn get_display_server_of_session(session: &str) -> String { - if let Ok(output) = Command::new("loginctl") - .args(vec!["show-session", "-p", "Type", session]) - .output() - // Check session type of the session - { - let display_server = String::from_utf8_lossy(&output.stdout) - .replace("Type=", "") - .trim_end() - .into(); - if display_server == "tty" { - // If the type is tty... - if let Ok(output) = Command::new("loginctl") - .args(vec!["show-session", "-p", "TTY", session]) - .output() - // Get the tty number - { - let tty: String = String::from_utf8_lossy(&output.stdout) - .replace("TTY=", "") - .trim_end() - .into(); - 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() != "" { - // If it is, manually return "x11", otherwise return tty - ENV_DESKTOP_PROTOCAL__X11.to_owned() - } else { - display_server - } - } else { - // If any of these commands fail just fall back to the display server - display_server - } - } else { - display_server - } - } else { - // If the session is not a tty, then just return the type as usual - display_server - } - } else { - "".to_owned() - } - } - - // fixme: remove - fn is_active(sid: &str) -> ResultType { - let output = Command::new("loginctl") - .args(vec!["show-session", "-p", "State", sid]) - .output()?; - - Ok(String::from_utf8_lossy(&output.stdout).contains("active")) - } - fn get_display_by_user(user: &str) -> String { // log::debug!("w {}", &user); if let Ok(output) = std::process::Command::new("w").arg(&user).output() { @@ -455,38 +343,47 @@ impl DesktopEnv { } } - fn refresh(&mut self) -> ResultType { + fn refresh(&mut self) { *self = Self::new(); - if self.get_env_seat0()? || self.get_env_active()? { - self.get_display(); - self.get_xauth(); - Ok(true) - } else { - Ok(false) + + let seat0_values = get_values_of_seat0(&[0, 1, 2]); + if seat0_values[0].is_empty() { + return; } + + self.sid = seat0_values[0].clone(); + self.uid = seat0_values[1].clone(); + self.username = seat0_values[2].clone(); + self.protocal = get_display_server_of_session(&self.sid).into(); + self.get_display(); + self.get_xauth(); } } -impl Drop for Desktop { +impl Drop for ChildFlags { fn drop(&mut self) { self.stop_children(); } } -impl Desktop { +impl ChildFlags { fn fatal_exit() { std::process::exit(0); } pub fn new() -> Self { Self { - env: DesktopEnv::new(), child_exit: Arc::new(AtomicBool::new(true)), is_child_running: Arc::new(AtomicBool::new(false)), } } - fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> { + fn try_start_x_session( + &mut self, + desktop: &mut Desktop, + username: &str, + password: &str, + ) -> ResultType<()> { match get_user_by_name(username) { Some(userinfo) => { let mut client = pam::Client::with_password(pam_get_service_name())?; @@ -495,22 +392,24 @@ impl Desktop { .set_credentials(username, password); match client.authenticate() { Ok(_) => { - if self.env.is_ready() && self.env.username == username { + if desktop.is_x11() && desktop.username == username { return Ok(()); } - self.env.username = username.to_string(); - self.env.uid = userinfo.uid().to_string(); - self.env.protocal = Protocal::Unknown; - match self.start_x_session(&userinfo, password) { + // to-do: sid is empty if xorg is managed by this process + desktop.sid = "".to_owned(); + desktop.username = username.to_string(); + desktop.uid = userinfo.uid().to_string(); + desktop.protocal = Protocal::Unknown; + match self.start_x_session(desktop, &userinfo, password) { Ok(_) => { - log::info!("Succeeded to start x11, update env {:?}", &self.env); - self.env.update_env(); + log::info!("Succeeded to start x11, update env {:?}", &desktop); + desktop.update_env(); Ok(()) } Err(e) => { - self.env = DesktopEnv::new(); - self.env.update_env(); + *desktop = Desktop::new(); + desktop.update_env(); bail!("failed to start x session, {}", e); } } @@ -526,19 +425,24 @@ impl Desktop { } } - fn start_x_session(&mut self, userinfo: &User, password: &str) -> ResultType<()> { + fn start_x_session( + &mut self, + desktop: &mut Desktop, + userinfo: &User, + password: &str, + ) -> ResultType<()> { self.stop_children(); - let display_num = DesktopEnv::get_avail_display()?; + let display_num = Desktop::get_avail_display()?; // "xServer_ip:display_num.screen_num" - self.env.display = format!(":{}", display_num); + desktop.display = format!(":{}", display_num); let uid = userinfo.uid(); let gid = userinfo.primary_group_id(); let envs = HashMap::from([ ("SHELL", userinfo.shell().to_string_lossy().to_string()), ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), - ("USER", self.env.username.clone()), + ("USER", desktop.username.clone()), ("UID", userinfo.uid().to_string()), ("HOME", userinfo.home_dir().to_string_lossy().to_string()), ( @@ -549,7 +453,7 @@ impl Desktop { // ("XAUTHORITY", self.xauth.clone()), // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), ]); - let env = self.env.clone(); + let desktop_clone = desktop.clone(); self.child_exit.store(false, Ordering::SeqCst); let is_child_running = self.is_child_running.clone(); @@ -560,7 +464,7 @@ impl Desktop { match Self::start_x_session_thread( tx_res.clone(), is_child_running, - env, + desktop_clone, uid, gid, display_num, @@ -579,7 +483,7 @@ impl Desktop { match rx_res.recv_timeout(Duration::from_millis(10_000)) { Ok(res) => { if res == "" { - self.env.protocal = Protocal::X11; + desktop.protocal = Protocal::X11; Ok(()) } else { bail!(res) @@ -594,7 +498,7 @@ impl Desktop { fn start_x_session_thread( tx_res: SyncSender, is_child_running: Arc, - env: DesktopEnv, + desktop: Desktop, uid: u32, gid: u32, display_num: u32, @@ -604,16 +508,16 @@ impl Desktop { let mut client = pam::Client::with_password(pam_get_service_name())?; client .conversation_mut() - .set_credentials(&env.username, &password); + .set_credentials(&desktop.username, &password); client.authenticate()?; - client.set_item(pam::PamItemType::TTY, &env.display)?; + client.set_item(pam::PamItemType::TTY, &desktop.display)?; client.open_session()?; // fixme: FreeBSD kernel needs to login here. // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 - let (child_xorg, child_wm) = Self::start_x11(&env, uid, gid, display_num, &envs)?; + let (child_xorg, child_wm) = Self::start_x11(&desktop, uid, gid, display_num, &envs)?; is_child_running.store(true, Ordering::SeqCst); log::info!("Start xorg and wm done, notify and wait xtop x11"); @@ -646,25 +550,25 @@ impl Desktop { } fn start_x11( - env: &DesktopEnv, + desktop: &Desktop, uid: u32, gid: u32, display_num: u32, envs: &HashMap<&str, String>, ) -> ResultType<(Child, Child)> { - log::debug!("envs of user {}: {:?}", &env.username, &envs); + log::debug!("envs of user {}: {:?}", &desktop.username, &envs); - DesktopEnv::add_xauth_cookie(&env.xauth, &env.display, uid, gid, &envs)?; + Desktop::add_xauth_cookie(&desktop.xauth, &desktop.display, uid, gid, &envs)?; // Start Xorg - let mut child_xorg = Self::start_x_server(&env.xauth, &env.display, uid, gid, &envs)?; + let mut child_xorg = + Self::start_x_server(&desktop.xauth, &desktop.display, uid, gid, &envs)?; log::info!("xorg started, wait 10 secs to ensuer x server is running"); let max_wait_secs = 10; // wait x server running - if let Err(e) = - DesktopEnv::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) + if let Err(e) = Desktop::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) { match Self::wait_xorg_exit(&mut child_xorg) { Ok(msg) => log::info!("{}", msg), @@ -678,12 +582,12 @@ impl Desktop { log::info!( "xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}", - &env.display, - &env.xauth + &desktop.display, + &desktop.xauth ); - std::env::set_var("DISPLAY", &env.display); - std::env::set_var("XAUTHORITY", &env.xauth); + std::env::set_var("DISPLAY", &desktop.display); + std::env::set_var("XAUTHORITY", &desktop.xauth); // start window manager (startwm.sh) let child_wm = match Self::start_x_window_manager(uid, gid, &envs) { Ok(c) => c, @@ -803,10 +707,10 @@ impl Desktop { } fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool { - let mut inst = DESKTOP_INST.lock().unwrap(); + let mut child_flags = CHILD_FLAGS.lock().unwrap(); let mut exited = true; - if let Some(inst) = &mut (*inst) { - if inst.child_exit.load(Ordering::SeqCst) { + if let Some(child_flags) = &mut (*child_flags) { + if child_flags.child_exit.load(Ordering::SeqCst) { exited = true; } else { exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); @@ -814,8 +718,8 @@ impl Desktop { if exited { println!("=============================MYDEBUG begin to wait x11 children exit"); Self::wait_x11_children_exit(child_xorg, child_wm); - inst.is_child_running.store(false, Ordering::SeqCst); - inst.child_exit.store(true, Ordering::SeqCst); + child_flags.is_child_running.store(false, Ordering::SeqCst); + child_flags.child_exit.store(true, Ordering::SeqCst); } } exited @@ -949,3 +853,13 @@ impl ToString for Protocal { } } } + +impl From for Protocal { + fn from(value: String) -> Self { + match &value as &str { + ENV_DESKTOP_PROTOCAL__X11 => Protocal::X11, + ENV_DESKTOP_PROTOCAL_WAYLAND => Protocal::Wayland, + _ => Protocal::Unknown, + } + } +} diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 339a45bf3..5a68a4a03 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -54,8 +54,6 @@ impl RendezvousMediator { pub async fn start_all() { let mut nat_tested = false; check_zombie(); - #[cfg(target_os = "linux")] - crate::server::check_xdesktop(); let server = new_server(); if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 { crate::test_nat_type(); diff --git a/src/server.rs b/src/server.rs index 81c967ba1..681e7bed1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -356,40 +356,6 @@ pub fn check_zombie() { }); } -#[cfg(target_os = "linux")] -pub fn check_xdesktop() { - std::thread::spawn(|| { - use crate::platform::linux_desktop::{get_desktop_env, DesktopEnv}; - let mut desktop_env = DesktopEnv::new(); - loop { - let new_env = match get_desktop_env() { - Some(env) => env, - None => DesktopEnv::new(), - }; - - if new_env.is_ready() { - if !desktop_env.is_ready() { - desktop_env = new_env.clone(); - } else { - if !desktop_env.is_same_env(&new_env) { - log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); - break; - } - } - } else { - if desktop_env.is_ready() { - log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); - break; - } - } - - std::thread::sleep(Duration::from_millis(300)); - } - - crate::RendezvousMediator::restart(); - }); -} - /// Start the host server that allows the remote peer to control the current machine. /// /// # Arguments diff --git a/src/server/connection.rs b/src/server/connection.rs index 36190485e..a5c63a80e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1073,43 +1073,35 @@ impl Connection { fn try_start_desktop(_username: &str, _passsword: &str) -> String { #[cfg(target_os = "linux")] - { - if _username.is_empty() { - match crate::platform::linux_desktop::get_desktop_env() { - Some(desktop_env) => { - if desktop_env.is_ready() { - "" - } else { - LOGIN_MSG_XSESSION_NOT_READY - } - } - None => LOGIN_MSG_XDESKTOP_NOT_INITED, - } - .to_owned() + if _username.is_empty() { + let desktop = crate::platform::linux_desktop::get_desktop_env(); + if desktop.is_ready() { + "" } else { - match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { - Ok(desktop_env) => { - if desktop_env.is_ready() { - if _username != desktop_env.username { - LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() - } else { - "".to_owned() - } + LOGIN_MSG_XSESSION_NOT_READY + } + .to_owned() + } else { + match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { + Ok(desktop) => { + if desktop.is_ready() { + if _username != desktop.username { + LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() } else { - LOGIN_MSG_XSESSION_NOT_READY.to_owned() + "".to_owned() } + } else { + LOGIN_MSG_XSESSION_NOT_READY.to_owned() } - Err(e) => { - log::error!("Failed to start xsession {}", e); - LOGIN_MSG_XSESSION_FAILED.to_owned() - } + } + Err(e) => { + log::error!("Failed to start xsession {}", e); + LOGIN_MSG_XSESSION_FAILED.to_owned() } } } #[cfg(not(target_os = "linux"))] - { - "".to_owned() - } + "".to_owned() } fn validate_one_password(&self, password: String) -> bool { From 30375a98536a6b3306a605d22692587ded5f3fb2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 22:20:15 +0800 Subject: [PATCH 038/112] refact linux desktop env Signed-off-by: fufesou --- src/platform/linux.rs | 4 +- ...ux_desktop.rs => linux_desktop_manager.rs} | 672 ++++++------------ src/platform/mod.rs | 2 +- src/rendezvous_mediator.rs | 4 +- src/server/connection.rs | 17 +- 5 files changed, 250 insertions(+), 449 deletions(-) rename src/platform/{linux_desktop.rs => linux_desktop_manager.rs} (63%) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 857372bd7..85f763ec6 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,4 +1,3 @@ -use super::linux_desktop::{get_desktop_env, Desktop}; use super::{CursorData, ResultType}; use desktop::Desktop; pub use hbb_common::platform::linux::*; @@ -297,6 +296,7 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); start_uinput_service(); + start_check_desktop_env(); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -371,6 +371,8 @@ pub fn start_os_service() { } } + stop_check_desktop_env(); + if let Some(ps) = user_server.take().as_mut() { allow_err!(ps.kill()); } diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop_manager.rs similarity index 63% rename from src/platform/linux_desktop.rs rename to src/platform/linux_desktop_manager.rs index 0cd66512a..8b9e20640 100644 --- a/src/platform/linux_desktop.rs +++ b/src/platform/linux_desktop_manager.rs @@ -17,188 +17,161 @@ use users::{get_user_by_name, os::unix::UserExt, User}; lazy_static::lazy_static! { static ref DESKTOP_RUNNING: Arc = Arc::new(AtomicBool::new(false)); - static ref DESKTOP_ENV: Arc> = Arc::new(Mutex::new(Desktop::new())); - static ref CHILD_FLAGS: Arc>> = Arc::new(Mutex::new(None)); -} - -pub const VIRTUAL_X11_DESKTOP: &str = "xfce4"; -pub const VIRTUAL_X11_DESKTOP_START: &str = "startxfce4"; -pub const XFCE4_PANEL: &str = "xfce4-panel"; -pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary"; -pub const ENV_DESKTOP_PROTOCAL: &str = "RUSTDESK_PROTOCAL"; -pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; -pub const ENV_DESKTOP_PROTOCAL__X11: &str = "x11"; -pub const ENV_DESKTOP_PROTOCAL_UNKNOWN: &str = "unknown"; - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum Protocal { - Wayland, - X11, // Xorg - Unknown, -} - -#[derive(Debug, Clone)] -pub struct Desktop { - pub sid: String, - pub protocal: Protocal, - pub username: String, - pub uid: String, - pub display: String, - pub xauth: String, + static ref DESKTOP_MANAGER: Arc>> = Arc::new(Mutex::new(None)); } #[derive(Debug)] -struct ChildFlags { +struct DesktopManager { + x11_username: String, + child_username: String, child_exit: Arc, is_child_running: Arc, } -fn check_update_env() { - let mut child_flags = CHILD_FLAGS.lock().unwrap(); - let mut desktop = DESKTOP_ENV.lock().unwrap(); - - if let Some(child_flags) = &mut (*child_flags) { - if child_flags.is_child_running.load(Ordering::SeqCst) { +fn check_desktop_manager() { + let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); + if let Some(desktop_manager) = &mut (*desktop_manager) { + if desktop_manager.is_child_running.load(Ordering::SeqCst) { return; } - child_flags.child_exit.store(true, Ordering::SeqCst); - } - - if !desktop.sid.is_empty() && is_active(&desktop.sid) { - return; - } - - let old_desktop = desktop.clone(); - desktop.refresh(); - if !desktop.is_same_env(&old_desktop) { - desktop.update_env(); - log::debug!("desktop env changed, {:?}", &desktop); + desktop_manager.child_exit.store(true, Ordering::SeqCst); } } +// --server process pub fn start_xdesktop() { std::thread::spawn(|| { - if wait_xdesktop(20) { - log::info!("Wait desktop: default"); - } else { - log::info!("Wait desktop: none"); - } - *CHILD_FLAGS.lock().unwrap() = Some(ChildFlags::new()); + *DESKTOP_MANAGER.lock().unwrap() = Some(DesktopManager::new()); let interval = time::Duration::from_millis(super::SERVICE_INTERVAL); DESKTOP_RUNNING.store(true, Ordering::SeqCst); while DESKTOP_RUNNING.load(Ordering::SeqCst) { - check_update_env(); + check_desktop_manager(); std::thread::sleep(interval); } - log::info!("xdesktop update thread exit"); + log::info!("xdesktop child thread exit"); }); } pub fn stop_xdesktop() { DESKTOP_RUNNING.store(false, Ordering::SeqCst); + *DESKTOP_MANAGER.lock().unwrap() = None; } -#[inline] -pub fn get_desktop_env() -> Desktop { - DESKTOP_ENV.lock().unwrap().clone() -} +pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { + let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); + if let Some(desktop_manager) = &mut (*desktop_manager) { + if !desktop_manager.x11_username.is_empty() { + return Ok((desktop_manager.x11_username.clone(), true)); + } -pub fn try_start_x_session(username: &str, password: &str) -> ResultType { - let mut child_flags = CHILD_FLAGS.lock().unwrap(); - let mut desktop_lock = DESKTOP_ENV.lock().unwrap(); - let mut desktop = Desktop::new(); - if let Some(child_flags) = &mut (*child_flags) { - let _ = child_flags.try_start_x_session(&mut desktop, username, password)?; + let _ = desktop_manager.try_start_x_session(username, password)?; log::debug!( "try_start_x_session, username: {}, {:?}", &username, - &child_flags + &desktop_manager ); - *desktop_lock = desktop.clone(); - Ok(desktop) + Ok(( + desktop_manager.child_username.clone(), + desktop_manager.is_running(), + )) } else { bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); } } -fn wait_xdesktop(timeout_secs: u64) -> bool { - let wait_begin = Instant::now(); - while wait_begin.elapsed().as_secs() < timeout_secs { - let uid = &get_values_of_seat0(&[1])[0]; - if !uid.is_empty() { - return true; +pub fn get_username() -> String { + match &*DESKTOP_MANAGER.lock().unwrap() { + Some(manager) => { + if !manager.x11_username.is_empty() { + manager.x11_username.clone() + } else { + if manager.is_running() && !manager.child_username.is_empty() { + manager.child_username.clone() + } else { + "".to_owned() + } + } } + None => "".to_owned(), + } +} - if let Ok(output) = run_cmds(format!( - "ps -ef | grep -v 'grep' | grep -E 'gnome-session-binary|{}'", - XFCE4_PANEL - )) { - if !output.is_empty() { - log::info!("wait xdesktop: find xclient {}", &output); - return true; +impl Drop for DesktopManager { + fn drop(&mut self) { + self.stop_children(); + } +} + +impl DesktopManager { + fn fatal_exit() { + std::process::exit(0); + } + + pub fn new() -> Self { + let mut x11_username = "".to_owned(); + let seat0_values = get_values_of_seat0(&[0, 1, 2]); + if !seat0_values[0].is_empty() { + if "x11" == get_display_server_of_session(&seat0_values[1]) { + x11_username = seat0_values[2].clone(); } } - std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); - } - - false -} - -impl Desktop { - pub fn new() -> Self { - let xauth = get_env_var("XAUTHORITY"); - Self { - sid: "".to_owned(), - protocal: Protocal::Unknown, - username: "".to_owned(), - uid: "".to_owned(), - display: "".to_owned(), - xauth: if xauth.is_empty() { - "/tmp/.Xauthority".to_owned() - } else { - xauth - }, + x11_username, + child_username: "".to_owned(), + child_exit: Arc::new(AtomicBool::new(true)), + is_child_running: Arc::new(AtomicBool::new(false)), } } - fn update_env(&self) { - if self.is_x11() { - std::env::set_var("DISPLAY", &self.display); - std::env::set_var("XAUTHORITY", &self.xauth); - std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); + #[inline] + fn get_xauth() -> String { + let xauth = get_env_var("XAUTHORITY"); + if xauth.is_empty() { + "/tmp/.Xauthority".to_owned() } else { - std::env::set_var("DISPLAY", ""); - std::env::set_var("XAUTHORITY", ""); - std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); + xauth } } - pub fn is_same_env(&self, other: &Self) -> bool { - self.sid == other.sid - && self.protocal == other.protocal - && self.uid == other.uid - && self.display == other.display - && self.xauth == other.xauth + #[inline] + fn is_running(&self) -> bool { + self.is_child_running.load(Ordering::SeqCst) } - #[inline] - pub fn is_x11(&self) -> bool { - self.protocal == Protocal::X11 - } + fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> { + match get_user_by_name(username) { + Some(userinfo) => { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(username, password); + match client.authenticate() { + Ok(_) => { + if self.is_running() { + return Ok(()); + } - #[inline] - pub fn is_wayland(&self) -> bool { - self.protocal == Protocal::Wayland - } - - #[inline] - pub fn is_ready(&self) -> bool { - match self.protocal { - Protocal::X11 | Protocal::Wayland => true, - _ => false, + match self.start_x_session(&userinfo, username, password) { + Ok(_) => { + log::info!("Succeeded to start x11"); + self.child_username = username.to_string(); + Ok(()) + } + Err(e) => { + bail!("failed to start x session, {}", e); + } + } + } + Err(e) => { + bail!("failed to check user pass for {}, {}", username, e); + } + } + } + None => { + bail!("failed to get userinfo of {}", username); + } } } @@ -220,83 +193,131 @@ impl Desktop { || Path::new(&format!("/tmp/.X{}-lock", display)).exists() } - fn get_display(&mut self) { - self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10); - if self.display.is_empty() { - self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10); - } - if self.display.is_empty() { - self.display = Self::get_display_by_user(&self.username); - } - if self.display.is_empty() { - self.display = ":0".to_owned(); - } - self.display = self - .display - .replace(&whoami::hostname(), "") - .replace("localhost", ""); - } + fn start_x_session( + &mut self, + userinfo: &User, + username: &str, + password: &str, + ) -> ResultType<()> { + self.stop_children(); - fn get_xauth(&mut self) { - self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10); - if self.xauth.is_empty() { - get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10); - } + let display_num = Self::get_avail_display()?; + // "xServer_ip:display_num.screen_num" - let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid); - if self.xauth.is_empty() { - self.xauth = if std::path::Path::new(&gdm).exists() { - gdm - } else { - let username = &self.username; - if username == "root" { - format!("/{}/.Xauthority", username) + let uid = userinfo.uid(); + let gid = userinfo.primary_group_id(); + let envs = HashMap::from([ + ("SHELL", userinfo.shell().to_string_lossy().to_string()), + ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), + ("USER", username.to_string()), + ("UID", userinfo.uid().to_string()), + ("HOME", userinfo.home_dir().to_string_lossy().to_string()), + ( + "XDG_RUNTIME_DIR", + format!("/run/user/{}", userinfo.uid().to_string()), + ), + // ("DISPLAY", self.display.clone()), + // ("XAUTHORITY", self.xauth.clone()), + // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), + ]); + self.child_exit.store(false, Ordering::SeqCst); + let is_child_running = self.is_child_running.clone(); + + let (tx_res, rx_res) = sync_channel(1); + let password = password.to_string(); + let username = username.to_string(); + // start x11 + std::thread::spawn(move || { + match Self::start_x_session_thread( + tx_res.clone(), + is_child_running, + uid, + gid, + display_num, + username, + password, + envs, + ) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to start x session thread"); + allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e))); + } + } + }); + + // wait x11 + match rx_res.recv_timeout(Duration::from_millis(10_000)) { + Ok(res) => { + if res == "" { + Ok(()) } else { - let tmp = format!("/home/{}/.Xauthority", username); - if std::path::Path::new(&tmp).exists() { - tmp - } else { - format!("/var/lib/{}/.Xauthority", username) - } + bail!(res) } - }; + } + Err(e) => { + bail!("Failed to recv x11 result {}", e) + } } } - fn get_display_by_user(user: &str) -> String { - // log::debug!("w {}", &user); - if let Ok(output) = std::process::Command::new("w").arg(&user).output() { - for line in String::from_utf8_lossy(&output.stdout).lines() { - let mut iter = line.split_whitespace(); - let b = iter.nth(2); - if let Some(b) = b { - if b.starts_with(":") { - return b.to_owned(); + #[inline] + fn display_from_num(num: u32) -> String { + format!(":{num}") + } + + fn start_x_session_thread( + tx_res: SyncSender, + is_child_running: Arc, + uid: u32, + gid: u32, + display_num: u32, + username: String, + password: String, + envs: HashMap<&str, String>, + ) -> ResultType<()> { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(&username, &password); + client.authenticate()?; + + client.set_item(pam::PamItemType::TTY, &Self::display_from_num(display_num))?; + client.open_session()?; + + // fixme: FreeBSD kernel needs to login here. + // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 + + let (child_xorg, child_wm) = Self::start_x11(uid, gid, username, display_num, &envs)?; + is_child_running.store(true, Ordering::SeqCst); + + log::info!("Start xorg and wm done, notify and wait xtop x11"); + allow_err!(tx_res.send("".to_owned())); + + Self::wait_stop_x11(child_xorg, child_wm); + log::info!("Wait x11 stop done"); + Ok(()) + } + + fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType { + if let Ok(_) = child_xorg.kill() { + for _ in 0..3 { + match child_xorg.try_wait() { + Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)), + Ok(None) => {} + Err(e) => { + // fatal error + log::error!("Failed to wait xorg process, {}", e); + bail!("Failed to wait xorg process, {}", e) } } + std::thread::sleep(std::time::Duration::from_millis(1_000)); } + log::error!("Failed to wait xorg process, not exit"); + bail!("Failed to wait xorg process, not exit") + } else { + Ok("Xorg is already exited".to_owned()) } - // above not work for gdm user - //log::debug!("ls -l /tmp/.X11-unix/"); - let mut last = "".to_owned(); - if let Ok(output) = std::process::Command::new("ls") - .args(vec!["-l", "/tmp/.X11-unix/"]) - .output() - { - for line in String::from_utf8_lossy(&output.stdout).lines() { - let mut iter = line.split_whitespace(); - let user_field = iter.nth(2); - if let Some(x) = iter.last() { - if x.starts_with("X") { - last = x.replace("X", ":").to_owned(); - if user_field == Some(&user) { - return last; - } - } - } - } - } - last } fn add_xauth_cookie( @@ -343,233 +364,28 @@ impl Desktop { } } - fn refresh(&mut self) { - *self = Self::new(); - - let seat0_values = get_values_of_seat0(&[0, 1, 2]); - if seat0_values[0].is_empty() { - return; - } - - self.sid = seat0_values[0].clone(); - self.uid = seat0_values[1].clone(); - self.username = seat0_values[2].clone(); - self.protocal = get_display_server_of_session(&self.sid).into(); - self.get_display(); - self.get_xauth(); - } -} - -impl Drop for ChildFlags { - fn drop(&mut self) { - self.stop_children(); - } -} - -impl ChildFlags { - fn fatal_exit() { - std::process::exit(0); - } - - pub fn new() -> Self { - Self { - child_exit: Arc::new(AtomicBool::new(true)), - is_child_running: Arc::new(AtomicBool::new(false)), - } - } - - fn try_start_x_session( - &mut self, - desktop: &mut Desktop, - username: &str, - password: &str, - ) -> ResultType<()> { - match get_user_by_name(username) { - Some(userinfo) => { - let mut client = pam::Client::with_password(pam_get_service_name())?; - client - .conversation_mut() - .set_credentials(username, password); - match client.authenticate() { - Ok(_) => { - if desktop.is_x11() && desktop.username == username { - return Ok(()); - } - - // to-do: sid is empty if xorg is managed by this process - desktop.sid = "".to_owned(); - desktop.username = username.to_string(); - desktop.uid = userinfo.uid().to_string(); - desktop.protocal = Protocal::Unknown; - match self.start_x_session(desktop, &userinfo, password) { - Ok(_) => { - log::info!("Succeeded to start x11, update env {:?}", &desktop); - desktop.update_env(); - Ok(()) - } - Err(e) => { - *desktop = Desktop::new(); - desktop.update_env(); - bail!("failed to start x session, {}", e); - } - } - } - Err(e) => { - bail!("failed to check user pass for {}, {}", username, e); - } - } - } - None => { - bail!("failed to get userinfo of {}", username); - } - } - } - - fn start_x_session( - &mut self, - desktop: &mut Desktop, - userinfo: &User, - password: &str, - ) -> ResultType<()> { - self.stop_children(); - - let display_num = Desktop::get_avail_display()?; - // "xServer_ip:display_num.screen_num" - desktop.display = format!(":{}", display_num); - - let uid = userinfo.uid(); - let gid = userinfo.primary_group_id(); - let envs = HashMap::from([ - ("SHELL", userinfo.shell().to_string_lossy().to_string()), - ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), - ("USER", desktop.username.clone()), - ("UID", userinfo.uid().to_string()), - ("HOME", userinfo.home_dir().to_string_lossy().to_string()), - ( - "XDG_RUNTIME_DIR", - format!("/run/user/{}", userinfo.uid().to_string()), - ), - // ("DISPLAY", self.display.clone()), - // ("XAUTHORITY", self.xauth.clone()), - // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), - ]); - let desktop_clone = desktop.clone(); - self.child_exit.store(false, Ordering::SeqCst); - let is_child_running = self.is_child_running.clone(); - - let (tx_res, rx_res) = sync_channel(1); - let password = password.to_string(); - // start x11 - std::thread::spawn(move || { - match Self::start_x_session_thread( - tx_res.clone(), - is_child_running, - desktop_clone, - uid, - gid, - display_num, - password, - envs, - ) { - Ok(_) => {} - Err(e) => { - log::error!("Failed to start x session thread"); - allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e))); - } - } - }); - - // wait x11 - match rx_res.recv_timeout(Duration::from_millis(10_000)) { - Ok(res) => { - if res == "" { - desktop.protocal = Protocal::X11; - Ok(()) - } else { - bail!(res) - } - } - Err(e) => { - bail!("Failed to recv x11 result {}", e) - } - } - } - - fn start_x_session_thread( - tx_res: SyncSender, - is_child_running: Arc, - desktop: Desktop, - uid: u32, - gid: u32, - display_num: u32, - password: String, - envs: HashMap<&str, String>, - ) -> ResultType<()> { - let mut client = pam::Client::with_password(pam_get_service_name())?; - client - .conversation_mut() - .set_credentials(&desktop.username, &password); - client.authenticate()?; - - client.set_item(pam::PamItemType::TTY, &desktop.display)?; - client.open_session()?; - - // fixme: FreeBSD kernel needs to login here. - // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 - - let (child_xorg, child_wm) = Self::start_x11(&desktop, uid, gid, display_num, &envs)?; - is_child_running.store(true, Ordering::SeqCst); - - log::info!("Start xorg and wm done, notify and wait xtop x11"); - allow_err!(tx_res.send("".to_owned())); - - Self::wait_stop_x11(child_xorg, child_wm); - log::info!("Wait x11 stop done"); - Ok(()) - } - - fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType { - if let Ok(_) = child_xorg.kill() { - for _ in 0..3 { - match child_xorg.try_wait() { - Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)), - Ok(None) => {} - Err(e) => { - // fatal error - log::error!("Failed to wait xorg process, {}", e); - bail!("Failed to wait xorg process, {}", e) - } - } - std::thread::sleep(std::time::Duration::from_millis(1_000)); - } - log::error!("Failed to wait xorg process, not exit"); - bail!("Failed to wait xorg process, not exit") - } else { - Ok("Xorg is already exited".to_owned()) - } - } - fn start_x11( - desktop: &Desktop, uid: u32, gid: u32, + username: String, display_num: u32, envs: &HashMap<&str, String>, ) -> ResultType<(Child, Child)> { - log::debug!("envs of user {}: {:?}", &desktop.username, &envs); + log::debug!("envs of user {}: {:?}", &username, &envs); - Desktop::add_xauth_cookie(&desktop.xauth, &desktop.display, uid, gid, &envs)?; + let xauth = Self::get_xauth(); + let display = Self::display_from_num(display_num); + + Self::add_xauth_cookie(&xauth, &display, uid, gid, &envs)?; // Start Xorg - let mut child_xorg = - Self::start_x_server(&desktop.xauth, &desktop.display, uid, gid, &envs)?; + let mut child_xorg = Self::start_x_server(&xauth, &display, uid, gid, &envs)?; log::info!("xorg started, wait 10 secs to ensuer x server is running"); let max_wait_secs = 10; // wait x server running - if let Err(e) = Desktop::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) - { + if let Err(e) = Self::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) { match Self::wait_xorg_exit(&mut child_xorg) { Ok(msg) => log::info!("{}", msg), Err(e) => { @@ -582,12 +398,12 @@ impl ChildFlags { log::info!( "xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}", - &desktop.display, - &desktop.xauth + &display, + &xauth ); - std::env::set_var("DISPLAY", &desktop.display); - std::env::set_var("XAUTHORITY", &desktop.xauth); + std::env::set_var("DISPLAY", &display); + std::env::set_var("XAUTHORITY", &xauth); // start window manager (startwm.sh) let child_wm = match Self::start_x_window_manager(uid, gid, &envs) { Ok(c) => c, @@ -707,10 +523,10 @@ impl ChildFlags { } fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool { - let mut child_flags = CHILD_FLAGS.lock().unwrap(); + let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); let mut exited = true; - if let Some(child_flags) = &mut (*child_flags) { - if child_flags.child_exit.load(Ordering::SeqCst) { + if let Some(desktop_manager) = &mut (*desktop_manager) { + if desktop_manager.child_exit.load(Ordering::SeqCst) { exited = true; } else { exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); @@ -718,8 +534,10 @@ impl ChildFlags { if exited { println!("=============================MYDEBUG begin to wait x11 children exit"); Self::wait_x11_children_exit(child_xorg, child_wm); - child_flags.is_child_running.store(false, Ordering::SeqCst); - child_flags.child_exit.store(true, Ordering::SeqCst); + desktop_manager + .is_child_running + .store(false, Ordering::SeqCst); + desktop_manager.child_exit.store(true, Ordering::SeqCst); } } exited @@ -843,23 +661,3 @@ fn pam_get_service_name() -> &'static str { "gdm" } } - -impl ToString for Protocal { - fn to_string(&self) -> String { - match self { - Protocal::X11 => ENV_DESKTOP_PROTOCAL__X11.to_owned(), - Protocal::Wayland => ENV_DESKTOP_PROTOCAL_WAYLAND.to_owned(), - Protocal::Unknown => ENV_DESKTOP_PROTOCAL_UNKNOWN.to_owned(), - } - } -} - -impl From for Protocal { - fn from(value: String) -> Self { - match &value as &str { - ENV_DESKTOP_PROTOCAL__X11 => Protocal::X11, - ENV_DESKTOP_PROTOCAL_WAYLAND => Protocal::Wayland, - _ => Protocal::Unknown, - } - } -} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 16bcc775a..7c9b2f0b0 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -18,7 +18,7 @@ pub mod delegate; pub mod linux; #[cfg(target_os = "linux")] -pub mod linux_desktop; +pub mod linux_desktop_manager; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::{message_proto::CursorData, ResultType}; diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 5a68a4a03..3fef9747b 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -73,7 +73,7 @@ impl RendezvousMediator { }); } #[cfg(target_os = "linux")] - crate::platform::linux_desktop::start_xdesktop(); + crate::platform::linux_desktop_manager::start_xdesktop(); SHOULD_EXIT.store(false, Ordering::SeqCst); while !SHOULD_EXIT.load(Ordering::SeqCst) { Config::reset_online(); @@ -100,7 +100,7 @@ impl RendezvousMediator { sleep(1.).await; } #[cfg(target_os = "linux")] - crate::platform::linux_desktop::stop_xdesktop(); + crate::platform::linux_desktop_manager::stop_xdesktop(); } pub async fn start(server: ServerPtr, host: String) -> ResultType<()> { diff --git a/src/server/connection.rs b/src/server/connection.rs index a5c63a80e..2d9e60f04 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1074,18 +1074,19 @@ impl Connection { fn try_start_desktop(_username: &str, _passsword: &str) -> String { #[cfg(target_os = "linux")] if _username.is_empty() { - let desktop = crate::platform::linux_desktop::get_desktop_env(); - if desktop.is_ready() { - "" - } else { + let username = crate::platform::linux_desktop_manager::get_username(); + if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY + } else { + "" } .to_owned() } else { - match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { - Ok(desktop) => { - if desktop.is_ready() { - if _username != desktop.username { + match crate::platform::linux_desktop_manager::try_start_x_session(_username, _passsword) + { + Ok((username, x11_ready)) => { + if x11_ready { + if _username != username { LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() } else { "".to_owned() From e4c2e9af00f4dda2ae18e898a196b848687ab744 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 22:47:29 +0800 Subject: [PATCH 039/112] refact linux desktop Signed-off-by: fufesou --- src/platform/linux.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 85f763ec6..dda4b115b 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -296,7 +296,6 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); start_uinput_service(); - start_check_desktop_env(); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -371,8 +370,6 @@ pub fn start_os_service() { } } - stop_check_desktop_env(); - if let Some(ps) = user_server.take().as_mut() { allow_err!(ps.kill()); } @@ -384,13 +381,13 @@ pub fn start_os_service() { #[inline] pub fn get_active_user_id_name() -> (String, String) { - let desktop = get_desktop_env(); - (desktop.uid.clone(), desktop.username.clone()) + let vec_id_name = get_values_of_seat0(&[1, 2]); + (vec_id_name[0].clone(), vec_id_name[1].clone()) } #[inline] pub fn get_active_userid() -> String { - get_desktop_env().uid.clone() + get_values_of_seat0(&[1])[0].clone() } fn get_cm() -> bool { @@ -436,7 +433,7 @@ fn _get_display_manager() -> String { #[inline] pub fn get_active_username() -> String { - get_desktop_env().username.clone() + get_values_of_seat0(&[2])[0].clone() } pub fn get_active_user_home() -> Option { From 6149c7f4777fe1f5a83188fd38a41487b5a90354 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 12:08:24 +0800 Subject: [PATCH 040/112] refact linux desktop env Signed-off-by: fufesou --- src/platform/linux.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index dda4b115b..28c4d9252 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -769,6 +769,7 @@ mod desktop { pub protocal: String, pub display: String, pub xauth: String, + pub is_rustdesk_subprocess: bool, } impl Desktop { @@ -787,6 +788,11 @@ mod desktop { self.sid.is_empty() } + #[inline] + pub fn is_headless(&self) -> bool { + self.sid.is_empty() || self.is_rustdesk_subprocess + } + fn get_display(&mut self) { self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10); if self.display.is_empty() { @@ -901,6 +907,16 @@ mod desktop { last } + fn set_is_subprocess(&mut self) { + self.is_rustdesk_subprocess = false; + let cmd = "ps -ef | grep 'rustdesk/xorg.conf' | grep -v grep | wc -l"; + if let Ok(res) = run_cmds(cmd) { + if res.trim() != "0" { + self.is_rustdesk_subprocess = true; + } + } + } + pub fn refresh(&mut self) { if !self.sid.is_empty() && is_active(&self.sid) { return; @@ -909,6 +925,7 @@ mod desktop { let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); + self.is_rustdesk_subprocess = false; return; } @@ -924,6 +941,9 @@ mod desktop { self.get_display(); self.get_xauth(); + self.set_is_subprocess(); + + println!("REMOVE ME ======================================= desktop: {:?}", self); } } } From b82207f20bb13428c781df8edb0dee4c65a40a65 Mon Sep 17 00:00:00 2001 From: qcloud Date: Wed, 29 Mar 2023 17:39:16 +0800 Subject: [PATCH 041/112] virtual display, linux, debug Signed-off-by: qcloud --- libs/hbb_common/src/platform/mod.rs | 2 +- src/platform/linux.rs | 12 +++- src/platform/linux_desktop_manager.rs | 3 +- src/server/connection.rs | 83 ++++++--------------------- 4 files changed, 31 insertions(+), 69 deletions(-) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index c37c8aaf5..f4a1122e5 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -57,6 +57,6 @@ where { unsafe { GLOBAL_CALLBACK = Some(Box::new(callback)); - libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); + // libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 28c4d9252..b166f1531 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -225,6 +225,13 @@ fn stop_rustdesk_servers() { )); } +#[inline] +fn stop_xorg_subprocess() { + let _ = run_cmds(format!( + r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, + )); +} + fn should_start_server( try_x11: bool, uid: &mut String, @@ -295,6 +302,7 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); + stop_xorg_subprocess(); start_uinput_service(); let running = Arc::new(AtomicBool::new(true)); @@ -329,6 +337,7 @@ pub fn start_os_service() { &mut last_restart, &mut server, ) { + stop_xorg_subprocess(); force_stop_server(); start_server(None, &mut server); } @@ -921,7 +930,6 @@ mod desktop { if !self.sid.is_empty() && is_active(&self.sid) { return; } - let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); @@ -942,8 +950,6 @@ mod desktop { self.get_display(); self.get_xauth(); self.set_is_subprocess(); - - println!("REMOVE ME ======================================= desktop: {:?}", self); } } } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 8b9e20640..ea7189b82 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -111,6 +111,7 @@ impl DesktopManager { pub fn new() -> Self { let mut x11_username = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); + println!("REMOVE ME ======================== DesktopManager seato values {:?}", &seat0_values); if !seat0_values[0].is_empty() { if "x11" == get_display_server_of_session(&seat0_values[1]) { x11_username = seat0_values[2].clone(); @@ -608,7 +609,7 @@ impl DesktopManager { //"-logfile", //"/tmp/RustDesk_xorg.log", "-config", - "rustdesk/xorg.conf", + "/etc/rustdesk/xorg.conf", "-auth", xauth, display, diff --git a/src/server/connection.rs b/src/server/connection.rs index 2d9e60f04..beefaac1a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1075,6 +1075,7 @@ impl Connection { #[cfg(target_os = "linux")] if _username.is_empty() { let username = crate::platform::linux_desktop_manager::get_username(); + println!("REMOVE ME ===================================== try_start_desktop username '{}'", &username); if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY } else { @@ -1274,6 +1275,7 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; + // If err is LOGIN_MSG_XSESSION_NOT_READY, just keep this msg and go on checking password. if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; @@ -1302,73 +1304,26 @@ impl Connection { self.send_login_error("Connection not allowed").await; return false; } else if self.is_recent_session() { - self.try_start_cm(lr.my_id, lr.my_name, true); - self.send_logon_response().await; - if self.port_forward_socket.is_some() { - return false; - } - } else if lr.password.is_empty() { - self.try_start_cm(lr.my_id, lr.my_name, false); - } else { - let mut failure = LOGIN_FAILURES - .lock() - .unwrap() - .get(&self.ip) - .map(|x| x.clone()) - .unwrap_or((0, 0, 0)); - let time = (get_time() / 60_000) as i32; - if failure.2 > 30 { - self.send_login_error("Too many wrong password attempts") - .await; - Self::post_alarm_audit( - AlarmAuditType::ManyWrongPassword, - true, - json!({ - "ip":self.ip, - }), - ); - } else if time == failure.0 && failure.1 > 6 { - self.send_login_error("Please try 1 minute later").await; - Self::post_alarm_audit( - AlarmAuditType::FrequentAttempt, - true, - json!({ - "ip":self.ip, - }), - ); - } else if !self.validate_password() { - if failure.0 == time { - failure.1 += 1; - failure.2 += 1; - } else { - failure.0 = time; - failure.1 = 1; - failure.2 += 1; - } - LOGIN_FAILURES - .lock() - .unwrap() - .insert(self.ip.clone(), failure); - if desktop_err.is_empty() { - self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; - self.try_start_cm(lr.my_id, lr.my_name, false); - } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) - .await; + if desktop_err.is_empty() { + self.try_start_cm(lr.my_id, lr.my_name, true); + self.send_logon_response().await; + if self.port_forward_socket.is_some() { + return false; } } else { - if failure.0 != 0 { - LOGIN_FAILURES.lock().unwrap().remove(&self.ip); - } - if desktop_err.is_empty() { - self.send_logon_response().await; - self.try_start_cm(lr.my_id, lr.my_name, true); - if self.port_forward_socket.is_some() { - return false; - } - } else { - self.send_login_error(desktop_err).await; + self.send_login_error(desktop_err).await; + } + } else { + if desktop_err.is_empty() { + println!("REMOVE ME =============================== send logon response"); + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; } + } else { + println!("REMOVE ME =============================== send login error {}", &desktop_err); + self.send_login_error(desktop_err).await; } } } else if let Some(message::Union::TestDelay(t)) = msg.union { From bcf08ba26d7adadb8a1f7777d3c173a334bbf357 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 18:31:42 +0800 Subject: [PATCH 042/112] virtual display, linux, debug Signed-off-by: fufesou --- src/platform/linux.rs | 2 +- src/platform/linux_desktop_manager.rs | 35 +++------------------------ src/server/connection.rs | 3 --- 3 files changed, 4 insertions(+), 36 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index b166f1531..8c4a68848 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -227,7 +227,7 @@ fn stop_rustdesk_servers() { #[inline] fn stop_xorg_subprocess() { - let _ = run_cmds(format!( + let _ = run_cmds(&format!( r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index ea7189b82..b21b2d5b1 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -111,7 +111,6 @@ impl DesktopManager { pub fn new() -> Self { let mut x11_username = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); - println!("REMOVE ME ======================== DesktopManager seato values {:?}", &seat0_values); if !seat0_values[0].is_empty() { if "x11" == get_display_server_of_session(&seat0_values[1]) { x11_username = seat0_values[2].clone(); @@ -427,10 +426,6 @@ impl DesktopManager { fn try_wait_x11_child_exit(child_xorg: &mut Child, child_wm: &mut Child) -> bool { match child_xorg.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG Xorg exit with {}", - status - ); log::info!("Xorg exit with {}", status); return true; } @@ -440,10 +435,7 @@ impl DesktopManager { match child_wm.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG: wm exit with {}", - status - ); + // Logout may result "wm exit with signal: 11 (SIGSEGV) (core dumped)" log::info!("wm exit with {}", status); return true; } @@ -460,20 +452,12 @@ impl DesktopManager { for _ in 0..2 { match child_xorg.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG Xorg exit with {}", - status - ); log::info!("Xorg exit with {}", status); exited = true; break; } Ok(None) => {} Err(e) => { - println!( - "=============================MYDEBUG Failed to wait xorg process, {}", - &e - ); log::error!("Failed to wait xorg process, {}", e); Self::fatal_exit(); } @@ -481,9 +465,6 @@ impl DesktopManager { std::thread::sleep(std::time::Duration::from_millis(1_000)); } if !exited { - println!( - "=============================MYDEBUG Failed to wait child xorg, after kill()" - ); log::error!("Failed to wait child xorg, after kill()"); // try kill -9? } @@ -494,19 +475,12 @@ impl DesktopManager { for _ in 0..2 { match child_wm.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG wm exit with {}", - status - ); + // Logout may result "wm exit with signal: 11 (SIGSEGV) (core dumped)" log::info!("wm exit with {}", status); exited = true; } Ok(None) => {} Err(e) => { - println!( - "=============================MYDEBUG Failed to wait wm process, {}", - &e - ); log::error!("Failed to wait wm process, {}", e); Self::fatal_exit(); } @@ -514,9 +488,6 @@ impl DesktopManager { std::thread::sleep(std::time::Duration::from_millis(1_000)); } if !exited { - println!( - "=============================MYDEBUG Failed to wait child xorg, after kill()" - ); log::error!("Failed to wait child xorg, after kill()"); // try kill -9? } @@ -533,7 +504,7 @@ impl DesktopManager { exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); } if exited { - println!("=============================MYDEBUG begin to wait x11 children exit"); + log::debug!("Wait x11 children exiting"); Self::wait_x11_children_exit(child_xorg, child_wm); desktop_manager .is_child_running diff --git a/src/server/connection.rs b/src/server/connection.rs index beefaac1a..6bbbffefc 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1075,7 +1075,6 @@ impl Connection { #[cfg(target_os = "linux")] if _username.is_empty() { let username = crate::platform::linux_desktop_manager::get_username(); - println!("REMOVE ME ===================================== try_start_desktop username '{}'", &username); if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY } else { @@ -1315,14 +1314,12 @@ impl Connection { } } else { if desktop_err.is_empty() { - println!("REMOVE ME =============================== send logon response"); self.send_logon_response().await; self.try_start_cm(lr.my_id, lr.my_name, true); if self.port_forward_socket.is_some() { return false; } } else { - println!("REMOVE ME =============================== send login error {}", &desktop_err); self.send_login_error(desktop_err).await; } } From c944d6093d0487bd6a2a995a6e3f031c8dab86a8 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 20:25:41 +0800 Subject: [PATCH 043/112] virtual display, linux, debug Signed-off-by: fufesou --- flutter/lib/models/model.dart | 4 +- libs/hbb_common/src/platform/mod.rs | 2 +- src/server/connection.rs | 85 +++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 155956c74..470bb47c3 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -294,10 +294,10 @@ class FfiModel with ChangeNotifier { } else if (type == 'input-password') { enterPasswordDialog(id, dialogManager); } else if (type == 'xsession-login' || type == 'xsession-re-login') { - // to-do + enterUserLoginDialog(id, dialogManager); } else if (type == 'xsession-login-password' || type == 'xsession-login-password') { - // to-do + enterUserLoginAndPasswordDialog(id, dialogManager); } else if (type == 'restarting') { showMsgBox(id, type, title, text, link, false, dialogManager, hasCancel: false); diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index f4a1122e5..c37c8aaf5 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -57,6 +57,6 @@ where { unsafe { GLOBAL_CALLBACK = Some(Box::new(callback)); - // libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); + libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 6bbbffefc..2f86ca5f0 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1285,16 +1285,11 @@ impl Connection { } else if password::approve_mode() == ApproveMode::Click || password::approve_mode() == ApproveMode::Both && !password::has_valid_password() { - if desktop_err.is_empty() { - self.try_start_cm(lr.my_id, lr.my_name, false); - if hbb_common::get_version_number(&lr.version) - >= hbb_common::get_version_number("1.2.0") - { - self.send_login_error("No Password Access").await; - } - } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) - .await; + self.try_start_cm(lr.my_id, lr.my_name, false); + if hbb_common::get_version_number(&lr.version) + >= hbb_common::get_version_number("1.2.0") + { + self.send_login_error("No Password Access").await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1312,15 +1307,73 @@ impl Connection { } else { self.send_login_error(desktop_err).await; } - } else { + } else if lr.password.is_empty() { if desktop_err.is_empty() { - self.send_logon_response().await; - self.try_start_cm(lr.my_id, lr.my_name, true); - if self.port_forward_socket.is_some() { - return false; + self.try_start_cm(lr.my_id, lr.my_name, false); + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) + .await; + } + } else { + let mut failure = LOGIN_FAILURES + .lock() + .unwrap() + .get(&self.ip) + .map(|x| x.clone()) + .unwrap_or((0, 0, 0)); + let time = (get_time() / 60_000) as i32; + if failure.2 > 30 { + self.send_login_error("Too many wrong password attempts") + .await; + Self::post_alarm_audit( + AlarmAuditType::ManyWrongPassword, + true, + json!({ + "ip":self.ip, + }), + ); + } else if time == failure.0 && failure.1 > 6 { + self.send_login_error("Please try 1 minute later").await; + Self::post_alarm_audit( + AlarmAuditType::FrequentAttempt, + true, + json!({ + "ip":self.ip, + }), + ); + } else if !self.validate_password() { + if failure.0 == time { + failure.1 += 1; + failure.2 += 1; + } else { + failure.0 = time; + failure.1 = 1; + failure.2 += 1; + } + LOGIN_FAILURES + .lock() + .unwrap() + .insert(self.ip.clone(), failure); + if desktop_err.is_empty() { + self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.try_start_cm(lr.my_id, lr.my_name, false); + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) + .await; } } else { - self.send_login_error(desktop_err).await; + if failure.0 != 0 { + LOGIN_FAILURES.lock().unwrap().remove(&self.ip); + } + if desktop_err.is_empty() { + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; + } + } else { + self.send_login_error(desktop_err).await; + } } } } else if let Some(message::Union::TestDelay(t)) = msg.union { From 9448e35b461691d57d418c3c0d381eecff902ae2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 23:18:27 +0800 Subject: [PATCH 044/112] update dialog style Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 177 ++-- .../lib/desktop/pages/connection_page.dart | 7 +- src/lang/ca.rs | 6 +- src/lang/cn.rs | 7 +- src/lang/cs.rs | 6 +- src/lang/da.rs | 6 +- src/lang/de.rs | 5 +- src/lang/el.rs | 5 +- src/lang/en.rs | 5 +- src/lang/eo.rs | 6 +- src/lang/es.rs | 6 +- src/lang/fa.rs | 6 +- src/lang/fr.rs | 2 +- src/lang/hu.rs | 6 +- src/lang/id.rs | 6 +- src/lang/it.rs | 6 +- src/lang/ja.rs | 6 +- src/lang/ko.rs | 6 +- src/lang/kz.rs | 6 +- src/lang/nl.rs | 6 +- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 6 +- src/lang/ptbr.rs | 6 +- src/lang/ro.rs | 6 +- src/lang/ru.rs | 5 +- src/lang/sk.rs | 6 +- src/lang/sl.rs | 6 +- src/lang/sq.rs | 6 +- src/lang/sr.rs | 6 +- src/lang/sv.rs | 6 +- src/lang/template.rs | 4 +- src/lang/th.rs | 6 +- src/lang/tr.rs | 6 +- src/lang/tw.rs | 979 +++++++++--------- src/lang/ua.rs | 6 +- src/lang/vn.rs | 6 +- 36 files changed, 764 insertions(+), 584 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 4562e519c..569d64428 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -456,7 +456,7 @@ void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async { await _connectDialog( id, dialogManager, - peerPasswordController: TextEditingController(), + passwordController: TextEditingController(), ); } @@ -464,8 +464,8 @@ void enterUserLoginDialog(String id, OverlayDialogManager dialogManager) async { await _connectDialog( id, dialogManager, - usernameController: TextEditingController(), - passwordController: TextEditingController(), + osUsernameController: TextEditingController(), + osPasswordController: TextEditingController(), ); } @@ -474,20 +474,27 @@ void enterUserLoginAndPasswordDialog( await _connectDialog( id, dialogManager, - usernameController: TextEditingController(), + osUsernameController: TextEditingController(), + osPasswordController: TextEditingController(), passwordController: TextEditingController(), - peerPasswordController: TextEditingController(), ); } _connectDialog( String id, OverlayDialogManager dialogManager, { - TextEditingController? usernameController, + TextEditingController? osUsernameController, + TextEditingController? osPasswordController, TextEditingController? passwordController, - TextEditingController? peerPasswordController, }) async { - var remember = await bind.sessionGetRemember(id: id) ?? false; + var rememberPassword = false; + if (passwordController != null) { + rememberPassword = await bind.sessionGetRemember(id: id) ?? false; + } + var rememberAccount = false; + if (osUsernameController != null) { + rememberAccount = await bind.sessionGetRemember(id: id) ?? false; + } dialogManager.dismissAll(); dialogManager.show((setState, close) { cancel() { @@ -501,77 +508,125 @@ _connectDialog( // If the remote side is headless. // The client side should login to remote OS account, to enable X desktop session. // `username` and `password` will be used in the near future. - final username = usernameController?.text.trim() ?? ''; + final osUsername = osUsernameController?.text.trim() ?? ''; + final osPassword = osPasswordController?.text.trim() ?? ''; final password = passwordController?.text.trim() ?? ''; - final peerPassword = peerPasswordController?.text.trim() ?? ''; - if (peerPasswordController != null && peerPassword.isEmpty) return; + if (passwordController != null && password.isEmpty) return; gFFI.login( - username, - password, + osUsername, + osPassword, id, - peerPassword, - remember, + password, + rememberPassword, ); close(); dialogManager.showLoading(translate('Logging in...'), onCancel: closeConnection); } + descWidget(String text) { + return Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + text, + maxLines: 3, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 16), + ), + ), + Container( + height: 8, + ), + ], + ); + } + + rememberWidget( + String desc, + bool remember, + ValueChanged? onChanged, + ) { + return CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text(desc), + value: remember, + onChanged: onChanged, + ); + } + + osAccountWidget() { + if (osUsernameController == null || osPasswordController == null) { + return Offstage(); + } + return Column( + children: [ + descWidget(translate('login_linux_tooltip_tip')), + DialogTextField( + title: translate(DialogTextField.kUsernameTitle), + controller: osUsernameController, + prefixIcon: DialogTextField.kUsernameIcon, + errorText: null, + ), + PasswordWidget( + controller: osPasswordController, + autoFocus: false, + ), + rememberWidget( + translate('remember_account_tip'), + rememberAccount, + (v) { + if (v != null) { + setState(() => rememberAccount = v); + } + }, + ), + ], + ); + } + + passwdWidget() { + if (passwordController == null) { + return Offstage(); + } + return Column( + children: [ + descWidget(translate('verify_rustdesk_password_tip')), + PasswordWidget( + controller: passwordController, + autoFocus: osUsernameController == null, + ), + rememberWidget( + translate('remember_password_tip'), + rememberPassword, + (v) { + if (v != null) { + setState(() => rememberPassword = v); + } + }, + ), + ], + ); + } + return CustomAlertDialog( title: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.password_rounded, color: MyTheme.accent), - (usernameController == null - ? Text(translate('Password Required')) - : Tooltip( - message: translate('login_linux_tooltip_tip'), - child: Text(translate('login_linux_tip')), - )) - .paddingOnly(left: 10), + Text(translate('Password Required')).paddingOnly(left: 10), ], ), content: Column(mainAxisSize: MainAxisSize.min, children: [ - usernameController == null + osAccountWidget(), + osUsernameController == null || passwordController == null ? Offstage() - : DialogTextField( - title: translate(DialogTextField.kUsernameTitle), - controller: usernameController, - prefixIcon: DialogTextField.kUsernameIcon, - errorText: null, - ), - passwordController == null - ? Offstage() - : PasswordWidget( - controller: passwordController, - autoFocus: false, - ), - usernameController == null || peerPasswordController == null - ? Offstage() - : const Divider(), - peerPasswordController == null - ? Offstage() - : PasswordWidget( - controller: peerPasswordController, - autoFocus: usernameController == null, - hintText: 'enter_rustdesk_passwd_tip', - ), - peerPasswordController == null - ? Offstage() - : CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('remember_rustdesk_passwd_tip'), - ), - value: remember, - onChanged: (v) { - if (v != null) { - setState(() => remember = v); - } - }, - ), + : Container(height: 10), + passwdWidget(), ]), actions: [ dialogButton( diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index a593810fd..63d6b9c7b 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -233,7 +233,12 @@ class _ConnectionPageState extends State const SizedBox( width: 17, ), - Button(onTap: onConnect, text: "Connect"), + Button( + onTap: () => enterUserLoginAndPasswordDialog( + 'fdsfd', + gFFI.dialogManager, + ), + text: "Connect"), ], ), ) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 162a48883..af744d153 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tancar"), ("Retry", "Reintentar"), ("OK", ""), - ("Password Required", "Es necessita la contrasenya"), + ("remember_password_tip", "Es necessita la contrasenya"), ("Please enter your password", "Si us plau, introdueixi la seva contrasenya"), ("Remember password", "Recordar contrasenya"), ("Wrong Password", "Contrasenya incorrecta"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 250f9a5b6..584be4333 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "关闭"), ("Retry", "再试"), ("OK", "确认"), - ("Password Required", "需要密码"), + ("remember_password_tip", "需要密码"), ("Please enter your password", "请输入密码"), ("Remember password", "记住密码"), ("Wrong Password", "密码错误"), @@ -483,6 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "请输入 RustDesk 密码"), ("remember_rustdesk_passwd_tip", "记住 RustDesk 密码"), ("login_linux_tip", "登录被控端的 Linux 账户"), - ("login_linux_tooltip_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), + ("login_linux_tooltip_tip", "登录被控端的 Linux 账户,才能启用 X 桌面。"), + ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), + ("remember_account_tip", "记住此账户"), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 2f9e52f6c..cb882277e 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zavřít"), ("Retry", "Zkusit znovu"), ("OK", "OK"), - ("Password Required", "Vyžadováno heslo"), + ("remember_password_tip", "Vyžadováno heslo"), ("Please enter your password", "Zadejte své heslo"), ("Remember password", "Zapamatovat heslo"), ("Wrong Password", "Nesprávné heslo"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index a9ca8e8a6..ad6ddfc48 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Luk"), ("Retry", "Prøv igen"), ("OK", "OK"), - ("Password Required", "Adgangskode påkrævet"), + ("remember_password_tip", "Adgangskode påkrævet"), ("Please enter your password", "Indtast venligst dit kodeord"), ("Remember password", "Husk kodeord"), ("Wrong Password", "Forkert kodeord"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index b70e9f872..263664d4c 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Schließen"), ("Retry", "Erneut versuchen"), ("OK", "OK"), - ("Password Required", "Passwort erforderlich"), + ("remember_password_tip", "Passwort erforderlich"), ("Please enter your password", "Bitte geben Sie Ihr Passwort ein"), ("Remember password", "Passwort merken"), ("Wrong Password", "Falsches Passwort"), @@ -484,5 +484,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", "RustDesk-Passwort merken."), ("login_linux_tip", "Anmeldung am entfernten Linux-Konto"), ("login_linux_tooltip_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 85c269d29..9597ae158 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Κλείσιμο"), ("Retry", "Δοκίμασε ξανά"), ("OK", "ΟΚ"), - ("Password Required", "Απαιτείται κωδικός πρόσβασης"), + ("remember_password_tip", "Απαιτείται κωδικός πρόσβασης"), ("Please enter your password", "Παρακαλώ εισάγετε τον κωδικό πρόσβασης"), ("Remember password", "Απομνημόνευση κωδικού πρόσβασης"), ("Wrong Password", "Λάθος κωδικός πρόσβασης"), @@ -484,5 +484,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", "Να θυμάσαι τον κωδικό του RustDesk."), ("login_linux_tip", "Είσοδος σε απομακρυσμένο λογαριασμό Linux"), ("login_linux_tooltip_tip", "Απαιτείται είσοδος σε απομακρυσμένο λογαριασμό Linux για την ενεργοποίηση του περιβάλλον εργασίας Χ."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 8a981564b..95186325f 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,7 +56,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password."), ("remember_rustdesk_passwd_tip", "Remember RustDesk password."), - ("login_linux_tip", "Login to remote Linux account"), + ("login_linux_tip", "Login to remote Linux account."), ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session."), + ("verify_rustdesk_password_tip", "Veryfy RustDesk password."), + ("remember_account_tip", "Remember this account."), + ("remember_password_tip", "Remember password."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index e430f8f67..aeee842f7 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermi"), ("Retry", "Reprovi"), ("OK", "Konfermi"), - ("Password Required", "Pasvorto deviga"), + ("remember_password_tip", "Pasvorto deviga"), ("Please enter your password", "Bonvolu tajpi vian pasvorton"), ("Remember password", "Memori pasvorton"), ("Wrong Password", "Erara pasvorto"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index a85a5e491..3b998ae7d 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Cerrar"), ("Retry", "Reintentar"), ("OK", ""), - ("Password Required", "Se requiere contraseña"), + ("remember_password_tip", "Se requiere contraseña"), ("Please enter your password", "Por favor, introduzca su contraseña"), ("Remember password", "Recordar contraseña"), ("Wrong Password", "Contraseña incorrecta"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Introduzca la contraseña de RustDesk"), ("remember_rustdesk_passwd_tip", "Recordar la contraseña de RustDesk"), ("login_linux_tip", "Iniciar sesión para la cuenta remota de Linux"), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index e4d026586..e2137c3e2 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "بستن"), ("Retry", "تلاش مجدد"), ("OK", "قبول"), - ("Password Required", "رمز عبور لازم است"), + ("remember_password_tip", "رمز عبور لازم است"), ("Please enter your password", "رمز عبور خود را وارد کنید"), ("Remember password", "رمز عبور را به خاطر بسپار"), ("Wrong Password", "رمز عبور اشتباه است"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 3279b9175..71c0f6728 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermer"), ("Retry", "Réessayer"), ("OK", "Valider"), - ("Password Required", "Mot de passe requis"), + ("remember_password_tip", "Mot de passe requis"), ("Please enter your password", "Veuillez saisir votre mot de passe"), ("Remember password", "Mémoriser le mot de passe"), ("Wrong Password", "Mauvais mot de passe"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 2e6a20b56..7c7fb2468 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Bezárás"), ("Retry", "Újra"), ("OK", "OK"), - ("Password Required", "Jelszó megadása kötelező"), + ("remember_password_tip", "Jelszó megadása kötelező"), ("Please enter your password", "Kérem írja be a jelszavát"), ("Remember password", "Jelszó megjegyzése"), ("Wrong Password", "Hibás jelszó"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index cca4ba6c1..174d970b6 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tutup"), ("Retry", "Ulangi"), ("OK", "Oke"), - ("Password Required", "Kata sandi dibutuhkan"), + ("remember_password_tip", "Kata sandi dibutuhkan"), ("Please enter your password", "Silahkan masukkan kata sandi anda"), ("Remember password", "Ingat Password"), ("Wrong Password", "Kata sandi Salah"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 3c5973053..94b7d10a9 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Chiudi"), ("Retry", "Riprova"), ("OK", "OK"), - ("Password Required", "Password Richiesta"), + ("remember_password_tip", "Password Richiesta"), ("Please enter your password", "Inserisci la tua password"), ("Remember password", "Ricorda password"), ("Wrong Password", "Password Errata"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Inserisci la password di RustDesk."), ("remember_rustdesk_passwd_tip", "Ricorda la passowrd di RustDesk."), ("login_linux_tip", "Effettua l'accesso sul tuo account Linux"), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 29bf3c346..95395c516 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "閉じる"), ("Retry", "再試行"), ("OK", "OK"), - ("Password Required", "パスワードが必要"), + ("remember_password_tip", "パスワードが必要"), ("Please enter your password", "パスワードを入力してください"), ("Remember password", "パスワードを記憶する"), ("Wrong Password", "パスワードが間違っています"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 929934016..4e3b52ea2 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "닫기"), ("Retry", "재시도"), ("OK", "확인"), - ("Password Required", "비밀번호 입력"), + ("remember_password_tip", "비밀번호 입력"), ("Please enter your password", "비밀번호를 입력해주세요"), ("Remember password", "이 비밀번호 기억하기"), ("Wrong Password", "틀린 비밀번호"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index a2c7c5983..bfe312e02 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Жабу"), ("Retry", "Қайтадан көру"), ("OK", "OK"), - ("Password Required", "Құпия сөз Қажет"), + ("remember_password_tip", "Құпия сөз Қажет"), ("Please enter your password", "Құпия сөзіңізді еңгізуді өтінеміз"), ("Remember password", "Құпия сөзді есте сақтау"), ("Wrong Password", "Бұрыс Құпия сөз"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index f31135c6c..4a3c9c062 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Sluit"), ("Retry", "Probeer opnieuw"), ("OK", "OK"), - ("Password Required", "Wachtwoord vereist"), + ("remember_password_tip", "Wachtwoord vereist"), ("Please enter your password", "Geef uw wachtwoord in"), ("Remember password", "Wachtwoord onthouden"), ("Wrong Password", "Verkeerd wachtwoord"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Geef het RustDesk-wachtwoord op."), ("remember_rustdesk_passwd_tip", "RustDesk Wachtwoord onthouden."), ("login_linux_tip", "Je moet inloggen op een Linux Account op afstand om een X desktop sessie te openen."), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 852aa520d..f68d37866 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zamknij"), ("Retry", "Ponów"), ("OK", "OK"), - ("Password Required", "Wymagane jest hasło"), + ("remember_password_tip", "Wymagane jest hasło"), ("Please enter your password", "Wpisz proszę twoje hasło"), ("Remember password", "Zapamiętaj hasło"), ("Wrong Password", "Błędne hasło"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 45226dec6..04689f713 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "Confirmar"), - ("Password Required", "Palavra-chave Necessária"), + ("remember_password_tip", "Palavra-chave Necessária"), ("Please enter your password", "Por favor introduza a sua palavra-chave"), ("Remember password", "Memorizar palavra-chave"), ("Wrong Password", "Palavra-chave inválida"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 8cf8ca6ef..6f6302a59 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "OK"), - ("Password Required", "Senha necessária"), + ("remember_password_tip", "Senha necessária"), ("Please enter your password", "Por favor informe sua senha"), ("Remember password", "Lembrar senha"), ("Wrong Password", "Senha incorreta"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index e81fa0cfd..ade2e3c36 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Închide"), ("Retry", "Reîncearcă"), ("OK", "OK"), - ("Password Required", "Parolă necesară"), + ("remember_password_tip", "Parolă necesară"), ("Please enter your password", "Introdu parola"), ("Remember password", "Memorează parola"), ("Wrong Password", "Parolă incorectă"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 12c3dfb34..c85582e86 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрыть"), ("Retry", "Повторить"), ("OK", "ОК"), - ("Password Required", "Требуется пароль"), + ("remember_password_tip", "Требуется пароль"), ("Please enter your password", "Введите пароль"), ("Remember password", "Запомнить пароль"), ("Wrong Password", "Неправильный пароль"), @@ -484,5 +484,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", "Запомнить пароль RustDesk"), ("login_linux_tip", "Вход в удалённый аккаунт Linux"), ("login_linux_tooltip_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 7d28cfb72..e7c108a8c 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvoriť"), ("Retry", "Zopakovať"), ("OK", "OK"), - ("Password Required", "Vyžaduje sa heslo"), + ("remember_password_tip", "Vyžaduje sa heslo"), ("Please enter your password", "Zadajte vaše heslo"), ("Remember password", "Zapamätať heslo"), ("Wrong Password", "Chybné heslo"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 544e544f7..0e5265b87 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zapri"), ("Retry", "Ponovi"), ("OK", "V redu"), - ("Password Required", "Potrebno je geslo"), + ("remember_password_tip", "Potrebno je geslo"), ("Please enter your password", "Vnesite vaše geslo"), ("Remember password", "Zapomni si geslo"), ("Wrong Password", "Napačno geslo"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index afd5c2c2d..68ef47be9 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Mbyll"), ("Retry", "Riprovo"), ("OK", "OK"), - ("Password Required", "Fjalëkalimi i detyrueshëm"), + ("remember_password_tip", "Fjalëkalimi i detyrueshëm"), ("Please enter your password", "Ju lutem vendosni fjalëkalimin tuaj"), ("Remember password", "Mbani mend fjalëkalimin"), ("Wrong Password", "Fjalëkalim i gabuar"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 05204f728..ccb328e3e 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvori"), ("Retry", "Ponovi"), ("OK", "Ok"), - ("Password Required", "Potrebna lozinka"), + ("remember_password_tip", "Potrebna lozinka"), ("Please enter your password", "Molimo unesite svoju lozinku"), ("Remember password", "Zapamti lozinku"), ("Wrong Password", "Pogrešna lozinka"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 7941c2f58..496c882bc 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Stäng"), ("Retry", "Försök igen"), ("OK", "OK"), - ("Password Required", "Lösenord krävs"), + ("remember_password_tip", "Lösenord krävs"), ("Please enter your password", "Skriv in ditt lösenord"), ("Remember password", "Kom ihåg lösenord"), ("Wrong Password", "Fel lösenord"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 56508d80a..6ec149289 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", ""), ("Retry", ""), ("OK", ""), - ("Password Required", ""), + ("remember_password_tip", ""), ("Please enter your password", ""), ("Remember password", ""), ("Wrong Password", ""), @@ -484,5 +484,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 4f178bf1d..92ae279c9 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "ปิด"), ("Retry", "ลองใหม่อีกครั้ง"), ("OK", "ตกลง"), - ("Password Required", "ต้องใช้รหัสผ่าน"), + ("remember_password_tip", "ต้องใช้รหัสผ่าน"), ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), ("Remember password", "จดจำรหัสผ่าน"), ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index f5209129b..b423a021b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Kapat"), ("Retry", "Tekrar Dene"), ("OK", "Tamam"), - ("Password Required", "Şifre Gerekli"), + ("remember_password_tip", "Şifre Gerekli"), ("Please enter your password", "Lütfen şifrenizi giriniz"), ("Remember password", "Şifreyi hatırla"), ("Wrong Password", "Hatalı şifre"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 870105b09..e8685747e 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -1,488 +1,491 @@ -lazy_static::lazy_static! { -pub static ref T: std::collections::HashMap<&'static str, &'static str> = - [ - ("Status", "狀態"), - ("Your Desktop", "您的桌面"), - ("desk_tip", "您可以透過此 ID 及密碼存取您的桌面"), - ("Password", "密碼"), - ("Ready", "就緒"), - ("Established", "已建立"), - ("connecting_status", "正在連線到 RustDesk 網路 ..."), - ("Enable Service", "啟用服務"), - ("Start Service", "啟動服務"), - ("Service is running", "服務正在執行"), - ("Service is not running", "服務尚未執行"), - ("not_ready_status", "尚未就緒,請檢查您的網路連線。"), - ("Control Remote Desktop", "控制遠端桌面"), - ("Transfer File", "傳輸檔案"), - ("Connect", "連線"), - ("Recent Sessions", "近期的工作階段"), - ("Address Book", "通訊錄"), - ("Confirmation", "確認"), - ("TCP Tunneling", "TCP 通道"), - ("Remove", "移除"), - ("Refresh random password", "重新產生隨機密碼"), - ("Set your own password", "自行設定密碼"), - ("Enable Keyboard/Mouse", "啟用鍵盤和滑鼠"), - ("Enable Clipboard", "啟用剪貼簿"), - ("Enable File Transfer", "啟用檔案傳輸"), - ("Enable TCP Tunneling", "啟用 TCP 通道"), - ("IP Whitelisting", "IP 白名單"), - ("ID/Relay Server", "ID / 轉送伺服器"), - ("Import Server Config", "匯入伺服器設定"), - ("Export Server Config", "匯出伺服器設定"), - ("Import server configuration successfully", "匯入伺服器設定成功"), - ("Export server configuration successfully", "匯出伺服器設定成功"), - ("Invalid server configuration", "無效的伺服器設定"), - ("Clipboard is empty", "剪貼簿是空的"), - ("Stop service", "停止服務"), - ("Change ID", "更改 ID"), - ("Your new ID", "您的新 ID"), - ("length %min% to %max%", "長度在 %min% 與 %max% 之間"), - ("starts with a letter", "以字母開頭"), - ("allowed characters", "使用允許的字元"), - ("id_change_tip", "僅能使用以下字元:a-z、A-Z、0-9、_ (底線)。首字元必須為 a-z 或 A-Z。長度介於 6 到 16 之間。"), - ("Website", "網站"), - ("About", "關於"), - ("Slogan_tip", ""), - ("Privacy Statement", "隱私權聲明"), - ("Mute", "靜音"), - ("Build Date", "構建日期"), - ("Version", "版本"), - ("Home", "首頁"), - ("Audio Input", "音訊輸入"), - ("Enhancements", "增強功能"), - ("Hardware Codec", "硬體編解碼器"), - ("Adaptive Bitrate", "自適應位元速率"), - ("ID Server", "ID 伺服器"), - ("Relay Server", "轉送伺服器"), - ("API Server", "API 伺服器"), - ("invalid_http", "開頭必須為 http:// 或 https://"), - ("Invalid IP", "IP 無效"), - ("Invalid format", "格式無效"), - ("server_not_support", "伺服器暫不支持"), - ("Not available", "無法使用"), - ("Too frequent", "修改過於頻繁,請稍後再試。"), - ("Cancel", "取消"), - ("Skip", "跳過"), - ("Close", "關閉"), - ("Retry", "重試"), - ("OK", "確定"), - ("Password Required", "需要密碼"), - ("Please enter your password", "請輸入您的密碼"), - ("Remember password", "記住密碼"), - ("Wrong Password", "密碼錯誤"), - ("Do you want to enter again?", "您要重新輸入嗎?"), - ("Connection Error", "連線錯誤"), - ("Error", "錯誤"), - ("Reset by the peer", "對方重設了連線"), - ("Connecting...", "正在連線 ..."), - ("Connection in progress. Please wait.", "正在連線,請稍候。"), - ("Please try 1 minute later", "請於 1 分鐘後再試"), - ("Login Error", "登入錯誤"), - ("Successful", "成功"), - ("Connected, waiting for image...", "已連線,等待畫面傳輸 ..."), - ("Name", "名稱"), - ("Type", "類型"), - ("Modified", "修改時間"), - ("Size", "大小"), - ("Show Hidden Files", "顯示隱藏檔案"), - ("Receive", "接收"), - ("Send", "傳送"), - ("Refresh File", "重新整理檔案"), - ("Local", "本地"), - ("Remote", "遠端"), - ("Remote Computer", "遠端電腦"), - ("Local Computer", "本地電腦"), - ("Confirm Delete", "確認刪除"), - ("Delete", "刪除"), - ("Properties", "屬性"), - ("Multi Select", "多選"), - ("Select All", "全選"), - ("Unselect All", "取消全選"), - ("Empty Directory", "空資料夾"), - ("Not an empty directory", "不是一個空資料夾"), - ("Are you sure you want to delete this file?", "您確定要刪除此檔案嗎?"), - ("Are you sure you want to delete this empty directory?", "您確定要刪除此空資料夾嗎?"), - ("Are you sure you want to delete the file of this directory?", "您確定要刪除此資料夾中的檔案嗎?"), - ("Do this for all conflicts", "套用到其他衝突"), - ("This is irreversible!", "此操作不可逆!"), - ("Deleting", "正在刪除 ..."), - ("files", "檔案"), - ("Waiting", "正在等候 ..."), - ("Finished", "已完成"), - ("Speed", "速度"), - ("Custom Image Quality", "自訂畫面品質"), - ("Privacy mode", "隱私模式"), - ("Block user input", "封鎖使用者輸入"), - ("Unblock user input", "取消封鎖使用者輸入"), - ("Adjust Window", "調整視窗"), - ("Original", "原始"), - ("Shrink", "縮減"), - ("Stretch", "延展"), - ("Scrollbar", "滾動條"), - ("ScrollAuto", "自動滾動"), - ("Good image quality", "最佳化畫面品質"), - ("Balanced", "平衡"), - ("Optimize reaction time", "最佳化反應時間"), - ("Custom", "自訂"), - ("Show remote cursor", "顯示遠端游標"), - ("Show quality monitor", "顯示質量監測"), - ("Disable clipboard", "停用剪貼簿"), - ("Lock after session end", "工作階段結束後鎖定電腦"), - ("Insert", "插入"), - ("Insert Lock", "鎖定遠端電腦"), - ("Refresh", "重新載入"), - ("ID does not exist", "ID 不存在"), - ("Failed to connect to rendezvous server", "無法連線到 rendezvous 伺服器"), - ("Please try later", "請稍候再試"), - ("Remote desktop is offline", "遠端桌面已離線"), - ("Key mismatch", "金鑰不符"), - ("Timeout", "逾時"), - ("Failed to connect to relay server", "無法連線到轉送伺服器"), - ("Failed to connect via rendezvous server", "無法透過 rendezvous 伺服器連線"), - ("Failed to connect via relay server", "無法透過轉送伺服器連線"), - ("Failed to make direct connection to remote desktop", "無法直接連線到遠端桌面"), - ("Set Password", "設定密碼"), - ("OS Password", "作業系統密碼"), - ("install_tip", "UAC 會導致 RustDesk 在某些情況下無法正常以遠端電腦執行。若要避開 UAC,請點擊下方按鈕將 RustDesk 安裝到系統中。"), - ("Click to upgrade", "點擊以升級"), - ("Click to download", "點擊以下載"), - ("Click to update", "點擊以更新"), - ("Configure", "設定"), - ("config_acc", "您需要授予 RustDesk「協助工具」權限才能存取遠端電腦。"), - ("config_screen", "您需要授予 RustDesk「畫面錄製」權限才能存取遠端電腦。"), - ("Installing ...", "正在安裝 ..."), - ("Install", "安裝"), - ("Installation", "安裝"), - ("Installation Path", "安裝路徑"), - ("Create start menu shortcuts", "新增開始功能表捷徑"), - ("Create desktop icon", "新增桌面捷徑"), - ("agreement_tip", "開始安裝即表示接受許可協議"), - ("Accept and Install", "接受並安裝"), - ("End-user license agreement", "使用者授權合約"), - ("Generating ...", "正在產生 ..."), - ("Your installation is lower version.", "您安裝的版本過舊。"), - ("not_close_tcp_tip", "使用通道時請不要關閉此視窗"), - ("Listening ...", "正在等待通道連線 ..."), - ("Remote Host", "遠端主機"), - ("Remote Port", "遠端連線端口"), - ("Action", "操作"), - ("Add", "新增"), - ("Local Port", "本機連線端口"), - ("Local Address", "本機地址"), - ("Change Local Port", "修改本機連線端口"), - ("setup_server_tip", "若您需要更快的連線速度,可以選擇自行建立伺服器"), - ("Too short, at least 6 characters.", "過短,至少需要 6 個字元。"), - ("The confirmation is not identical.", "兩次輸入不相符"), - ("Permissions", "權限"), - ("Accept", "接受"), - ("Dismiss", "關閉"), - ("Disconnect", "中斷連線"), - ("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"), - ("Allow using clipboard", "允許使用剪貼簿"), - ("Allow hearing sound", "允許分享音訊"), - ("Allow file copy and paste", "允許檔案複製和貼上"), - ("Connected", "已連線"), - ("Direct and encrypted connection", "加密直接連線"), - ("Relayed and encrypted connection", "加密轉送連線"), - ("Direct and unencrypted connection", "未加密直接連線"), - ("Relayed and unencrypted connection", "未加密轉送連線"), - ("Enter Remote ID", "輸入遠端 ID"), - ("Enter your password", "輸入您的密碼"), - ("Logging in...", "正在登入 ..."), - ("Enable RDP session sharing", "啟用 RDP 工作階段共享"), - ("Auto Login", "自動登入 (鎖定將在設定關閉後套用)"), - ("Enable Direct IP Access", "允許 IP 直接存取"), - ("Rename", "重新命名"), - ("Space", "空白"), - ("Create Desktop Shortcut", "新增桌面捷徑"), - ("Change Path", "更改路徑"), - ("Create Folder", "新增資料夾"), - ("Please enter the folder name", "請輸入資料夾名稱"), - ("Fix it", "修復"), - ("Warning", "警告"), - ("Login screen using Wayland is not supported", "不支援使用 Wayland 的登入畫面"), - ("Reboot required", "需要重新啟動"), - ("Unsupported display server", "不支援顯示伺服器"), - ("x11 expected", "預期 x11"), - ("Port", "端口"), - ("Settings", "設定"), - ("Username", "使用者名稱"), - ("Invalid port", "連線端口無效"), - ("Closed manually by the peer", "遠端使用者關閉了工作階段"), - ("Enable remote configuration modification", "允許遠端使用者更改設定"), - ("Run without install", "跳過安裝直接執行"), - ("Connect via relay", "中繼連線"), - ("Always connect via relay", "一律透過轉送連線"), - ("whitelist_tip", "只有白名單中的 IP 可以存取"), - ("Login", "登入"), - ("Verify", "驗證"), - ("Remember me", "記住我"), - ("Trust this device", "信任此裝置"), - ("Verification code", "驗證碼"), - ("verification_tip", "檢測到新裝置登入,已向註冊電子信箱發送了登入驗證碼,請輸入驗證碼以繼續登入"), - ("Logout", "登出"), - ("Tags", "標籤"), - ("Search ID", "搜尋 ID"), - ("whitelist_sep", "使用逗號、分號、空白,或是換行來分隔"), - ("Add ID", "新增 ID"), - ("Add Tag", "新增標籤"), - ("Unselect all tags", "取消選取所有標籤"), - ("Network error", "網路錯誤"), - ("Username missed", "缺少使用者名稱"), - ("Password missed", "缺少密碼"), - ("Wrong credentials", "提供的登入資訊有誤"), - ("Edit Tag", "編輯標籤"), - ("Unremember Password", "忘掉密碼"), - ("Favorites", "我的最愛"), - ("Add to Favorites", "新增到我的最愛"), - ("Remove from Favorites", "從我的最愛中刪除"), - ("Empty", "空空如也"), - ("Invalid folder name", "資料夾名稱無效"), - ("Socks5 Proxy", "Socks5 代理"), - ("Hostname", "主機名稱"), - ("Discovered", "已探索"), - ("install_daemon_tip", "為了能夠開機時自動啟動,請先安裝系統服務。"), - ("Remote ID", "遠端 ID"), - ("Paste", "貼上"), - ("Paste here?", "貼上到這裡?"), - ("Are you sure to close the connection?", "您確定要關閉連線嗎?"), - ("Download new version", "下載新版本"), - ("Touch mode", "觸控模式"), - ("Mouse mode", "滑鼠模式"), - ("One-Finger Tap", "單指輕觸"), - ("Left Mouse", "滑鼠左鍵"), - ("One-Long Tap", "單指長按"), - ("Two-Finger Tap", "雙指輕觸"), - ("Right Mouse", "滑鼠右鍵"), - ("One-Finger Move", "單指移動"), - ("Double Tap & Move", "雙擊並移動"), - ("Mouse Drag", "滑鼠選中拖動"), - ("Three-Finger vertically", "三指垂直滑動"), - ("Mouse Wheel", "滑鼠滾輪"), - ("Two-Finger Move", "雙指移動"), - ("Canvas Move", "移動畫布"), - ("Pinch to Zoom", "雙指縮放"), - ("Canvas Zoom", "縮放畫布"), - ("Reset canvas", "重設畫布"), - ("No permission of file transfer", "沒有檔案傳輸權限"), - ("Note", "備註"), - ("Connection", "連線"), - ("Share Screen", "共享螢幕畫面"), - ("Chat", "聊天訊息"), - ("Total", "總計"), - ("items", "個項目"), - ("Selected", "已選擇"), - ("Screen Capture", "畫面錄製"), - ("Input Control", "輸入控制"), - ("Audio Capture", "音訊錄製"), - ("File Connection", "檔案連線"), - ("Screen Connection", "畫面連線"), - ("Do you accept?", "是否接受?"), - ("Open System Setting", "開啟系統設定"), - ("How to get Android input permission?", "如何獲取 Android 的輸入權限?"), - ("android_input_permission_tip1", "取得輸入權限後可以讓遠端裝置透過滑鼠控制此 Android 裝置"), - ("android_input_permission_tip2", "請在接下來的系統設定頁面中,找到並進入「已安裝的服務」頁面,並將「RustDesk Input」服務開啟"), - ("android_new_connection_tip", "收到新的連線控制請求,對方想要控制您目前的裝置"), - ("android_service_will_start_tip", "開啟畫面錄製權限將自動開啟服務,允許其他裝置向此裝置請求建立連線。"), - ("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連線。"), - ("android_version_audio_tip", "目前的 Android 版本不支援音訊錄製,請升級到 Android 10 或以上版本。"), - ("android_start_service_tip", "點擊「啟動服務」或啟用「螢幕錄製」權限,以啟動螢幕共享服務。"), - ("android_permission_may_not_change_tip", "對於已經建立的連線,權限可能不會立即發生改變,除非重新建立連線。"), - ("Account", "帳號"), - ("Overwrite", "取代"), - ("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要略過或是取代此檔案嗎?"), - ("Quit", "退出"), - ("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"), - ("Help", "幫助"), - ("Failed", "失敗"), - ("Succeeded", "成功"), - ("Someone turns on privacy mode, exit", "其他使用者開啟隱私模式,退出"), - ("Unsupported", "不支援"), - ("Peer denied", "被控端拒絕"), - ("Please install plugins", "請安裝插件"), - ("Peer exit", "被控端退出"), - ("Failed to turn off", "退出失敗"), - ("Turned off", "退出"), - ("In privacy mode", "開啟隱私模式"), - ("Out privacy mode", "退出隱私模式"), - ("Language", "語言"), - ("Keep RustDesk background service", "保持 RustDesk 後台服務"), - ("Ignore Battery Optimizations", "忽略電池最佳化"), - ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的 RustDesk 應用設定頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), - ("Start on Boot", "開機自動啟動"), - ("Start the screen sharing service on boot, requires special permissions", "開機自動啟動螢幕共享服務,此功能需要一些特殊權限。"), - ("Connection not allowed", "對方不允許連線"), - ("Legacy mode", "傳統模式"), - ("Map mode", "1:1 傳輸模式"), - ("Translate mode", "翻譯模式"), - ("Use permanent password", "使用固定密碼"), - ("Use both passwords", "同時使用兩種密碼"), - ("Set permanent password", "設定固定密碼"), - ("Enable Remote Restart", "啟用遠端重新啟動"), - ("Allow remote restart", "允許遠端重新啟動"), - ("Restart Remote Device", "重新啟動遠端電腦"), - ("Are you sure you want to restart", "確定要重新啟動"), - ("Restarting Remote Device", "正在重新啟動遠端裝置"), - ("remote_restarting_tip", "遠端裝置正在重新啟動,請關閉當前提示框,並在一段時間後使用永久密碼重新連線"), - ("Copied", "已複製"), - ("Exit Fullscreen", "退出全螢幕"), - ("Fullscreen", "全螢幕"), - ("Mobile Actions", "手機操作"), - ("Select Monitor", "選擇顯示器"), - ("Control Actions", "控制操作"), - ("Display Settings", "顯示設定"), - ("Ratio", "比例"), - ("Image Quality", "畫質"), - ("Scroll Style", "滾動樣式"), - ("Show Menubar", "顯示選單欄"), - ("Hide Menubar", "隱藏選單欄"), - ("Direct Connection", "直接連線"), - ("Relay Connection", "中繼連線"), - ("Secure Connection", "安全連線"), - ("Insecure Connection", "非安全連線"), - ("Scale original", "原始尺寸"), - ("Scale adaptive", "適應視窗"), - ("General", "通用"), - ("Security", "安全"), - ("Theme", "主題"), - ("Dark Theme", "黑暗主題"), - ("Light Theme", "明亮主題"), - ("Dark", "黑暗"), - ("Light", "明亮"), - ("Follow System", "跟隨系統"), - ("Enable hardware codec", "使用硬體編解碼器"), - ("Unlock Security Settings", "解鎖安全設定"), - ("Enable Audio", "允許傳輸音訊"), - ("Unlock Network Settings", "解鎖網路設定"), - ("Server", "伺服器"), - ("Direct IP Access", "IP 直接連線"), - ("Proxy", "代理"), - ("Apply", "應用"), - ("Disconnect all devices?", "中斷所有遠端連線?"), - ("Clear", "清空"), - ("Audio Input Device", "音訊輸入裝置"), - ("Deny remote access", "拒絕遠端存取"), - ("Use IP Whitelisting", "只允許白名單上的 IP 進行連線"), - ("Network", "網路"), - ("Enable RDP", "允許 RDP 訪問"), - ("Pin menubar", "固定選單欄"), - ("Unpin menubar", "取消固定選單欄"), - ("Recording", "錄製"), - ("Directory", "路徑"), - ("Automatically record incoming sessions", "自動錄製連入的工作階段"), - ("Change", "變更"), - ("Start session recording", "開始錄影"), - ("Stop session recording", "停止錄影"), - ("Enable Recording Session", "啟用錄製工作階段"), - ("Allow recording session", "允許錄製工作階段"), - ("Enable LAN Discovery", "允許區域網路探索"), - ("Deny LAN Discovery", "拒絕區域網路探索"), - ("Write a message", "輸入聊天訊息"), - ("Prompt", "提示"), - ("Please wait for confirmation of UAC...", "請等待對方確認 UAC ..."), - ("elevated_foreground_window_tip", "目前的遠端桌面視窗需要更高的權限才能繼續操作,暫時無法使用滑鼠、鍵盤,可以請求對方最小化目前視窗,或者在連線管理視窗點擊提升權限。為了避免這個問題,建議在遠端裝置上安裝本軟體。"), - ("Disconnected", "斷開連線"), - ("Other", "其他"), - ("Confirm before closing multiple tabs", "關閉多個分頁前詢問我"), - ("Keyboard Settings", "鍵盤設定"), - ("Full Access", "完全訪問"), - ("Screen Share", "僅分享螢幕畫面"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland 需要 Ubuntu 21.04 或更高版本。"), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland 需要更高版本的 Linux 發行版。請嘗試使用 X11 桌面或更改您的作業系統。"), - ("JumpLink", "查看"), - ("Please Select the screen to be shared(Operate on the peer side).", "請選擇要分享的螢幕畫面(在對端操作)。"), - ("Show RustDesk", "顯示 RustDesk"), - ("This PC", "此電腦"), - ("or", "或"), - ("Continue with", "繼續"), - ("Elevate", "提升權限"), - ("Zoom cursor", "縮放游標"), - ("Accept sessions via password", "只允許透過輸入密碼進行連線"), - ("Accept sessions via click", "只允許透過點擊接受進行連線"), - ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), - ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), - ("One-time Password", "一次性密碼"), - ("Use one-time password", "使用一次性密碼"), - ("One-time password length", "一次性密碼長度"), - ("Request access to your device", "請求訪問您的裝置"), - ("Hide connection management window", "隱藏連線管理視窗"), - ("hide_cm_tip", "在只允許密碼連線並且只用固定密碼的情況下才允許隱藏"), - ("wayland_experiment_tip", "Wayland 支援處於實驗階段,如果您需要使用無人值守訪問,請使用 X11。"), - ("Right click to select tabs", "右鍵選擇分頁"), - ("Skipped", "已略過"), - ("Add to Address Book", "新增到通訊錄"), - ("Group", "群組"), - ("Search", "搜尋"), - ("Closed manually by web console", "被 Web 控制台手動關閉"), - ("Local keyboard type", "本地鍵盤類型"), - ("Select local keyboard type", "請選擇本地鍵盤類型"), - ("software_render_tip", "如果您使用 NVIDIA 顯示卡,並且遠端視窗在建立連線後會立刻關閉,那麼請安裝 nouveau 顯示卡驅動程式並且選擇使用軟體渲染可能會有幫助。重新啟動軟體後生效。"), - ("Always use software rendering", "使用軟體渲染"), - ("config_input", "為了能夠透過鍵盤控制遠端桌面,請給予 RustDesk \"輸入監控\" 權限。"), - ("config_microphone", "為了支援透過麥克風進行音訊傳輸,請給予 RustDesk \"錄音\"權限。"), - ("request_elevation_tip", "如果遠端使用者可以操作電腦,也可以請求提升權限。"), - ("Wait", "等待"), - ("Elevation Error", "權限提升失敗"), - ("Ask the remote user for authentication", "請求遠端使用者進行身分驗證"), - ("Choose this if the remote account is administrator", "當遠端使用者帳戶是管理員時,請選擇此選項"), - ("Transmit the username and password of administrator", "發送管理員的使用者名稱和密碼"), - ("still_click_uac_tip", "依然需要遠端使用者在 UAC 視窗點擊確認。"), - ("Request Elevation", "請求權限提升"), - ("wait_accept_uac_tip", "請等待遠端使用者確認 UAC 對話框。"), - ("Elevate successfully", "權限提升成功"), - ("uppercase", "大寫字母"), - ("lowercase", "小寫字母"), - ("digit", "數字"), - ("special character", "特殊字元"), - ("length>=8", "長度不能小於 8"), - ("Weak", "弱"), - ("Medium", "中"), - ("Strong", "強"), - ("Switch Sides", "反轉存取方向"), - ("Please confirm if you want to share your desktop?", "請確認是否要讓對方存取您的桌面?"), - ("Display", "顯示"), - ("Default View Style", "預設顯示方式"), - ("Default Scroll Style", "預設滾動方式"), - ("Default Image Quality", "預設圖像質量"), - ("Default Codec", "預設編解碼器"), - ("Bitrate", "位元速率"), - ("FPS", "幀率"), - ("Auto", "自動"), - ("Other Default Options", "其他預設選項"), - ("Voice call", "語音通話"), - ("Text chat", "文字聊天"), - ("Stop voice call", "停止語音通話"), - ("relay_hint_tip", "可能無法直接連線,可以嘗試中繼連線。\n另外,如果想要直接使用中繼連線,可以在 ID 後面新增/r,或者在卡片選項裡選擇強制走中繼連線。"), - ("Reconnect", "重新連線"), - ("Codec", "編解碼器"), - ("Resolution", "解析度"), - ("No transfers in progress", "沒有正在進行的傳輸"), - ("Set one-time password length", "設定一次性密碼長度"), - ("idd_driver_tip", "安裝虛擬顯示器驅動程式,以便在沒有連接顯示器的情況下啟動虛擬顯示器進行控制。"), - ("confirm_idd_driver_tip", "安裝虛擬顯示器驅動程式的選項已勾選。請注意,測試證書將被安裝以信任虛擬顯示器驅動。測試證書僅會用於信任 RustDesk 的驅動程式。"), - ("RDP Settings", "RDP 設定"), - ("Sort by", "排序方式"), - ("New Connection", "新連線"), - ("Restore", "還原"), - ("Minimize", "最小化"), - ("Maximize", "最大化"), - ("Your Device", "您的裝置"), - ("empty_recent_tip", "空空如也"), - ("empty_favorite_tip", "空空如也"), - ("empty_lan_tip", "空空如也"), - ("empty_address_book_tip", "空空如也"), - ("eg: admin", "例如:admin"), - ("Empty Username", "空使用者帳號"), - ("Empty Password", "空密碼"), - ("Me", "我"), - ("identical_file_tip", "此檔案與對方的檔案一致"), - ("show_monitors_tip", "在工具列中顯示顯示器"), - ("View Mode", "瀏覽模式"), - ("enter_rustdesk_passwd_tip", "輸入 RustDesk 密碼"), - ("remember_rustdesk_passwd_tip", "記住 RustDesk 密碼"), - ("login_linux_tip", "登入到遠端 Linux 使用者帳戶"), - ("login_linux_tooltip_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), - ].iter().cloned().collect(); -} +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "狀態"), + ("Your Desktop", "您的桌面"), + ("desk_tip", "您可以透過此 ID 及密碼存取您的桌面"), + ("Password", "密碼"), + ("Ready", "就緒"), + ("Established", "已建立"), + ("connecting_status", "正在連線到 RustDesk 網路 ..."), + ("Enable Service", "啟用服務"), + ("Start Service", "啟動服務"), + ("Service is running", "服務正在執行"), + ("Service is not running", "服務尚未執行"), + ("not_ready_status", "尚未就緒,請檢查您的網路連線。"), + ("Control Remote Desktop", "控制遠端桌面"), + ("Transfer File", "傳輸檔案"), + ("Connect", "連線"), + ("Recent Sessions", "近期的工作階段"), + ("Address Book", "通訊錄"), + ("Confirmation", "確認"), + ("TCP Tunneling", "TCP 通道"), + ("Remove", "移除"), + ("Refresh random password", "重新產生隨機密碼"), + ("Set your own password", "自行設定密碼"), + ("Enable Keyboard/Mouse", "啟用鍵盤和滑鼠"), + ("Enable Clipboard", "啟用剪貼簿"), + ("Enable File Transfer", "啟用檔案傳輸"), + ("Enable TCP Tunneling", "啟用 TCP 通道"), + ("IP Whitelisting", "IP 白名單"), + ("ID/Relay Server", "ID / 轉送伺服器"), + ("Import Server Config", "匯入伺服器設定"), + ("Export Server Config", "匯出伺服器設定"), + ("Import server configuration successfully", "匯入伺服器設定成功"), + ("Export server configuration successfully", "匯出伺服器設定成功"), + ("Invalid server configuration", "無效的伺服器設定"), + ("Clipboard is empty", "剪貼簿是空的"), + ("Stop service", "停止服務"), + ("Change ID", "更改 ID"), + ("Your new ID", "您的新 ID"), + ("length %min% to %max%", "長度在 %min% 與 %max% 之間"), + ("starts with a letter", "以字母開頭"), + ("allowed characters", "使用允許的字元"), + ("id_change_tip", "僅能使用以下字元:a-z、A-Z、0-9、_ (底線)。首字元必須為 a-z 或 A-Z。長度介於 6 到 16 之間。"), + ("Website", "網站"), + ("About", "關於"), + ("Slogan_tip", ""), + ("Privacy Statement", "隱私權聲明"), + ("Mute", "靜音"), + ("Build Date", "構建日期"), + ("Version", "版本"), + ("Home", "首頁"), + ("Audio Input", "音訊輸入"), + ("Enhancements", "增強功能"), + ("Hardware Codec", "硬體編解碼器"), + ("Adaptive Bitrate", "自適應位元速率"), + ("ID Server", "ID 伺服器"), + ("Relay Server", "轉送伺服器"), + ("API Server", "API 伺服器"), + ("invalid_http", "開頭必須為 http:// 或 https://"), + ("Invalid IP", "IP 無效"), + ("Invalid format", "格式無效"), + ("server_not_support", "伺服器暫不支持"), + ("Not available", "無法使用"), + ("Too frequent", "修改過於頻繁,請稍後再試。"), + ("Cancel", "取消"), + ("Skip", "跳過"), + ("Close", "關閉"), + ("Retry", "重試"), + ("OK", "確定"), + ("remember_password_tip", "需要密碼"), + ("Please enter your password", "請輸入您的密碼"), + ("Remember password", "記住密碼"), + ("Wrong Password", "密碼錯誤"), + ("Do you want to enter again?", "您要重新輸入嗎?"), + ("Connection Error", "連線錯誤"), + ("Error", "錯誤"), + ("Reset by the peer", "對方重設了連線"), + ("Connecting...", "正在連線 ..."), + ("Connection in progress. Please wait.", "正在連線,請稍候。"), + ("Please try 1 minute later", "請於 1 分鐘後再試"), + ("Login Error", "登入錯誤"), + ("Successful", "成功"), + ("Connected, waiting for image...", "已連線,等待畫面傳輸 ..."), + ("Name", "名稱"), + ("Type", "類型"), + ("Modified", "修改時間"), + ("Size", "大小"), + ("Show Hidden Files", "顯示隱藏檔案"), + ("Receive", "接收"), + ("Send", "傳送"), + ("Refresh File", "重新整理檔案"), + ("Local", "本地"), + ("Remote", "遠端"), + ("Remote Computer", "遠端電腦"), + ("Local Computer", "本地電腦"), + ("Confirm Delete", "確認刪除"), + ("Delete", "刪除"), + ("Properties", "屬性"), + ("Multi Select", "多選"), + ("Select All", "全選"), + ("Unselect All", "取消全選"), + ("Empty Directory", "空資料夾"), + ("Not an empty directory", "不是一個空資料夾"), + ("Are you sure you want to delete this file?", "您確定要刪除此檔案嗎?"), + ("Are you sure you want to delete this empty directory?", "您確定要刪除此空資料夾嗎?"), + ("Are you sure you want to delete the file of this directory?", "您確定要刪除此資料夾中的檔案嗎?"), + ("Do this for all conflicts", "套用到其他衝突"), + ("This is irreversible!", "此操作不可逆!"), + ("Deleting", "正在刪除 ..."), + ("files", "檔案"), + ("Waiting", "正在等候 ..."), + ("Finished", "已完成"), + ("Speed", "速度"), + ("Custom Image Quality", "自訂畫面品質"), + ("Privacy mode", "隱私模式"), + ("Block user input", "封鎖使用者輸入"), + ("Unblock user input", "取消封鎖使用者輸入"), + ("Adjust Window", "調整視窗"), + ("Original", "原始"), + ("Shrink", "縮減"), + ("Stretch", "延展"), + ("Scrollbar", "滾動條"), + ("ScrollAuto", "自動滾動"), + ("Good image quality", "最佳化畫面品質"), + ("Balanced", "平衡"), + ("Optimize reaction time", "最佳化反應時間"), + ("Custom", "自訂"), + ("Show remote cursor", "顯示遠端游標"), + ("Show quality monitor", "顯示質量監測"), + ("Disable clipboard", "停用剪貼簿"), + ("Lock after session end", "工作階段結束後鎖定電腦"), + ("Insert", "插入"), + ("Insert Lock", "鎖定遠端電腦"), + ("Refresh", "重新載入"), + ("ID does not exist", "ID 不存在"), + ("Failed to connect to rendezvous server", "無法連線到 rendezvous 伺服器"), + ("Please try later", "請稍候再試"), + ("Remote desktop is offline", "遠端桌面已離線"), + ("Key mismatch", "金鑰不符"), + ("Timeout", "逾時"), + ("Failed to connect to relay server", "無法連線到轉送伺服器"), + ("Failed to connect via rendezvous server", "無法透過 rendezvous 伺服器連線"), + ("Failed to connect via relay server", "無法透過轉送伺服器連線"), + ("Failed to make direct connection to remote desktop", "無法直接連線到遠端桌面"), + ("Set Password", "設定密碼"), + ("OS Password", "作業系統密碼"), + ("install_tip", "UAC 會導致 RustDesk 在某些情況下無法正常以遠端電腦執行。若要避開 UAC,請點擊下方按鈕將 RustDesk 安裝到系統中。"), + ("Click to upgrade", "點擊以升級"), + ("Click to download", "點擊以下載"), + ("Click to update", "點擊以更新"), + ("Configure", "設定"), + ("config_acc", "您需要授予 RustDesk「協助工具」權限才能存取遠端電腦。"), + ("config_screen", "您需要授予 RustDesk「畫面錄製」權限才能存取遠端電腦。"), + ("Installing ...", "正在安裝 ..."), + ("Install", "安裝"), + ("Installation", "安裝"), + ("Installation Path", "安裝路徑"), + ("Create start menu shortcuts", "新增開始功能表捷徑"), + ("Create desktop icon", "新增桌面捷徑"), + ("agreement_tip", "開始安裝即表示接受許可協議"), + ("Accept and Install", "接受並安裝"), + ("End-user license agreement", "使用者授權合約"), + ("Generating ...", "正在產生 ..."), + ("Your installation is lower version.", "您安裝的版本過舊。"), + ("not_close_tcp_tip", "使用通道時請不要關閉此視窗"), + ("Listening ...", "正在等待通道連線 ..."), + ("Remote Host", "遠端主機"), + ("Remote Port", "遠端連線端口"), + ("Action", "操作"), + ("Add", "新增"), + ("Local Port", "本機連線端口"), + ("Local Address", "本機地址"), + ("Change Local Port", "修改本機連線端口"), + ("setup_server_tip", "若您需要更快的連線速度,可以選擇自行建立伺服器"), + ("Too short, at least 6 characters.", "過短,至少需要 6 個字元。"), + ("The confirmation is not identical.", "兩次輸入不相符"), + ("Permissions", "權限"), + ("Accept", "接受"), + ("Dismiss", "關閉"), + ("Disconnect", "中斷連線"), + ("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"), + ("Allow using clipboard", "允許使用剪貼簿"), + ("Allow hearing sound", "允許分享音訊"), + ("Allow file copy and paste", "允許檔案複製和貼上"), + ("Connected", "已連線"), + ("Direct and encrypted connection", "加密直接連線"), + ("Relayed and encrypted connection", "加密轉送連線"), + ("Direct and unencrypted connection", "未加密直接連線"), + ("Relayed and unencrypted connection", "未加密轉送連線"), + ("Enter Remote ID", "輸入遠端 ID"), + ("Enter your password", "輸入您的密碼"), + ("Logging in...", "正在登入 ..."), + ("Enable RDP session sharing", "啟用 RDP 工作階段共享"), + ("Auto Login", "自動登入 (鎖定將在設定關閉後套用)"), + ("Enable Direct IP Access", "允許 IP 直接存取"), + ("Rename", "重新命名"), + ("Space", "空白"), + ("Create Desktop Shortcut", "新增桌面捷徑"), + ("Change Path", "更改路徑"), + ("Create Folder", "新增資料夾"), + ("Please enter the folder name", "請輸入資料夾名稱"), + ("Fix it", "修復"), + ("Warning", "警告"), + ("Login screen using Wayland is not supported", "不支援使用 Wayland 的登入畫面"), + ("Reboot required", "需要重新啟動"), + ("Unsupported display server", "不支援顯示伺服器"), + ("x11 expected", "預期 x11"), + ("Port", "端口"), + ("Settings", "設定"), + ("Username", "使用者名稱"), + ("Invalid port", "連線端口無效"), + ("Closed manually by the peer", "遠端使用者關閉了工作階段"), + ("Enable remote configuration modification", "允許遠端使用者更改設定"), + ("Run without install", "跳過安裝直接執行"), + ("Connect via relay", "中繼連線"), + ("Always connect via relay", "一律透過轉送連線"), + ("whitelist_tip", "只有白名單中的 IP 可以存取"), + ("Login", "登入"), + ("Verify", "驗證"), + ("Remember me", "記住我"), + ("Trust this device", "信任此裝置"), + ("Verification code", "驗證碼"), + ("verification_tip", "檢測到新裝置登入,已向註冊電子信箱發送了登入驗證碼,請輸入驗證碼以繼續登入"), + ("Logout", "登出"), + ("Tags", "標籤"), + ("Search ID", "搜尋 ID"), + ("whitelist_sep", "使用逗號、分號、空白,或是換行來分隔"), + ("Add ID", "新增 ID"), + ("Add Tag", "新增標籤"), + ("Unselect all tags", "取消選取所有標籤"), + ("Network error", "網路錯誤"), + ("Username missed", "缺少使用者名稱"), + ("Password missed", "缺少密碼"), + ("Wrong credentials", "提供的登入資訊有誤"), + ("Edit Tag", "編輯標籤"), + ("Unremember Password", "忘掉密碼"), + ("Favorites", "我的最愛"), + ("Add to Favorites", "新增到我的最愛"), + ("Remove from Favorites", "從我的最愛中刪除"), + ("Empty", "空空如也"), + ("Invalid folder name", "資料夾名稱無效"), + ("Socks5 Proxy", "Socks5 代理"), + ("Hostname", "主機名稱"), + ("Discovered", "已探索"), + ("install_daemon_tip", "為了能夠開機時自動啟動,請先安裝系統服務。"), + ("Remote ID", "遠端 ID"), + ("Paste", "貼上"), + ("Paste here?", "貼上到這裡?"), + ("Are you sure to close the connection?", "您確定要關閉連線嗎?"), + ("Download new version", "下載新版本"), + ("Touch mode", "觸控模式"), + ("Mouse mode", "滑鼠模式"), + ("One-Finger Tap", "單指輕觸"), + ("Left Mouse", "滑鼠左鍵"), + ("One-Long Tap", "單指長按"), + ("Two-Finger Tap", "雙指輕觸"), + ("Right Mouse", "滑鼠右鍵"), + ("One-Finger Move", "單指移動"), + ("Double Tap & Move", "雙擊並移動"), + ("Mouse Drag", "滑鼠選中拖動"), + ("Three-Finger vertically", "三指垂直滑動"), + ("Mouse Wheel", "滑鼠滾輪"), + ("Two-Finger Move", "雙指移動"), + ("Canvas Move", "移動畫布"), + ("Pinch to Zoom", "雙指縮放"), + ("Canvas Zoom", "縮放畫布"), + ("Reset canvas", "重設畫布"), + ("No permission of file transfer", "沒有檔案傳輸權限"), + ("Note", "備註"), + ("Connection", "連線"), + ("Share Screen", "共享螢幕畫面"), + ("Chat", "聊天訊息"), + ("Total", "總計"), + ("items", "個項目"), + ("Selected", "已選擇"), + ("Screen Capture", "畫面錄製"), + ("Input Control", "輸入控制"), + ("Audio Capture", "音訊錄製"), + ("File Connection", "檔案連線"), + ("Screen Connection", "畫面連線"), + ("Do you accept?", "是否接受?"), + ("Open System Setting", "開啟系統設定"), + ("How to get Android input permission?", "如何獲取 Android 的輸入權限?"), + ("android_input_permission_tip1", "取得輸入權限後可以讓遠端裝置透過滑鼠控制此 Android 裝置"), + ("android_input_permission_tip2", "請在接下來的系統設定頁面中,找到並進入「已安裝的服務」頁面,並將「RustDesk Input」服務開啟"), + ("android_new_connection_tip", "收到新的連線控制請求,對方想要控制您目前的裝置"), + ("android_service_will_start_tip", "開啟畫面錄製權限將自動開啟服務,允許其他裝置向此裝置請求建立連線。"), + ("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連線。"), + ("android_version_audio_tip", "目前的 Android 版本不支援音訊錄製,請升級到 Android 10 或以上版本。"), + ("android_start_service_tip", "點擊「啟動服務」或啟用「螢幕錄製」權限,以啟動螢幕共享服務。"), + ("android_permission_may_not_change_tip", "對於已經建立的連線,權限可能不會立即發生改變,除非重新建立連線。"), + ("Account", "帳號"), + ("Overwrite", "取代"), + ("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要略過或是取代此檔案嗎?"), + ("Quit", "退出"), + ("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"), + ("Help", "幫助"), + ("Failed", "失敗"), + ("Succeeded", "成功"), + ("Someone turns on privacy mode, exit", "其他使用者開啟隱私模式,退出"), + ("Unsupported", "不支援"), + ("Peer denied", "被控端拒絕"), + ("Please install plugins", "請安裝插件"), + ("Peer exit", "被控端退出"), + ("Failed to turn off", "退出失敗"), + ("Turned off", "退出"), + ("In privacy mode", "開啟隱私模式"), + ("Out privacy mode", "退出隱私模式"), + ("Language", "語言"), + ("Keep RustDesk background service", "保持 RustDesk 後台服務"), + ("Ignore Battery Optimizations", "忽略電池最佳化"), + ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的 RustDesk 應用設定頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), + ("Start on Boot", "開機自動啟動"), + ("Start the screen sharing service on boot, requires special permissions", "開機自動啟動螢幕共享服務,此功能需要一些特殊權限。"), + ("Connection not allowed", "對方不允許連線"), + ("Legacy mode", "傳統模式"), + ("Map mode", "1:1 傳輸模式"), + ("Translate mode", "翻譯模式"), + ("Use permanent password", "使用固定密碼"), + ("Use both passwords", "同時使用兩種密碼"), + ("Set permanent password", "設定固定密碼"), + ("Enable Remote Restart", "啟用遠端重新啟動"), + ("Allow remote restart", "允許遠端重新啟動"), + ("Restart Remote Device", "重新啟動遠端電腦"), + ("Are you sure you want to restart", "確定要重新啟動"), + ("Restarting Remote Device", "正在重新啟動遠端裝置"), + ("remote_restarting_tip", "遠端裝置正在重新啟動,請關閉當前提示框,並在一段時間後使用永久密碼重新連線"), + ("Copied", "已複製"), + ("Exit Fullscreen", "退出全螢幕"), + ("Fullscreen", "全螢幕"), + ("Mobile Actions", "手機操作"), + ("Select Monitor", "選擇顯示器"), + ("Control Actions", "控制操作"), + ("Display Settings", "顯示設定"), + ("Ratio", "比例"), + ("Image Quality", "畫質"), + ("Scroll Style", "滾動樣式"), + ("Show Menubar", "顯示選單欄"), + ("Hide Menubar", "隱藏選單欄"), + ("Direct Connection", "直接連線"), + ("Relay Connection", "中繼連線"), + ("Secure Connection", "安全連線"), + ("Insecure Connection", "非安全連線"), + ("Scale original", "原始尺寸"), + ("Scale adaptive", "適應視窗"), + ("General", "通用"), + ("Security", "安全"), + ("Theme", "主題"), + ("Dark Theme", "黑暗主題"), + ("Light Theme", "明亮主題"), + ("Dark", "黑暗"), + ("Light", "明亮"), + ("Follow System", "跟隨系統"), + ("Enable hardware codec", "使用硬體編解碼器"), + ("Unlock Security Settings", "解鎖安全設定"), + ("Enable Audio", "允許傳輸音訊"), + ("Unlock Network Settings", "解鎖網路設定"), + ("Server", "伺服器"), + ("Direct IP Access", "IP 直接連線"), + ("Proxy", "代理"), + ("Apply", "應用"), + ("Disconnect all devices?", "中斷所有遠端連線?"), + ("Clear", "清空"), + ("Audio Input Device", "音訊輸入裝置"), + ("Deny remote access", "拒絕遠端存取"), + ("Use IP Whitelisting", "只允許白名單上的 IP 進行連線"), + ("Network", "網路"), + ("Enable RDP", "允許 RDP 訪問"), + ("Pin menubar", "固定選單欄"), + ("Unpin menubar", "取消固定選單欄"), + ("Recording", "錄製"), + ("Directory", "路徑"), + ("Automatically record incoming sessions", "自動錄製連入的工作階段"), + ("Change", "變更"), + ("Start session recording", "開始錄影"), + ("Stop session recording", "停止錄影"), + ("Enable Recording Session", "啟用錄製工作階段"), + ("Allow recording session", "允許錄製工作階段"), + ("Enable LAN Discovery", "允許區域網路探索"), + ("Deny LAN Discovery", "拒絕區域網路探索"), + ("Write a message", "輸入聊天訊息"), + ("Prompt", "提示"), + ("Please wait for confirmation of UAC...", "請等待對方確認 UAC ..."), + ("elevated_foreground_window_tip", "目前的遠端桌面視窗需要更高的權限才能繼續操作,暫時無法使用滑鼠、鍵盤,可以請求對方最小化目前視窗,或者在連線管理視窗點擊提升權限。為了避免這個問題,建議在遠端裝置上安裝本軟體。"), + ("Disconnected", "斷開連線"), + ("Other", "其他"), + ("Confirm before closing multiple tabs", "關閉多個分頁前詢問我"), + ("Keyboard Settings", "鍵盤設定"), + ("Full Access", "完全訪問"), + ("Screen Share", "僅分享螢幕畫面"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland 需要 Ubuntu 21.04 或更高版本。"), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland 需要更高版本的 Linux 發行版。請嘗試使用 X11 桌面或更改您的作業系統。"), + ("JumpLink", "查看"), + ("Please Select the screen to be shared(Operate on the peer side).", "請選擇要分享的螢幕畫面(在對端操作)。"), + ("Show RustDesk", "顯示 RustDesk"), + ("This PC", "此電腦"), + ("or", "或"), + ("Continue with", "繼續"), + ("Elevate", "提升權限"), + ("Zoom cursor", "縮放游標"), + ("Accept sessions via password", "只允許透過輸入密碼進行連線"), + ("Accept sessions via click", "只允許透過點擊接受進行連線"), + ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), + ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), + ("One-time Password", "一次性密碼"), + ("Use one-time password", "使用一次性密碼"), + ("One-time password length", "一次性密碼長度"), + ("Request access to your device", "請求訪問您的裝置"), + ("Hide connection management window", "隱藏連線管理視窗"), + ("hide_cm_tip", "在只允許密碼連線並且只用固定密碼的情況下才允許隱藏"), + ("wayland_experiment_tip", "Wayland 支援處於實驗階段,如果您需要使用無人值守訪問,請使用 X11。"), + ("Right click to select tabs", "右鍵選擇分頁"), + ("Skipped", "已略過"), + ("Add to Address Book", "新增到通訊錄"), + ("Group", "群組"), + ("Search", "搜尋"), + ("Closed manually by web console", "被 Web 控制台手動關閉"), + ("Local keyboard type", "本地鍵盤類型"), + ("Select local keyboard type", "請選擇本地鍵盤類型"), + ("software_render_tip", "如果您使用 NVIDIA 顯示卡,並且遠端視窗在建立連線後會立刻關閉,那麼請安裝 nouveau 顯示卡驅動程式並且選擇使用軟體渲染可能會有幫助。重新啟動軟體後生效。"), + ("Always use software rendering", "使用軟體渲染"), + ("config_input", "為了能夠透過鍵盤控制遠端桌面,請給予 RustDesk \"輸入監控\" 權限。"), + ("config_microphone", "為了支援透過麥克風進行音訊傳輸,請給予 RustDesk \"錄音\"權限。"), + ("request_elevation_tip", "如果遠端使用者可以操作電腦,也可以請求提升權限。"), + ("Wait", "等待"), + ("Elevation Error", "權限提升失敗"), + ("Ask the remote user for authentication", "請求遠端使用者進行身分驗證"), + ("Choose this if the remote account is administrator", "當遠端使用者帳戶是管理員時,請選擇此選項"), + ("Transmit the username and password of administrator", "發送管理員的使用者名稱和密碼"), + ("still_click_uac_tip", "依然需要遠端使用者在 UAC 視窗點擊確認。"), + ("Request Elevation", "請求權限提升"), + ("wait_accept_uac_tip", "請等待遠端使用者確認 UAC 對話框。"), + ("Elevate successfully", "權限提升成功"), + ("uppercase", "大寫字母"), + ("lowercase", "小寫字母"), + ("digit", "數字"), + ("special character", "特殊字元"), + ("length>=8", "長度不能小於 8"), + ("Weak", "弱"), + ("Medium", "中"), + ("Strong", "強"), + ("Switch Sides", "反轉存取方向"), + ("Please confirm if you want to share your desktop?", "請確認是否要讓對方存取您的桌面?"), + ("Display", "顯示"), + ("Default View Style", "預設顯示方式"), + ("Default Scroll Style", "預設滾動方式"), + ("Default Image Quality", "預設圖像質量"), + ("Default Codec", "預設編解碼器"), + ("Bitrate", "位元速率"), + ("FPS", "幀率"), + ("Auto", "自動"), + ("Other Default Options", "其他預設選項"), + ("Voice call", "語音通話"), + ("Text chat", "文字聊天"), + ("Stop voice call", "停止語音通話"), + ("relay_hint_tip", "可能無法直接連線,可以嘗試中繼連線。\n另外,如果想要直接使用中繼連線,可以在 ID 後面新增/r,或者在卡片選項裡選擇強制走中繼連線。"), + ("Reconnect", "重新連線"), + ("Codec", "編解碼器"), + ("Resolution", "解析度"), + ("No transfers in progress", "沒有正在進行的傳輸"), + ("Set one-time password length", "設定一次性密碼長度"), + ("idd_driver_tip", "安裝虛擬顯示器驅動程式,以便在沒有連接顯示器的情況下啟動虛擬顯示器進行控制。"), + ("confirm_idd_driver_tip", "安裝虛擬顯示器驅動程式的選項已勾選。請注意,測試證書將被安裝以信任虛擬顯示器驅動。測試證書僅會用於信任 RustDesk 的驅動程式。"), + ("RDP Settings", "RDP 設定"), + ("Sort by", "排序方式"), + ("New Connection", "新連線"), + ("Restore", "還原"), + ("Minimize", "最小化"), + ("Maximize", "最大化"), + ("Your Device", "您的裝置"), + ("empty_recent_tip", "空空如也"), + ("empty_favorite_tip", "空空如也"), + ("empty_lan_tip", "空空如也"), + ("empty_address_book_tip", "空空如也"), + ("eg: admin", "例如:admin"), + ("Empty Username", "空使用者帳號"), + ("Empty Password", "空密碼"), + ("Me", "我"), + ("identical_file_tip", "此檔案與對方的檔案一致"), + ("show_monitors_tip", "在工具列中顯示顯示器"), + ("View Mode", "瀏覽模式"), + ("enter_rustdesk_passwd_tip", "輸入 RustDesk 密碼"), + ("remember_rustdesk_passwd_tip", "記住 RustDesk 密碼"), + ("login_linux_tip", "登入到遠端 Linux 使用者帳戶"), + ("login_linux_tooltip_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), + ].iter().cloned().collect(); +} diff --git a/src/lang/ua.rs b/src/lang/ua.rs index e21cd88df..952c36b13 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрити"), ("Retry", "Спробувати знову"), ("OK", "ОК"), - ("Password Required", "Потрібен пароль"), + ("remember_password_tip", "Потрібен пароль"), ("Please enter your password", "Будь ласка, введіть ваш пароль"), ("Remember password", "Запам'ятати пароль"), ("Wrong Password", "Невірний пароль"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index f716bd536..ab1aa2820 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Đóng"), ("Retry", "Thử lại"), ("OK", "OK"), - ("Password Required", "Yêu cầu mật khẩu"), + ("remember_password_tip", "Yêu cầu mật khẩu"), ("Please enter your password", "Mời nhập mật khẩu"), ("Remember password", "Nhớ mật khẩu"), ("Wrong Password", "Sai mật khẩu"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } From 3fd1da05f4d9fc1bd6f8c866998888eae0d58e4c Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 10:48:18 +0800 Subject: [PATCH 045/112] tmp commit Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 2 +- .../lib/desktop/widgets/remote_toolbar.dart | 68 ++++++++++++++++++- flutter/lib/models/model.dart | 1 + src/lang/en.rs | 14 ++-- src/platform/linux_desktop_manager.rs | 9 +++ src/server/connection.rs | 10 ++- 6 files changed, 92 insertions(+), 12 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 569d64428..0fdd17a4b 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -625,7 +625,7 @@ _connectDialog( osAccountWidget(), osUsernameController == null || passwordController == null ? Offstage() - : Container(height: 10), + : Container(height: 12), passwdWidget(), ]), actions: [ diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 703ed6db9..467cd69b0 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -639,7 +639,7 @@ class _ControlMenu extends StatelessWidget { ffi: ffi, menuChildren: [ requestElevation(), - osPassword(), + ffi.ffiModel.pi.is_headless ? osAccount() : osPassword(), transferFile(context), tcpTunneling(context), note(), @@ -662,6 +662,72 @@ class _ControlMenu extends StatelessWidget { onPressed: () => showRequestElevationDialog(id, ffi.dialogManager)); } + osAccount() { + return _MenuItemButton( + child: Text(translate('OS Account')), + trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), + ffi: ffi, + onPressed: () => _showSetOSAccount(id, false, ffi.dialogManager)); + } + + _showSetOSAccount( + String id, bool login, OverlayDialogManager dialogManager) async { + final usernameController = TextEditingController(); + final passwdController = TextEditingController(); + var username = + await bind.sessionGetOption(id: id, arg: 'os-username') ?? ''; + var password = + await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; + usernameController.text = username; + passwdController.text = password; + dialogManager.show((setState, close) { + submit() { + final username = usernameController.text.trim(); + final password = usernameController.text.trim(); + bind.sessionPeerOption(id: id, name: 'os-username', value: username); + bind.sessionPeerOption(id: id, name: 'os-password', value: password); + close(); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate('OS Password')).paddingOnly(left: 10), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + DialogTextField( + title: translate(DialogTextField.kUsernameTitle), + controller: usernameController, + prefixIcon: DialogTextField.kUsernameIcon, + errorText: null, + ), + PasswordWidget(controller: passwdController), + ], + ), + actions: [ + dialogButton( + "Cancel", + icon: Icon(Icons.close_rounded), + onPressed: close, + isOutline: true, + ), + dialogButton( + "OK", + icon: Icon(Icons.done_rounded), + onPressed: submit, + ), + ], + onSubmit: submit, + onCancel: close, + ); + }); + } + osPassword() { return _MenuItemButton( child: Text(translate('OS Password')), diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 470bb47c3..7d1249906 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1725,6 +1725,7 @@ class PeerInfo { Map platform_additions = {}; bool get is_wayland => platform_additions['is_wayland'] == true; + bool get is_headless => platform_additions['headless'] == true; } const canvasKey = 'canvas'; diff --git a/src/lang/en.rs b/src/lang/en.rs index 95186325f..79f18ebc5 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -54,12 +54,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("empty_address_book_tip", "Oh dear, it appears that there are currently no peers listed in your address book."), ("identical_file_tip", "This file is identical with the peer's one."), ("show_monitors_tip", "Show monitors in toolbar."), - ("enter_rustdesk_passwd_tip", "Enter RustDesk password."), - ("remember_rustdesk_passwd_tip", "Remember RustDesk password."), - ("login_linux_tip", "Login to remote Linux account."), - ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session."), - ("verify_rustdesk_password_tip", "Veryfy RustDesk password."), - ("remember_account_tip", "Remember this account."), - ("remember_password_tip", "Remember password."), + ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), + ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), + ("login_linux_tip", "Login to remote Linux account"), + ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session"), + ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), + ("remember_account_tip", "Remember this account"), + ("remember_password_tip", "Remember password"), ].iter().cloned().collect(); } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index b21b2d5b1..a957e045e 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -80,6 +80,15 @@ pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String } } +#[inline] +pub fn is_headless() -> bool { + DESKTOP_MANAGER + .lock() + .unwrap() + .as_ref() + .map_or(false, |manager| manager.x11_username.is_empty()) +} + pub fn get_username() -> String { match &*DESKTOP_MANAGER.lock().unwrap() { Some(manager) => { diff --git a/src/server/connection.rs b/src/server/connection.rs index 2f86ca5f0..ee4bdda35 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -3,6 +3,8 @@ use super::{input_service::*, *}; use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::update_clipboard; +#[cfg(target_os = "linux")] +use crate::platform::linux_desktop_manager; #[cfg(windows)] use crate::portable_service::client as portable_client; use crate::{ @@ -866,6 +868,9 @@ impl Connection { if crate::platform::current_is_wayland() { platform_additions.insert("is_wayland".into(), json!(true)); } + if linux_desktop_manager::is_headless() { + platform_additions.insert("headless".into(), json!(true)); + } if !platform_additions.is_empty() { pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); @@ -1074,7 +1079,7 @@ impl Connection { fn try_start_desktop(_username: &str, _passsword: &str) -> String { #[cfg(target_os = "linux")] if _username.is_empty() { - let username = crate::platform::linux_desktop_manager::get_username(); + let username = linux_desktop_manager::get_username(); if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY } else { @@ -1082,8 +1087,7 @@ impl Connection { } .to_owned() } else { - match crate::platform::linux_desktop_manager::try_start_x_session(_username, _passsword) - { + match linux_desktop_manager::try_start_x_session(_username, _passsword) { Ok((username, x11_ready)) => { if x11_ready { if _username != username { From 888c8511671bac9e649b6bc3a28de912e63155ac Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 11:17:24 +0800 Subject: [PATCH 046/112] desktop, remote toolbar, os account Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 7 +------ .../lib/desktop/pages/connection_page.dart | 14 +++++-------- .../lib/desktop/widgets/remote_toolbar.dart | 21 +++++++++++++++++++ libs/hbb_common/src/config.rs | 4 ++-- src/lang/ca.rs | 8 +++---- src/lang/cn.rs | 10 ++++----- src/lang/cs.rs | 8 +++---- src/lang/da.rs | 8 +++---- src/lang/de.rs | 10 ++++----- src/lang/el.rs | 10 ++++----- src/lang/en.rs | 4 ++-- src/lang/eo.rs | 8 +++---- src/lang/es.rs | 10 ++++----- src/lang/fa.rs | 8 +++---- src/lang/fr.rs | 2 +- src/lang/hu.rs | 8 +++---- src/lang/id.rs | 8 +++---- src/lang/it.rs | 10 ++++----- src/lang/ja.rs | 8 +++---- src/lang/ko.rs | 8 +++---- src/lang/kz.rs | 8 +++---- src/lang/nl.rs | 10 ++++----- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 8 +++---- src/lang/ptbr.rs | 8 +++---- src/lang/ro.rs | 8 +++---- src/lang/ru.rs | 10 ++++----- src/lang/sk.rs | 8 +++---- src/lang/sl.rs | 8 +++---- src/lang/sq.rs | 8 +++---- src/lang/sr.rs | 8 +++---- src/lang/sv.rs | 8 +++---- src/lang/template.rs | 5 ++--- src/lang/th.rs | 8 +++---- src/lang/tr.rs | 8 +++---- src/lang/tw.rs | 10 ++++----- src/lang/ua.rs | 8 +++---- src/lang/vn.rs | 8 +++---- 38 files changed, 133 insertions(+), 182 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 0fdd17a4b..86611cd8a 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -503,11 +503,6 @@ _connectDialog( } submit() { - // to-do: - // username and password are about remote OS account. - // If the remote side is headless. - // The client side should login to remote OS account, to enable X desktop session. - // `username` and `password` will be used in the near future. final osUsername = osUsernameController?.text.trim() ?? ''; final osPassword = osPasswordController?.text.trim() ?? ''; final password = passwordController?.text.trim() ?? ''; @@ -565,7 +560,7 @@ _connectDialog( } return Column( children: [ - descWidget(translate('login_linux_tooltip_tip')), + descWidget(translate('login_linux_tip')), DialogTextField( title: translate(DialogTextField.kUsernameTitle), controller: osUsernameController, diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 63d6b9c7b..480ea75ff 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -225,20 +225,16 @@ class _ConnectionPageState extends State children: [ Button( isOutline: true, - onTap: () { - onConnect(isFileTransfer: true); - }, + onTap: () => enterUserLoginAndPasswordDialog( + 'fdsfd', + gFFI.dialogManager, + ), text: "Transfer File", ), const SizedBox( width: 17, ), - Button( - onTap: () => enterUserLoginAndPasswordDialog( - 'fdsfd', - gFFI.dialogManager, - ), - text: "Connect"), + Button(onTap: onConnect, text: "Connect"), ], ), ) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 467cd69b0..ec4d32ee0 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -689,6 +689,26 @@ class _ControlMenu extends StatelessWidget { close(); } + descWidget(String text) { + return Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + text, + maxLines: 3, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 16), + ), + ), + Container( + height: 8, + ), + ], + ); + } + return CustomAlertDialog( title: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -700,6 +720,7 @@ class _ControlMenu extends StatelessWidget { content: Column( mainAxisSize: MainAxisSize.min, children: [ + descWidget(translate("os_account_desk_tip")), DialogTextField( title: translate(DialogTextField.kUsernameTitle), controller: usernameController, diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6a823c7b7..3841bf976 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -915,7 +915,7 @@ impl PeerConfig { decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); config.password = password; store = store || store2; - for opt in ["rdp_password", "os-password"] { + for opt in ["rdp_password", "os-username", "os-password"] { if let Some(v) = config.options.get_mut(opt) { let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; @@ -938,7 +938,7 @@ impl PeerConfig { let _lock = CONFIG.read().unwrap(); let mut config = self.clone(); config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); - for opt in ["rdp_password", "os-password"] { + for opt in ["rdp_password", "os-username", "os-password"] { if let Some(v) = config.options.get_mut(opt) { *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index af744d153..6f4ce9543 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tancar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", "Es necessita la contrasenya"), + ("remember_password_tip", ""), ("Please enter your password", "Si us plau, introdueixi la seva contrasenya"), ("Remember password", "Recordar contrasenya"), ("Wrong Password", "Contrasenya incorrecta"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 584be4333..5262db44c 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "关闭"), ("Retry", "再试"), ("OK", "确认"), - ("remember_password_tip", "需要密码"), + ("remember_password_tip", ""), ("Please enter your password", "请输入密码"), ("Remember password", "记住密码"), ("Wrong Password", "密码错误"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此文件与对方的一致"), ("show_monitors_tip", ""), ("View Mode", "浏览模式"), - ("enter_rustdesk_passwd_tip", "请输入 RustDesk 密码"), - ("remember_rustdesk_passwd_tip", "记住 RustDesk 密码"), - ("login_linux_tip", "登录被控端的 Linux 账户"), - ("login_linux_tooltip_tip", "登录被控端的 Linux 账户,才能启用 X 桌面。"), + ("login_linux_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), ("remember_account_tip", "记住此账户"), - ("remember_password_tip", ""), + ("os_account_desk_tip", "在无显示器的环境下,此账户用于登录被控系统,并启用桌面"), + ("OS Account", "系统账户"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index cb882277e..58d10b5ee 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zavřít"), ("Retry", "Zkusit znovu"), ("OK", "OK"), - ("remember_password_tip", "Vyžadováno heslo"), + ("remember_password_tip", ""), ("Please enter your password", "Zadejte své heslo"), ("Remember password", "Zapamatovat heslo"), ("Wrong Password", "Nesprávné heslo"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index ad6ddfc48..7e6888239 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Luk"), ("Retry", "Prøv igen"), ("OK", "OK"), - ("remember_password_tip", "Adgangskode påkrævet"), + ("remember_password_tip", ""), ("Please enter your password", "Indtast venligst dit kodeord"), ("Remember password", "Husk kodeord"), ("Wrong Password", "Forkert kodeord"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 263664d4c..ad9196785 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Schließen"), ("Retry", "Erneut versuchen"), ("OK", "OK"), - ("remember_password_tip", "Passwort erforderlich"), + ("remember_password_tip", ""), ("Please enter your password", "Bitte geben Sie Ihr Passwort ein"), ("Remember password", "Passwort merken"), ("Wrong Password", "Falsches Passwort"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Diese Datei ist identisch mit der Datei der Gegenstelle."), ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), ("View Mode", "Ansichtsmodus"), - ("enter_rustdesk_passwd_tip", "RustDesk-Passwort eingeben."), - ("remember_rustdesk_passwd_tip", "RustDesk-Passwort merken."), - ("login_linux_tip", "Anmeldung am entfernten Linux-Konto"), - ("login_linux_tooltip_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), + ("login_linux_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 9597ae158..50778f4c4 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Κλείσιμο"), ("Retry", "Δοκίμασε ξανά"), ("OK", "ΟΚ"), - ("remember_password_tip", "Απαιτείται κωδικός πρόσβασης"), + ("remember_password_tip", ""), ("Please enter your password", "Παρακαλώ εισάγετε τον κωδικό πρόσβασης"), ("Remember password", "Απομνημόνευση κωδικού πρόσβασης"), ("Wrong Password", "Λάθος κωδικός πρόσβασης"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Το αρχείο είναι πανομοιότυπο με αυτό του άλλου υπολογιστή."), ("show_monitors_tip", "Εμφάνιση οθονών στη γραμμή εργαλείων"), ("View Mode", "Λειτουργία προβολής"), - ("enter_rustdesk_passwd_tip", "Εισαγωγή του κωδικού RustDesk."), - ("remember_rustdesk_passwd_tip", "Να θυμάσαι τον κωδικό του RustDesk."), - ("login_linux_tip", "Είσοδος σε απομακρυσμένο λογαριασμό Linux"), - ("login_linux_tooltip_tip", "Απαιτείται είσοδος σε απομακρυσμένο λογαριασμό Linux για την ενεργοποίηση του περιβάλλον εργασίας Χ."), + ("login_linux_tip", "Απαιτείται είσοδος σε απομακρυσμένο λογαριασμό Linux για την ενεργοποίηση του περιβάλλον εργασίας Χ."), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 79f18ebc5..2ea65b05f 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,10 +56,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), - ("login_linux_tip", "Login to remote Linux account"), - ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session"), + ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("remember_password_tip", "Remember password"), + ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index aeee842f7..9fcc2b451 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermi"), ("Retry", "Reprovi"), ("OK", "Konfermi"), - ("remember_password_tip", "Pasvorto deviga"), + ("remember_password_tip", ""), ("Please enter your password", "Bonvolu tajpi vian pasvorton"), ("Remember password", "Memori pasvorton"), ("Wrong Password", "Erara pasvorto"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 3b998ae7d..c8120de49 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Cerrar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", "Se requiere contraseña"), + ("remember_password_tip", ""), ("Please enter your password", "Por favor, introduzca su contraseña"), ("Remember password", "Recordar contraseña"), ("Wrong Password", "Contraseña incorrecta"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Este archivo es idéntico al del par."), ("show_monitors_tip", "Mostrar monitores en la barra de herramientas"), ("View Mode", "Modo Vista"), - ("enter_rustdesk_passwd_tip", "Introduzca la contraseña de RustDesk"), - ("remember_rustdesk_passwd_tip", "Recordar la contraseña de RustDesk"), - ("login_linux_tip", "Iniciar sesión para la cuenta remota de Linux"), - ("login_linux_tooltip_tip", ""), + ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index e2137c3e2..ccf2a670a 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "بستن"), ("Retry", "تلاش مجدد"), ("OK", "قبول"), - ("remember_password_tip", "رمز عبور لازم است"), + ("remember_password_tip", ""), ("Please enter your password", "رمز عبور خود را وارد کنید"), ("Remember password", "رمز عبور را به خاطر بسپار"), ("Wrong Password", "رمز عبور اشتباه است"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "این فایل با فایل همتا یکسان است."), ("show_monitors_tip", "نمایش مانیتورها در نوار ابزار"), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 71c0f6728..2ff02d8f9 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermer"), ("Retry", "Réessayer"), ("OK", "Valider"), - ("remember_password_tip", "Mot de passe requis"), + ("remember_password_tip", ""), ("Please enter your password", "Veuillez saisir votre mot de passe"), ("Remember password", "Mémoriser le mot de passe"), ("Wrong Password", "Mauvais mot de passe"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 7c7fb2468..49e767b84 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Bezárás"), ("Retry", "Újra"), ("OK", "OK"), - ("remember_password_tip", "Jelszó megadása kötelező"), + ("remember_password_tip", ""), ("Please enter your password", "Kérem írja be a jelszavát"), ("Remember password", "Jelszó megjegyzése"), ("Wrong Password", "Hibás jelszó"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 174d970b6..2c410c0ec 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tutup"), ("Retry", "Ulangi"), ("OK", "Oke"), - ("remember_password_tip", "Kata sandi dibutuhkan"), + ("remember_password_tip", ""), ("Please enter your password", "Silahkan masukkan kata sandi anda"), ("Remember password", "Ingat Password"), ("Wrong Password", "Kata sandi Salah"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 94b7d10a9..4427743f1 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Chiudi"), ("Retry", "Riprova"), ("OK", "OK"), - ("remember_password_tip", "Password Richiesta"), + ("remember_password_tip", ""), ("Please enter your password", "Inserisci la tua password"), ("Remember password", "Ricorda password"), ("Wrong Password", "Password Errata"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Questo file è identico a quello del peer."), ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), ("View Mode", "Modalità di visualizzazione"), - ("enter_rustdesk_passwd_tip", "Inserisci la password di RustDesk."), - ("remember_rustdesk_passwd_tip", "Ricorda la passowrd di RustDesk."), - ("login_linux_tip", "Effettua l'accesso sul tuo account Linux"), - ("login_linux_tooltip_tip", ""), + ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 95395c516..dba70c1e1 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "閉じる"), ("Retry", "再試行"), ("OK", "OK"), - ("remember_password_tip", "パスワードが必要"), + ("remember_password_tip", ""), ("Please enter your password", "パスワードを入力してください"), ("Remember password", "パスワードを記憶する"), ("Wrong Password", "パスワードが間違っています"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 4e3b52ea2..ab7714190 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "닫기"), ("Retry", "재시도"), ("OK", "확인"), - ("remember_password_tip", "비밀번호 입력"), + ("remember_password_tip", ""), ("Please enter your password", "비밀번호를 입력해주세요"), ("Remember password", "이 비밀번호 기억하기"), ("Wrong Password", "틀린 비밀번호"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index bfe312e02..7fe2719cc 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Жабу"), ("Retry", "Қайтадан көру"), ("OK", "OK"), - ("remember_password_tip", "Құпия сөз Қажет"), + ("remember_password_tip", ""), ("Please enter your password", "Құпия сөзіңізді еңгізуді өтінеміз"), ("Remember password", "Құпия сөзді есте сақтау"), ("Wrong Password", "Бұрыс Құпия сөз"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 4a3c9c062..360166e50 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Sluit"), ("Retry", "Probeer opnieuw"), ("OK", "OK"), - ("remember_password_tip", "Wachtwoord vereist"), + ("remember_password_tip", ""), ("Please enter your password", "Geef uw wachtwoord in"), ("Remember password", "Wachtwoord onthouden"), ("Wrong Password", "Verkeerd wachtwoord"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Dit bestand is identiek aan het bestand van het externe station."), ("show_monitors_tip", "Monitoren weergeven in de werkbalk"), ("View Mode", "Weergave Mode"), - ("enter_rustdesk_passwd_tip", "Geef het RustDesk-wachtwoord op."), - ("remember_rustdesk_passwd_tip", "RustDesk Wachtwoord onthouden."), - ("login_linux_tip", "Je moet inloggen op een Linux Account op afstand om een X desktop sessie te openen."), - ("login_linux_tooltip_tip", ""), + ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index f68d37866..d89b6f763 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zamknij"), ("Retry", "Ponów"), ("OK", "OK"), - ("remember_password_tip", "Wymagane jest hasło"), + ("remember_password_tip", ""), ("Please enter your password", "Wpisz proszę twoje hasło"), ("Remember password", "Zapamiętaj hasło"), ("Wrong Password", "Błędne hasło"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 04689f713..c23aa1e46 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "Confirmar"), - ("remember_password_tip", "Palavra-chave Necessária"), + ("remember_password_tip", ""), ("Please enter your password", "Por favor introduza a sua palavra-chave"), ("Remember password", "Memorizar palavra-chave"), ("Wrong Password", "Palavra-chave inválida"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 6f6302a59..c741944a1 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "OK"), - ("remember_password_tip", "Senha necessária"), + ("remember_password_tip", ""), ("Please enter your password", "Por favor informe sua senha"), ("Remember password", "Lembrar senha"), ("Wrong Password", "Senha incorreta"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index ade2e3c36..d77345356 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Închide"), ("Retry", "Reîncearcă"), ("OK", "OK"), - ("remember_password_tip", "Parolă necesară"), + ("remember_password_tip", ""), ("Please enter your password", "Introdu parola"), ("Remember password", "Memorează parola"), ("Wrong Password", "Parolă incorectă"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index c85582e86..fbd1c4c62 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрыть"), ("Retry", "Повторить"), ("OK", "ОК"), - ("remember_password_tip", "Требуется пароль"), + ("remember_password_tip", ""), ("Please enter your password", "Введите пароль"), ("Remember password", "Запомнить пароль"), ("Wrong Password", "Неправильный пароль"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Файл идентичен файлу на удалённом узле."), ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), - ("enter_rustdesk_passwd_tip", "Введите пароль RustDesk"), - ("remember_rustdesk_passwd_tip", "Запомнить пароль RustDesk"), - ("login_linux_tip", "Вход в удалённый аккаунт Linux"), - ("login_linux_tooltip_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), + ("login_linux_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index e7c108a8c..efa66f7ae 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvoriť"), ("Retry", "Zopakovať"), ("OK", "OK"), - ("remember_password_tip", "Vyžaduje sa heslo"), + ("remember_password_tip", ""), ("Please enter your password", "Zadajte vaše heslo"), ("Remember password", "Zapamätať heslo"), ("Wrong Password", "Chybné heslo"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 0e5265b87..d897e4755 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zapri"), ("Retry", "Ponovi"), ("OK", "V redu"), - ("remember_password_tip", "Potrebno je geslo"), + ("remember_password_tip", ""), ("Please enter your password", "Vnesite vaše geslo"), ("Remember password", "Zapomni si geslo"), ("Wrong Password", "Napačno geslo"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 68ef47be9..88d678ed3 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Mbyll"), ("Retry", "Riprovo"), ("OK", "OK"), - ("remember_password_tip", "Fjalëkalimi i detyrueshëm"), + ("remember_password_tip", ""), ("Please enter your password", "Ju lutem vendosni fjalëkalimin tuaj"), ("Remember password", "Mbani mend fjalëkalimin"), ("Wrong Password", "Fjalëkalim i gabuar"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index ccb328e3e..078d9ba29 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvori"), ("Retry", "Ponovi"), ("OK", "Ok"), - ("remember_password_tip", "Potrebna lozinka"), + ("remember_password_tip", ""), ("Please enter your password", "Molimo unesite svoju lozinku"), ("Remember password", "Zapamti lozinku"), ("Wrong Password", "Pogrešna lozinka"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 496c882bc..df5caee28 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Stäng"), ("Retry", "Försök igen"), ("OK", "OK"), - ("remember_password_tip", "Lösenord krävs"), + ("remember_password_tip", ""), ("Please enter your password", "Skriv in ditt lösenord"), ("Remember password", "Kom ihåg lösenord"), ("Wrong Password", "Fel lösenord"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 6ec149289..1b8bf69c2 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -480,11 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 92ae279c9..a152eb7b1 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "ปิด"), ("Retry", "ลองใหม่อีกครั้ง"), ("OK", "ตกลง"), - ("remember_password_tip", "ต้องใช้รหัสผ่าน"), + ("remember_password_tip", ""), ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), ("Remember password", "จดจำรหัสผ่าน"), ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index b423a021b..c5d21460a 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Kapat"), ("Retry", "Tekrar Dene"), ("OK", "Tamam"), - ("remember_password_tip", "Şifre Gerekli"), + ("remember_password_tip", ""), ("Please enter your password", "Lütfen şifrenizi giriniz"), ("Remember password", "Şifreyi hatırla"), ("Wrong Password", "Hatalı şifre"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index e8685747e..d2156f4b7 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "關閉"), ("Retry", "重試"), ("OK", "確定"), - ("remember_password_tip", "需要密碼"), + ("remember_password_tip", ""), ("Please enter your password", "請輸入您的密碼"), ("Remember password", "記住密碼"), ("Wrong Password", "密碼錯誤"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此檔案與對方的檔案一致"), ("show_monitors_tip", "在工具列中顯示顯示器"), ("View Mode", "瀏覽模式"), - ("enter_rustdesk_passwd_tip", "輸入 RustDesk 密碼"), - ("remember_rustdesk_passwd_tip", "記住 RustDesk 密碼"), - ("login_linux_tip", "登入到遠端 Linux 使用者帳戶"), - ("login_linux_tooltip_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), + ("login_linux_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 952c36b13..1a91bb879 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрити"), ("Retry", "Спробувати знову"), ("OK", "ОК"), - ("remember_password_tip", "Потрібен пароль"), + ("remember_password_tip", ""), ("Please enter your password", "Будь ласка, введіть ваш пароль"), ("Remember password", "Запам'ятати пароль"), ("Wrong Password", "Невірний пароль"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index ab1aa2820..eeafcfa54 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Đóng"), ("Retry", "Thử lại"), ("OK", "OK"), - ("remember_password_tip", "Yêu cầu mật khẩu"), + ("remember_password_tip", ""), ("Please enter your password", "Mời nhập mật khẩu"), ("Remember password", "Nhớ mật khẩu"), ("Wrong Password", "Sai mật khẩu"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } From 8aa5f3a2a7382f1050195612e89140247058d416 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 11:47:32 +0800 Subject: [PATCH 047/112] mobile, edit os account Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 146 +++++++++++++++++ .../lib/desktop/pages/connection_page.dart | 5 +- .../lib/desktop/widgets/remote_toolbar.dart | 149 +----------------- flutter/lib/mobile/pages/remote_page.dart | 92 ++++------- 4 files changed, 184 insertions(+), 208 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 86611cd8a..0c61abb0d 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -897,3 +897,149 @@ void showRestartRemoteDevice( )); if (res == true) bind.sessionRestartRemoteDevice(id: id); } + +showSetOSPassword( + String id, + bool login, + OverlayDialogManager dialogManager, +) async { + final controller = TextEditingController(); + var password = await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; + var autoLogin = await bind.sessionGetOption(id: id, arg: 'auto-login') != ''; + controller.text = password; + dialogManager.show((setState, close) { + submit() { + var text = controller.text.trim(); + bind.sessionPeerOption(id: id, name: 'os-password', value: text); + bind.sessionPeerOption( + id: id, name: 'auto-login', value: autoLogin ? 'Y' : ''); + if (text != '' && login) { + bind.sessionInputOsPassword(id: id, value: text); + } + close(); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate('OS Password')).paddingOnly(left: 10), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + PasswordWidget(controller: controller), + CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text( + translate('Auto Login'), + ), + value: autoLogin, + onChanged: (v) { + if (v == null) return; + setState(() => autoLogin = v); + }, + ), + ], + ), + actions: [ + dialogButton( + "Cancel", + icon: Icon(Icons.close_rounded), + onPressed: close, + isOutline: true, + ), + dialogButton( + "OK", + icon: Icon(Icons.done_rounded), + onPressed: submit, + ), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + +showSetOSAccount( + String id, + OverlayDialogManager dialogManager, +) async { + final usernameController = TextEditingController(); + final passwdController = TextEditingController(); + var username = await bind.sessionGetOption(id: id, arg: 'os-username') ?? ''; + var password = await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; + usernameController.text = username; + passwdController.text = password; + dialogManager.show((setState, close) { + submit() { + final username = usernameController.text.trim(); + final password = usernameController.text.trim(); + bind.sessionPeerOption(id: id, name: 'os-username', value: username); + bind.sessionPeerOption(id: id, name: 'os-password', value: password); + close(); + } + + descWidget(String text) { + return Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + text, + maxLines: 3, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 16), + ), + ), + Container( + height: 8, + ), + ], + ); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate('OS Account')).paddingOnly(left: 10), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + descWidget(translate("os_account_desk_tip")), + DialogTextField( + title: translate(DialogTextField.kUsernameTitle), + controller: usernameController, + prefixIcon: DialogTextField.kUsernameIcon, + errorText: null, + ), + PasswordWidget(controller: passwdController), + ], + ), + actions: [ + dialogButton( + "Cancel", + icon: Icon(Icons.close_rounded), + onPressed: close, + isOutline: true, + ), + dialogButton( + "OK", + icon: Icon(Icons.done_rounded), + onPressed: submit, + ), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 480ea75ff..3f6bb7d16 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -225,10 +225,7 @@ class _ConnectionPageState extends State children: [ Button( isOutline: true, - onTap: () => enterUserLoginAndPasswordDialog( - 'fdsfd', - gFFI.dialogManager, - ), + onTap: () => onConnect(isFileTransfer: true), text: "Transfer File", ), const SizedBox( diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index ec4d32ee0..b6f97790e 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -667,86 +667,7 @@ class _ControlMenu extends StatelessWidget { child: Text(translate('OS Account')), trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), ffi: ffi, - onPressed: () => _showSetOSAccount(id, false, ffi.dialogManager)); - } - - _showSetOSAccount( - String id, bool login, OverlayDialogManager dialogManager) async { - final usernameController = TextEditingController(); - final passwdController = TextEditingController(); - var username = - await bind.sessionGetOption(id: id, arg: 'os-username') ?? ''; - var password = - await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; - usernameController.text = username; - passwdController.text = password; - dialogManager.show((setState, close) { - submit() { - final username = usernameController.text.trim(); - final password = usernameController.text.trim(); - bind.sessionPeerOption(id: id, name: 'os-username', value: username); - bind.sessionPeerOption(id: id, name: 'os-password', value: password); - close(); - } - - descWidget(String text) { - return Column( - children: [ - Align( - alignment: Alignment.centerLeft, - child: Text( - text, - maxLines: 3, - softWrap: true, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 16), - ), - ), - Container( - height: 8, - ), - ], - ); - } - - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.password_rounded, color: MyTheme.accent), - Text(translate('OS Password')).paddingOnly(left: 10), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - descWidget(translate("os_account_desk_tip")), - DialogTextField( - title: translate(DialogTextField.kUsernameTitle), - controller: usernameController, - prefixIcon: DialogTextField.kUsernameIcon, - errorText: null, - ), - PasswordWidget(controller: passwdController), - ], - ), - actions: [ - dialogButton( - "Cancel", - icon: Icon(Icons.close_rounded), - onPressed: close, - isOutline: true, - ), - dialogButton( - "OK", - icon: Icon(Icons.done_rounded), - onPressed: submit, - ), - ], - onSubmit: submit, - onCancel: close, - ); - }); + onPressed: () => showSetOSAccount(id, ffi.dialogManager)); } osPassword() { @@ -754,73 +675,7 @@ class _ControlMenu extends StatelessWidget { child: Text(translate('OS Password')), trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), ffi: ffi, - onPressed: () => _showSetOSPassword(id, false, ffi.dialogManager)); - } - - _showSetOSPassword( - String id, bool login, OverlayDialogManager dialogManager) async { - final controller = TextEditingController(); - var password = - await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; - var autoLogin = - await bind.sessionGetOption(id: id, arg: 'auto-login') != ''; - controller.text = password; - dialogManager.show((setState, close) { - submit() { - var text = controller.text.trim(); - bind.sessionPeerOption(id: id, name: 'os-password', value: text); - bind.sessionPeerOption( - id: id, name: 'auto-login', value: autoLogin ? 'Y' : ''); - if (text != '' && login) { - bind.sessionInputOsPassword(id: id, value: text); - } - close(); - } - - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.password_rounded, color: MyTheme.accent), - Text(translate('OS Password')).paddingOnly(left: 10), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - PasswordWidget(controller: controller), - CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('Auto Login'), - ), - value: autoLogin, - onChanged: (v) { - if (v == null) return; - setState(() => autoLogin = v); - }, - ), - ], - ), - actions: [ - dialogButton( - "Cancel", - icon: Icon(Icons.close_rounded), - onPressed: close, - isOutline: true, - ), - dialogButton( - "OK", - icon: Icon(Icons.done_rounded), - onPressed: submit, - ), - ], - onSubmit: submit, - onCancel: close, - ); - }); + onPressed: () => showSetOSPassword(id, false, ffi.dialogManager)); } transferFile(BuildContext context) { diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 083cdcd1c..1a7452dd7 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -548,19 +548,39 @@ class _RemotePageState extends State { more.add(PopupMenuItem( child: Text(translate('Refresh')), value: 'refresh')); } - more.add(PopupMenuItem( - child: Row( - children: ([ - Text(translate('OS Password')), - TextButton( - style: flatButtonStyle, - onPressed: () { - showSetOSPassword(id, false, gFFI.dialogManager); - }, - child: Icon(Icons.edit, color: MyTheme.accent), - ) - ])), - value: 'enter_os_password')); + if (gFFI.ffiModel.pi.is_headless) { + more.add( + PopupMenuItem( + child: Row( + children: ([ + Text(translate('OS Account')), + TextButton( + style: flatButtonStyle, + onPressed: () { + showSetOSAccount(id, gFFI.dialogManager); + }, + child: Icon(Icons.edit, color: MyTheme.accent), + ) + ])), + value: 'enter_os_account'), + ); + } else { + more.add( + PopupMenuItem( + child: Row( + children: ([ + Text(translate('OS Password')), + TextButton( + style: flatButtonStyle, + onPressed: () { + showSetOSPassword(id, false, gFFI.dialogManager); + }, + child: Icon(Icons.edit, color: MyTheme.accent), + ) + ])), + value: 'enter_os_password'), + ); + } if (!isWebDesktop) { if (perms['keyboard'] != false && perms['clipboard'] != false) { more.add(PopupMenuItem( @@ -657,6 +677,8 @@ class _RemotePageState extends State { } else { showSetOSPassword(id, true, gFFI.dialogManager); } + } else if (value == 'enter_os_account') { + showSetOSAccount(id, gFFI.dialogManager); } else if (value == 'reset_canvas') { gFFI.cursorModel.reset(); } else if (value == 'restart') { @@ -1071,50 +1093,6 @@ void showOptions( }, clickMaskDismiss: true, backDismiss: true); } -void showSetOSPassword( - String id, bool login, OverlayDialogManager dialogManager) async { - final controller = TextEditingController(); - var password = await bind.sessionGetOption(id: id, arg: "os-password") ?? ""; - var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != ""; - controller.text = password; - dialogManager.show((setState, close) { - return CustomAlertDialog( - title: Text(translate('OS Password')), - content: Column(mainAxisSize: MainAxisSize.min, children: [ - PasswordWidget(controller: controller), - CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('Auto Login'), - ), - value: autoLogin, - onChanged: (v) { - if (v == null) return; - setState(() => autoLogin = v); - }, - ), - ]), - actions: [ - dialogButton('Cancel', onPressed: close, isOutline: true), - dialogButton( - 'OK', - onPressed: () { - var text = controller.text.trim(); - bind.sessionPeerOption(id: id, name: "os-password", value: text); - bind.sessionPeerOption( - id: id, name: "auto-login", value: autoLogin ? 'Y' : ''); - if (text != "" && login) { - bind.sessionInputOsPassword(id: id, value: text); - } - close(); - }, - ), - ]); - }); -} - void sendPrompt(bool isMac, String key) { final old = isMac ? gFFI.inputModel.command : gFFI.inputModel.ctrl; if (isMac) { From 127ab57d62539949be82ed330f41a9a53cbc79b5 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 14:11:56 +0800 Subject: [PATCH 048/112] ignore 'gdm' on loginctl Signed-off-by: fufesou --- flutter/lib/models/model.dart | 6 ++--- libs/hbb_common/src/platform/linux.rs | 2 +- src/client.rs | 22 ++++++++-------- src/lang/en.rs | 2 +- src/platform/linux.rs | 10 ++++++++ src/platform/linux_desktop_manager.rs | 29 +++++++++++++-------- src/server/connection.rs | 36 ++++++++++++++++----------- src/ui/common.tis | 4 +-- src/ui/msgbox.tis | 24 +++++++++--------- 9 files changed, 80 insertions(+), 55 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 7d1249906..77e1b8fbf 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -293,10 +293,10 @@ class FfiModel with ChangeNotifier { wrongPasswordDialog(id, dialogManager, type, title, text); } else if (type == 'input-password') { enterPasswordDialog(id, dialogManager); - } else if (type == 'xsession-login' || type == 'xsession-re-login') { + } else if (type == 'session-login' || type == 'session-re-login') { enterUserLoginDialog(id, dialogManager); - } else if (type == 'xsession-login-password' || - type == 'xsession-login-password') { + } else if (type == 'session-login-password' || + type == 'session-login-password') { enterUserLoginAndPasswordDialog(id, dialogManager); } else if (type == 'restarting') { showMsgBox(id, type, title, text, link, false, dialogManager, diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1d826ea97..cf1cf6da5 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { + if !line.contains("gdm") && line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/client.rs b/src/client.rs index f03cb0e55..3819ed382 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1927,34 +1927,34 @@ pub fn handle_login_error( lc.write().unwrap().password = Default::default(); interface.msgbox("re-input-password", err, "Do you want to enter again?", ""); true - } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY { + } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY { interface.msgbox( - "xsession-login", - "xsession is unready", + "session-login", + "session is unready", "Input linux user/password", "", ); true - } else if err == crate::server::LOGIN_MSG_XSESSION_FAILED { + } else if err == crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED { interface.msgbox( - "xsession-re-login", + "session-re-login", "xsession username/password is wrong", "Do you want to enter again?", "", ); true - } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY { + } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY { interface.msgbox( - "xsession-login-password", - "xsession is unready", + "session-login-password", + "session is unready", "Input connection password and linux user/password", "", ); true - } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG { + } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG { interface.msgbox( - "xsession-login-re-password", - "xsession is unready and password is wrong", + "session-login-re-password", + "session is unready and password is wrong", "Do you want to enter again?", "", ); diff --git a/src/lang/en.rs b/src/lang/en.rs index 2ea65b05f..11ad719ab 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,7 +56,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), - ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), + ("login_linux_tip", "Remote desktop is unready. Please \n 1. Login on remote side and then try again\n 2. Or input remote account to login and start a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("remember_password_tip", "Remember password"), diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 8c4a68848..fa83fdc1f 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -21,6 +21,9 @@ use std::{ type Xdo = *const c_void; +pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; +pub const ENV_DESKTOP_PROTOCAL_X11: &str = "x11"; + pub const PA_SAMPLE_RATE: u32 = 48000; static mut UNMODIFIED: bool = true; @@ -930,6 +933,8 @@ mod desktop { if !self.sid.is_empty() && is_active(&self.sid) { return; } + + println!("REMOVE ME ================================== desktop: refresh"); let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); @@ -950,6 +955,11 @@ mod desktop { self.get_display(); self.get_xauth(); self.set_is_subprocess(); + + println!( + "REMOVE ME ================================== desktop: {:?}", + self + ); } } } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index a957e045e..f1ddec58e 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -22,7 +22,7 @@ lazy_static::lazy_static! { #[derive(Debug)] struct DesktopManager { - x11_username: String, + seat0_username: String, child_username: String, child_exit: Arc, is_child_running: Arc, @@ -61,8 +61,8 @@ pub fn stop_xdesktop() { pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); if let Some(desktop_manager) = &mut (*desktop_manager) { - if !desktop_manager.x11_username.is_empty() { - return Ok((desktop_manager.x11_username.clone(), true)); + if !desktop_manager.seat0_username.is_empty() { + return Ok((desktop_manager.seat0_username.clone(), true)); } let _ = desktop_manager.try_start_x_session(username, password)?; @@ -76,7 +76,7 @@ pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String desktop_manager.is_running(), )) } else { - bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); + bail!(crate::server::LOGIN_MSG_DESKTOP_NOT_INITED); } } @@ -86,14 +86,14 @@ pub fn is_headless() -> bool { .lock() .unwrap() .as_ref() - .map_or(false, |manager| manager.x11_username.is_empty()) + .map_or(false, |manager| manager.seat0_username.is_empty()) } pub fn get_username() -> String { match &*DESKTOP_MANAGER.lock().unwrap() { Some(manager) => { - if !manager.x11_username.is_empty() { - manager.x11_username.clone() + if !manager.seat0_username.is_empty() { + manager.seat0_username.clone() } else { if manager.is_running() && !manager.child_username.is_empty() { manager.child_username.clone() @@ -118,16 +118,23 @@ impl DesktopManager { } pub fn new() -> Self { - let mut x11_username = "".to_owned(); + let mut seat0_username = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); + println!( + "REMOVE ME ================================== DesktopManager: {:?}", + &seat0_values + ); if !seat0_values[0].is_empty() { - if "x11" == get_display_server_of_session(&seat0_values[1]) { - x11_username = seat0_values[2].clone(); + let display_server = get_display_server_of_session(&seat0_values[1]); + if display_server == ENV_DESKTOP_PROTOCAL_X11 + || display_server == ENV_DESKTOP_PROTOCAL_WAYLAND + { + seat0_username = seat0_values[2].clone(); } } Self { - x11_username, + seat0_username, child_username: "".to_owned(), child_exit: Arc::new(AtomicBool::new(true)), is_child_running: Arc::new(AtomicBool::new(false)), diff --git a/src/server/connection.rs b/src/server/connection.rs index ee4bdda35..f9c64378e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -59,12 +59,14 @@ lazy_static::lazy_static! { pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); -pub const LOGIN_MSG_XDESKTOP_NOT_INITED: &str = "xdesktop env is not inited"; -pub const LOGIN_MSG_XSESSION_NOT_READY: &str = "xsession unready"; -pub const LOGIN_MSG_XSESSION_FAILED: &str = "xsession failed"; -pub const LOGIN_MSG_XSESSION_ANOTHER_USER_READTY: &str = "xsession another user login"; -pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY: &str = "xsession unready, password empty"; -pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG: &str = "xsession unready, password wrong"; +pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session unready"; +pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; +pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = + "Desktop session unready, password empty"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = + "Desktop session unready, password wrong"; pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; pub const LOGIN_MSG_OFFLINE: &str = "Offline"; @@ -1081,27 +1083,33 @@ impl Connection { if _username.is_empty() { let username = linux_desktop_manager::get_username(); if username.is_empty() { - LOGIN_MSG_XSESSION_NOT_READY + LOGIN_MSG_DESKTOP_SESSION_NOT_READY } else { "" } .to_owned() } else { + let username = linux_desktop_manager::get_username(); + if username == _username { + // No need to verify password here. + return "".to_owned(); + } + match linux_desktop_manager::try_start_x_session(_username, _passsword) { Ok((username, x11_ready)) => { if x11_ready { if _username != username { - LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() + LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER.to_owned() } else { "".to_owned() } } else { - LOGIN_MSG_XSESSION_NOT_READY.to_owned() + LOGIN_MSG_DESKTOP_SESSION_NOT_READY.to_owned() } } Err(e) => { log::error!("Failed to start xsession {}", e); - LOGIN_MSG_XSESSION_FAILED.to_owned() + LOGIN_MSG_DESKTOP_XSESSION_FAILED.to_owned() } } } @@ -1278,8 +1286,8 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; - // If err is LOGIN_MSG_XSESSION_NOT_READY, just keep this msg and go on checking password. - if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY { + // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. + if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; } @@ -1315,7 +1323,7 @@ impl Connection { if desktop_err.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) + self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY) .await; } } else { @@ -1362,7 +1370,7 @@ impl Connection { self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) + self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG) .await; } } else { diff --git a/src/ui/common.tis b/src/ui/common.tis index ef6d215aa..92e704052 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -262,7 +262,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= else msgbox("connecting", "Connecting...", "Logging in..."); } }; - } else if (type == "xsession-login" || type == "xsession-re-login") { + } else if (type == "session-login" || type == "session-re-login") { callback = function (res) { if (!res) { view.close(); @@ -274,7 +274,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= else msgbox("connecting", "Connecting...", "Logging in..."); } }; - } else if (type.indexOf("xsession-login") >= 0) { + } else if (type.indexOf("session-login") >= 0) { callback = function (res) { if (!res) { view.close(); diff --git a/src/ui/msgbox.tis b/src/ui/msgbox.tis index d9a311452..2099a8e7b 100644 --- a/src/ui/msgbox.tis +++ b/src/ui/msgbox.tis @@ -32,7 +32,7 @@ class MsgboxComponent: Reactor.Component { } function getIcon(color) { - if (this.type == "input-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { + if (this.type == "input-password" || this.type == "session-login" || this.type == "session-login-password") { return ; } if (this.type == "connecting") { @@ -41,7 +41,7 @@ class MsgboxComponent: Reactor.Component { if (this.type == "success") { return ; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "session-re-login" || this.type == "session-login-re-password") { return ; } return null; @@ -81,9 +81,9 @@ class MsgboxComponent: Reactor.Component { function getContent() { if (this.type == "input-password") { return this.getInputPasswordContent(); - } else if (this.type == "xsession-login") { + } else if (this.type == "session-login") { return this.getInputUserPasswordContent(); - } else if (this.type == "xsession-login-password") { + } else if (this.type == "session-login-password") { return this.getXsessionPasswordContent(); } else if (this.type == "custom-os-password") { var ts = this.auto_login ? { checked: true } : {}; @@ -96,13 +96,13 @@ class MsgboxComponent: Reactor.Component { } function getColor() { - if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { + if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "session-login" || this.type == "session-login-password") { return "#AD448E"; } if (this.type == "success") { return "#32bea6"; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "session-re-login" || this.type == "session-login-re-password") { return "#e04f5f"; } return "#2C8CFF"; @@ -202,13 +202,13 @@ class MsgboxComponent: Reactor.Component { this.update(); return; } - if (this.type == "xsession-re-login") { - this.type = "xsession-login"; + if (this.type == "session-re-login") { + this.type = "session-login"; this.update(); return; } - if (this.type == "xsession-login-re-password") { - this.type = "xsession-login-password"; + if (this.type == "session-login-re-password") { + this.type = "session-login-password"; this.update(); return; } @@ -273,14 +273,14 @@ class MsgboxComponent: Reactor.Component { return; } } - if (this.type == "xsession-login") { + if (this.type == "session-login") { values.osusername = (values.osusername || "").trim(); values.ospassword = (values.ospassword || "").trim(); if (!values.osusername || !values.ospassword) { return; } } - if (this.type == "xsession-login-password") { + if (this.type == "session-login-password") { values.password = (values.password || "").trim(); values.osusername = (values.osusername || "").trim(); values.ospassword = (values.ospassword || "").trim(); From 9ef4f4c1dea28b2c32b5697f0e394008f46a8405 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 15:36:03 +0800 Subject: [PATCH 049/112] handle_hash empty password Signed-off-by: fufesou --- src/client.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3819ed382..741c45416 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1991,7 +1991,7 @@ pub async fn handle_hash( lc: Arc>, password_preset: &str, hash: Hash, - _interface: &impl Interface, + interface: &impl Interface, peer: &mut Stream, ) { lc.write().unwrap().hash = hash.clone(); @@ -2015,22 +2015,21 @@ pub async fn handle_hash( if password.is_empty() { password = lc.read().unwrap().config.password.clone(); } - if password.is_empty() { + let password = if password.is_empty() { // login without password, the remote side can click accept - send_login(lc.clone(), "".to_owned(), "".to_owned(), Vec::new(), peer).await; + interface.msgbox("input-password", "Password Required", "", ""); + Vec::new() } else { let mut hasher = Sha256::new(); hasher.update(&password); hasher.update(&hash.challenge); - send_login( - lc.clone(), - "".to_owned(), - "".to_owned(), - hasher.finalize()[..].into(), - peer, - ) - .await; - } + hasher.finalize()[..].into() + }; + + let os_username = lc.read().unwrap().get_option("os-username"); + let os_password = lc.read().unwrap().get_option("os-password"); + + send_login(lc.clone(), os_username, os_password, password, peer).await; lc.write().unwrap().hash = hash; } From 34c36153201aa4103b20c488784d24c356d5b7d3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 16:17:24 +0800 Subject: [PATCH 050/112] get values of seat0, do not filter gdm Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 2 +- src/lang/en.rs | 2 +- src/platform/linux.rs | 12 +++++++++ src/platform/linux_desktop_manager.rs | 37 +++++++++++++++++++-------- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index cf1cf6da5..1d826ea97 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if !line.contains("gdm") && line.contains("seat0") { + if line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/lang/en.rs b/src/lang/en.rs index 11ad719ab..2ea65b05f 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,7 +56,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), - ("login_linux_tip", "Remote desktop is unready. Please \n 1. Login on remote side and then try again\n 2. Or input remote account to login and start a X desktop session"), + ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("remember_password_tip", "Remember password"), diff --git a/src/platform/linux.rs b/src/platform/linux.rs index fa83fdc1f..800875a61 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -357,6 +357,7 @@ pub fn start_os_service() { &mut last_restart, &mut user_server, ) { + stop_xorg_subprocess(); force_stop_server(); start_server( Some((desktop.uid.clone(), desktop.username.clone())), @@ -402,6 +403,12 @@ pub fn get_active_userid() -> String { get_values_of_seat0(&[1])[0].clone() } +#[inline] +pub fn is_gdm_user(username: &str) -> bool { + username == "gdm" + // || username == "lightgdm" +} + fn get_cm() -> bool { if let Ok(output) = Command::new("ps").args(vec!["aux"]).output() { for line in String::from_utf8_lossy(&output.stdout).lines() { @@ -800,6 +807,11 @@ mod desktop { self.sid.is_empty() } + #[inline] + pub fn is_login_wayland(&self) -> bool { + super::is_gdm_user(&self.username) && self.protocal == ENV_DESKTOP_PROTOCAL_WAYLAND + } + #[inline] pub fn is_headless(&self) -> bool { self.sid.is_empty() || self.is_rustdesk_subprocess diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index f1ddec58e..f1f098c69 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -23,6 +23,7 @@ lazy_static::lazy_static! { #[derive(Debug)] struct DesktopManager { seat0_username: String, + seat0_display_server: String, child_username: String, child_exit: Arc, is_child_running: Arc, @@ -61,8 +62,8 @@ pub fn stop_xdesktop() { pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); if let Some(desktop_manager) = &mut (*desktop_manager) { - if !desktop_manager.seat0_username.is_empty() { - return Ok((desktop_manager.seat0_username.clone(), true)); + if let Some(seat0_username) = desktop_manager.get_supported_display_seat0_username() { + return Ok((seat0_username, true)); } let _ = desktop_manager.try_start_x_session(username, password)?; @@ -86,14 +87,16 @@ pub fn is_headless() -> bool { .lock() .unwrap() .as_ref() - .map_or(false, |manager| manager.seat0_username.is_empty()) + .map_or(false, |manager| { + manager.get_supported_display_seat0_username().is_none() + }) } pub fn get_username() -> String { match &*DESKTOP_MANAGER.lock().unwrap() { Some(manager) => { - if !manager.seat0_username.is_empty() { - manager.seat0_username.clone() + if let Some(seat0_username) = manager.get_supported_display_seat0_username() { + seat0_username } else { if manager.is_running() && !manager.child_username.is_empty() { manager.child_username.clone() @@ -119,28 +122,40 @@ impl DesktopManager { pub fn new() -> Self { let mut seat0_username = "".to_owned(); + let mut seat0_display_server = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); println!( "REMOVE ME ================================== DesktopManager: {:?}", &seat0_values ); if !seat0_values[0].is_empty() { - let display_server = get_display_server_of_session(&seat0_values[1]); - if display_server == ENV_DESKTOP_PROTOCAL_X11 - || display_server == ENV_DESKTOP_PROTOCAL_WAYLAND - { - seat0_username = seat0_values[2].clone(); - } + seat0_username = seat0_values[2].clone(); + seat0_display_server = get_display_server_of_session(&seat0_values[1]); } Self { seat0_username, + seat0_display_server, child_username: "".to_owned(), child_exit: Arc::new(AtomicBool::new(true)), is_child_running: Arc::new(AtomicBool::new(false)), } } + fn get_supported_display_seat0_username(&self) -> Option { + if is_gdm_user(&self.seat0_username) + && self.seat0_display_server == ENV_DESKTOP_PROTOCAL_WAYLAND + { + None + } else { + if self.seat0_username.is_empty() { + None + } else { + Some(self.seat0_username.clone()) + } + } + } + #[inline] fn get_xauth() -> String { let xauth = get_env_var("XAUTHORITY"); From d82d2471d752633bc170e2d1c250a202d2c42384 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:16:48 +0800 Subject: [PATCH 051/112] temp commit Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 22 ++++++++++++++++++++-- src/core_main.rs | 5 +++++ src/flutter.rs | 17 ++++++++++++++++- src/platform/linux.rs | 12 ++---------- src/platform/linux_desktop_manager.rs | 16 ++++++++-------- src/server/connection.rs | 17 +++++++++++++++-- src/ui_interface.rs | 8 +++++--- 7 files changed, 71 insertions(+), 26 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1d826ea97..30ceaa760 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -35,6 +35,7 @@ pub fn is_gdm_user(username: &str) -> bool { // || username == "lightgdm" } +<<<<<<< HEAD #[inline] pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND @@ -43,12 +44,23 @@ pub fn is_desktop_wayland() -> bool { #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() +======= +pub fn is_x11_or_headless() -> bool { + let (username, display_server) = get_user_and_display_server(); + display_server == DISPLAY_SERVER_WAYLAND && is_gdm_user(&username) + || display_server != DISPLAY_SERVER_WAYLAND +} + +pub fn is_desktop_wayland() -> bool { + let (username, display_server) = get_user_and_display_server(); + display_server == DISPLAY_SERVER_WAYLAND && !is_gdm_user(&username) +>>>>>>> temp commit } // -1 const INVALID_SESSION: &str = "4294967295"; -pub fn get_display_server() -> String { +pub fn get_user_and_display_server() -> (String, String) { let mut session = get_values_of_seat0(&[0])[0].clone(); if session.is_empty() { // loginctl has not given the expected output. try something else. @@ -63,11 +75,17 @@ pub fn get_display_server() -> String { } } } +<<<<<<< HEAD if session.is_empty() { +======= + + let display_server = if session.is_empty() { +>>>>>>> temp commit "".to_owned() } else { get_display_server_of_session(&session) - } + }; + (session, display_server) } pub fn get_display_server_of_session(session: &str) -> String { diff --git a/src/core_main.rs b/src/core_main.rs index e5eb64136..05a5ad769 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -224,6 +224,11 @@ pub fn core_main() -> Option> { // call connection manager to establish connections // meanwhile, return true to call flutter window to show control panel crate::ui_interface::start_option_status_sync(); + } else if args[0] == "--cm-no-ui" { + #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + crate::flutter::connection_manager::start_cm_no_ui(); + return None; } } //_async_logger_holder.map(|x| x.flush()); diff --git a/src/flutter.rs b/src/flutter.rs index b293059d0..3d49dcf03 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -815,8 +815,19 @@ pub mod connection_manager { } } + #[inline] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + pub fn start_cm_no_ui() { + start_listen_ipc(false); + } + + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn start_listen_ipc_thread() { + start_listen_ipc(true); + } + + fn start_listen_ipc(new_thread: bool) { use crate::ui_cm_interface::{start_ipc, ConnectionManager}; #[cfg(target_os = "linux")] @@ -825,7 +836,11 @@ pub mod connection_manager { let cm = ConnectionManager { ui_handler: FlutterHandler {}, }; - std::thread::spawn(move || start_ipc(cm)); + if new_thread { + std::thread::spawn(move || start_ipc(cm)); + } else { + start_ipc(cm); + } } #[cfg(target_os = "android")] diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 800875a61..745464a58 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -21,9 +21,6 @@ use std::{ type Xdo = *const c_void; -pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; -pub const ENV_DESKTOP_PROTOCAL_X11: &str = "x11"; - pub const PA_SAMPLE_RATE: u32 = 48000; static mut UNMODIFIED: bool = true; @@ -403,12 +400,6 @@ pub fn get_active_userid() -> String { get_values_of_seat0(&[1])[0].clone() } -#[inline] -pub fn is_gdm_user(username: &str) -> bool { - username == "gdm" - // || username == "lightgdm" -} - fn get_cm() -> bool { if let Ok(output) = Command::new("ps").args(vec!["aux"]).output() { for line in String::from_utf8_lossy(&output.stdout).lines() { @@ -809,7 +800,7 @@ mod desktop { #[inline] pub fn is_login_wayland(&self) -> bool { - super::is_gdm_user(&self.username) && self.protocal == ENV_DESKTOP_PROTOCAL_WAYLAND + super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND } #[inline] @@ -961,6 +952,7 @@ mod desktop { if self.is_login_wayland() { self.display = "".to_owned(); self.xauth = "".to_owned(); + self.is_rustdesk_subprocess = false; return; } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index f1f098c69..1896060fe 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -123,15 +123,15 @@ impl DesktopManager { pub fn new() -> Self { let mut seat0_username = "".to_owned(); let mut seat0_display_server = "".to_owned(); - let seat0_values = get_values_of_seat0(&[0, 1, 2]); - println!( - "REMOVE ME ================================== DesktopManager: {:?}", - &seat0_values - ); + let seat0_values = get_values_of_seat0(&[0, 2]); if !seat0_values[0].is_empty() { - seat0_username = seat0_values[2].clone(); - seat0_display_server = get_display_server_of_session(&seat0_values[1]); + seat0_username = seat0_values[1].clone(); + seat0_display_server = get_display_server_of_session(&seat0_values[0]); } + println!( + "REMOVE ME ================================== DesktopManager: {:?}, display server: {}", + &seat0_values, &seat0_display_server + ); Self { seat0_username, @@ -144,7 +144,7 @@ impl DesktopManager { fn get_supported_display_seat0_username(&self) -> Option { if is_gdm_user(&self.seat0_username) - && self.seat0_display_server == ENV_DESKTOP_PROTOCAL_WAYLAND + && self.seat0_display_server == DISPLAY_SERVER_WAYLAND { None } else { diff --git a/src/server/connection.rs b/src/server/connection.rs index f9c64378e..2777824df 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -899,8 +899,10 @@ impl Connection { } #[cfg(target_os = "linux")] if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() { - let dtype = crate::platform::linux::get_display_server(); - if dtype != "x11" && dtype != "wayland" { + let (_, dtype) = crate::platform::linux::get_user_and_display_server(); + if dtype != crate::platform::linux::DISPLAY_SERVER_X11 + && dtype != crate::platform::linux::DISPLAY_SERVER_WAYLAND + { res.set_error(format!( "Unsupported display server type \"{}\", x11 or wayland expected", dtype @@ -1131,6 +1133,7 @@ impl Connection { } fn validate_password(&mut self) -> bool { + return true; if password::temporary_enabled() { let password = password::temporary_password(); if self.validate_one_password(password.clone()) { @@ -1286,6 +1289,12 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; + + println!( + "REMOVE ME =================================== try_start_desktop '{}'", + &desktop_err + ); + // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; @@ -2170,6 +2179,10 @@ async fn start_ipc( if password::hide_cm() { args.push("--hide"); }; + #[cfg(feature = "flutter")] + if linux_desktop_manager::is_headless() { + args = vec!["--cm-no-ui"]; + } let run_done; if crate::platform::is_root() { let mut res = Ok(None); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 11357be4a..1181598a6 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -511,11 +511,13 @@ pub fn get_error() -> String { #[cfg(not(any(feature = "cli")))] #[cfg(target_os = "linux")] { - let dtype = crate::platform::linux::get_display_server(); - if "wayland" == dtype { + let (username, dtype) = crate::platform::linux::get_user_and_display_server(); + if crate::platform::linux::DISPLAY_SERVER_WAYLAND == dtype + && !crate::platform::linux::is_gdm_user(&username) + { return crate::server::wayland::common_get_error(); } - if dtype != "x11" { + if dtype != crate::platform::linux::DISPLAY_SERVER_X11 { return format!( "{} {}, {}", crate::client::translate("Unsupported display server".to_owned()), From d86ef4a86ebeafdf3e20b438aebb6413257ec141 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:39:21 +0800 Subject: [PATCH 052/112] headless, linux, debug Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 6 ++++-- src/platform/linux.rs | 1 + src/server/video_service.rs | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 30ceaa760..ce0782a61 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -61,7 +61,9 @@ pub fn is_desktop_wayland() -> bool { const INVALID_SESSION: &str = "4294967295"; pub fn get_user_and_display_server() -> (String, String) { - let mut session = get_values_of_seat0(&[0])[0].clone(); + let seat0_values = get_values_of_seat0(&[0, 2]); + let mut session = seat0_values[0].clone(); + let username = seat0_values[1].clone(); if session.is_empty() { // loginctl has not given the expected output. try something else. if let Ok(sid) = std::env::var("XDG_SESSION_ID") { @@ -85,7 +87,7 @@ pub fn get_user_and_display_server() -> (String, String) { } else { get_display_server_of_session(&session) }; - (session, display_server) + (username, display_server) } pub fn get_display_server_of_session(session: &str) -> String { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 745464a58..5bfa6a9df 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -464,6 +464,7 @@ pub fn get_env_var(k: &str) -> String { } } +// Headless is enabled, no need to wait prelogin. pub fn is_prelogin() -> bool { let n = get_active_userid().len(); n < 4 && n > 1 diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 205d0584c..483c29cfa 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -450,6 +450,8 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] ensure_close_virtual_device()?; + println!("REMOVE ME ================================= run 111"); + // ensure_inited() is needed because release_resource() may be called. #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; From 4ed6681bfdd554aba284bcca2d32f0d629ac84ed Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:52:47 +0800 Subject: [PATCH 053/112] run cm as user Signed-off-by: fufesou --- src/server/connection.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 2777824df..1a83cfed6 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2165,6 +2165,8 @@ async fn start_ipc( mut rx_to_cm: mpsc::UnboundedReceiver, tx_from_cm: mpsc::UnboundedSender, ) -> ResultType<()> { + use hbb_common::platform::linux::run_cmds; + loop { if crate::platform::is_prelogin() { break; @@ -2179,8 +2181,21 @@ async fn start_ipc( if password::hide_cm() { args.push("--hide"); }; + #[cfg(not(feature = "flutter"))] + let user = None; + let mut user = None; #[cfg(feature = "flutter")] if linux_desktop_manager::is_headless() { + let username = linux_desktop_manager::get_username(); + let uid = { + let output = run_cmds(format!("id -u {}", &username))?; + let output = output.trim(); + if output.is_empty() || !output.parse::().is_ok() { + bail!("Invalid username {}", &username); + } + output.to_string() + }; + user = Some((username, uid)); args = vec!["--cm-no-ui"]; } let run_done; @@ -2195,7 +2210,7 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); - res = crate::platform::run_as_user(args.clone(), None); + res = crate::platform::run_as_user(args.clone(), user); } if res.is_ok() { break; From b33ae6ec64085d5690622bbf073c099b123f2ac0 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:55:03 +0800 Subject: [PATCH 054/112] fix build Signed-off-by: fufesou --- src/server/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 1a83cfed6..24e3af5b1 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2210,7 +2210,7 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); - res = crate::platform::run_as_user(args.clone(), user); + res = crate::platform::run_as_user(args.clone(), user.clone()); } if res.is_ok() { break; From 6238098cd0defe4529f58360d3383047e5924172 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 21:17:33 +0800 Subject: [PATCH 055/112] start cm, tmp commit Signed-off-by: fufesou --- src/server/connection.rs | 63 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 24e3af5b1..28c661537 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -18,6 +18,8 @@ use crate::{ use crate::{common::DEVICE_NAME, flutter::connection_manager::start_channel}; use crate::{ipc, VERSION}; use cidr_utils::cidr::IpCidr; +#[cfg(all(target_os = "linux", feature = "flutter"))] +use hbb_common::platform::linux::run_cmds; use hbb_common::{ config::Config, fs, @@ -148,6 +150,8 @@ pub struct Connection { audio_input_device_before_voice_call: Option, options_in_login: Option, pressed_modifiers: HashSet, + rx_cm_stream_ready: mpsc::Receiver<()>, + tx_desktop_ready: mpsc::Sender<()>, } impl ConnInner { @@ -208,6 +212,8 @@ impl Connection { let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc)>(); let (tx_input, _rx_input) = std_mpsc::channel(); let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver(); + let (tx_cm_stream_ready, rx_cm_stream_ready) = mpsc::channel(1); + let (tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); #[cfg(not(any(target_os = "android", target_os = "ios")))] let tx_cloned = tx.clone(); @@ -260,10 +266,14 @@ impl Connection { audio_input_device_before_voice_call: None, options_in_login: None, pressed_modifiers: Default::default(), + rx_cm_stream_ready, + tx_desktop_ready, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { - if let Err(err) = start_ipc(rx_to_cm, tx_from_cm).await { + if let Err(err) = + start_ipc(rx_to_cm, tx_from_cm, rx_desktop_ready, tx_cm_stream_ready).await + { log::error!("ipc to connection manager exit: {}", err); } }); @@ -1289,6 +1299,9 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; + #[cfg(target_os = "linux")] + let is_headless = linux_desktop_manager::is_headless(); + let wait_ipc_timeout = 10_000; println!( "REMOVE ME =================================== try_start_desktop '{}'", @@ -1320,6 +1333,11 @@ impl Connection { return false; } else if self.is_recent_session() { if desktop_err.is_empty() { + #[cfg(target_os = "linux")] + if is_headless { + self.tx_desktop_ready.send(()).await.ok(); + let _res = timeout(wait_ipc_timeout, self.rx_cm_stream_ready.recv()).await; + } self.try_start_cm(lr.my_id, lr.my_name, true); self.send_logon_response().await; if self.port_forward_socket.is_some() { @@ -1387,6 +1405,12 @@ impl Connection { LOGIN_FAILURES.lock().unwrap().remove(&self.ip); } if desktop_err.is_empty() { + #[cfg(target_os = "linux")] + if is_headless { + self.tx_desktop_ready.send(()).await.ok(); + let _res = + timeout(wait_ipc_timeout, self.rx_cm_stream_ready.recv()).await; + } self.send_logon_response().await; self.try_start_cm(lr.my_id, lr.my_name, true); if self.port_forward_socket.is_some() { @@ -2164,9 +2188,9 @@ pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) { async fn start_ipc( mut rx_to_cm: mpsc::UnboundedReceiver, tx_from_cm: mpsc::UnboundedSender, + mut _rx_desktop_ready: mpsc::Receiver<()>, + tx_stream_ready: mpsc::Sender<()>, ) -> ResultType<()> { - use hbb_common::platform::linux::run_cmds; - loop { if crate::platform::is_prelogin() { break; @@ -2181,12 +2205,25 @@ async fn start_ipc( if password::hide_cm() { args.push("--hide"); }; - #[cfg(not(feature = "flutter"))] + + #[cfg(all(target_os = "linux", not(feature = "flutter")))] let user = None; + #[cfg(all(target_os = "linux", feature = "flutter"))] let mut user = None; - #[cfg(feature = "flutter")] + #[cfg(all(target_os = "linux", feature = "flutter"))] if linux_desktop_manager::is_headless() { - let username = linux_desktop_manager::get_username(); + let mut username = linux_desktop_manager::get_username(); + loop { + if !username.is_empty() { + break; + } + let _res = timeout(500, _rx_desktop_ready.recv()).await; + username = linux_desktop_manager::get_username(); + } + println!( + "REMOVE ME =================================== headless username '{}' ", + &username + ); let uid = { let output = run_cmds(format!("id -u {}", &username))?; let output = output.trim(); @@ -2196,6 +2233,10 @@ async fn start_ipc( output.to_string() }; user = Some((username, uid)); + println!( + "REMOVE ME =================================== headless user '{:?}' ", + &user + ); args = vec!["--cm-no-ui"]; } let run_done; @@ -2210,6 +2251,10 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); + println!( + "REMOVE ME =================================== start_ipc 222 '{}' ", + linux_desktop_manager::is_headless() + ); res = crate::platform::run_as_user(args.clone(), user.clone()); } if res.is_ok() { @@ -2224,6 +2269,10 @@ async fn start_ipc( } else { run_done = false; } + println!( + "REMOVE ME =================================== start_ipc 333 '{}' ", + run_done + ); if !run_done { log::debug!("Start cm"); super::CHILD_PROCESS @@ -2242,6 +2291,8 @@ async fn start_ipc( bail!("Failed to connect to connection manager"); } } + + let _res = tx_stream_ready.send(()).await; let mut stream = stream.unwrap(); loop { tokio::select! { From f91514e164f73f494b103db01dcffb7886d76d8f Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 21:35:47 +0800 Subject: [PATCH 056/112] cm-no-ui, debug Signed-off-by: fufesou --- src/platform/linux_desktop_manager.rs | 11 ++++------- src/server/connection.rs | 20 ++------------------ src/server/video_service.rs | 2 -- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 1896060fe..7b87b9b02 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -143,16 +143,13 @@ impl DesktopManager { } fn get_supported_display_seat0_username(&self) -> Option { - if is_gdm_user(&self.seat0_username) - && self.seat0_display_server == DISPLAY_SERVER_WAYLAND + if is_gdm_user(&self.seat0_username) && self.seat0_display_server == DISPLAY_SERVER_WAYLAND { None + } else if self.seat0_username.is_empty() { + None } else { - if self.seat0_username.is_empty() { - None - } else { - Some(self.seat0_username.clone()) - } + Some(self.seat0_username.clone()) } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 28c661537..e629fa93a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2217,13 +2217,9 @@ async fn start_ipc( if !username.is_empty() { break; } - let _res = timeout(500, _rx_desktop_ready.recv()).await; + let _res = timeout(1_000, _rx_desktop_ready.recv()).await; username = linux_desktop_manager::get_username(); } - println!( - "REMOVE ME =================================== headless username '{}' ", - &username - ); let uid = { let output = run_cmds(format!("id -u {}", &username))?; let output = output.trim(); @@ -2232,11 +2228,7 @@ async fn start_ipc( } output.to_string() }; - user = Some((username, uid)); - println!( - "REMOVE ME =================================== headless user '{:?}' ", - &user - ); + user = Some((uid, username)); args = vec!["--cm-no-ui"]; } let run_done; @@ -2251,10 +2243,6 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); - println!( - "REMOVE ME =================================== start_ipc 222 '{}' ", - linux_desktop_manager::is_headless() - ); res = crate::platform::run_as_user(args.clone(), user.clone()); } if res.is_ok() { @@ -2269,10 +2257,6 @@ async fn start_ipc( } else { run_done = false; } - println!( - "REMOVE ME =================================== start_ipc 333 '{}' ", - run_done - ); if !run_done { log::debug!("Start cm"); super::CHILD_PROCESS diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 483c29cfa..205d0584c 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -450,8 +450,6 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] ensure_close_virtual_device()?; - println!("REMOVE ME ================================= run 111"); - // ensure_inited() is needed because release_resource() may be called. #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; From 39917c174a2b6eb592e6ab19311e42b857b0f275 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 21:54:20 +0800 Subject: [PATCH 057/112] kill --cm-no-ui Signed-off-by: fufesou --- src/platform/linux.rs | 15 ++++++--------- src/platform/linux_desktop_manager.rs | 5 ----- src/server/connection.rs | 5 ----- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 5bfa6a9df..27c0fde48 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -230,6 +230,9 @@ fn stop_xorg_subprocess() { let _ = run_cmds(&format!( r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); + let _ = run_cmds(format!( + r##"ps -ef | grep -E 'rustdesk +--cm-no-ui' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, + )); } fn should_start_server( @@ -302,7 +305,7 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); - stop_xorg_subprocess(); + stop_subprocess(); start_uinput_service(); let running = Arc::new(AtomicBool::new(true)); @@ -337,7 +340,7 @@ pub fn start_os_service() { &mut last_restart, &mut server, ) { - stop_xorg_subprocess(); + stop_subprocess(); force_stop_server(); start_server(None, &mut server); } @@ -354,7 +357,7 @@ pub fn start_os_service() { &mut last_restart, &mut user_server, ) { - stop_xorg_subprocess(); + stop_subprocess(); force_stop_server(); start_server( Some((desktop.uid.clone(), desktop.username.clone())), @@ -938,7 +941,6 @@ mod desktop { return; } - println!("REMOVE ME ================================== desktop: refresh"); let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); @@ -960,11 +962,6 @@ mod desktop { self.get_display(); self.get_xauth(); self.set_is_subprocess(); - - println!( - "REMOVE ME ================================== desktop: {:?}", - self - ); } } } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 7b87b9b02..c70724a5e 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -128,11 +128,6 @@ impl DesktopManager { seat0_username = seat0_values[1].clone(); seat0_display_server = get_display_server_of_session(&seat0_values[0]); } - println!( - "REMOVE ME ================================== DesktopManager: {:?}, display server: {}", - &seat0_values, &seat0_display_server - ); - Self { seat0_username, seat0_display_server, diff --git a/src/server/connection.rs b/src/server/connection.rs index e629fa93a..7a9ab86c2 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1303,11 +1303,6 @@ impl Connection { let is_headless = linux_desktop_manager::is_headless(); let wait_ipc_timeout = 10_000; - println!( - "REMOVE ME =================================== try_start_desktop '{}'", - &desktop_err - ); - // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; From 86c063eecbf4d5d8672c4ff7f32049c0ed71887e Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 22:01:43 +0800 Subject: [PATCH 058/112] remember os account Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 4 ++++ src/server/connection.rs | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 0c61abb0d..6b24c9d28 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -507,6 +507,10 @@ _connectDialog( final osPassword = osPasswordController?.text.trim() ?? ''; final password = passwordController?.text.trim() ?? ''; if (passwordController != null && password.isEmpty) return; + if (rememberAccount) { + bind.sessionPeerOption(id: id, name: 'os-username', value: osUsername); + bind.sessionPeerOption(id: id, name: 'os-password', value: osPassword); + } gFFI.login( osUsername, osPassword, diff --git a/src/server/connection.rs b/src/server/connection.rs index 7a9ab86c2..1a8d807bc 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1143,7 +1143,6 @@ impl Connection { } fn validate_password(&mut self) -> bool { - return true; if password::temporary_enabled() { let password = password::temporary_password(); if self.validate_one_password(password.clone()) { From 8373c5994127f4aba27c3f824090a3a41247fcfa Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 22:44:05 +0800 Subject: [PATCH 059/112] update lang Signed-off-by: fufesou --- src/lang/pl.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index d89b6f763..f8c853026 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -480,8 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Ten plik jest identyczny jak plik na drugim komputerze."), ("show_monitors_tip", "Pokaż monitory w zasobniku."), ("View Mode", "Tryb widoku"), - ("enter_rustdesk_passwd_tip", "Podaj hasło RustDesk."), - ("remember_rustdesk_passwd_tip", "Zapamiętaj hasło RustDesk."), ("login_linux_tip", "Zaloguj do zdalnego konta Linux"), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } From 41573a94b4dc90dcfef95e2d2ab900b125afc936 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 23:18:09 +0800 Subject: [PATCH 060/112] win, remove build warnings Signed-off-by: fufesou --- src/server/connection.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 1a8d807bc..125800040 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -150,7 +150,9 @@ pub struct Connection { audio_input_device_before_voice_call: Option, options_in_login: Option, pressed_modifiers: HashSet, + #[cfg(target_os = "linux")] rx_cm_stream_ready: mpsc::Receiver<()>, + #[cfg(target_os = "linux")] tx_desktop_ready: mpsc::Sender<()>, } @@ -212,8 +214,8 @@ impl Connection { let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc)>(); let (tx_input, _rx_input) = std_mpsc::channel(); let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver(); - let (tx_cm_stream_ready, rx_cm_stream_ready) = mpsc::channel(1); - let (tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); + let (tx_cm_stream_ready, _rx_cm_stream_ready) = mpsc::channel(1); + let (_tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); #[cfg(not(any(target_os = "android", target_os = "ios")))] let tx_cloned = tx.clone(); @@ -266,8 +268,10 @@ impl Connection { audio_input_device_before_voice_call: None, options_in_login: None, pressed_modifiers: Default::default(), - rx_cm_stream_ready, - tx_desktop_ready, + #[cfg(target_os = "linux")] + rx_cm_stream_ready: _rx_cm_stream_ready, + #[cfg(target_os = "linux")] + tx_desktop_ready: _tx_desktop_ready, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -1300,6 +1304,7 @@ impl Connection { }; #[cfg(target_os = "linux")] let is_headless = linux_desktop_manager::is_headless(); + #[cfg(target_os = "linux")] let wait_ipc_timeout = 10_000; // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. From 5fcb30d3c736c91436da04ccb03adba9648388d7 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 23:26:03 +0800 Subject: [PATCH 061/112] restore lang Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 2 +- src/lang/ca.rs | 2 +- src/lang/cn.rs | 2 +- src/lang/cs.rs | 2 +- src/lang/da.rs | 2 +- src/lang/de.rs | 2 +- src/lang/el.rs | 2 +- src/lang/en.rs | 1 - src/lang/eo.rs | 2 +- src/lang/es.rs | 2 +- src/lang/fa.rs | 2 +- src/lang/fr.rs | 2 +- src/lang/hu.rs | 2 +- src/lang/id.rs | 2 +- src/lang/it.rs | 2 +- src/lang/ja.rs | 2 +- src/lang/ko.rs | 2 +- src/lang/kz.rs | 2 +- src/lang/nl.rs | 2 +- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 2 +- src/lang/ptbr.rs | 2 +- src/lang/ro.rs | 2 +- src/lang/ru.rs | 2 +- src/lang/sk.rs | 2 +- src/lang/sl.rs | 2 +- src/lang/sq.rs | 2 +- src/lang/sr.rs | 2 +- src/lang/sv.rs | 2 +- src/lang/template.rs | 2 +- src/lang/th.rs | 2 +- src/lang/tr.rs | 2 +- src/lang/tw.rs | 2 +- src/lang/ua.rs | 2 +- src/lang/vn.rs | 2 +- 35 files changed, 34 insertions(+), 35 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 6b24c9d28..cf1ced92f 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -600,7 +600,7 @@ _connectDialog( autoFocus: osUsernameController == null, ), rememberWidget( - translate('remember_password_tip'), + translate('Remember password'), rememberPassword, (v) { if (v != null) { diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 6f4ce9543..146160fca 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tancar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", ""), + ("Password Required", "Es necessita la contrasenya"), ("Please enter your password", "Si us plau, introdueixi la seva contrasenya"), ("Remember password", "Recordar contrasenya"), ("Wrong Password", "Contrasenya incorrecta"), diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 5262db44c..3f562ebd4 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "关闭"), ("Retry", "再试"), ("OK", "确认"), - ("remember_password_tip", ""), + ("Password Required", "需要密码"), ("Please enter your password", "请输入密码"), ("Remember password", "记住密码"), ("Wrong Password", "密码错误"), diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 58d10b5ee..aa40f1442 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zavřít"), ("Retry", "Zkusit znovu"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Vyžadováno heslo"), ("Please enter your password", "Zadejte své heslo"), ("Remember password", "Zapamatovat heslo"), ("Wrong Password", "Nesprávné heslo"), diff --git a/src/lang/da.rs b/src/lang/da.rs index 7e6888239..2737336c0 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Luk"), ("Retry", "Prøv igen"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Adgangskode påkrævet"), ("Please enter your password", "Indtast venligst dit kodeord"), ("Remember password", "Husk kodeord"), ("Wrong Password", "Forkert kodeord"), diff --git a/src/lang/de.rs b/src/lang/de.rs index ad9196785..6583c25e5 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Schließen"), ("Retry", "Erneut versuchen"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Passwort erforderlich"), ("Please enter your password", "Bitte geben Sie Ihr Passwort ein"), ("Remember password", "Passwort merken"), ("Wrong Password", "Falsches Passwort"), diff --git a/src/lang/el.rs b/src/lang/el.rs index 50778f4c4..fa0a3f990 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Κλείσιμο"), ("Retry", "Δοκίμασε ξανά"), ("OK", "ΟΚ"), - ("remember_password_tip", ""), + ("Password Required", "Απαιτείται κωδικός πρόσβασης"), ("Please enter your password", "Παρακαλώ εισάγετε τον κωδικό πρόσβασης"), ("Remember password", "Απομνημόνευση κωδικού πρόσβασης"), ("Wrong Password", "Λάθος κωδικός πρόσβασης"), diff --git a/src/lang/en.rs b/src/lang/en.rs index 2ea65b05f..fede80eff 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -59,7 +59,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), - ("remember_password_tip", "Remember password"), ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 9fcc2b451..0d8815588 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermi"), ("Retry", "Reprovi"), ("OK", "Konfermi"), - ("remember_password_tip", ""), + ("Password Required", "Pasvorto deviga"), ("Please enter your password", "Bonvolu tajpi vian pasvorton"), ("Remember password", "Memori pasvorton"), ("Wrong Password", "Erara pasvorto"), diff --git a/src/lang/es.rs b/src/lang/es.rs index c8120de49..a9d71dea5 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Cerrar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", ""), + ("Password Required", "Se requiere contraseña"), ("Please enter your password", "Por favor, introduzca su contraseña"), ("Remember password", "Recordar contraseña"), ("Wrong Password", "Contraseña incorrecta"), diff --git a/src/lang/fa.rs b/src/lang/fa.rs index ccf2a670a..f73bf3fec 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "بستن"), ("Retry", "تلاش مجدد"), ("OK", "قبول"), - ("remember_password_tip", ""), + ("Password Required", "رمز عبور لازم است"), ("Please enter your password", "رمز عبور خود را وارد کنید"), ("Remember password", "رمز عبور را به خاطر بسپار"), ("Wrong Password", "رمز عبور اشتباه است"), diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 2ff02d8f9..3279b9175 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermer"), ("Retry", "Réessayer"), ("OK", "Valider"), - ("remember_password_tip", ""), + ("Password Required", "Mot de passe requis"), ("Please enter your password", "Veuillez saisir votre mot de passe"), ("Remember password", "Mémoriser le mot de passe"), ("Wrong Password", "Mauvais mot de passe"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 49e767b84..cfad983a2 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Bezárás"), ("Retry", "Újra"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Jelszó megadása kötelező"), ("Please enter your password", "Kérem írja be a jelszavát"), ("Remember password", "Jelszó megjegyzése"), ("Wrong Password", "Hibás jelszó"), diff --git a/src/lang/id.rs b/src/lang/id.rs index 2c410c0ec..3c0254f8d 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tutup"), ("Retry", "Ulangi"), ("OK", "Oke"), - ("remember_password_tip", ""), + ("Password Required", "Kata sandi dibutuhkan"), ("Please enter your password", "Silahkan masukkan kata sandi anda"), ("Remember password", "Ingat Password"), ("Wrong Password", "Kata sandi Salah"), diff --git a/src/lang/it.rs b/src/lang/it.rs index 4427743f1..a09bfa97a 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Chiudi"), ("Retry", "Riprova"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Password Richiesta"), ("Please enter your password", "Inserisci la tua password"), ("Remember password", "Ricorda password"), ("Wrong Password", "Password Errata"), diff --git a/src/lang/ja.rs b/src/lang/ja.rs index dba70c1e1..ae153feac 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "閉じる"), ("Retry", "再試行"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "パスワードが必要"), ("Please enter your password", "パスワードを入力してください"), ("Remember password", "パスワードを記憶する"), ("Wrong Password", "パスワードが間違っています"), diff --git a/src/lang/ko.rs b/src/lang/ko.rs index ab7714190..843211c49 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "닫기"), ("Retry", "재시도"), ("OK", "확인"), - ("remember_password_tip", ""), + ("Password Required", "비밀번호 입력"), ("Please enter your password", "비밀번호를 입력해주세요"), ("Remember password", "이 비밀번호 기억하기"), ("Wrong Password", "틀린 비밀번호"), diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 7fe2719cc..b8d665a60 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Жабу"), ("Retry", "Қайтадан көру"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Құпия сөз Қажет"), ("Please enter your password", "Құпия сөзіңізді еңгізуді өтінеміз"), ("Remember password", "Құпия сөзді есте сақтау"), ("Wrong Password", "Бұрыс Құпия сөз"), diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 360166e50..caaf80077 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Sluit"), ("Retry", "Probeer opnieuw"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Wachtwoord vereist"), ("Please enter your password", "Geef uw wachtwoord in"), ("Remember password", "Wachtwoord onthouden"), ("Wrong Password", "Verkeerd wachtwoord"), diff --git a/src/lang/pl.rs b/src/lang/pl.rs index f8c853026..c1f2ef3dc 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zamknij"), ("Retry", "Ponów"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Wymagane jest hasło"), ("Please enter your password", "Wpisz proszę twoje hasło"), ("Remember password", "Zapamiętaj hasło"), ("Wrong Password", "Błędne hasło"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index c23aa1e46..880658ff4 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "Confirmar"), - ("remember_password_tip", ""), + ("Password Required", "Palavra-chave Necessária"), ("Please enter your password", "Por favor introduza a sua palavra-chave"), ("Remember password", "Memorizar palavra-chave"), ("Wrong Password", "Palavra-chave inválida"), diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index c741944a1..fe7f4a36b 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Senha necessária"), ("Please enter your password", "Por favor informe sua senha"), ("Remember password", "Lembrar senha"), ("Wrong Password", "Senha incorreta"), diff --git a/src/lang/ro.rs b/src/lang/ro.rs index d77345356..046774f32 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Închide"), ("Retry", "Reîncearcă"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Parolă necesară"), ("Please enter your password", "Introdu parola"), ("Remember password", "Memorează parola"), ("Wrong Password", "Parolă incorectă"), diff --git a/src/lang/ru.rs b/src/lang/ru.rs index fbd1c4c62..ac87dcb48 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрыть"), ("Retry", "Повторить"), ("OK", "ОК"), - ("remember_password_tip", ""), + ("Password Required", "Требуется пароль"), ("Please enter your password", "Введите пароль"), ("Remember password", "Запомнить пароль"), ("Wrong Password", "Неправильный пароль"), diff --git a/src/lang/sk.rs b/src/lang/sk.rs index efa66f7ae..1a156d8a6 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvoriť"), ("Retry", "Zopakovať"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Vyžaduje sa heslo"), ("Please enter your password", "Zadajte vaše heslo"), ("Remember password", "Zapamätať heslo"), ("Wrong Password", "Chybné heslo"), diff --git a/src/lang/sl.rs b/src/lang/sl.rs index d897e4755..1f07b027c 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zapri"), ("Retry", "Ponovi"), ("OK", "V redu"), - ("remember_password_tip", ""), + ("Password Required", "Potrebno je geslo"), ("Please enter your password", "Vnesite vaše geslo"), ("Remember password", "Zapomni si geslo"), ("Wrong Password", "Napačno geslo"), diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 88d678ed3..558984a1b 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Mbyll"), ("Retry", "Riprovo"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Fjalëkalimi i detyrueshëm"), ("Please enter your password", "Ju lutem vendosni fjalëkalimin tuaj"), ("Remember password", "Mbani mend fjalëkalimin"), ("Wrong Password", "Fjalëkalim i gabuar"), diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 078d9ba29..3ec1d32d1 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvori"), ("Retry", "Ponovi"), ("OK", "Ok"), - ("remember_password_tip", ""), + ("Password Required", "Potrebna lozinka"), ("Please enter your password", "Molimo unesite svoju lozinku"), ("Remember password", "Zapamti lozinku"), ("Wrong Password", "Pogrešna lozinka"), diff --git a/src/lang/sv.rs b/src/lang/sv.rs index df5caee28..93bbaf74e 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Stäng"), ("Retry", "Försök igen"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Lösenord krävs"), ("Please enter your password", "Skriv in ditt lösenord"), ("Remember password", "Kom ihåg lösenord"), ("Wrong Password", "Fel lösenord"), diff --git a/src/lang/template.rs b/src/lang/template.rs index 1b8bf69c2..db1519e9d 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", ""), ("Retry", ""), ("OK", ""), - ("remember_password_tip", ""), + ("Password Required", ""), ("Please enter your password", ""), ("Remember password", ""), ("Wrong Password", ""), diff --git a/src/lang/th.rs b/src/lang/th.rs index a152eb7b1..1b41ff694 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "ปิด"), ("Retry", "ลองใหม่อีกครั้ง"), ("OK", "ตกลง"), - ("remember_password_tip", ""), + ("Password Required", "ต้องใช้รหัสผ่าน"), ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), ("Remember password", "จดจำรหัสผ่าน"), ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), diff --git a/src/lang/tr.rs b/src/lang/tr.rs index c5d21460a..0aa49a3e4 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Kapat"), ("Retry", "Tekrar Dene"), ("OK", "Tamam"), - ("remember_password_tip", ""), + ("Password Required", "Şifre Gerekli"), ("Please enter your password", "Lütfen şifrenizi giriniz"), ("Remember password", "Şifreyi hatırla"), ("Wrong Password", "Hatalı şifre"), diff --git a/src/lang/tw.rs b/src/lang/tw.rs index d2156f4b7..0077fdaad 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "關閉"), ("Retry", "重試"), ("OK", "確定"), - ("remember_password_tip", ""), + ("Password Required", "需要密碼"), ("Please enter your password", "請輸入您的密碼"), ("Remember password", "記住密碼"), ("Wrong Password", "密碼錯誤"), diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 1a91bb879..811bcd913 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрити"), ("Retry", "Спробувати знову"), ("OK", "ОК"), - ("remember_password_tip", ""), + ("Password Required", "Потрібен пароль"), ("Please enter your password", "Будь ласка, введіть ваш пароль"), ("Remember password", "Запам'ятати пароль"), ("Wrong Password", "Невірний пароль"), diff --git a/src/lang/vn.rs b/src/lang/vn.rs index eeafcfa54..6ad03f0f8 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Đóng"), ("Retry", "Thử lại"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Yêu cầu mật khẩu"), ("Please enter your password", "Mời nhập mật khẩu"), ("Remember password", "Nhớ mật khẩu"), ("Wrong Password", "Sai mật khẩu"), From b18bfa749b9747dd7067d28e3c8284b408638031 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 23:46:06 +0800 Subject: [PATCH 062/112] remove wait_prelogin Signed-off-by: fufesou --- src/platform/linux.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 27c0fde48..b3fde7808 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -467,7 +467,7 @@ pub fn get_env_var(k: &str) -> String { } } -// Headless is enabled, no need to wait prelogin. +// Headless is enabled, always return true. pub fn is_prelogin() -> bool { let n = get_active_userid().len(); n < 4 && n > 1 From 8ea63af4b510ab9503b2d2aa6761c27fc39bc7ed Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 09:49:00 +0800 Subject: [PATCH 063/112] fix build android Signed-off-by: fufesou --- src/flutter.rs | 1 + src/server/connection.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/flutter.rs b/src/flutter.rs index 3d49dcf03..6c9ff7f37 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -827,6 +827,7 @@ pub mod connection_manager { start_listen_ipc(true); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn start_listen_ipc(new_thread: bool) { use crate::ui_cm_interface::{start_ipc, ConnectionManager}; diff --git a/src/server/connection.rs b/src/server/connection.rs index 125800040..8a8714c6f 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -214,7 +214,9 @@ impl Connection { let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc)>(); let (tx_input, _rx_input) = std_mpsc::channel(); let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let (tx_cm_stream_ready, _rx_cm_stream_ready) = mpsc::channel(1); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let (_tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); #[cfg(not(any(target_os = "android", target_os = "ios")))] From ee3ac310833ddc447e3e0e293d44286088550bba Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 10:25:58 +0800 Subject: [PATCH 064/112] add deps libpam0g-dev Signed-off-by: fufesou --- .github/workflows/flutter-nightly.yml | 2 +- libs/hbb_common/src/platform/linux.rs | 30 +++++---------------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index ab625e431..5aa82b847 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -11,4 +11,4 @@ jobs: uses: ./.github/workflows/flutter-build.yml with: upload-artifact: true - \ No newline at end of file + diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index ce0782a61..89c96799d 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -35,7 +35,6 @@ pub fn is_gdm_user(username: &str) -> bool { // || username == "lightgdm" } -<<<<<<< HEAD #[inline] pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND @@ -44,26 +43,13 @@ pub fn is_desktop_wayland() -> bool { #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() -======= -pub fn is_x11_or_headless() -> bool { - let (username, display_server) = get_user_and_display_server(); - display_server == DISPLAY_SERVER_WAYLAND && is_gdm_user(&username) - || display_server != DISPLAY_SERVER_WAYLAND -} - -pub fn is_desktop_wayland() -> bool { - let (username, display_server) = get_user_and_display_server(); - display_server == DISPLAY_SERVER_WAYLAND && !is_gdm_user(&username) ->>>>>>> temp commit } // -1 const INVALID_SESSION: &str = "4294967295"; -pub fn get_user_and_display_server() -> (String, String) { - let seat0_values = get_values_of_seat0(&[0, 2]); - let mut session = seat0_values[0].clone(); - let username = seat0_values[1].clone(); +pub fn get_display_server() -> String { + let mut session = get_values_of_seat0(&[0])[0].clone(); if session.is_empty() { // loginctl has not given the expected output. try something else. if let Ok(sid) = std::env::var("XDG_SESSION_ID") { @@ -77,17 +63,11 @@ pub fn get_user_and_display_server() -> (String, String) { } } } -<<<<<<< HEAD if session.is_empty() { -======= - - let display_server = if session.is_empty() { ->>>>>>> temp commit "".to_owned() } else { get_display_server_of_session(&session) - }; - (username, display_server) + } } pub fn get_display_server_of_session(session: &str) -> String { @@ -211,7 +191,7 @@ pub fn run_cmds(cmds: &str) -> ResultType { } #[cfg(not(feature = "flatpak"))] -pub(super) fn run_loginctl(args: Option>) -> std::io::Result { +fn run_loginctl(args: Option>) -> std::io::Result { let mut cmd = std::process::Command::new("loginctl"); if let Some(a) = args { return cmd.args(a).output(); @@ -220,7 +200,7 @@ pub(super) fn run_loginctl(args: Option>) -> std::io::Result>) -> std::io::Result { +fn run_loginctl(args: Option>) -> std::io::Result { let mut l_args = String::from("loginctl"); if let Some(a) = args { l_args = format!("{} {}", l_args, a.join(" ")); From 571c1df5c446885dfc388527df30c643478d0257 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 17:49:35 +0800 Subject: [PATCH 065/112] fix build Signed-off-by: fufesou --- src/platform/linux.rs | 14 ++------------ src/platform/linux_desktop_manager.rs | 2 +- src/server/connection.rs | 4 ++-- src/ui_interface.rs | 3 +-- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index b3fde7808..33ebcc82c 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -226,11 +226,11 @@ fn stop_rustdesk_servers() { } #[inline] -fn stop_xorg_subprocess() { +fn stop_subprocess() { let _ = run_cmds(&format!( r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); - let _ = run_cmds(format!( + let _ = run_cmds(&format!( r##"ps -ef | grep -E 'rustdesk +--cm-no-ui' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); } @@ -797,16 +797,6 @@ mod desktop { super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND } - #[inline] - pub fn is_headless(&self) -> bool { - self.sid.is_empty() - } - - #[inline] - pub fn is_login_wayland(&self) -> bool { - super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND - } - #[inline] pub fn is_headless(&self) -> bool { self.sid.is_empty() || self.is_rustdesk_subprocess diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index c70724a5e..3a72f8976 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -373,7 +373,7 @@ impl DesktopManager { fn wait_x_server_running(pid: u32, display_num: u32, max_wait_secs: u64) -> ResultType<()> { let wait_begin = Instant::now(); loop { - if run_cmds(format!("ls /proc/{}", pid))?.is_empty() { + if run_cmds(&format!("ls /proc/{}", pid))?.is_empty() { bail!("X server exit"); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 8a8714c6f..65a3fd025 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -915,7 +915,7 @@ impl Connection { } #[cfg(target_os = "linux")] if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() { - let (_, dtype) = crate::platform::linux::get_user_and_display_server(); + let dtype = crate::platform::linux::get_display_server(); if dtype != crate::platform::linux::DISPLAY_SERVER_X11 && dtype != crate::platform::linux::DISPLAY_SERVER_WAYLAND { @@ -2222,7 +2222,7 @@ async fn start_ipc( username = linux_desktop_manager::get_username(); } let uid = { - let output = run_cmds(format!("id -u {}", &username))?; + let output = run_cmds(&format!("id -u {}", &username))?; let output = output.trim(); if output.is_empty() || !output.parse::().is_ok() { bail!("Invalid username {}", &username); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 1181598a6..fa73490ad 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -511,9 +511,8 @@ pub fn get_error() -> String { #[cfg(not(any(feature = "cli")))] #[cfg(target_os = "linux")] { - let (username, dtype) = crate::platform::linux::get_user_and_display_server(); + let dtype = crate::platform::linux::get_display_server(); if crate::platform::linux::DISPLAY_SERVER_WAYLAND == dtype - && !crate::platform::linux::is_gdm_user(&username) { return crate::server::wayland::common_get_error(); } From 73358502e93062d3012fbcc223e322d911e1f8c4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 00:28:56 +0800 Subject: [PATCH 066/112] linux headless feature, tmp commit Signed-off-by: fufesou --- Cargo.toml | 5 +- libs/hbb_common/src/config.rs | 13 ++- src/client.rs | 119 +++++++++++++++++--------- src/client/io_loop.rs | 1 + src/lang/ca.rs | 16 +++- src/lang/cn.rs | 16 +++- src/lang/cs.rs | 16 +++- src/lang/da.rs | 16 +++- src/lang/de.rs | 16 +++- src/lang/el.rs | 16 +++- src/lang/en.rs | 15 ++++ src/lang/eo.rs | 16 +++- src/lang/es.rs | 16 +++- src/lang/fa.rs | 16 +++- src/lang/fr.rs | 1 - src/lang/hu.rs | 16 +++- src/lang/id.rs | 16 +++- src/lang/it.rs | 16 +++- src/lang/ja.rs | 16 +++- src/lang/ko.rs | 16 +++- src/lang/kz.rs | 16 +++- src/lang/nl.rs | 16 +++- src/lang/pl.rs | 16 +++- src/lang/pt_PT.rs | 16 +++- src/lang/ptbr.rs | 16 +++- src/lang/ro.rs | 16 +++- src/lang/ru.rs | 16 +++- src/lang/sk.rs | 16 +++- src/lang/sl.rs | 16 +++- src/lang/sq.rs | 16 +++- src/lang/sr.rs | 16 +++- src/lang/sv.rs | 16 +++- src/lang/template.rs | 16 +++- src/lang/th.rs | 16 +++- src/lang/tr.rs | 16 +++- src/lang/tw.rs | 16 +++- src/lang/ua.rs | 16 +++- src/lang/vn.rs | 16 +++- src/platform/linux_desktop_manager.rs | 81 ++++++++++++++++-- src/platform/mod.rs | 2 +- src/rendezvous_mediator.rs | 4 +- src/server/connection.rs | 105 +++++++++++------------ 42 files changed, 713 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 10f90f387..48bd16045 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ flutter = ["flutter_rust_bridge"] default = ["use_dasp"] hwcodec = ["scrap/hwcodec"] mediacodec = ["scrap/mediacodec"] +linux_headless = ["pam", "users"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -122,8 +123,8 @@ mouce = { git="https://github.com/fufesou/mouce.git" } evdev = { git="https://github.com/fufesou/evdev" } dbus = "0.9" dbus-crossroads = "0.5" -pam = { git="https://github.com/fufesou/pam" } -users = "0.11.0" +pam = { git="https://github.com/fufesou/pam", optional = true } +users = { version = "0.11.0", optional = true } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 3841bf976..e6c788f9a 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -64,11 +64,15 @@ lazy_static::lazy_static! { pub static ref APP_HOME_DIR: Arc> = Default::default(); } -// #[cfg(any(target_os = "android", target_os = "ios"))] +pub const LINK_DOCS_HOME: &str = "https://rustdesk.com/docs/en/"; +pub const LINK_DOCS_X11_REQUIRED: &str = "https://rustdesk.com/docs/en/manual/linux/#x11-required"; +pub const LINK_HEADLESS_LINUX_SUPPORT: &str = + "https://github.com/rustdesk/rustdesk/wiki/Headless-Linux-Support"; lazy_static::lazy_static! { pub static ref HELPER_URL: HashMap<&'static str, &'static str> = HashMap::from([ - ("rustdesk docs home", "https://rustdesk.com/docs/en/"), - ("rustdesk docs x11-required", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), + ("rustdesk docs home", LINK_DOCS_HOME), + ("rustdesk docs x11-required", LINK_DOCS_X11_REQUIRED), + ("rustdesk x11 headless", LINK_HEADLESS_LINUX_SUPPORT), ]); } @@ -917,7 +921,8 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-username", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = + decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } diff --git a/src/client.rs b/src/client.rs index 741c45416..3f587dfe0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1912,6 +1912,71 @@ fn _input_os_password(p: String, activate: bool, interface: impl Interface) { interface.send(Data::Message(msg_out)); } +#[derive(Copy, Clone)] +struct LoginErrorMsgBox { + msgtype: &'static str, + title: &'static str, + text: &'static str, + link: &'static str, + try_again: bool, +} + +lazy_static::lazy_static! { + static ref LOGIN_ERROR_MAP: Arc> = { + use hbb_common::config::LINK_HEADLESS_LINUX_SUPPORT; + let map = HashMap::from([(crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{ + msgtype: "session-login", + title: "session_not_ready_title_tip", + text: "session_not_ready_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ + msgtype: "session-re-login", + title: "xsession_failed_title_tip", + text: "xsession_failed_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ + msgtype: "info", + title: "another_user_login_title_tip", + text: "another_user_login_text_tip", + link: "", + try_again: false, + }), (crate::server::LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ + msgtype: "session-re-login", + title: "xorg_not_found_title_tip", + text: "xorg_not_found_text_tip", + link: LINK_HEADLESS_LINUX_SUPPORT, + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ + msgtype: "session-re-login", + title: "no_desktop_title_tip", + text: "no_desktop_text_tip", + link: LINK_HEADLESS_LINUX_SUPPORT, + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ + msgtype: "session-login-password", + title: "session_unready_no_password_title_tip", + text: "session_unready_no_password_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ + msgtype: "session-login-re-password", + title: "session_unready_wrong_password_title_tip", + text: "session_unready_wrong_password_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ + msgtype: "wait-remote-accept-nook", + title: "Prompt", + text: "no_password_access_text_tip", + link: "", + try_again: true, + })]); + Arc::new(map) + }; +} + /// Handle login error. /// Return true if the password is wrong, return false if there's an actual error. pub fn handle_login_error( @@ -1927,47 +1992,19 @@ pub fn handle_login_error( lc.write().unwrap().password = Default::default(); interface.msgbox("re-input-password", err, "Do you want to enter again?", ""); true - } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY { - interface.msgbox( - "session-login", - "session is unready", - "Input linux user/password", - "", - ); - true - } else if err == crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED { - interface.msgbox( - "session-re-login", - "xsession username/password is wrong", - "Do you want to enter again?", - "", - ); - true - } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY { - interface.msgbox( - "session-login-password", - "session is unready", - "Input connection password and linux user/password", - "", - ); - true - } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG { - interface.msgbox( - "session-login-re-password", - "session is unready and password is wrong", - "Do you want to enter again?", - "", - ); - true - } else if err == "No Password Access" { - lc.write().unwrap().password = Default::default(); - interface.msgbox( - "wait-remote-accept-nook", - "Prompt", - "Please wait for the remote side to accept your session request...", - "", - ); - true + } else if LOGIN_ERROR_MAP.contains_key(err) { + if let Some(msgbox_info) = LOGIN_ERROR_MAP.get(err) { + interface.msgbox( + msgbox_info.msgtype, + msgbox_info.title, + msgbox_info.text, + msgbox_info.link, + ); + msgbox_info.try_again + } else { + // unreachable! + false + } } else { if err.contains(SCRAP_X11_REQUIRED) { interface.msgbox("error", "Login Error", err, SCRAP_X11_REF_URL); diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index c1c38af40..ab8b76a7f 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1256,6 +1256,7 @@ impl Remote { }, Some(message::Union::MessageBox(msgbox)) => { let mut link = msgbox.link; + // Links from remote side must be verified. if !link.starts_with("rustdesk://") { if let Some(v) = hbb_common::config::HELPER_URL.get(&link as &str) { link = v.to_string(); diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 146160fca..3dc8cdb12 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 3f562ebd4..b8df31c77 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允许密码访问"), ("Accept sessions via click", "只允许点击访问"), ("Accept sessions via both", "允许密码或点击访问"), - ("Please wait for the remote side to accept your session request...", "请等待对方接受你的连接..."), ("One-time Password", "一次性密码"), ("Use one-time password", "使用一次性密码"), ("One-time password length", "一次性密码长度"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", "记住此账户"), ("os_account_desk_tip", "在无显示器的环境下,此账户用于登录被控系统,并启用桌面"), ("OS Account", "系统账户"), + ("session_not_ready_title_tip", "桌面 session 未准备好"), + ("session_not_ready_text_tip", "请输入用户名密码"), + ("xsession_failed_title_tip", "用户名密码错误"), + ("xsession_failed_text_tip", "是否重试"), + ("another_user_login_title_tip", "其他用户已登录"), + ("another_user_login_text_tip", "断开"), + ("xorg_not_found_title_tip", "Xorg 未安装"), + ("xorg_not_found_text_tip", "请安装 Xorg"), + ("no_desktop_title_tip", "desktop 未安装"), + ("no_desktop_text_tip", "请安装 desktop"), + ("session_unready_no_password_title_tip", "桌面 session 未准备好"), + ("session_unready_no_password_text_tip", "请输入用户名密码和连接密码"), + ("session_unready_wrong_password_title_tip", "密码错误"), + ("session_unready_wrong_password_text_tip", "是否重试"), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index aa40f1442..020bea621 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 2737336c0..754b9671c 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptér sessioner via adgangskode"), ("Accept sessions via click", "Acceptér sessioner via klik"), ("Accept sessions via both", "Acceptér sessioner via begge"), - ("Please wait for the remote side to accept your session request...", "Vent venligst på at fjernklienten accepterer din sessionsforespørgsel..."), ("One-time Password", "Engangskode"), ("Use one-time password", "Brug engangskode"), ("One-time password length", "Engangskode længde"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 6583c25e5..3d72a0242 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sitzung mit Passwort bestätigen"), ("Accept sessions via click", "Sitzung mit einem Klick bestätigen"), ("Accept sessions via both", "Sitzung mit Klick und Passwort bestätigen"), - ("Please wait for the remote side to accept your session request...", "Bitte warten Sie, bis die Gegenseite Ihre Sitzungsanfrage akzeptiert hat …"), ("One-time Password", "Einmalpasswort"), ("Use one-time password", "Einmalpasswort verwenden"), ("One-time password length", "Länge des Einmalpassworts"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index fa0a3f990..b10d9f971 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Αποδοχή συνεδριών με κωδικό πρόσβασης"), ("Accept sessions via click", "Αποδοχή συνεδριών με κλικ"), ("Accept sessions via both", "Αποδοχή συνεδριών και με τα δύο"), - ("Please wait for the remote side to accept your session request...", "Παρακαλώ περιμένετε μέχρι η απομακρυσμένη πλευρά να αποδεχτεί το αίτημα συνεδρίας σας..."), ("One-time Password", "Κωδικός μίας χρήσης"), ("Use one-time password", "Χρήση κωδικού πρόσβασης μίας χρήσης"), ("One-time password length", "Μήκος κωδικού πρόσβασης μίας χρήσης"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index fede80eff..6035db7ba 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -60,5 +60,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), + ("session_not_ready_title_tip", "Session is unready"), + ("session_not_ready_text_tip", "Input linux user/password"), + ("xsession_failed_title_tip", "Xsession username/password is wrong"), + ("xsession_failed_text_tip", "Do you want to enter again"), + ("another_user_login_title_tip", "Another user already login"), + ("another_user_login_text_tip", "Disconnect"), + ("xorg_not_found_title_tip", "Xorg not found"), + ("xorg_not_found_text_tip", "Please install Xorg"), + ("no_desktop_title_tip", "No desktop is avaliable"), + ("no_desktop_text_tip", "Please install GNOME desktop"), + ("session_unready_no_password_title_tip", "Session is unready"), + ("session_unready_no_password_text_tip", "Input connection password and linux user/password"), + ("session_unready_wrong_password_title_tip", "Password is wrong"), + ("session_unready_wrong_password_text_tip", "Do you want to enter again"), + ("no_password_access_text_tip", "Please wait for the remote side to accept your session request..."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 0d8815588..3f2a0d573 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index a9d71dea5..9f06fff59 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceptar sesiones a través de contraseña"), ("Accept sessions via click", "Aceptar sesiones a través de clic"), ("Accept sessions via both", "Aceptar sesiones a través de ambos"), - ("Please wait for the remote side to accept your session request...", "Por favor, espere a que el lado remoto acepte su solicitud de sesión"), ("One-time Password", "Constaseña de un solo uso"), ("Use one-time password", "Usar contraseña de un solo uso"), ("One-time password length", "Longitud de la contraseña de un solo uso"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index f73bf3fec..1fad5bba1 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "قبول درخواست با رمز عبور"), ("Accept sessions via click", "قبول درخواست با کلیک موس"), ("Accept sessions via both", "قبول درخواست با هر دو"), - ("Please wait for the remote side to accept your session request...", "...لطفا صبر کنید تا میزبان درخواست شما را قبول کند"), ("One-time Password", "رمز عبور یکبار مصرف"), ("Use one-time password", "استفاده از رمز عبور یکبار مصرف"), ("One-time password length", "طول رمز عبور یکبار مصرف"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 3279b9175..58a65b3f4 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accepter les sessions via mot de passe"), ("Accept sessions via click", "Accepter les sessions via clique de confirmation"), ("Accept sessions via both", "Accepter les sessions via mot de passe ou clique de confirmation"), - ("Please wait for the remote side to accept your session request...", "Veuillez attendre que votre demande de session distante soit accepter ..."), ("One-time Password", "Mot de passe unique"), ("Use one-time password", "Utiliser un mot de passe unique"), ("One-time password length", "Longueur du mot de passe unique"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index cfad983a2..5075802f1 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 3c0254f8d..4f87a926c 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Izinkan sesi dengan kata sandi"), ("Accept sessions via click", "Izinkan sesi dengan klik"), ("Accept sessions via both", "Izinkan sesi dengan keduanya"), - ("Please wait for the remote side to accept your session request...", "Harap tunggu sisi jarak jauh untuk menerima permintaan sesi Anda..."), ("One-time Password", "Kata sandi satu kali"), ("Use one-time password", "Gunakan kata sandi satu kali"), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index a09bfa97a..14a50ede9 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accetta sessioni via password"), ("Accept sessions via click", "Accetta sessioni via click"), ("Accept sessions via both", "Accetta sessioni con entrambi"), - ("Please wait for the remote side to accept your session request...", "Attendere che il lato remoto accetti la richiesta di sessione..."), ("One-time Password", "Password monouso"), ("Use one-time password", "Usa password monouso"), ("One-time password length", "Lunghezza password monouso"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index ae153feac..e91137d8c 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 843211c49..c7c654632 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index b8d665a60..5422083bb 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index caaf80077..b043b8f7f 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sessies accepteren via wachtwoord"), ("Accept sessions via click", "Sessies accepteren via klik"), ("Accept sessions via both", "Accepteer sessies via beide"), - ("Please wait for the remote side to accept your session request...", "Wacht tot de andere kant uw sessieverzoek accepteert..."), ("One-time Password", "Eenmalig Wachtwoord"), ("Use one-time password", "Gebruik een eenmalig Wachtwoord"), ("One-time password length", "Eenmalig Wachtwoord lengre"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index c1f2ef3dc..938a4e881 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Uwierzytelnij sesję używając hasła"), ("Accept sessions via click", "Uwierzytelnij sesję poprzez kliknięcie"), ("Accept sessions via both", "Uwierzytelnij sesję za pomocą obu sposobów"), - ("Please wait for the remote side to accept your session request...", "Oczekiwanie, na zatwierdzenie sesji przez host zdalny..."), ("One-time Password", "Hasło jednorazowe"), ("Use one-time password", "Użyj hasła jednorazowego"), ("One-time password length", "Długość hasła jednorazowego"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 880658ff4..ae4dbccba 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index fe7f4a36b..c9c0b84f7 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceitar sessões via senha"), ("Accept sessions via click", "Aceitar sessões via clique"), ("Accept sessions via both", "Aceitar sessões de ambos os modos"), - ("Please wait for the remote side to accept your session request...", "Por favor aguarde enquanto o cliente remoto aceita seu pedido de sessão..."), ("One-time Password", "Senha de uso único"), ("Use one-time password", "Usar senha de uso único"), ("One-time password length", "Comprimento da senha de uso único"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 046774f32..d6632d18a 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptă sesiunile folosind parola"), ("Accept sessions via click", "Acceptă sesiunile cu un clic de confirmare"), ("Accept sessions via both", "Acceptă sesiunile folosind ambele moduri"), - ("Please wait for the remote side to accept your session request...", "Așteaptă ca solicitarea ta de conectare la distanță să fie acceptată..."), ("One-time Password", "Parolă unică"), ("Use one-time password", "Folosește parola unică"), ("One-time password length", "Lungimea parolei unice"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index ac87dcb48..a99523fca 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Принимать сеансы по паролю"), ("Accept sessions via click", "Принимать сеансы по нажатию"), ("Accept sessions via both", "Принимать сеансы по паролю+нажатию"), - ("Please wait for the remote side to accept your session request...", "Подождите, пока удалённая сторона примет ваш запрос на сеанс..."), ("One-time Password", "Одноразовый пароль"), ("Use one-time password", "Использовать одноразовый пароль"), ("One-time password length", "Длина одноразового пароля"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 1a156d8a6..7a8b9a778 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 1f07b027c..581507457 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sprejmi seje z geslom"), ("Accept sessions via click", "Sprejmi seje s potrditvijo"), ("Accept sessions via both", "Sprejmi seje z geslom ali potrditvijo"), - ("Please wait for the remote side to accept your session request...", "Počakajte, da oddaljeni računalnik sprejme povezavo..."), ("One-time Password", "Enkratno geslo"), ("Use one-time password", "Uporabi enkratno geslo"), ("One-time password length", "Dolžina enkratnega gesla"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 558984a1b..eb1b50762 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prano sesionin nëpërmjet fjalëkalimit"), ("Accept sessions via click", "Prano sesionet nëpërmjet klikimit"), ("Accept sessions via both", "Prano sesionet nëpërmjet të dyjave"), - ("Please wait for the remote side to accept your session request...", "Ju lutem prisni që ana në distancë të pranoj kërkësen tuaj"), ("One-time Password", "Fjalëkalim Një-herë"), ("Use one-time password", "Përdorni fjalëkalim Një-herë"), ("One-time password length", "Gjatësia e fjalëkalimit një herë"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 3ec1d32d1..260eadc24 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prihvati sesije preko lozinke"), ("Accept sessions via click", "Prihvati sesije preko klika"), ("Accept sessions via both", "Prihvati sesije preko oboje"), - ("Please wait for the remote side to accept your session request...", "Molimo sačekajte da udaljena strana prihvati vaš zahtev za sesijom..."), ("One-time Password", "Jednokratna lozinka"), ("Use one-time password", "Koristi jednokratnu lozinku"), ("One-time password length", "Dužina jednokratne lozinke"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 93bbaf74e..9db56dcc9 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptera sessioner via lösenord"), ("Accept sessions via click", "Acceptera sessioner via klick"), ("Accept sessions via both", "Acceptera sessioner via båda"), - ("Please wait for the remote side to accept your session request...", "Var god vänta på att klienten accepterar din förfrågan..."), ("One-time Password", "En-gångs lösenord"), ("Use one-time password", "Använd en-gångs lösenord"), ("One-time password length", "Längd på en-gångs lösenord"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index db1519e9d..ac4413201 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 1b41ff694..11260f440 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"), ("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"), ("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"), - ("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."), ("One-time Password", "รหัสผ่านครั้งเดียว"), ("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"), ("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 0aa49a3e4..b76484b33 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Oturumları parola ile kabul etme"), ("Accept sessions via click", "Tıklama yoluyla oturumları kabul edin"), ("Accept sessions via both", "Her ikisi aracılığıyla oturumları kabul edin"), - ("Please wait for the remote side to accept your session request...", "Lütfen uzak tarafın oturum isteğinizi kabul etmesini bekleyin..."), ("One-time Password", "Tek Kullanımlık Şifre"), ("Use one-time password", "Tek seferlik parola kullanın"), ("One-time password length", "Tek seferlik şifre uzunluğu"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 0077fdaad..f77014e8f 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允許透過輸入密碼進行連線"), ("Accept sessions via click", "只允許透過點擊接受進行連線"), ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), - ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), ("One-time Password", "一次性密碼"), ("Use one-time password", "使用一次性密碼"), ("One-time password length", "一次性密碼長度"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 811bcd913..873dad281 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Підтверджувати сеанси паролем"), ("Accept sessions via click", "Підтверджувати сеанси натисканням"), ("Accept sessions via both", "Підтверджувати сеанси обома способами"), - ("Please wait for the remote side to accept your session request...", "Буль ласка, зачекайте, поки віддалена сторона підтвердить запит на сеанс..."), ("One-time Password", "Одноразовий пароль"), ("Use one-time password", "Використати одноразовий пароль"), ("One-time password length", "Довжина одноразового пароля"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 6ad03f0f8..7756b8894 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 3a72f8976..e9dc00ef6 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -1,4 +1,9 @@ use super::{linux::*, ResultType}; +use crate::server::{ + LOGIN_MSG_DESKTOP_NO_DESKTOP, LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, + LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, + LOGIN_MSG_DESKTOP_XSESSION_FAILED, +}; use hbb_common::{allow_err, bail, log, rand::prelude::*, tokio::time}; use pam; use std::{ @@ -59,7 +64,65 @@ pub fn stop_xdesktop() { *DESKTOP_MANAGER.lock().unwrap() = None; } -pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { +pub fn try_start_desktop(_username: &str, _passsword: &str) -> String { + if _username.is_empty() { + let username = get_username(); + if username.is_empty() { + LOGIN_MSG_DESKTOP_SESSION_NOT_READY + } else { + "" + } + .to_owned() + } else { + let username = get_username(); + if username == _username { + // No need to verify password here. + return "".to_owned(); + } + + match run_cmds(&format!("which {}", DesktopManager::get_xorg())) { + Ok(output) => { + if output.trim().is_empty() { + return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); + } + } + _ => { + return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); + } + } + + match run_cmds("ls /usr/share/xsessions/") { + Ok(output) => { + if output.trim().is_empty() { + return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); + } + } + _ => { + return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); + } + } + + match try_start_x_session(_username, _passsword) { + Ok((username, x11_ready)) => { + if x11_ready { + if _username != username { + LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER.to_owned() + } else { + "".to_owned() + } + } else { + LOGIN_MSG_DESKTOP_SESSION_NOT_READY.to_owned() + } + } + Err(e) => { + log::error!("Failed to start xsession {}", e); + LOGIN_MSG_DESKTOP_XSESSION_FAILED.to_owned() + } + } + } +} + +fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); if let Some(desktop_manager) = &mut (*desktop_manager) { if let Some(seat0_username) = desktop_manager.get_supported_display_seat0_username() { @@ -547,36 +610,36 @@ impl DesktopManager { } } - fn get_xorg() -> ResultType<&'static str> { + fn get_xorg() -> &'static str { // Fedora 26 or later let xorg = "/usr/libexec/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Debian 9 or later let xorg = "/usr/lib/xorg/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Ubuntu 16.04 or later let xorg = "/usr/lib/xorg/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Arch Linux let xorg = "/usr/lib/xorg-server/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Arch Linux let xorg = "/usr/lib/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // CentOS 7 /usr/bin/Xorg or param=Xorg log::warn!("Failed to find xorg, use default Xorg.\n Please add \"allowed_users=anybody\" to \"/etc/X11/Xwrapper.config\"."); - Ok("Xorg") + "Xorg" } fn start_x_server( @@ -586,7 +649,7 @@ impl DesktopManager { gid: u32, envs: &HashMap<&str, String>, ) -> ResultType { - let xorg = Self::get_xorg()?; + let xorg = Self::get_xorg(); log::info!("Use xorg: {}", &xorg); match Command::new(xorg) .envs(envs) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 7c9b2f0b0..fc27be098 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -17,7 +17,7 @@ pub mod delegate; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", feature = "linux_headless"))] pub mod linux_desktop_manager; #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 3fef9747b..cc93dbb45 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -72,7 +72,7 @@ impl RendezvousMediator { allow_err!(super::lan::start_listening()); }); } - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] crate::platform::linux_desktop_manager::start_xdesktop(); SHOULD_EXIT.store(false, Ordering::SeqCst); while !SHOULD_EXIT.load(Ordering::SeqCst) { @@ -99,7 +99,7 @@ impl RendezvousMediator { } sleep(1.).await; } - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] crate::platform::linux_desktop_manager::stop_xdesktop(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 65a3fd025..b442ff899 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -3,7 +3,7 @@ use super::{input_service::*, *}; use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::update_clipboard; -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", feature = "linux_headless"))] use crate::platform::linux_desktop_manager; #[cfg(windows)] use crate::portable_service::client as portable_client; @@ -18,7 +18,7 @@ use crate::{ use crate::{common::DEVICE_NAME, flutter::connection_manager::start_channel}; use crate::{ipc, VERSION}; use cidr_utils::cidr::IpCidr; -#[cfg(all(target_os = "linux", feature = "flutter"))] +#[cfg(all(target_os = "linux", feature = "linux_headless"))] use hbb_common::platform::linux::run_cmds; use hbb_common::{ config::Config, @@ -65,12 +65,16 @@ pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session unready"; pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; +pub const LOGIN_MSG_DESKTOP_XORG_NOT_FOUND: &str = "Desktop xorg not found"; +// ls /usr/share/xsessions/ +pub const LOGIN_MSG_DESKTOP_NO_DESKTOP: &str = "Desktop none"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = "Desktop session unready, password empty"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = "Desktop session unready, password wrong"; pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; +pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access"; pub const LOGIN_MSG_OFFLINE: &str = "Offline"; #[derive(Clone, Default)] @@ -150,9 +154,9 @@ pub struct Connection { audio_input_device_before_voice_call: Option, options_in_login: Option, pressed_modifiers: HashSet, - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: mpsc::Receiver<()>, - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] tx_desktop_ready: mpsc::Sender<()>, } @@ -270,9 +274,9 @@ impl Connection { audio_input_device_before_voice_call: None, options_in_login: None, pressed_modifiers: Default::default(), - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: _rx_cm_stream_ready, - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] tx_desktop_ready: _tx_desktop_ready, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -886,6 +890,7 @@ impl Connection { if crate::platform::current_is_wayland() { platform_additions.insert("is_wayland".into(), json!(true)); } + #[cfg(feature = "linux_headless")] if linux_desktop_manager::is_headless() { platform_additions.insert("headless".into(), json!(true)); } @@ -1096,45 +1101,6 @@ impl Connection { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } - fn try_start_desktop(_username: &str, _passsword: &str) -> String { - #[cfg(target_os = "linux")] - if _username.is_empty() { - let username = linux_desktop_manager::get_username(); - if username.is_empty() { - LOGIN_MSG_DESKTOP_SESSION_NOT_READY - } else { - "" - } - .to_owned() - } else { - let username = linux_desktop_manager::get_username(); - if username == _username { - // No need to verify password here. - return "".to_owned(); - } - - match linux_desktop_manager::try_start_x_session(_username, _passsword) { - Ok((username, x11_ready)) => { - if x11_ready { - if _username != username { - LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER.to_owned() - } else { - "".to_owned() - } - } else { - LOGIN_MSG_DESKTOP_SESSION_NOT_READY.to_owned() - } - } - Err(e) => { - log::error!("Failed to start xsession {}", e); - LOGIN_MSG_DESKTOP_XSESSION_FAILED.to_owned() - } - } - } - #[cfg(not(target_os = "linux"))] - "".to_owned() - } - fn validate_one_password(&self, password: String) -> bool { if password.len() == 0 { return false; @@ -1300,16 +1266,20 @@ impl Connection { _ => {} } + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let desktop_err = match lr.os_login.as_ref() { - Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), - None => Self::try_start_desktop("", ""), + Some(os_login) => { + linux_desktop_manager::try_start_desktop(&os_login.username, &os_login.password) + } + None => linux_desktop_manager::try_start_desktop("", ""), }; - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let is_headless = linux_desktop_manager::is_headless(); - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let wait_ipc_timeout = 10_000; // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; @@ -1324,7 +1294,7 @@ impl Connection { if hbb_common::get_version_number(&lr.version) >= hbb_common::get_version_number("1.2.0") { - self.send_login_error("No Password Access").await; + self.send_login_error(LOGIN_MSG_NO_PASSWORD_ACCESS).await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1333,6 +1303,7 @@ impl Connection { self.send_login_error("Connection not allowed").await; return false; } else if self.is_recent_session() { + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { #[cfg(target_os = "linux")] if is_headless { @@ -1347,13 +1318,24 @@ impl Connection { } else { self.send_login_error(desktop_err).await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + { + self.try_start_cm(lr.my_id, lr.my_name, true); + self.send_logon_response().await; + if self.port_forward_socket.is_some() { + return false; + } + } } else if lr.password.is_empty() { + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY) .await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + self.try_start_cm(lr.my_id, lr.my_name, false); } else { let mut failure = LOGIN_FAILURES .lock() @@ -1394,6 +1376,7 @@ impl Connection { .lock() .unwrap() .insert(self.ip.clone(), failure); + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; self.try_start_cm(lr.my_id, lr.my_name, false); @@ -1401,10 +1384,16 @@ impl Connection { self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG) .await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + { + self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.try_start_cm(lr.my_id, lr.my_name, false); + } } else { if failure.0 != 0 { LOGIN_FAILURES.lock().unwrap().remove(&self.ip); } + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { #[cfg(target_os = "linux")] if is_headless { @@ -1420,6 +1409,14 @@ impl Connection { } else { self.send_login_error(desktop_err).await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + { + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; + } + } } } } else if let Some(message::Union::TestDelay(t)) = msg.union { @@ -2207,11 +2204,13 @@ async fn start_ipc( args.push("--hide"); }; - #[cfg(all(target_os = "linux", not(feature = "flutter")))] + #[cfg(target_os = "linux")] + #[cfg(not(feature = "linux_headless"))] let user = None; - #[cfg(all(target_os = "linux", feature = "flutter"))] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let mut user = None; - #[cfg(all(target_os = "linux", feature = "flutter"))] + // Cm run as user, wait until desktop session is ready. + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if linux_desktop_manager::is_headless() { let mut username = linux_desktop_manager::get_username(); loop { From 5eedbdb436b719656fcf885e4d74ecd945cc2965 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 22:24:42 +0800 Subject: [PATCH 067/112] tmp commit Signed-off-by: fufesou --- src/lang/fr.rs | 22 +++++++++++++++++++--- src/server/connection.rs | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 58a65b3f4..96ac0d7bd 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -479,9 +479,25 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Ce fichier est identique à celui du pair."), ("show_monitors_tip", "Afficher les moniteurs dans la barre d'outils."), ("View Mode", "Mode vue"), - ("enter_rustdesk_passwd_tip", "Saisissez le mot de passe RustDesk."), - ("remember_rustdesk_passwd_tip", "Se rappeler du mot de passe RustDesk."), ("login_linux_tip", "Se connecter au compte Linux distant"), - ("login_linux_tooltip_tip", "Vous devez vous connecter à un compte Linux distant pour activer une session de bureau X."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index b442ff899..297ab18c7 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -942,7 +942,7 @@ impl Connection { } #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.file_transfer.is_some() { - if !crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { + if crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { username = "".to_owned(); } } @@ -2190,7 +2190,7 @@ async fn start_ipc( tx_stream_ready: mpsc::Sender<()>, ) -> ResultType<()> { loop { - if crate::platform::is_prelogin() { + if !crate::platform::is_prelogin() { break; } sleep(1.).await; From 86313fa32855121b497478b29c8448e4e07d2f8b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 22:47:57 +0800 Subject: [PATCH 068/112] revert lang Signed-off-by: fufesou --- src/client.rs | 2 +- src/lang/ca.rs | 2 +- src/lang/cn.rs | 2 +- src/lang/cs.rs | 2 +- src/lang/da.rs | 2 +- src/lang/de.rs | 2 +- src/lang/el.rs | 2 +- src/lang/en.rs | 1 - src/lang/eo.rs | 2 +- src/lang/es.rs | 2 +- src/lang/fa.rs | 2 +- src/lang/fr.rs | 2 +- src/lang/hu.rs | 2 +- src/lang/id.rs | 2 +- src/lang/it.rs | 2 +- src/lang/ja.rs | 2 +- src/lang/ko.rs | 2 +- src/lang/kz.rs | 2 +- src/lang/nl.rs | 2 +- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 2 +- src/lang/ptbr.rs | 2 +- src/lang/ro.rs | 2 +- src/lang/ru.rs | 2 +- src/lang/sk.rs | 2 +- src/lang/sl.rs | 2 +- src/lang/sq.rs | 2 +- src/lang/sr.rs | 2 +- src/lang/sv.rs | 2 +- src/lang/template.rs | 2 +- src/lang/th.rs | 2 +- src/lang/tr.rs | 2 +- src/lang/tw.rs | 2 +- src/lang/ua.rs | 2 +- src/lang/vn.rs | 2 +- 35 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3f587dfe0..a541a8458 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1969,7 +1969,7 @@ lazy_static::lazy_static! { }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ msgtype: "wait-remote-accept-nook", title: "Prompt", - text: "no_password_access_text_tip", + text: "Please wait for the remote side to accept your session request...", link: "", try_again: true, })]); diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 3dc8cdb12..230a9730b 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index b8df31c77..7f0e40581 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允许密码访问"), ("Accept sessions via click", "只允许点击访问"), ("Accept sessions via both", "允许密码或点击访问"), + ("Please wait for the remote side to accept your session request...", "请等待对方接受你的连接..."), ("One-time Password", "一次性密码"), ("Use one-time password", "使用一次性密码"), ("One-time password length", "一次性密码长度"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", "请输入用户名密码和连接密码"), ("session_unready_wrong_password_title_tip", "密码错误"), ("session_unready_wrong_password_text_tip", "是否重试"), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 020bea621..7237b7a79 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 754b9671c..f761832da 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptér sessioner via adgangskode"), ("Accept sessions via click", "Acceptér sessioner via klik"), ("Accept sessions via both", "Acceptér sessioner via begge"), + ("Please wait for the remote side to accept your session request...", "Vent venligst på at fjernklienten accepterer din sessionsforespørgsel..."), ("One-time Password", "Engangskode"), ("Use one-time password", "Brug engangskode"), ("One-time password length", "Engangskode længde"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 3d72a0242..e77ab938c 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sitzung mit Passwort bestätigen"), ("Accept sessions via click", "Sitzung mit einem Klick bestätigen"), ("Accept sessions via both", "Sitzung mit Klick und Passwort bestätigen"), + ("Please wait for the remote side to accept your session request...", "Bitte warten Sie, bis die Gegenseite Ihre Sitzungsanfrage akzeptiert hat …"), ("One-time Password", "Einmalpasswort"), ("Use one-time password", "Einmalpasswort verwenden"), ("One-time password length", "Länge des Einmalpassworts"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index b10d9f971..3703aad27 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Αποδοχή συνεδριών με κωδικό πρόσβασης"), ("Accept sessions via click", "Αποδοχή συνεδριών με κλικ"), ("Accept sessions via both", "Αποδοχή συνεδριών και με τα δύο"), + ("Please wait for the remote side to accept your session request...", "Παρακαλώ περιμένετε μέχρι η απομακρυσμένη πλευρά να αποδεχτεί το αίτημα συνεδρίας σας..."), ("One-time Password", "Κωδικός μίας χρήσης"), ("Use one-time password", "Χρήση κωδικού πρόσβασης μίας χρήσης"), ("One-time password length", "Μήκος κωδικού πρόσβασης μίας χρήσης"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 6035db7ba..e6499ea1c 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -74,6 +74,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", "Input connection password and linux user/password"), ("session_unready_wrong_password_title_tip", "Password is wrong"), ("session_unready_wrong_password_text_tip", "Do you want to enter again"), - ("no_password_access_text_tip", "Please wait for the remote side to accept your session request..."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 3f2a0d573..645c8be7c 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 9f06fff59..7a672dfec 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceptar sesiones a través de contraseña"), ("Accept sessions via click", "Aceptar sesiones a través de clic"), ("Accept sessions via both", "Aceptar sesiones a través de ambos"), + ("Please wait for the remote side to accept your session request...", "Por favor, espere a que el lado remoto acepte su solicitud de sesión"), ("One-time Password", "Constaseña de un solo uso"), ("Use one-time password", "Usar contraseña de un solo uso"), ("One-time password length", "Longitud de la contraseña de un solo uso"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 1fad5bba1..281478bb0 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "قبول درخواست با رمز عبور"), ("Accept sessions via click", "قبول درخواست با کلیک موس"), ("Accept sessions via both", "قبول درخواست با هر دو"), + ("Please wait for the remote side to accept your session request...", "...لطفا صبر کنید تا میزبان درخواست شما را قبول کند"), ("One-time Password", "رمز عبور یکبار مصرف"), ("Use one-time password", "استفاده از رمز عبور یکبار مصرف"), ("One-time password length", "طول رمز عبور یکبار مصرف"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 96ac0d7bd..1c4126377 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accepter les sessions via mot de passe"), ("Accept sessions via click", "Accepter les sessions via clique de confirmation"), ("Accept sessions via both", "Accepter les sessions via mot de passe ou clique de confirmation"), + ("Please wait for the remote side to accept your session request...", "Veuillez attendre que votre demande de session distante soit accepter ..."), ("One-time Password", "Mot de passe unique"), ("Use one-time password", "Utiliser un mot de passe unique"), ("One-time password length", "Longueur du mot de passe unique"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 5075802f1..547107607 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 4f87a926c..c896db6ba 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Izinkan sesi dengan kata sandi"), ("Accept sessions via click", "Izinkan sesi dengan klik"), ("Accept sessions via both", "Izinkan sesi dengan keduanya"), + ("Please wait for the remote side to accept your session request...", "Harap tunggu sisi jarak jauh untuk menerima permintaan sesi Anda..."), ("One-time Password", "Kata sandi satu kali"), ("Use one-time password", "Gunakan kata sandi satu kali"), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 14a50ede9..0d1b6914e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accetta sessioni via password"), ("Accept sessions via click", "Accetta sessioni via click"), ("Accept sessions via both", "Accetta sessioni con entrambi"), + ("Please wait for the remote side to accept your session request...", "Attendere che il lato remoto accetti la richiesta di sessione..."), ("One-time Password", "Password monouso"), ("Use one-time password", "Usa password monouso"), ("One-time password length", "Lunghezza password monouso"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index e91137d8c..b7cfaf40f 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index c7c654632..849fa7ffb 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 5422083bb..b7e242eaa 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index b043b8f7f..6cd679953 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sessies accepteren via wachtwoord"), ("Accept sessions via click", "Sessies accepteren via klik"), ("Accept sessions via both", "Accepteer sessies via beide"), + ("Please wait for the remote side to accept your session request...", "Wacht tot de andere kant uw sessieverzoek accepteert..."), ("One-time Password", "Eenmalig Wachtwoord"), ("Use one-time password", "Gebruik een eenmalig Wachtwoord"), ("One-time password length", "Eenmalig Wachtwoord lengre"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 938a4e881..529289751 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Uwierzytelnij sesję używając hasła"), ("Accept sessions via click", "Uwierzytelnij sesję poprzez kliknięcie"), ("Accept sessions via both", "Uwierzytelnij sesję za pomocą obu sposobów"), + ("Please wait for the remote side to accept your session request...", "Oczekiwanie, na zatwierdzenie sesji przez host zdalny..."), ("One-time Password", "Hasło jednorazowe"), ("Use one-time password", "Użyj hasła jednorazowego"), ("One-time password length", "Długość hasła jednorazowego"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index ae4dbccba..0fcbd360c 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index c9c0b84f7..491b48a44 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceitar sessões via senha"), ("Accept sessions via click", "Aceitar sessões via clique"), ("Accept sessions via both", "Aceitar sessões de ambos os modos"), + ("Please wait for the remote side to accept your session request...", "Por favor aguarde enquanto o cliente remoto aceita seu pedido de sessão..."), ("One-time Password", "Senha de uso único"), ("Use one-time password", "Usar senha de uso único"), ("One-time password length", "Comprimento da senha de uso único"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index d6632d18a..60d293e36 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptă sesiunile folosind parola"), ("Accept sessions via click", "Acceptă sesiunile cu un clic de confirmare"), ("Accept sessions via both", "Acceptă sesiunile folosind ambele moduri"), + ("Please wait for the remote side to accept your session request...", "Așteaptă ca solicitarea ta de conectare la distanță să fie acceptată..."), ("One-time Password", "Parolă unică"), ("Use one-time password", "Folosește parola unică"), ("One-time password length", "Lungimea parolei unice"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index a99523fca..90be51f01 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Принимать сеансы по паролю"), ("Accept sessions via click", "Принимать сеансы по нажатию"), ("Accept sessions via both", "Принимать сеансы по паролю+нажатию"), + ("Please wait for the remote side to accept your session request...", "Подождите, пока удалённая сторона примет ваш запрос на сеанс..."), ("One-time Password", "Одноразовый пароль"), ("Use one-time password", "Использовать одноразовый пароль"), ("One-time password length", "Длина одноразового пароля"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 7a8b9a778..aec122f70 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 581507457..18bd8f44c 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sprejmi seje z geslom"), ("Accept sessions via click", "Sprejmi seje s potrditvijo"), ("Accept sessions via both", "Sprejmi seje z geslom ali potrditvijo"), + ("Please wait for the remote side to accept your session request...", "Počakajte, da oddaljeni računalnik sprejme povezavo..."), ("One-time Password", "Enkratno geslo"), ("Use one-time password", "Uporabi enkratno geslo"), ("One-time password length", "Dolžina enkratnega gesla"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index eb1b50762..746eed96d 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prano sesionin nëpërmjet fjalëkalimit"), ("Accept sessions via click", "Prano sesionet nëpërmjet klikimit"), ("Accept sessions via both", "Prano sesionet nëpërmjet të dyjave"), + ("Please wait for the remote side to accept your session request...", "Ju lutem prisni që ana në distancë të pranoj kërkësen tuaj"), ("One-time Password", "Fjalëkalim Një-herë"), ("Use one-time password", "Përdorni fjalëkalim Një-herë"), ("One-time password length", "Gjatësia e fjalëkalimit një herë"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 260eadc24..0a94fe103 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prihvati sesije preko lozinke"), ("Accept sessions via click", "Prihvati sesije preko klika"), ("Accept sessions via both", "Prihvati sesije preko oboje"), + ("Please wait for the remote side to accept your session request...", "Molimo sačekajte da udaljena strana prihvati vaš zahtev za sesijom..."), ("One-time Password", "Jednokratna lozinka"), ("Use one-time password", "Koristi jednokratnu lozinku"), ("One-time password length", "Dužina jednokratne lozinke"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 9db56dcc9..25d3144d8 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptera sessioner via lösenord"), ("Accept sessions via click", "Acceptera sessioner via klick"), ("Accept sessions via both", "Acceptera sessioner via båda"), + ("Please wait for the remote side to accept your session request...", "Var god vänta på att klienten accepterar din förfrågan..."), ("One-time Password", "En-gångs lösenord"), ("Use one-time password", "Använd en-gångs lösenord"), ("One-time password length", "Längd på en-gångs lösenord"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index ac4413201..1b4b6ff14 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 11260f440..2fe64b100 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"), ("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"), ("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"), + ("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."), ("One-time Password", "รหัสผ่านครั้งเดียว"), ("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"), ("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index b76484b33..3a925942b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Oturumları parola ile kabul etme"), ("Accept sessions via click", "Tıklama yoluyla oturumları kabul edin"), ("Accept sessions via both", "Her ikisi aracılığıyla oturumları kabul edin"), + ("Please wait for the remote side to accept your session request...", "Lütfen uzak tarafın oturum isteğinizi kabul etmesini bekleyin..."), ("One-time Password", "Tek Kullanımlık Şifre"), ("Use one-time password", "Tek seferlik parola kullanın"), ("One-time password length", "Tek seferlik şifre uzunluğu"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index f77014e8f..86e0dfb98 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允許透過輸入密碼進行連線"), ("Accept sessions via click", "只允許透過點擊接受進行連線"), ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), + ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), ("One-time Password", "一次性密碼"), ("Use one-time password", "使用一次性密碼"), ("One-time password length", "一次性密碼長度"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 873dad281..36b27089c 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Підтверджувати сеанси паролем"), ("Accept sessions via click", "Підтверджувати сеанси натисканням"), ("Accept sessions via both", "Підтверджувати сеанси обома способами"), + ("Please wait for the remote side to accept your session request...", "Буль ласка, зачекайте, поки віддалена сторона підтвердить запит на сеанс..."), ("One-time Password", "Одноразовий пароль"), ("Use one-time password", "Використати одноразовий пароль"), ("One-time password length", "Довжина одноразового пароля"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 7756b8894..35c40a794 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } From b207468795f2c7164e0573045519012ce5473240 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 23:02:37 +0800 Subject: [PATCH 069/112] tmp commit Signed-off-by: fufesou --- src/rendezvous_mediator.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index cc93dbb45..6dd0a1284 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -74,8 +74,7 @@ impl RendezvousMediator { } #[cfg(all(target_os = "linux", feature = "linux_headless"))] crate::platform::linux_desktop_manager::start_xdesktop(); - SHOULD_EXIT.store(false, Ordering::SeqCst); - while !SHOULD_EXIT.load(Ordering::SeqCst) { + loop { Config::reset_online(); if Config::get_option("stop-service").is_empty() { if !nat_tested { @@ -99,8 +98,10 @@ impl RendezvousMediator { } sleep(1.).await; } - #[cfg(all(target_os = "linux", feature = "linux_headless"))] - crate::platform::linux_desktop_manager::stop_xdesktop(); + // It should be better to call stop_xdesktop. + // But for server, it also is Ok without calling this method. + // #[cfg(all(target_os = "linux", feature = "linux_headless"))] + // crate::platform::linux_desktop_manager::stop_xdesktop(); } pub async fn start(server: ServerPtr, host: String) -> ResultType<()> { From 51b77100a7b6157c19ab2d0f8f7cad153270ae39 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 10:10:06 +0800 Subject: [PATCH 070/112] add libpam0g-dev Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 97a4073af..198e6d03e 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -402,7 +402,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless + sudo apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libpam0g-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless - name: Checkout source code uses: actions/checkout@v3 - name: Install flutter @@ -654,7 +654,7 @@ jobs: install: | apt update -y echo -e "installing deps" - apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libpam0g-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null # we have libopus compiled by us. apt remove -y libopus-dev || true # output devs @@ -816,7 +816,7 @@ jobs: install: | apt update -y echo -e "installing deps" - apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + apt-get -qq install -y 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 libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libpam0g-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null # we have libopus compiled by us. apt remove -y libopus-dev || true # output devs @@ -947,7 +947,7 @@ jobs: apt update -y apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev - apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev + apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev libpam0g-dev run: | # disable git safe.directory git config --global --add safe.directory "*" From a5158d2bb75866c95375ee003a1e93883de0795b Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 10:13:25 +0800 Subject: [PATCH 071/112] build linux headless Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 198e6d03e..bf73bdaa1 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -687,7 +687,7 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features hwcodec,flutter,flutter_texture_render,linux_headless,${{ matrix.job.extra-build-features }} --release ;; esac From a0c899bd6749c2e2c5c410d5ff0c21ad10fe4b64 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 13:14:20 +0800 Subject: [PATCH 072/112] debug fedora Signed-off-by: fufesou --- src/client.rs | 4 +- src/platform/linux_desktop_manager.rs | 54 ++++++++++++++++----------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/client.rs b/src/client.rs index a541a8458..72bdac531 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1943,13 +1943,13 @@ lazy_static::lazy_static! { link: "", try_again: false, }), (crate::server::LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ - msgtype: "session-re-login", + msgtype: "info-nocancel-nocancel", title: "xorg_not_found_title_tip", text: "xorg_not_found_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ - msgtype: "session-re-login", + msgtype: "info-nocancel-nocancel", title: "no_desktop_title_tip", text: "no_desktop_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index e9dc00ef6..25fada503 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -64,11 +64,41 @@ pub fn stop_xdesktop() { *DESKTOP_MANAGER.lock().unwrap() = None; } +fn detect_headless() -> Option<&'static str> { + match run_cmds(&format!("which {}", DesktopManager::get_xorg())) { + Ok(output) => { + if output.trim().is_empty() { + return Some(LOGIN_MSG_DESKTOP_XORG_NOT_FOUND); + } + } + _ => { + return Some(LOGIN_MSG_DESKTOP_XORG_NOT_FOUND); + } + } + + match run_cmds("ls /usr/share/xsessions/") { + Ok(output) => { + if output.trim().is_empty() { + return Some(LOGIN_MSG_DESKTOP_NO_DESKTOP); + } + } + _ => { + return Some(LOGIN_MSG_DESKTOP_NO_DESKTOP); + } + } + + None +} + pub fn try_start_desktop(_username: &str, _passsword: &str) -> String { if _username.is_empty() { let username = get_username(); if username.is_empty() { - LOGIN_MSG_DESKTOP_SESSION_NOT_READY + if let Some(msg) = detect_headless() { + msg + } else { + LOGIN_MSG_DESKTOP_SESSION_NOT_READY + } } else { "" } @@ -80,26 +110,8 @@ pub fn try_start_desktop(_username: &str, _passsword: &str) -> String { return "".to_owned(); } - match run_cmds(&format!("which {}", DesktopManager::get_xorg())) { - Ok(output) => { - if output.trim().is_empty() { - return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); - } - } - _ => { - return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); - } - } - - match run_cmds("ls /usr/share/xsessions/") { - Ok(output) => { - if output.trim().is_empty() { - return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); - } - } - _ => { - return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); - } + if let Some(msg) = detect_headless() { + return msg.to_owned(); } match try_start_x_session(_username, _passsword) { From 96a62f188dd9bb4fe1895941e6cec32988c385bd Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 13:36:47 +0800 Subject: [PATCH 073/112] trivial changes Signed-off-by: fufesou --- .github/workflows/flutter-nightly.yml | 1 - src/client/io_loop.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 5aa82b847..c812f43fa 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -11,4 +11,3 @@ jobs: uses: ./.github/workflows/flutter-build.yml with: upload-artifact: true - diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index ab8b76a7f..509b5bd24 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1256,7 +1256,7 @@ impl Remote { }, Some(message::Union::MessageBox(msgbox)) => { let mut link = msgbox.link; - // Links from remote side must be verified. + // Links from the remote side must be verified. if !link.starts_with("rustdesk://") { if let Some(v) = hbb_common::config::HELPER_URL.get(&link as &str) { link = v.to_string(); From 67ddb49aab6f97feea436df528c2e678f6912819 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 14:24:17 +0800 Subject: [PATCH 074/112] remove unused lang Signed-off-by: fufesou --- src/client.rs | 18 +++++++++--------- src/lang/ca.rs | 8 -------- src/lang/cn.rs | 8 -------- src/lang/cs.rs | 8 -------- src/lang/da.rs | 8 -------- src/lang/de.rs | 8 -------- src/lang/el.rs | 8 -------- src/lang/en.rs | 12 ++---------- src/lang/eo.rs | 8 -------- src/lang/es.rs | 8 -------- src/lang/fa.rs | 8 -------- src/lang/fr.rs | 8 -------- src/lang/hu.rs | 8 -------- src/lang/id.rs | 8 -------- src/lang/it.rs | 8 -------- src/lang/ja.rs | 8 -------- src/lang/ko.rs | 8 -------- src/lang/kz.rs | 8 -------- src/lang/nl.rs | 8 -------- src/lang/pl.rs | 8 -------- src/lang/pt_PT.rs | 8 -------- src/lang/ptbr.rs | 8 -------- src/lang/ro.rs | 8 -------- src/lang/ru.rs | 8 -------- src/lang/sk.rs | 8 -------- src/lang/sl.rs | 8 -------- src/lang/sq.rs | 8 -------- src/lang/sr.rs | 8 -------- src/lang/sv.rs | 8 -------- src/lang/template.rs | 8 -------- src/lang/th.rs | 8 -------- src/lang/tr.rs | 8 -------- src/lang/tw.rs | 8 -------- src/lang/ua.rs | 8 -------- src/lang/vn.rs | 8 -------- src/server/connection.rs | 6 +++--- 36 files changed, 14 insertions(+), 286 deletions(-) diff --git a/src/client.rs b/src/client.rs index 72bdac531..3bd00db47 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1926,8 +1926,8 @@ lazy_static::lazy_static! { use hbb_common::config::LINK_HEADLESS_LINUX_SUPPORT; let map = HashMap::from([(crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{ msgtype: "session-login", - title: "session_not_ready_title_tip", - text: "session_not_ready_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ @@ -1937,33 +1937,33 @@ lazy_static::lazy_static! { link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ - msgtype: "info", + msgtype: "info-nocancel", title: "another_user_login_title_tip", text: "another_user_login_text_tip", link: "", try_again: false, }), (crate::server::LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ - msgtype: "info-nocancel-nocancel", + msgtype: "info-nocancel", title: "xorg_not_found_title_tip", text: "xorg_not_found_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ - msgtype: "info-nocancel-nocancel", + msgtype: "info-nocancel", title: "no_desktop_title_tip", text: "no_desktop_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ msgtype: "session-login-password", - title: "session_unready_no_password_title_tip", - text: "session_unready_no_password_text_tip", + title: "session_not_ready_no_password_title_tip", + text: "session_not_ready_no_password_text_tip", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ msgtype: "session-login-re-password", - title: "session_unready_wrong_password_title_tip", - text: "session_unready_wrong_password_text_tip", + title: "session_not_ready_wrong_password_title_tip", + text: "session_not_ready_wrong_password_text_tip", link: "", try_again: true, }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 230a9730b..f4a8a1204 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 7f0e40581..11b3144c7 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", "记住此账户"), ("os_account_desk_tip", "在无显示器的环境下,此账户用于登录被控系统,并启用桌面"), ("OS Account", "系统账户"), - ("session_not_ready_title_tip", "桌面 session 未准备好"), - ("session_not_ready_text_tip", "请输入用户名密码"), - ("xsession_failed_title_tip", "用户名密码错误"), - ("xsession_failed_text_tip", "是否重试"), ("another_user_login_title_tip", "其他用户已登录"), ("another_user_login_text_tip", "断开"), ("xorg_not_found_title_tip", "Xorg 未安装"), ("xorg_not_found_text_tip", "请安装 Xorg"), ("no_desktop_title_tip", "desktop 未安装"), ("no_desktop_text_tip", "请安装 desktop"), - ("session_unready_no_password_title_tip", "桌面 session 未准备好"), - ("session_unready_no_password_text_tip", "请输入用户名密码和连接密码"), - ("session_unready_wrong_password_title_tip", "密码错误"), - ("session_unready_wrong_password_text_tip", "是否重试"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 7237b7a79..acfbed636 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index f761832da..b165540e8 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index e77ab938c..64ac4d9d4 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 3703aad27..e74551046 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index e6499ea1c..b20035dc9 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -57,22 +57,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), - ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), + ("verify_rustdesk_password_tip", "Verify RustDesk password"), ("remember_account_tip", "Remember this account"), ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), - ("session_not_ready_title_tip", "Session is unready"), - ("session_not_ready_text_tip", "Input linux user/password"), - ("xsession_failed_title_tip", "Xsession username/password is wrong"), - ("xsession_failed_text_tip", "Do you want to enter again"), - ("another_user_login_title_tip", "Another user already login"), + ("another_user_login_title_tip", "Another user already logged in"), ("another_user_login_text_tip", "Disconnect"), ("xorg_not_found_title_tip", "Xorg not found"), ("xorg_not_found_text_tip", "Please install Xorg"), ("no_desktop_title_tip", "No desktop is avaliable"), ("no_desktop_text_tip", "Please install GNOME desktop"), - ("session_unready_no_password_title_tip", "Session is unready"), - ("session_unready_no_password_text_tip", "Input connection password and linux user/password"), - ("session_unready_wrong_password_title_tip", "Password is wrong"), - ("session_unready_wrong_password_text_tip", "Do you want to enter again"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 645c8be7c..435bc6b0a 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 7a672dfec..6207fac3b 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 281478bb0..d78cc8b37 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 1c4126377..cd2a6c3cc 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 547107607..e8efc6aa3 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index c896db6ba..97cab077c 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 0d1b6914e..1211ceb3c 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index b7cfaf40f..cfe029cc4 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 849fa7ffb..9e1278c6e 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index b7e242eaa..a86d1c81a 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 6cd679953..329c4dd70 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 529289751..afa07046a 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 0fcbd360c..6a44584a0 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 491b48a44..51ef174f5 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 60d293e36..5de632ec4 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 90be51f01..f813c4b2f 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index aec122f70..3b7f62b74 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 18bd8f44c..2f050333c 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 746eed96d..453534a86 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 0a94fe103..e2728caaa 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 25d3144d8..802eff33d 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 1b4b6ff14..8941c00d4 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 2fe64b100..1bdece3b7 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 3a925942b..99977515b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 86e0dfb98..bff471b37 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 36b27089c..755e7b041 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 35c40a794..b8086f126 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 297ab18c7..5eb3bb374 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -62,16 +62,16 @@ pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; -pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session unready"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session not ready"; pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; pub const LOGIN_MSG_DESKTOP_XORG_NOT_FOUND: &str = "Desktop xorg not found"; // ls /usr/share/xsessions/ pub const LOGIN_MSG_DESKTOP_NO_DESKTOP: &str = "Desktop none"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = - "Desktop session unready, password empty"; + "Desktop session not ready, password empty"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = - "Desktop session unready, password wrong"; + "Desktop session not ready, password wrong"; pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access"; From 9def078424d2c9661b037b2c54cd9d970f8fc49f Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 14:46:28 +0800 Subject: [PATCH 075/112] remove unused tips Signed-off-by: fufesou --- src/client.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3bd00db47..0cc20e1e2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1932,8 +1932,8 @@ lazy_static::lazy_static! { try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ msgtype: "session-re-login", - title: "xsession_failed_title_tip", - text: "xsession_failed_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ @@ -1956,14 +1956,14 @@ lazy_static::lazy_static! { try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ msgtype: "session-login-password", - title: "session_not_ready_no_password_title_tip", - text: "session_not_ready_no_password_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ msgtype: "session-login-re-password", - title: "session_not_ready_wrong_password_title_tip", - text: "session_not_ready_wrong_password_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ From 07366a7fd54d9bb7485051e7199b2ff7f87d4776 Mon Sep 17 00:00:00 2001 From: Dmitrii Kirianov Date: Mon, 3 Apr 2023 12:02:26 +0500 Subject: [PATCH 076/112] Update ru.rs --- src/lang/ru.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index f813c4b2f..447e5223b 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -481,15 +481,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), ("login_linux_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("verify_rustdesk_password_tip", "Подтвердить пароль RustDesk"), + ("remember_account_tip", "Запомнить этот аккаунт"), + ("os_account_desk_tip", "Эта учетная запись используется для входа в удаленную ОС и включения сеанса рабочего стола в режиме headless"), + ("OS Account", "Аккаунт ОС"), + ("another_user_login_title_tip", "Другой пользователь уже вошел в систему"), + ("another_user_login_text_tip", "Отключить"), + ("xorg_not_found_title_tip", "Xorg не найден"), + ("xorg_not_found_text_tip", "Установите, пожалуйста, Xorg"), + ("no_desktop_title_tip", "Нет доступных рабочих столов"), + ("no_desktop_text_tip", "Установите, пожалуйста, GNOME Desktop"), ].iter().cloned().collect(); } From 2437b9e851d7136ab8482f68fcd66fc6ac20d192 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 3 Apr 2023 19:55:46 +0800 Subject: [PATCH 077/112] fix #3909 --- .github/workflows/flutter-nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index c812f43fa..c251a2b25 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -9,5 +9,6 @@ on: jobs: run-flutter-nightly-build: uses: ./.github/workflows/flutter-build.yml + secrets: inherit with: upload-artifact: true From eb79ce7a3292e62495d3e15d1e09cb2e697c589c Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 3 Apr 2023 20:22:17 +0800 Subject: [PATCH 078/112] fix: change running host >= 20.04 https://github.com/actions/runner-images/issues/6002 --- .github/workflows/bridge.yml | 2 +- .github/workflows/flutter-build.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index 5d6ebbb57..2a69ca871 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -13,7 +13,7 @@ jobs: job: - { target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, + os: ubuntu-20.04, extra-build-args: "", } steps: diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index bf73bdaa1..f94e2dc9c 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -394,7 +394,7 @@ jobs: - { arch: x86_64, target: armv7-linux-androideabi, - os: ubuntu-18.04, + os: ubuntu-20.04, extra-build-features: "", openssl-arch: android-arm } @@ -709,14 +709,14 @@ jobs: - { arch: aarch64, target: aarch64-unknown-linux-gnu, - os: ubuntu-20.04, + os: ubuntu-20.04, # just for naming package, not running host use-cross: true, extra-build-features: "", } - { arch: aarch64, target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host + os: ubuntu-20.04, # just for naming package, not running host use-cross: true, extra-build-features: "appimage", } @@ -1281,7 +1281,7 @@ jobs: - { arch: x86_64, target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, + os: ubuntu-18.04, extra-build-features: "", } - { From 8e1c5ad64d73131c6c46488241b2a263a3238039 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 20:21:50 +0300 Subject: [PATCH 079/112] Update lang.rs --- src/lang.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lang.rs b/src/lang.rs index 4776eb89e..6de810f23 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -32,6 +32,7 @@ mod tr; mod tw; mod ua; mod vn; +mod lt; pub const LANGS: &[(&str, &str)] = &[ ("en", "English"), @@ -66,6 +67,7 @@ pub const LANGS: &[(&str, &str)] = &[ ("th", "ภาษาไทย"), ("sl", "Slovenščina"), ("ro", "Română"), + ("lt", "Lietuvių"), ]; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -129,6 +131,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "th" => th::T.deref(), "sl" => sl::T.deref(), "ro" => ro::T.deref(), + "lt" => lt::T.deref(), _ => en::T.deref(), }; if let Some(v) = m.get(&name as &str) { From 005c1704011e5a4419bf1abb6d8dc5f59c954fe1 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 21:36:18 +0300 Subject: [PATCH 080/112] Add files via upload --- src/lang/lt.rs | 495 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/lang/lt.rs diff --git a/src/lang/lt.rs b/src/lang/lt.rs new file mode 100644 index 000000000..87fbd3752 --- /dev/null +++ b/src/lang/lt.rs @@ -0,0 +1,495 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Būsena"), + ("Your Desktop", "Jūsų darbalaukis"), + ("desk_tip", "desk_tip"), + ("Password", "Slaptažodis"), + ("Ready", "Pasiruošęs"), + ("Established", "Įsteigta"), + ("connecting_status", "jungimo_būsena"), + ("Enable Service", "Įgalinti paslaugą"), + ("Start Service", "Pradėti paslaugą"), + ("Service is running", "Paslauga veikia"), + ("Service is not running", "Paslauga neveikia"), + ("not_ready_status", "not_ready_status"), + ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), + ("Transfer File", "Perkelti failą"), + ("Connect", "Prisijungti"), + ("Recent Sessions", "Naujausios sesijos"), + ("Address Book", "Adresų knyga"), + ("Confirmation", "Patvirtinimas"), + ("TCP Tunneling", "TCP tuneliavimas"), + ("Remove", "Pašalinti"), + ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), + ("Set your own password", "Nustatykite savo slaptažodį"), + ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), + ("Enable Clipboard", "Įgalinti iškarpinę"), + ("Enable File Transfer", "Įgalinti failų perdavimą"), + ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), + ("IP Whitelisting", "IP baltasis sąrašas"), + ("ID/Relay Server", "ID / perdavimo serveris"), + ("Import Server Config", "Importuoti serverio konfigūraciją"), + ("Export Server Config", "Eksportuoti serverio konfigūraciją"), + ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), + ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), + ("Invalid server configuration", "Netinkama serverio konfigūracija"), + ("Clipboard is empty", "Iškarpinė tuščia"), + ("Stop service", "Sustabdyti paslaugą"), + ("Change ID", "Keisti ID"), + ("Your new ID", "Jūsų naujasis ID"), + ("length %min% to %max%", "ilgis %min% iki %max%"), + ("starts with a letter", "prasideda raide"), + ("allowed characters", "leistini simboliai"), + ("id_change_tip", "id_change_tip"), + ("Website", "Interneto svetainė"), + ("About", "Apie"), + ("Slogan_tip", "Slogan_tip"), + ("Privacy Statement", "Privatumo pareiškimas"), + ("Mute", "Nutildyti"), + ("Build Date", "Sukūrimo data"), + ("Version", "Versija"), + ("Home", "Namai"), + ("Audio Input", "Garso įvestis"), + ("Enhancements", "Patobulinimai"), + ("Hardware Codec", "Aparatūros kodekas"), + ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), + ("ID Server", "ID serveris"), + ("Relay Server", "Perdavimo serveris"), + ("API Server", "API serveris"), + ("invalid_http", "netinkamas_http"), + ("Invalid IP", "Netinkamas IP"), + ("Invalid format", "Neteisingas formatas"), + ("server_not_support", "server_not_support"), + ("Not available", "Nepasiekiamas"), + ("Too frequent", "Per dažnai"), + ("Cancel", "Atšaukti"), + ("Skip", "Praleisti"), + ("Close", "Uždaryti"), + ("Retry", "Bandykite dar kartą"), + ("OK", "GERAI"), + ("Password Required", "Reikalingas slaptažodis"), + ("Please enter your password", "Prašome įvesti savo slaptažodį"), + ("Remember password", "Prisiminti slaptažodį"), + ("Wrong Password", "Neteisingas slaptažodis"), + ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), + ("Connection Error", "Ryšio klaida"), + ("Error", "Klaida"), + ("Reset by the peer", "Iš naujo nustatė bendraamžis"), + ("Connecting...", "Prisijungiama..."), + ("Connection in progress. Please wait.", "Ryšys vyksta. Palaukite."), + ("Please try 1 minute later", "Prašome pabandyti po 1 minutės vėliau"), + ("Login Error", "Prisijungimo klaida"), + ("Successful", "Sėkmingai"), + ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), + ("Name", "Vardas"), + ("Type", "Tipas"), + ("Modified", "Pakeista"), + ("Size", "Dydis"), + ("Show Hidden Files", "Rodyti paslėptus failus"), + ("Receive", "Gauti"), + ("Send", "Siųsti"), + ("Refresh File", "Atnaujinti failą"), + ("Local", "Vietinis"), + ("Remote", "Nuotolinis"), + ("Remote Computer", "Nuotolinis kompiuteris"), + ("Local Computer", "Vietinis kompiuteris"), + ("Confirm Delete", "Patvirtinti ištrynimą"), + ("Delete", "Ištrinti"), + ("Properties", "Ypatybės"), + ("Multi Select", "Keli pasirinkimas"), + ("Select All", "Pasirinkti viską"), + ("Unselect All", "Atšaukti visų pasirinkimą"), + ("Empty Directory", "Tuščias katalogas"), + ("Not an empty directory", "Ne tuščias katalogas"), + ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), + ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), + ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), + ("Do this for all conflicts", "Padarykite tai visiems konfliktams"), + ("This is irreversible!", "Tai negrįžtama!"), + ("Deleting", "Ištrinama"), + ("files", "failai"), + ("Waiting", "Laukiu"), + ("Finished", "Baigta"), + ("Speed", "Greitis"), + ("Custom Image Quality", "Tinkinta vaizdo kokybė"), + ("Privacy mode", "Privatumo režimas"), + ("Block user input", "Blokuoti naudotojo įvestį"), + ("Unblock user input", "Atblokuoti naudotojo įvestį"), + ("Adjust Window", "Koreguoti langą"), + ("Original", "Originalas"), + ("Shrink", "Susitraukti"), + ("Stretch", "Ištempti"), + ("Scrollbar", "Slinkties juosta"), + ("ScrollAuto", "ScrollAuto"), + ("Good image quality", "Gera vaizdo kokybė"), + ("Balanced", "Subalansuotas"), + ("Optimize reaction time", "Optimizuoti reakcijos laiką"), + ("Custom", "Tinkintas"), + ("Show remote cursor", "Rodyti nuotolinį žymeklį"), + ("Show quality monitor", "Rodyti kokybės monitorių"), + ("Disable clipboard", "Išjungti mainų sritį"), + ("Lock after session end", "Užrakinti pasibaigus seansui"), + ("Insert", "Įdėti"), + ("Insert Lock", "Įterpti užraktą"), + ("Refresh", "Atnaujinti"), + ("ID does not exist", "ID neegzistuoja"), + ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), + ("Please try later", "Prašome pabandyti vėliau"), + ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), + ("Key mismatch", "Raktų neatitikimas"), + ("Timeout", "Laikas baigėsi"), + ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), + ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), + ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), + ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), + ("Set Password", "Nustatyti slaptažodį"), + ("OS Password", "OS slaptažodis"), + ("install_tip", "install_tip"), + ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), + ("Click to download", "Spustelėkite norėdami atsisiųsti"), + ("Click to update", "Spustelėkite norėdami atnaujinti"), + ("Configure", "Konfigūruoti"), + ("config_acc", "config_acc"), + ("config_screen", "config_screen"), + ("Installing ...", "Diegiama ..."), + ("Install", "Diegti"), + ("Installation", "Įdiegimas"), + ("Installation Path", "Įdiegimo kelias"), + ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), + ("Create desktop icon", "Sukurti darbalaukio piktogramą"), + ("agreement_tip", "agreement_tip"), + ("Accept and Install", "Priimti ir įdiegti"), + ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), + ("Generating ...", "Generuojamas..."), + ("Your installation is lower version.", "Jūsų įdiegta senesnė versija"), + ("not_close_tcp_tip", "not_close_tcp_tip"), + ("Listening ...", "Klausau..."), + ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), + ("Remote Port", "Nuotolinis prievadas"), + ("Action", "Veiksmas"), + ("Add", "Papildyti"), + ("Local Port", "Vietinis uostas"), + ("Local Address", "Vietinis adresas"), + ("Change Local Port", "Keisti vietinį prievadą"), + ("setup_server_tip", "setup_server_tip"), + ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), + ("The confirmation is not identical.", "Patvirtinimas nėra tapatus"), + ("Permissions", "Leidimai"), + ("Accept", "Priimti"), + ("Dismiss", "Atmesti"), + ("Disconnect", "Atjungti"), + ("Allow using keyboard and mouse", "Leisti naudojant klaviatūrą ir pelę"), + ("Allow using clipboard", "Leisti naudoti mainų sritį"), + ("Allow hearing sound", "Leisti girdėti garsą"), + ("Allow file copy and paste", "Leisti failą kopijuoti ir įklijuoti"), + ("Connected", "Prisijungta"), + ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), + ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), + ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), + ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), + ("Enter Remote ID", "Įveskite nuotolinio ID"), + ("Enter your password", "Įveskite savo slaptažodį"), + ("Logging in...", "Prisijungiama..."), + ("Enable RDP session sharing", "Įgalinti KPP seansų bendrinimą"), + ("Auto Login", "Automatinis prisijungimas"), + ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), + ("Rename", "Pervardyti"), + ("Space", "Erdvė"), + ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), + ("Change Path", "Keisti kelią"), + ("Create Folder", "Sukurti aplanką"), + ("Please enter the folder name", "Įveskite aplanko pavadinimą"), + ("Fix it", "Pataisyk tai"), + ("Warning", "Įspėjimas"), + ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant „Wayland“ nepalaikomas"), + ("Reboot required", "Reikia paleisti iš naujo"), + ("Unsupported display server", "Nepalaikomas rodymo serveris"), + ("x11 expected", "tikimasi x 11"), + ("Port", "Uostas"), + ("Settings", "Nustatymai"), + ("Username", "Vartotojo vardas"), + ("Invalid port", "Netinkamas prievadas"), + ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), + ("Enable remote configuration modification", "Įgalinti nuotolinio konfigūracijos modifikavimą"), + ("Run without install", "Vykdyti be diegimo"), + ("Connect via relay", "Prisijungti per relę"), + ("Always connect via relay", "Visada prisijunkite per relę"), + ("whitelist_tip", "whitelist_tip"), + ("Login", "Prisijungti"), + ("Verify", "Patvirtinti"), + ("Remember me", "Prisimink mane"), + ("Trust this device", "Pasitikėk šiuo įrenginiu"), + ("Verification code", "Patvirtinimo kodas"), + ("verification_tip", "verification_tip"), + ("Logout", "Atsijungti"), + ("Tags", "Žymos"), + ("Search ID", "Paieškos ID"), + ("whitelist_sep", "whitelist_sep"), + ("Add ID", "Pridėti ID"), + ("Add Tag", "Pridėti žymą"), + ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), + ("Network error", "Tinklo klaida"), + ("Username missed", "Prarastas vartotojo vardas"), + ("Password missed", "Slaptažodis praleistas"), + ("Wrong credentials", "Klaidingi kredencialai"), + ("Edit Tag", "Redaguoti žymą"), + ("Unremember Password", "Neprisiminti slaptažodžio"), + ("Favorites", "Mėgstamiausi"), + ("Add to Favorites", "Įtraukti į adresyną"), + ("Remove from Favorites", "Pašalinti iš parankinių"), + ("Empty", "Tuščia"), + ("Invalid folder name", "Neteisingas aplanko pavadinimas"), + ("Socks5 Proxy", "Socks5 Proxy"), + ("Hostname", "Pagrindinio kompiuterio pavadinimas"), + ("Discovered", "Atrasta"), + ("install_daemon_tip", "install_daemon_tip"), + ("Remote ID", "Nuotolinis ID"), + ("Paste", "Įklijuoti"), + ("Paste here?", "Įklijuoti čia?"), + ("Are you sure to close the connection?", "Ar tikrai uždarysite ryšį?"), + ("Download new version", "Atsisiųsti naują versiją"), + ("Touch mode", "Palietimo režimas"), + ("Mouse mode", "Pelės režimas"), + ("One-Finger Tap", "Palietimas vienu pirštu"), + ("Left Mouse", "Kairė pelė"), + ("One-Long Tap", "Vienas palietimas"), + ("Two-Finger Tap", "Palietimas dviem pirštais"), + ("Right Mouse", "Dešinysis pelė"), + ("One-Finger Move", "Vieno piršto judesys"), + ("Double Tap & Move", "Dukart palieskite ir perkelkite"), + ("Mouse Drag", "Pelės vilkimas"), + ("Three-Finger vertically", "Trys pirštai vertikaliai"), + ("Mouse Wheel", "Pelės ratas"), + ("Two-Finger Move", "Dviejų pirštų judesys"), + ("Canvas Move", "Drobės perkėlimas"), + ("Pinch to Zoom", "Suimkite, kad padidintumėte"), + ("Canvas Zoom", "Drobės mastelis"), + ("Reset canvas", "Atstatyti drobę"), + ("No permission of file transfer", "Nėra leidimo perkelti failus"), + ("Note", "Pastaba"), + ("Connection", "Ryšys"), + ("Share Screen", "Bendrinti ekraną"), + ("Chat", "Pokalbis"), + ("Total", "Iš viso"), + ("items", "elementai"), + ("Selected", "Pasirinkta"), + ("Screen Capture", "Ekrano nuotrauka"), + ("Input Control", "Įvesties valdymas"), + ("Audio Capture", "Garso fiksavimas"), + ("File Connection", "Failo ryšys"), + ("Screen Connection", "Ekrano jungtis"), + ("Do you accept?", "Ar sutinki?"), + ("Open System Setting", "Atviros sistemos nustatymas"), + ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), + ("android_input_permission_tip1", "android_input_permission_tip1"), + ("android_input_permission_tip2", "android_input_permission_tip2"), + ("android_new_connection_tip", "android_new_connection_tip"), + ("android_service_will_start_tip", "android_service_will_start_tip"), + ("android_stop_service_tip", "android_stop_service_tip"), + ("android_version_audio_tip", "android_version_audio_tip"), + ("android_start_service_tip", "android_start_service_tip"), + ("android_permission_may_not_change_tip", "android_permission_may_not_change_tip"), + ("Account", "Paskyra"), + ("Overwrite", "Perrašyti"), + ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), + ("Quit", "Išeiti"), + ("doc_mac_permission", "doc_mac_permission"), + ("Help", "Pagalba"), + ("Failed", "Nepavyko"), + ("Succeeded", "Pavyko"), + ("Someone turns on privacy mode, exit", "Kažkas įjungia privatumo režimą, išeiti"), + ("Unsupported", "Nepalaikomas"), + ("Peer denied", "Atšaukė"), + ("Please install plugins", "Įdiekite papildinius"), + ("Peer exit", "Peer exit"), + ("Failed to turn off", "Nepavyko išjungti"), + ("Turned off", "Išjungė"), + ("In privacy mode", "Privatumo režimu"), + ("Out privacy mode", "Iš privatumo režimo"), + ("Language", "Kalba"), + ("Keep RustDesk background service", "Keep RustDesk foninė paslauga"), + ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), + ("android_open_battery_optimizations_tip", "android_open_battery_optimizations_tip"), + ("Start on Boot", "Start on Boot"), + ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), + ("Connection not allowed", "Ryšys neleidžiamas"), + ("Legacy mode", "Senasis režimas"), + ("Map mode", "Žemėlapio režimas"), + ("Translate mode", "Vertimo režimas"), + ("Use permanent password", "Naudoti nuolatinį slaptažodį"), + ("Use both passwords", "Naudoti abu slaptažodžius"), + ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), + ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), + ("Allow remote restart", "Leisti nuotolinį paleidimą iš naujo"), + ("Restart Remote Device", "Paleisti nuotolinį įrenginį iš naujo"), + ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo"), + ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), + ("remote_restarting_tip", "remote_restarting_tip"), + ("Copied", "Nukopijuota"), + ("Exit Fullscreen", "Išeiti per visą ekraną"), + ("Fullscreen", "Per visą ekraną"), + ("Mobile Actions", "Veiksmai mobiliesiems"), + ("Select Monitor", "Pasirinkite monitorių"), + ("Control Actions", "Valdymo veiksmai"), + ("Display Settings", "Ekrano nustatymai"), + ("Ratio", "Santykis"), + ("Image Quality", "Vaizdo kokybė"), + ("Scroll Style", "Slinkimo stilius"), + ("Show Menubar", "Rodyti meniu juostą"), + ("Hide Menubar", "Slėpti meniu juostą"), + ("Direct Connection", "Tiesioginis ryšys"), + ("Relay Connection", "Relės jungtis"), + ("Secure Connection", "Saugus ryšys"), + ("Insecure Connection", "Nesaugus ryšys"), + ("Scale original", "Pakeisti originalų mastelį"), + ("Scale adaptive", "Pritaikomas mastelis"), + ("General", "Bendra"), + ("Security", "Sauga"), + ("Theme", "Tema"), + ("Dark Theme", "Tamsioji tema"), + ("Light Theme", "Šviesi tema"), + ("Dark", "Tamsiai"), + ("Light", "Šviesa"), + ("Follow System", "Sekti sistemą"), + ("Enable hardware codec", "Įgalinti aparatūros kodeką"), + ("Unlock Security Settings", "Atrakinti saugos nustatymus"), + ("Enable Audio", "Įgalinti garsą"), + ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), + ("Server", "Serveris"), + ("Direct IP Access", "Tiesioginė IP prieiga"), + ("Proxy", "Įgaliotinis serveris"), + ("Apply", "Taikyti"), + ("Disconnect all devices?", "Atjungti visus įrenginius?"), + ("Clear", "Išvalyti"), + ("Audio Input Device", "Garso įvesties įrenginys"), + ("Deny remote access", "Uždrausti nuotolinę prieigą"), + ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), + ("Network", "Tinklas"), + ("Enable RDP", "Įgalinti KPP"), + ("Pin menubar", "Prisegti meniu juostą"), + ("Unpin menubar", "Atsegti meniu juostą"), + ("Recording", "Įrašymas"), + ("Directory", "Katalogas"), + ("Automatically record incoming sessions", "Automatiškai įrašyti gaunamus seansus"), + ("Change", "Keisti"), + ("Start session recording", "Pradėti seanso įrašymą"), + ("Stop session recording", "Sustabdyti seanso įrašymą"), + ("Enable Recording Session", "Įgalinti įrašymo seansą"), + ("Allow recording session", "Leisti įrašymo sesiją"), + ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), + ("Deny LAN Discovery", "Neleisti LAN atradimo"), + ("Write a message", "Rašyti žinutę"), + ("Prompt", "Prompt"), + ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), + ("elevated_foreground_window_tip", "iškeltas_foreground_window_tip"), + ("Disconnected", "Atjungtas"), + ("Other", "Kita"), + ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), + ("Keyboard Settings", "Klaviatūros nustatymai"), + ("Full Access", "Visa prieiga"), + ("Screen Share", "Ekrano bendrinimas"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland“ reikalinga naujesnė „Linux Distro“ versija. Išbandykite X11 darbalaukį arba pakeiskite OS."), + ("JumpLink", "JumpLink"), + ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikti lygiavertėje pusėje)."), + ("Show RustDesk", "Rodyti RustDesk"), + ("This PC", "Šis kompiuteris"), + ("or", "arba"), + ("Continue with", "Tęsti su"), + ("Elevate", "Pakelti"), + ("Zoom cursor", "Mastelio keitimo žymeklis"), + ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), + ("Accept sessions via click", "Priimti seansus spustelėjus"), + ("Accept sessions via both", "Priimti seansus per abu"), + ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), + ("One-time Password", "Vienkartinis slaptažodis"), + ("Use one-time password", "Naudoti vienkartinį slaptažodį"), + ("One-time password length", "Vienkartinio slaptažodžio ilgis"), + ("Request access to your device", "Prašyti prieigos prie įrenginio"), + ("Hide connection management window", "Slėpti ryšio valdymo langą"), + ("hide_cm_tip", "hide_cm_tip"), + ("wayland_experiment_tip", "wayland_experiment_tip"), + ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), + ("Skipped", "Praleisti"), + ("Add to Address Book", "Pridėti prie adresų knygos"), + ("Group", "Grupė"), + ("Search", "Paieška"), + ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), + ("Local keyboard type", "Vietinės klaviatūros tipas"), + ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), + ("software_render_tip", "software_render_tip"), + ("Always use software rendering", "Visada naudoti programinės įrangos atvaizdavimą"), + ("config_input", "config_input"), + ("config_microphone", "config_microphone"), + ("request_elevation_tip", "request_elevation_tip"), + ("Wait", "Laukti"), + ("Elevation Error", "Aukščio klaida"), + ("Ask the remote user for authentication", "Paklauskite nuotolinio vartotojo autentifikavimo"), + ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), + ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), + ("still_click_uac_tip", "still_click_uac_tip"), + ("Request Elevation", "Prašyti paaukštinimo"), + ("wait_accept_uac_tip", "wait_accept_uac_tip"), + ("Elevate successfully", "Pakelti sėkmingai"), + ("uppercase", "didžiosios raidės"), + ("lowercase", "mažosios raidės"), + ("digit", "skaitmenų"), + ("special character", "specialusis simbolis"), + ("length>=8", "ilgis>=8"), + ("Weak", "Silpnas"), + ("Medium", "Vidutinis"), + ("Strong", "Stiprus"), + ("Switch Sides", "Perjungti puses"), + ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jei norite bendrinti darbalaukį?"), + ("Display", "Ekranas"), + ("Default View Style", "Numatytasis peržiūros stilius"), + ("Default Scroll Style", "Numatytasis slinkties stilius"), + ("Default Image Quality", "Numatytoji vaizdo kokybė"), + ("Default Codec", "Numatytasis kodekas"), + ("Bitrate", "Bitrate"), + ("FPS", "FPS"), + ("Auto", "Automatinis"), + ("Other Default Options", "Kitos numatytosios parinktys"), + ("Voice call", "Balso skambutis"), + ("Text chat", "Teksto pokalbis"), + ("Stop voice call", "Sustabdyti balso skambutį"), + ("relay_hint_tip", "relay_hint_tip"), + ("Reconnect", "Prisijungti iš naujo"), + ("Codec", "Kodekas"), + ("Resolution", "Rezoliucija"), + ("No transfers in progress", "Nevyksta jokių pervedimų"), + ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), + ("idd_driver_tip", "idd_driver_tip"), + ("confirm_idd_driver_tip", "confirm_idd_driver_tip"), + ("RDP Settings", "RDP nustatymai"), + ("Sort by", "Rūšiuoti pagal"), + ("New Connection", "Naujas ryšys"), + ("Restore", "Atkurti"), + ("Minimize", "Sumažinti"), + ("Maximize", "Padidinti"), + ("Your Device", "Jūsų įrenginys"), + ("empty_recent_tip", "tuščias_paskutinis_patarimas"), + ("empty_favorite_tip", "empty_favorite_tip"), + ("empty_lan_tip", "empty_lan_tip"), + ("empty_address_book_tip", "empty_address_book_tip"), + ("eg: admin", "pvz.: administratorius"), + ("Empty Username", "Tuščias naudotojo vardas"), + ("Empty Password", "Tuščias slaptažodis"), + ("Me", "Aš"), + ("identical_file_tip", "identical_file_tip"), + ("show_monitors_tip", "show_monitors_tip"), + ("View Mode", "Peržiūros režimas"), + ("login_linux_tip", "login_linux_tip"), + ("verify_rustdesk_password_tip", "verify_rustdesk_password_tip"), + ("remember_account_tip", "remember_account_tip"), + ("os_account_desk_tip", "os_account_desk_tip"), + ("OS Account", "OS paskyra"), + ("another_user_login_title_tip", "another_user_login_title_tip"), + ("another_user_login_text_tip", "antro_user_login_text_tip"), + ("xorg_not_found_title_tip", "xorg_not_found_title_tip"), + ("xorg_not_found_text_tip", "xorg_not_found_text_tip"), + ("no_desktop_title_tip", "no_desktop_title_tip"), + ("no_desktop_text_tip", "no_desktop_text_tip"), + ].iter().cloned().collect(); +} \ No newline at end of file From a08ba2a1014993114cea5546b1f4978789d8532c Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:18:43 +0200 Subject: [PATCH 081/112] Update de.rs --- src/lang/de.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 64ac4d9d4..6777ad733 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -481,15 +481,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), ("View Mode", "Ansichtsmodus"), ("login_linux_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("verify_rustdesk_password_tip", "RustDesk-Passwort bestätigen"), + ("remember_account_tip", "Dieses Konto merken"), + ("os_account_desk_tip", "Dieses Konto wird verwendet, um sich beim entfernten Betriebssystem anzumelden und die Desktop-Sitzung im Headless-Modus zu aktivieren."), + ("OS Account", "Betriebssystem-Konto"), + ("another_user_login_title_tip", "Ein anderer Benutzer ist bereits angemeldet."), + ("another_user_login_text_tip", "Trennen"), + ("xorg_not_found_title_tip", "Xorg nicht gefunden."), + ("xorg_not_found_text_tip", "Bitte installieren Sie Xorg."), + ("no_desktop_title_tip", "Es ist kein Desktop verfügbar."), + ("no_desktop_text_tip", "Bitte installieren Sie den GNOME-Desktop."), ].iter().cloned().collect(); } From f210bf98e2e32f9c741ffe5edfb829ebe268b126 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:22:22 +0300 Subject: [PATCH 082/112] Delete lt.rs --- src/lang/lt.rs | 495 ------------------------------------------------- 1 file changed, 495 deletions(-) delete mode 100644 src/lang/lt.rs diff --git a/src/lang/lt.rs b/src/lang/lt.rs deleted file mode 100644 index 87fbd3752..000000000 --- a/src/lang/lt.rs +++ /dev/null @@ -1,495 +0,0 @@ -lazy_static::lazy_static! { -pub static ref T: std::collections::HashMap<&'static str, &'static str> = - [ - ("Status", "Būsena"), - ("Your Desktop", "Jūsų darbalaukis"), - ("desk_tip", "desk_tip"), - ("Password", "Slaptažodis"), - ("Ready", "Pasiruošęs"), - ("Established", "Įsteigta"), - ("connecting_status", "jungimo_būsena"), - ("Enable Service", "Įgalinti paslaugą"), - ("Start Service", "Pradėti paslaugą"), - ("Service is running", "Paslauga veikia"), - ("Service is not running", "Paslauga neveikia"), - ("not_ready_status", "not_ready_status"), - ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), - ("Transfer File", "Perkelti failą"), - ("Connect", "Prisijungti"), - ("Recent Sessions", "Naujausios sesijos"), - ("Address Book", "Adresų knyga"), - ("Confirmation", "Patvirtinimas"), - ("TCP Tunneling", "TCP tuneliavimas"), - ("Remove", "Pašalinti"), - ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), - ("Set your own password", "Nustatykite savo slaptažodį"), - ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), - ("Enable Clipboard", "Įgalinti iškarpinę"), - ("Enable File Transfer", "Įgalinti failų perdavimą"), - ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), - ("IP Whitelisting", "IP baltasis sąrašas"), - ("ID/Relay Server", "ID / perdavimo serveris"), - ("Import Server Config", "Importuoti serverio konfigūraciją"), - ("Export Server Config", "Eksportuoti serverio konfigūraciją"), - ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), - ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), - ("Invalid server configuration", "Netinkama serverio konfigūracija"), - ("Clipboard is empty", "Iškarpinė tuščia"), - ("Stop service", "Sustabdyti paslaugą"), - ("Change ID", "Keisti ID"), - ("Your new ID", "Jūsų naujasis ID"), - ("length %min% to %max%", "ilgis %min% iki %max%"), - ("starts with a letter", "prasideda raide"), - ("allowed characters", "leistini simboliai"), - ("id_change_tip", "id_change_tip"), - ("Website", "Interneto svetainė"), - ("About", "Apie"), - ("Slogan_tip", "Slogan_tip"), - ("Privacy Statement", "Privatumo pareiškimas"), - ("Mute", "Nutildyti"), - ("Build Date", "Sukūrimo data"), - ("Version", "Versija"), - ("Home", "Namai"), - ("Audio Input", "Garso įvestis"), - ("Enhancements", "Patobulinimai"), - ("Hardware Codec", "Aparatūros kodekas"), - ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), - ("ID Server", "ID serveris"), - ("Relay Server", "Perdavimo serveris"), - ("API Server", "API serveris"), - ("invalid_http", "netinkamas_http"), - ("Invalid IP", "Netinkamas IP"), - ("Invalid format", "Neteisingas formatas"), - ("server_not_support", "server_not_support"), - ("Not available", "Nepasiekiamas"), - ("Too frequent", "Per dažnai"), - ("Cancel", "Atšaukti"), - ("Skip", "Praleisti"), - ("Close", "Uždaryti"), - ("Retry", "Bandykite dar kartą"), - ("OK", "GERAI"), - ("Password Required", "Reikalingas slaptažodis"), - ("Please enter your password", "Prašome įvesti savo slaptažodį"), - ("Remember password", "Prisiminti slaptažodį"), - ("Wrong Password", "Neteisingas slaptažodis"), - ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), - ("Connection Error", "Ryšio klaida"), - ("Error", "Klaida"), - ("Reset by the peer", "Iš naujo nustatė bendraamžis"), - ("Connecting...", "Prisijungiama..."), - ("Connection in progress. Please wait.", "Ryšys vyksta. Palaukite."), - ("Please try 1 minute later", "Prašome pabandyti po 1 minutės vėliau"), - ("Login Error", "Prisijungimo klaida"), - ("Successful", "Sėkmingai"), - ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), - ("Name", "Vardas"), - ("Type", "Tipas"), - ("Modified", "Pakeista"), - ("Size", "Dydis"), - ("Show Hidden Files", "Rodyti paslėptus failus"), - ("Receive", "Gauti"), - ("Send", "Siųsti"), - ("Refresh File", "Atnaujinti failą"), - ("Local", "Vietinis"), - ("Remote", "Nuotolinis"), - ("Remote Computer", "Nuotolinis kompiuteris"), - ("Local Computer", "Vietinis kompiuteris"), - ("Confirm Delete", "Patvirtinti ištrynimą"), - ("Delete", "Ištrinti"), - ("Properties", "Ypatybės"), - ("Multi Select", "Keli pasirinkimas"), - ("Select All", "Pasirinkti viską"), - ("Unselect All", "Atšaukti visų pasirinkimą"), - ("Empty Directory", "Tuščias katalogas"), - ("Not an empty directory", "Ne tuščias katalogas"), - ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), - ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), - ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), - ("Do this for all conflicts", "Padarykite tai visiems konfliktams"), - ("This is irreversible!", "Tai negrįžtama!"), - ("Deleting", "Ištrinama"), - ("files", "failai"), - ("Waiting", "Laukiu"), - ("Finished", "Baigta"), - ("Speed", "Greitis"), - ("Custom Image Quality", "Tinkinta vaizdo kokybė"), - ("Privacy mode", "Privatumo režimas"), - ("Block user input", "Blokuoti naudotojo įvestį"), - ("Unblock user input", "Atblokuoti naudotojo įvestį"), - ("Adjust Window", "Koreguoti langą"), - ("Original", "Originalas"), - ("Shrink", "Susitraukti"), - ("Stretch", "Ištempti"), - ("Scrollbar", "Slinkties juosta"), - ("ScrollAuto", "ScrollAuto"), - ("Good image quality", "Gera vaizdo kokybė"), - ("Balanced", "Subalansuotas"), - ("Optimize reaction time", "Optimizuoti reakcijos laiką"), - ("Custom", "Tinkintas"), - ("Show remote cursor", "Rodyti nuotolinį žymeklį"), - ("Show quality monitor", "Rodyti kokybės monitorių"), - ("Disable clipboard", "Išjungti mainų sritį"), - ("Lock after session end", "Užrakinti pasibaigus seansui"), - ("Insert", "Įdėti"), - ("Insert Lock", "Įterpti užraktą"), - ("Refresh", "Atnaujinti"), - ("ID does not exist", "ID neegzistuoja"), - ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), - ("Please try later", "Prašome pabandyti vėliau"), - ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), - ("Key mismatch", "Raktų neatitikimas"), - ("Timeout", "Laikas baigėsi"), - ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), - ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), - ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), - ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), - ("Set Password", "Nustatyti slaptažodį"), - ("OS Password", "OS slaptažodis"), - ("install_tip", "install_tip"), - ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), - ("Click to download", "Spustelėkite norėdami atsisiųsti"), - ("Click to update", "Spustelėkite norėdami atnaujinti"), - ("Configure", "Konfigūruoti"), - ("config_acc", "config_acc"), - ("config_screen", "config_screen"), - ("Installing ...", "Diegiama ..."), - ("Install", "Diegti"), - ("Installation", "Įdiegimas"), - ("Installation Path", "Įdiegimo kelias"), - ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), - ("Create desktop icon", "Sukurti darbalaukio piktogramą"), - ("agreement_tip", "agreement_tip"), - ("Accept and Install", "Priimti ir įdiegti"), - ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), - ("Generating ...", "Generuojamas..."), - ("Your installation is lower version.", "Jūsų įdiegta senesnė versija"), - ("not_close_tcp_tip", "not_close_tcp_tip"), - ("Listening ...", "Klausau..."), - ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), - ("Remote Port", "Nuotolinis prievadas"), - ("Action", "Veiksmas"), - ("Add", "Papildyti"), - ("Local Port", "Vietinis uostas"), - ("Local Address", "Vietinis adresas"), - ("Change Local Port", "Keisti vietinį prievadą"), - ("setup_server_tip", "setup_server_tip"), - ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), - ("The confirmation is not identical.", "Patvirtinimas nėra tapatus"), - ("Permissions", "Leidimai"), - ("Accept", "Priimti"), - ("Dismiss", "Atmesti"), - ("Disconnect", "Atjungti"), - ("Allow using keyboard and mouse", "Leisti naudojant klaviatūrą ir pelę"), - ("Allow using clipboard", "Leisti naudoti mainų sritį"), - ("Allow hearing sound", "Leisti girdėti garsą"), - ("Allow file copy and paste", "Leisti failą kopijuoti ir įklijuoti"), - ("Connected", "Prisijungta"), - ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), - ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), - ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), - ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), - ("Enter Remote ID", "Įveskite nuotolinio ID"), - ("Enter your password", "Įveskite savo slaptažodį"), - ("Logging in...", "Prisijungiama..."), - ("Enable RDP session sharing", "Įgalinti KPP seansų bendrinimą"), - ("Auto Login", "Automatinis prisijungimas"), - ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), - ("Rename", "Pervardyti"), - ("Space", "Erdvė"), - ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), - ("Change Path", "Keisti kelią"), - ("Create Folder", "Sukurti aplanką"), - ("Please enter the folder name", "Įveskite aplanko pavadinimą"), - ("Fix it", "Pataisyk tai"), - ("Warning", "Įspėjimas"), - ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant „Wayland“ nepalaikomas"), - ("Reboot required", "Reikia paleisti iš naujo"), - ("Unsupported display server", "Nepalaikomas rodymo serveris"), - ("x11 expected", "tikimasi x 11"), - ("Port", "Uostas"), - ("Settings", "Nustatymai"), - ("Username", "Vartotojo vardas"), - ("Invalid port", "Netinkamas prievadas"), - ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), - ("Enable remote configuration modification", "Įgalinti nuotolinio konfigūracijos modifikavimą"), - ("Run without install", "Vykdyti be diegimo"), - ("Connect via relay", "Prisijungti per relę"), - ("Always connect via relay", "Visada prisijunkite per relę"), - ("whitelist_tip", "whitelist_tip"), - ("Login", "Prisijungti"), - ("Verify", "Patvirtinti"), - ("Remember me", "Prisimink mane"), - ("Trust this device", "Pasitikėk šiuo įrenginiu"), - ("Verification code", "Patvirtinimo kodas"), - ("verification_tip", "verification_tip"), - ("Logout", "Atsijungti"), - ("Tags", "Žymos"), - ("Search ID", "Paieškos ID"), - ("whitelist_sep", "whitelist_sep"), - ("Add ID", "Pridėti ID"), - ("Add Tag", "Pridėti žymą"), - ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), - ("Network error", "Tinklo klaida"), - ("Username missed", "Prarastas vartotojo vardas"), - ("Password missed", "Slaptažodis praleistas"), - ("Wrong credentials", "Klaidingi kredencialai"), - ("Edit Tag", "Redaguoti žymą"), - ("Unremember Password", "Neprisiminti slaptažodžio"), - ("Favorites", "Mėgstamiausi"), - ("Add to Favorites", "Įtraukti į adresyną"), - ("Remove from Favorites", "Pašalinti iš parankinių"), - ("Empty", "Tuščia"), - ("Invalid folder name", "Neteisingas aplanko pavadinimas"), - ("Socks5 Proxy", "Socks5 Proxy"), - ("Hostname", "Pagrindinio kompiuterio pavadinimas"), - ("Discovered", "Atrasta"), - ("install_daemon_tip", "install_daemon_tip"), - ("Remote ID", "Nuotolinis ID"), - ("Paste", "Įklijuoti"), - ("Paste here?", "Įklijuoti čia?"), - ("Are you sure to close the connection?", "Ar tikrai uždarysite ryšį?"), - ("Download new version", "Atsisiųsti naują versiją"), - ("Touch mode", "Palietimo režimas"), - ("Mouse mode", "Pelės režimas"), - ("One-Finger Tap", "Palietimas vienu pirštu"), - ("Left Mouse", "Kairė pelė"), - ("One-Long Tap", "Vienas palietimas"), - ("Two-Finger Tap", "Palietimas dviem pirštais"), - ("Right Mouse", "Dešinysis pelė"), - ("One-Finger Move", "Vieno piršto judesys"), - ("Double Tap & Move", "Dukart palieskite ir perkelkite"), - ("Mouse Drag", "Pelės vilkimas"), - ("Three-Finger vertically", "Trys pirštai vertikaliai"), - ("Mouse Wheel", "Pelės ratas"), - ("Two-Finger Move", "Dviejų pirštų judesys"), - ("Canvas Move", "Drobės perkėlimas"), - ("Pinch to Zoom", "Suimkite, kad padidintumėte"), - ("Canvas Zoom", "Drobės mastelis"), - ("Reset canvas", "Atstatyti drobę"), - ("No permission of file transfer", "Nėra leidimo perkelti failus"), - ("Note", "Pastaba"), - ("Connection", "Ryšys"), - ("Share Screen", "Bendrinti ekraną"), - ("Chat", "Pokalbis"), - ("Total", "Iš viso"), - ("items", "elementai"), - ("Selected", "Pasirinkta"), - ("Screen Capture", "Ekrano nuotrauka"), - ("Input Control", "Įvesties valdymas"), - ("Audio Capture", "Garso fiksavimas"), - ("File Connection", "Failo ryšys"), - ("Screen Connection", "Ekrano jungtis"), - ("Do you accept?", "Ar sutinki?"), - ("Open System Setting", "Atviros sistemos nustatymas"), - ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), - ("android_input_permission_tip1", "android_input_permission_tip1"), - ("android_input_permission_tip2", "android_input_permission_tip2"), - ("android_new_connection_tip", "android_new_connection_tip"), - ("android_service_will_start_tip", "android_service_will_start_tip"), - ("android_stop_service_tip", "android_stop_service_tip"), - ("android_version_audio_tip", "android_version_audio_tip"), - ("android_start_service_tip", "android_start_service_tip"), - ("android_permission_may_not_change_tip", "android_permission_may_not_change_tip"), - ("Account", "Paskyra"), - ("Overwrite", "Perrašyti"), - ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), - ("Quit", "Išeiti"), - ("doc_mac_permission", "doc_mac_permission"), - ("Help", "Pagalba"), - ("Failed", "Nepavyko"), - ("Succeeded", "Pavyko"), - ("Someone turns on privacy mode, exit", "Kažkas įjungia privatumo režimą, išeiti"), - ("Unsupported", "Nepalaikomas"), - ("Peer denied", "Atšaukė"), - ("Please install plugins", "Įdiekite papildinius"), - ("Peer exit", "Peer exit"), - ("Failed to turn off", "Nepavyko išjungti"), - ("Turned off", "Išjungė"), - ("In privacy mode", "Privatumo režimu"), - ("Out privacy mode", "Iš privatumo režimo"), - ("Language", "Kalba"), - ("Keep RustDesk background service", "Keep RustDesk foninė paslauga"), - ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), - ("android_open_battery_optimizations_tip", "android_open_battery_optimizations_tip"), - ("Start on Boot", "Start on Boot"), - ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), - ("Connection not allowed", "Ryšys neleidžiamas"), - ("Legacy mode", "Senasis režimas"), - ("Map mode", "Žemėlapio režimas"), - ("Translate mode", "Vertimo režimas"), - ("Use permanent password", "Naudoti nuolatinį slaptažodį"), - ("Use both passwords", "Naudoti abu slaptažodžius"), - ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), - ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), - ("Allow remote restart", "Leisti nuotolinį paleidimą iš naujo"), - ("Restart Remote Device", "Paleisti nuotolinį įrenginį iš naujo"), - ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo"), - ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), - ("remote_restarting_tip", "remote_restarting_tip"), - ("Copied", "Nukopijuota"), - ("Exit Fullscreen", "Išeiti per visą ekraną"), - ("Fullscreen", "Per visą ekraną"), - ("Mobile Actions", "Veiksmai mobiliesiems"), - ("Select Monitor", "Pasirinkite monitorių"), - ("Control Actions", "Valdymo veiksmai"), - ("Display Settings", "Ekrano nustatymai"), - ("Ratio", "Santykis"), - ("Image Quality", "Vaizdo kokybė"), - ("Scroll Style", "Slinkimo stilius"), - ("Show Menubar", "Rodyti meniu juostą"), - ("Hide Menubar", "Slėpti meniu juostą"), - ("Direct Connection", "Tiesioginis ryšys"), - ("Relay Connection", "Relės jungtis"), - ("Secure Connection", "Saugus ryšys"), - ("Insecure Connection", "Nesaugus ryšys"), - ("Scale original", "Pakeisti originalų mastelį"), - ("Scale adaptive", "Pritaikomas mastelis"), - ("General", "Bendra"), - ("Security", "Sauga"), - ("Theme", "Tema"), - ("Dark Theme", "Tamsioji tema"), - ("Light Theme", "Šviesi tema"), - ("Dark", "Tamsiai"), - ("Light", "Šviesa"), - ("Follow System", "Sekti sistemą"), - ("Enable hardware codec", "Įgalinti aparatūros kodeką"), - ("Unlock Security Settings", "Atrakinti saugos nustatymus"), - ("Enable Audio", "Įgalinti garsą"), - ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), - ("Server", "Serveris"), - ("Direct IP Access", "Tiesioginė IP prieiga"), - ("Proxy", "Įgaliotinis serveris"), - ("Apply", "Taikyti"), - ("Disconnect all devices?", "Atjungti visus įrenginius?"), - ("Clear", "Išvalyti"), - ("Audio Input Device", "Garso įvesties įrenginys"), - ("Deny remote access", "Uždrausti nuotolinę prieigą"), - ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), - ("Network", "Tinklas"), - ("Enable RDP", "Įgalinti KPP"), - ("Pin menubar", "Prisegti meniu juostą"), - ("Unpin menubar", "Atsegti meniu juostą"), - ("Recording", "Įrašymas"), - ("Directory", "Katalogas"), - ("Automatically record incoming sessions", "Automatiškai įrašyti gaunamus seansus"), - ("Change", "Keisti"), - ("Start session recording", "Pradėti seanso įrašymą"), - ("Stop session recording", "Sustabdyti seanso įrašymą"), - ("Enable Recording Session", "Įgalinti įrašymo seansą"), - ("Allow recording session", "Leisti įrašymo sesiją"), - ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), - ("Deny LAN Discovery", "Neleisti LAN atradimo"), - ("Write a message", "Rašyti žinutę"), - ("Prompt", "Prompt"), - ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), - ("elevated_foreground_window_tip", "iškeltas_foreground_window_tip"), - ("Disconnected", "Atjungtas"), - ("Other", "Kita"), - ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), - ("Keyboard Settings", "Klaviatūros nustatymai"), - ("Full Access", "Visa prieiga"), - ("Screen Share", "Ekrano bendrinimas"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland“ reikalinga naujesnė „Linux Distro“ versija. Išbandykite X11 darbalaukį arba pakeiskite OS."), - ("JumpLink", "JumpLink"), - ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikti lygiavertėje pusėje)."), - ("Show RustDesk", "Rodyti RustDesk"), - ("This PC", "Šis kompiuteris"), - ("or", "arba"), - ("Continue with", "Tęsti su"), - ("Elevate", "Pakelti"), - ("Zoom cursor", "Mastelio keitimo žymeklis"), - ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), - ("Accept sessions via click", "Priimti seansus spustelėjus"), - ("Accept sessions via both", "Priimti seansus per abu"), - ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), - ("One-time Password", "Vienkartinis slaptažodis"), - ("Use one-time password", "Naudoti vienkartinį slaptažodį"), - ("One-time password length", "Vienkartinio slaptažodžio ilgis"), - ("Request access to your device", "Prašyti prieigos prie įrenginio"), - ("Hide connection management window", "Slėpti ryšio valdymo langą"), - ("hide_cm_tip", "hide_cm_tip"), - ("wayland_experiment_tip", "wayland_experiment_tip"), - ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), - ("Skipped", "Praleisti"), - ("Add to Address Book", "Pridėti prie adresų knygos"), - ("Group", "Grupė"), - ("Search", "Paieška"), - ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), - ("Local keyboard type", "Vietinės klaviatūros tipas"), - ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), - ("software_render_tip", "software_render_tip"), - ("Always use software rendering", "Visada naudoti programinės įrangos atvaizdavimą"), - ("config_input", "config_input"), - ("config_microphone", "config_microphone"), - ("request_elevation_tip", "request_elevation_tip"), - ("Wait", "Laukti"), - ("Elevation Error", "Aukščio klaida"), - ("Ask the remote user for authentication", "Paklauskite nuotolinio vartotojo autentifikavimo"), - ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), - ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), - ("still_click_uac_tip", "still_click_uac_tip"), - ("Request Elevation", "Prašyti paaukštinimo"), - ("wait_accept_uac_tip", "wait_accept_uac_tip"), - ("Elevate successfully", "Pakelti sėkmingai"), - ("uppercase", "didžiosios raidės"), - ("lowercase", "mažosios raidės"), - ("digit", "skaitmenų"), - ("special character", "specialusis simbolis"), - ("length>=8", "ilgis>=8"), - ("Weak", "Silpnas"), - ("Medium", "Vidutinis"), - ("Strong", "Stiprus"), - ("Switch Sides", "Perjungti puses"), - ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jei norite bendrinti darbalaukį?"), - ("Display", "Ekranas"), - ("Default View Style", "Numatytasis peržiūros stilius"), - ("Default Scroll Style", "Numatytasis slinkties stilius"), - ("Default Image Quality", "Numatytoji vaizdo kokybė"), - ("Default Codec", "Numatytasis kodekas"), - ("Bitrate", "Bitrate"), - ("FPS", "FPS"), - ("Auto", "Automatinis"), - ("Other Default Options", "Kitos numatytosios parinktys"), - ("Voice call", "Balso skambutis"), - ("Text chat", "Teksto pokalbis"), - ("Stop voice call", "Sustabdyti balso skambutį"), - ("relay_hint_tip", "relay_hint_tip"), - ("Reconnect", "Prisijungti iš naujo"), - ("Codec", "Kodekas"), - ("Resolution", "Rezoliucija"), - ("No transfers in progress", "Nevyksta jokių pervedimų"), - ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), - ("idd_driver_tip", "idd_driver_tip"), - ("confirm_idd_driver_tip", "confirm_idd_driver_tip"), - ("RDP Settings", "RDP nustatymai"), - ("Sort by", "Rūšiuoti pagal"), - ("New Connection", "Naujas ryšys"), - ("Restore", "Atkurti"), - ("Minimize", "Sumažinti"), - ("Maximize", "Padidinti"), - ("Your Device", "Jūsų įrenginys"), - ("empty_recent_tip", "tuščias_paskutinis_patarimas"), - ("empty_favorite_tip", "empty_favorite_tip"), - ("empty_lan_tip", "empty_lan_tip"), - ("empty_address_book_tip", "empty_address_book_tip"), - ("eg: admin", "pvz.: administratorius"), - ("Empty Username", "Tuščias naudotojo vardas"), - ("Empty Password", "Tuščias slaptažodis"), - ("Me", "Aš"), - ("identical_file_tip", "identical_file_tip"), - ("show_monitors_tip", "show_monitors_tip"), - ("View Mode", "Peržiūros režimas"), - ("login_linux_tip", "login_linux_tip"), - ("verify_rustdesk_password_tip", "verify_rustdesk_password_tip"), - ("remember_account_tip", "remember_account_tip"), - ("os_account_desk_tip", "os_account_desk_tip"), - ("OS Account", "OS paskyra"), - ("another_user_login_title_tip", "another_user_login_title_tip"), - ("another_user_login_text_tip", "antro_user_login_text_tip"), - ("xorg_not_found_title_tip", "xorg_not_found_title_tip"), - ("xorg_not_found_text_tip", "xorg_not_found_text_tip"), - ("no_desktop_title_tip", "no_desktop_title_tip"), - ("no_desktop_text_tip", "no_desktop_text_tip"), - ].iter().cloned().collect(); -} \ No newline at end of file From b120f4383c21cb8f6785403db4512e2ac370b366 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:23:39 +0300 Subject: [PATCH 083/112] Add files via upload --- src/lang/lt.rs | 495 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/lang/lt.rs diff --git a/src/lang/lt.rs b/src/lang/lt.rs new file mode 100644 index 000000000..e21ff677d --- /dev/null +++ b/src/lang/lt.rs @@ -0,0 +1,495 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Būsena"), + ("Your Desktop", "Jūsų darbalaukis"), + ("desk_tip", "Jūsų darbalaukis pasiekiamas naudojant šį ID ir slaptažodį"), + ("Password", "Slaptažodis"), + ("Ready", "Pasiruošęs"), + ("Established", "Įsteigta"), + ("connecting_status", "Prisijungiama prie RustDesk tinklo..."), + ("Enable Service", "Įgalinti paslaugą"), + ("Start Service", "Pradėti paslaugą"), + ("Service is running", "Paslauga veikia"), + ("Service is not running", "Paslauga neveikia"), + ("not_ready_status", "Neprisijungęs. Patikrinkite ryšį."), + ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), + ("Transfer File", "Perkelti failą"), + ("Connect", "Prisijungti"), + ("Recent Sessions", "Naujausios sesijos"), + ("Address Book", "Adresų knyga"), + ("Confirmation", "Patvirtinimas"), + ("TCP Tunneling", "TCP tuneliavimas"), + ("Remove", "Pašalinti"), + ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), + ("Set your own password", "Nustatykite savo slaptažodį"), + ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), + ("Enable Clipboard", "Įgalinti iškarpinę"), + ("Enable File Transfer", "Įgalinti failų perdavimą"), + ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), + ("IP Whitelisting", "IP baltasis sąrašas"), + ("ID/Relay Server", "ID / perdavimo serveris"), + ("Import Server Config", "Importuoti serverio konfigūraciją"), + ("Export Server Config", "Eksportuoti serverio konfigūraciją"), + ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), + ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), + ("Invalid server configuration", "Netinkama serverio konfigūracija"), + ("Clipboard is empty", "Iškarpinė tuščia"), + ("Stop service", "Sustabdyti paslaugą"), + ("Change ID", "Keisti ID"), + ("Your new ID", "Jūsų naujasis ID"), + ("length %min% to %max%", "ilgis %min% iki %max%"), + ("starts with a letter", "prasideda raide"), + ("allowed characters", "leistini simboliai"), + ("id_change_tip", "Leidžiami tik simboliai a–z, A–Z, 0–9 ir _ (pabraukimas). Pirmoji raidė turi būti a-z, A-Z. Ilgis nuo 6 iki 16."), + ("Website", "Interneto svetainė"), + ("About", "Apie"), + ("Slogan_tip", "Sukurta su siela šiame beprotiškame pasaulyje!"), + ("Privacy Statement", "Privatumo pareiškimas"), + ("Mute", "Nutildyti"), + ("Build Date", "Sukūrimo data"), + ("Version", "Versija"), + ("Home", "Namai"), + ("Audio Input", "Garso įvestis"), + ("Enhancements", "Patobulinimai"), + ("Hardware Codec", "Aparatūros kodekas"), + ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), + ("ID Server", "ID serveris"), + ("Relay Server", "Perdavimo serveris"), + ("API Server", "API serveris"), + ("invalid_http", "Turi prasidėti http:// arba https://"), + ("Invalid IP", "Netinkamas IP"), + ("Invalid format", "Neteisingas formatas"), + ("server_not_support", "Serveris dar nepalaikomas"), + ("Not available", "Nepasiekiamas"), + ("Too frequent", "Per dažnai"), + ("Cancel", "Atšaukti"), + ("Skip", "Praleisti"), + ("Close", "Uždaryti"), + ("Retry", "Bandykite dar kartą"), + ("OK", "GERAI"), + ("Password Required", "Reikalingas slaptažodis"), + ("Please enter your password", "Prašome įvesti savo slaptažodį"), + ("Remember password", "Prisiminti slaptažodį"), + ("Wrong Password", "Neteisingas slaptažodis"), + ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), + ("Connection Error", "Ryšio klaida"), + ("Error", "Klaida"), + ("Reset by the peer", "Atmetė nuotolinis kompiuteris"), + ("Connecting...", "Jungiamasi..."), + ("Connection in progress. Please wait.", "Jungiamasi. Palaukite."), + ("Please try 1 minute later", "Prašome pabandyti po 1 minutės"), + ("Login Error", "Prisijungimo klaida"), + ("Successful", "Sėkmingai"), + ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), + ("Name", "Vardas"), + ("Type", "Tipas"), + ("Modified", "Pakeista"), + ("Size", "Dydis"), + ("Show Hidden Files", "Rodyti paslėptus failus"), + ("Receive", "Gauti"), + ("Send", "Siųsti"), + ("Refresh File", "Atnaujinti failą"), + ("Local", "Vietinis"), + ("Remote", "Nuotolinis"), + ("Remote Computer", "Nuotolinis kompiuteris"), + ("Local Computer", "Vietinis kompiuteris"), + ("Confirm Delete", "Patvirtinti ištrynimą"), + ("Delete", "Ištrinti"), + ("Properties", "Ypatybės"), + ("Multi Select", "Keli pasirinkimas"), + ("Select All", "Pasirinkti viską"), + ("Unselect All", "Atšaukti visų pasirinkimą"), + ("Empty Directory", "Tuščias katalogas"), + ("Not an empty directory", "Ne tuščias katalogas"), + ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), + ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), + ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), + ("Do this for all conflicts", "Taikyti visiems konfliktams"), + ("This is irreversible!", "Tai negrįžtama!"), + ("Deleting", "Ištrinama"), + ("files", "failai"), + ("Waiting", "Laukiu"), + ("Finished", "Baigta"), + ("Speed", "Greitis"), + ("Custom Image Quality", "Tinkinta vaizdo kokybė"), + ("Privacy mode", "Privatumo režimas"), + ("Block user input", "Blokuoti naudotojo įvestį"), + ("Unblock user input", "Atblokuoti naudotojo įvestį"), + ("Adjust Window", "Koreguoti langą"), + ("Original", "Originalas"), + ("Shrink", "Susitraukti"), + ("Stretch", "Ištempti"), + ("Scrollbar", "Slinkties juosta"), + ("ScrollAuto", "Automatinis slinkimas"), + ("Good image quality", "Gera vaizdo kokybė"), + ("Balanced", "Subalansuotas"), + ("Optimize reaction time", "Optimizuoti reakcijos laiką"), + ("Custom", "Tinkintas"), + ("Show remote cursor", "Rodyti nuotolinį žymeklį"), + ("Show quality monitor", "Rodyti kokybės monitorių"), + ("Disable clipboard", "Išjungti mainų sritį"), + ("Lock after session end", "Užrakinti pasibaigus seansui"), + ("Insert", "Įdėti"), + ("Insert Lock", "Įterpti užraktą"), + ("Refresh", "Atnaujinti"), + ("ID does not exist", "ID neegzistuoja"), + ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), + ("Please try later", "Prašome pabandyti vėliau"), + ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), + ("Key mismatch", "Raktų neatitikimas"), + ("Timeout", "Laikas baigėsi"), + ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), + ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), + ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), + ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), + ("Set Password", "Nustatyti slaptažodį"), + ("OS Password", "OS slaptažodis"), + ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami išvengti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk savo sistemoje."), + ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), + ("Click to download", "Spustelėkite norėdami atsisiųsti"), + ("Click to update", "Spustelėkite norėdami atnaujinti"), + ("Configure", "Konfigūruoti"), + ("config_acc", "Norėdami nuotoliniu būdu valdyti darbalaukį, turite suteikti RustDesk \"prieigos\" leidimus"), + ("config_screen", "Norėdami nuotoliniu būdu pasiekti darbalaukį, turite suteikti RustDesk leidimus \"ekrano kopija\""), + ("Installing ...", "Diegiama ..."), + ("Install", "Diegti"), + ("Installation", "Įdiegimas"), + ("Installation Path", "Įdiegimo kelias"), + ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), + ("Create desktop icon", "Sukurti darbalaukio piktogramą"), + ("agreement_tip", "Pradėdami diegimą sutinkate su licencijos sutarties sąlygomis"), + ("Accept and Install", "Priimti ir įdiegti"), + ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), + ("Generating ...", "Generuojamas..."), + ("Your installation is lower version.", "Jūsų įdiegta versija senesnė."), + ("not_close_tcp_tip", "Naudodami tunelį neuždarykite šio lango"), + ("Listening ...", "Laukimas..."), + ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), + ("Remote Port", "Nuotolinis prievadas"), + ("Action", "Veiksmas"), + ("Add", "Papildyti"), + ("Local Port", "Vietinis prievadas"), + ("Local Address", "Vietinis adresas"), + ("Change Local Port", "Keisti vietinį prievadą"), + ("setup_server_tip", "Kad ryšys būtų greitesnis, nustatykite savo serverį"), + ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), + ("The confirmation is not identical.", "Patvirtinimas nėra tapatus."), + ("Permissions", "Leidimai"), + ("Accept", "Priimti"), + ("Dismiss", "Atmesti"), + ("Disconnect", "Atjungti"), + ("Allow using keyboard and mouse", "Leisti naudojant klaviatūrą ir pelę"), + ("Allow using clipboard", "Leisti naudoti mainų sritį"), + ("Allow hearing sound", "Leisti girdėti garsą"), + ("Allow file copy and paste", "Leisti failą kopijuoti ir įklijuoti"), + ("Connected", "Prisijungta"), + ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), + ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), + ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), + ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), + ("Enter Remote ID", "Įveskite nuotolinio ID"), + ("Enter your password", "Įveskite savo slaptažodį"), + ("Logging in...", "Prisijungiama..."), + ("Enable RDP session sharing", "Įgalinti RDP seansų bendrinimą"), + ("Auto Login", "Automatinis prisijungimas"), + ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), + ("Rename", "Pervardyti"), + ("Space", "Erdvė"), + ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), + ("Change Path", "Keisti kelią"), + ("Create Folder", "Sukurti aplanką"), + ("Please enter the folder name", "Įveskite aplanko pavadinimą"), + ("Fix it", "Pataisyk tai"), + ("Warning", "Įspėjimas"), + ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant Wayland nepalaikomas"), + ("Reboot required", "Reikia paleisti iš naujo"), + ("Unsupported display server", "Nepalaikomas rodymo serveris"), + ("x11 expected", "reikalingas x11"), + ("Port", "Prievadas"), + ("Settings", "Nustatymai"), + ("Username", "Vartotojo vardas"), + ("Invalid port", "Netinkamas prievadas"), + ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), + ("Enable remote configuration modification", "Įgalinti nuotolinį konfigūracijos modifikavimą"), + ("Run without install", "Vykdyti be diegimo"), + ("Connect via relay", "Prisijungti per relę"), + ("Always connect via relay", "Visada prisijunkite per relę"), + ("whitelist_tip", "Mane gali pasiekti tik baltajame sąraše esantys IP adresai"), + ("Login", "Prisijungti"), + ("Verify", "Patvirtinti"), + ("Remember me", "Prisimink mane"), + ("Trust this device", "Pasitikėk šiuo įrenginiu"), + ("Verification code", "Patvirtinimo kodas"), + ("verification_tip", "Aptiktas naujas įrenginys ir registruotu el. pašto adresu išsiųstas patvirtinimo kodas. Įveskite jį norėdami tęsti prisijungimą."), + ("Logout", "Atsijungti"), + ("Tags", "Žymos"), + ("Search ID", "Paieškos ID"), + ("whitelist_sep", "Atskirti kableliu, kabliataškiu, tarpu arba nauja eilute"), + ("Add ID", "Pridėti ID"), + ("Add Tag", "Pridėti žymą"), + ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), + ("Network error", "Tinklo klaida"), + ("Username missed", "Prarastas vartotojo vardas"), + ("Password missed", "Slaptažodis praleistas"), + ("Wrong credentials", "Klaidingi kredencialai"), + ("Edit Tag", "Redaguoti žymą"), + ("Unremember Password", "Neprisiminti slaptažodžio"), + ("Favorites", "Mėgstamiausi"), + ("Add to Favorites", "Įtraukti į parankinius"), + ("Remove from Favorites", "Pašalinti iš parankinių"), + ("Empty", "Tuščia"), + ("Invalid folder name", "Neteisingas aplanko pavadinimas"), + ("Socks5 Proxy", "Socks5 Proxy"), + ("Hostname", "Pagrindinio kompiuterio pavadinimas"), + ("Discovered", "Atrasta"), + ("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"), + ("Remote ID", "Nuotolinis ID"), + ("Paste", "Įklijuoti"), + ("Paste here?", "Įklijuoti čia?"), + ("Are you sure to close the connection?", "Ar tikrai norite atsijungti?"), + ("Download new version", "Atsisiųsti naują versiją"), + ("Touch mode", "Palietimo režimas"), + ("Mouse mode", "Pelės režimas"), + ("One-Finger Tap", "Palietimas vienu pirštu"), + ("Left Mouse", "Kairysis pelės kl."), + ("One-Long Tap", "Vienas palietimas"), + ("Two-Finger Tap", "Palietimas dviem pirštais"), + ("Right Mouse", "Dešinysis pelės kl."), + ("One-Finger Move", "Vieno piršto judesys"), + ("Double Tap & Move", "Dukart palieskite ir perkelkite"), + ("Mouse Drag", "Pelės vilkimas"), + ("Three-Finger vertically", "Trys pirštai vertikaliai"), + ("Mouse Wheel", "Pelės ratukas"), + ("Two-Finger Move", "Dviejų pirštų judesys"), + ("Canvas Move", "Drobės perkėlimas"), + ("Pinch to Zoom", "Suimkite, kad padidintumėte"), + ("Canvas Zoom", "Drobės mastelis"), + ("Reset canvas", "Atstatyti drobę"), + ("No permission of file transfer", "Nėra leidimo perkelti failus"), + ("Note", "Pastaba"), + ("Connection", "Ryšys"), + ("Share Screen", "Bendrinti ekraną"), + ("Chat", "Pokalbis"), + ("Total", "Iš viso"), + ("items", "elementai"), + ("Selected", "Pasirinkta"), + ("Screen Capture", "Ekrano nuotrauka"), + ("Input Control", "Įvesties valdymas"), + ("Audio Capture", "Garso fiksavimas"), + ("File Connection", "Failo ryšys"), + ("Screen Connection", "Ekrano jungtis"), + ("Do you accept?", "Ar sutinki?"), + ("Open System Setting", "Atviros sistemos nustatymas"), + ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), + ("android_input_permission_tip1", "Kad nuotolinis įrenginys galėtų valdyti Android įrenginį pele arba liesti, turite leisti RustDesk naudoti \"Prieinamumo\" paslaugą."), + ("android_input_permission_tip2", "Eikite į kitą sistemos nustatymų puslapį, suraskite \"Įdiegtos paslaugos\" ir įgalinkite \"RustDesk įvestis\" paslaugą."), + ("android_new_connection_tip", "Gauta nauja užklausa tvarkyti dabartinį įrenginį."), + ("android_service_will_start_tip", "Įgalinus ekrano fiksavimo paslaugą, kiti įrenginiai gali pateikti užklausą prisijungti prie to įrenginio."), + ("android_stop_service_tip", "Uždarius paslaugą automatiškai bus uždaryti visi užmegzti ryšiai."), + ("android_version_audio_tip", "Dabartinė Android versija nepalaiko garso įrašymo, atnaujinkite į Android 10 ar naujesnę versiją."), + ("android_start_service_tip", "Spustelėkite [Paleisti paslaugą] arba įjunkite [Fiksuoti ekraną], kad paleistumėte ekrano bendrinimo paslaugą."), + ("android_permission_may_not_change_tip", "Užmegztų ryšių leidimų keisti negalima, reikia prisijungti iš naujo."), + ("Account", "Paskyra"), + ("Overwrite", "Perrašyti"), + ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), + ("Quit", "Išeiti"), + ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/"), + ("Help", "Pagalba"), + ("Failed", "Nepavyko"), + ("Succeeded", "Pavyko"), + ("Someone turns on privacy mode, exit", "Kažkas įjungė privatumo režimą, išeiti"), + ("Unsupported", "Nepalaikomas"), + ("Peer denied", "Atšaukė"), + ("Please install plugins", "Įdiekite papildinius"), + ("Peer exit", "Nuotolinis mazgas neveikia"), + ("Failed to turn off", "Nepavyko išjungti"), + ("Turned off", "Išjungti"), + ("In privacy mode", "Privatumo režimas"), + ("Out privacy mode", "Išėjimas iš privatumo režimo"), + ("Language", "Kalba"), + ("Keep RustDesk background service", "Palikti RustDesk fonine paslauga"), + ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), + ("android_open_battery_optimizations_tip", "Eikite į kitą nustatymų puslapį"), + ("Start on Boot", "Pradėti paleidžiant"), + ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), + ("Connection not allowed", "Ryšys neleidžiamas"), + ("Legacy mode", "Senasis režimas"), + ("Map mode", "Žemėlapio režimas"), + ("Translate mode", "Vertimo režimas"), + ("Use permanent password", "Naudoti nuolatinį slaptažodį"), + ("Use both passwords", "Naudoti abu slaptažodžius"), + ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), + ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), + ("Allow remote restart", "Leisti nuotolinį paleidimą iš naujo"), + ("Restart Remote Device", "Paleisti nuotolinį įrenginį iš naujo"), + ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo"), + ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), + ("remote_restarting_tip", "Nuotolinis įrenginys paleidžiamas iš naujo. Uždarykite šį pranešimą ir po kurio laiko vėl prisijunkite naudodami nuolatinį slaptažodį."), + ("Copied", "Nukopijuota"), + ("Exit Fullscreen", "Išeiti iš pilno ekrano"), + ("Fullscreen", "Per visą ekraną"), + ("Mobile Actions", "Veiksmai mobiliesiems"), + ("Select Monitor", "Pasirinkite monitorių"), + ("Control Actions", "Valdymo veiksmai"), + ("Display Settings", "Ekrano nustatymai"), + ("Ratio", "Santykis"), + ("Image Quality", "Vaizdo kokybė"), + ("Scroll Style", "Slinkimo stilius"), + ("Show Menubar", "Rodyti meniu juostą"), + ("Hide Menubar", "Slėpti meniu juostą"), + ("Direct Connection", "Tiesioginis ryšys"), + ("Relay Connection", "Tarpinė jungtis"), + ("Secure Connection", "Saugus ryšys"), + ("Insecure Connection", "Nesaugus ryšys"), + ("Scale original", "Pakeisti originalų mastelį"), + ("Scale adaptive", "Pritaikomas mastelis"), + ("General", "Bendra"), + ("Security", "Sauga"), + ("Theme", "Tema"), + ("Dark Theme", "Tamsioji tema"), + ("Light Theme", "Šviesi tema"), + ("Dark", "Tamsiai"), + ("Light", "Šviesiai"), + ("Follow System", "Sekti sistemą"), + ("Enable hardware codec", "Įgalinti aparatūrinį kodavimą"), + ("Unlock Security Settings", "Atrakinti saugos nustatymus"), + ("Enable Audio", "Įgalinti garsą"), + ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), + ("Server", "Serveris"), + ("Direct IP Access", "Tiesioginė IP prieiga"), + ("Proxy", "Įgaliotinis serveris"), + ("Apply", "Taikyti"), + ("Disconnect all devices?", "Atjungti visus įrenginius?"), + ("Clear", "Išvalyti"), + ("Audio Input Device", "Garso įvesties įrenginys"), + ("Deny remote access", "Uždrausti nuotolinę prieigą"), + ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), + ("Network", "Tinklas"), + ("Enable RDP", "Įgalinti RDP"), + ("Pin menubar", "Prisegti meniu juostą"), + ("Unpin menubar", "Atsegti meniu juostą"), + ("Recording", "Įrašymas"), + ("Directory", "Katalogas"), + ("Automatically record incoming sessions", "Automatiškai įrašyti įeinančius seansus"), + ("Change", "Keisti"), + ("Start session recording", "Pradėti seanso įrašymą"), + ("Stop session recording", "Sustabdyti seanso įrašymą"), + ("Enable Recording Session", "Įgalinti įrašymo seansą"), + ("Allow recording session", "Leisti įrašymo sesiją"), + ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), + ("Deny LAN Discovery", "Neleisti LAN aptikimo"), + ("Write a message", "Rašyti žinutę"), + ("Prompt", "Užuomina"), + ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), + ("elevated_foreground_window_tip", "Dabartinis nuotolinio darbalaukio langas reikalauja didesnių privilegijų, todėl laikinai neįmanoma naudoti pelės ir klaviatūros. Galite paprašyti nuotolinio vartotojo sumažinti dabartinį langą arba spustelėti aukščio mygtuką ryšio valdymo lange. Norint išvengti šios problemos ateityje, rekomenduojama programinę įrangą įdiegti nuotoliniame įrenginyje."), + ("Disconnected", "Atjungtas"), + ("Other", "Kita"), + ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), + ("Keyboard Settings", "Klaviatūros nustatymai"), + ("Full Access", "Pilna prieiga"), + ("Screen Share", "Ekrano bendrinimas"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland reikalinga naujesnės Linux Distro versijos. Išbandykite X11 darbalaukį arba pakeiskite OS."), + ("JumpLink", "Peržiūra"), + ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikiantį kitoje pusėje)."), + ("Show RustDesk", "Rodyti RustDesk"), + ("This PC", "Šis kompiuteris"), + ("or", "arba"), + ("Continue with", "Tęsti su"), + ("Elevate", "Pakelti"), + ("Zoom cursor", "Mastelio keitimo žymeklis"), + ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), + ("Accept sessions via click", "Priimti seansus spustelėjus"), + ("Accept sessions via both", "Priimti seansus per abu"), + ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), + ("One-time Password", "Vienkartinis slaptažodis"), + ("Use one-time password", "Naudoti vienkartinį slaptažodį"), + ("One-time password length", "Vienkartinio slaptažodžio ilgis"), + ("Request access to your device", "Prašyti prieigos prie įrenginio"), + ("Hide connection management window", "Slėpti ryšio valdymo langą"), + ("hide_cm_tip", "Leisti paslėpti didžiąją ir mažąją raidę, jei priimamos slaptažodžio sesijos arba naudojamas nuolatinis slaptažodis"), + ("wayland_experiment_tip", "Wayland palaikymas yra eksperimentinis, naudokite X11, jei jums reikalingas automatinis prisijungimas."), + ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), + ("Skipped", "Praleisti"), + ("Add to Address Book", "Pridėti prie adresų knygos"), + ("Group", "Grupė"), + ("Search", "Paieška"), + ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), + ("Local keyboard type", "Vietinės klaviatūros tipas"), + ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), + ("software_render_tip", "Jei turite Nvidia vaizdo plokštę ir nuotolinis langas iškart užsidaro prisijungus, gali padėti „Nouveau“ tvarkyklės įdiegimas ir programinės įrangos atvaizdavimo pasirinkimas. Būtina paleisti iš naujo."), + ("Always use software rendering", "Visada naudoti programinį spartintuvą"), + ("config_input", "Norėdami valdyti nuotolinį darbalaukį naudodami klaviatūrą, turite suteikti RustDesk leidimus \"Įvesties monitoringas\"."), + ("config_microphone", "Norėdami kalbėtis su nuotoline puse, turite suteikti RustDesk leidimą \"Įrašyti garsą\"."), + ("request_elevation_tip", "Taip pat galite prašyti tesių suteikimo, jeigu kas nors yra nuotolinėje pusėje."), + ("Wait", "Laukti"), + ("Elevation Error", "Teisių suteikimo klaida"), + ("Ask the remote user for authentication", "Klauskite nuotolinio vartotojo autentifikavimo"), + ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), + ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), + ("still_click_uac_tip", "Vis tiek reikia, kad nuotolinis vartotojas paleidžiant RustDesk UAC lange paspaustų \"OK\"."), + ("Request Elevation", "Prašyti teisių"), + ("wait_accept_uac_tip", "Palaukite, kol nuotolinis vartotojas patvirtins UAC užklausą."), + ("Elevate successfully", "Teisės suteiktos"), + ("uppercase", "didžiosios raidės"), + ("lowercase", "mažosios raidės"), + ("digit", "skaitmuo"), + ("special character", "specialusis simbolis"), + ("length>=8", "ilgis>=8"), + ("Weak", "Silpnas"), + ("Medium", "Vidutinis"), + ("Strong", "Stiprus"), + ("Switch Sides", "Perjungti puses"), + ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jeigu norite bendrinti darbalaukį?"), + ("Display", "Ekranas"), + ("Default View Style", "Numatytasis peržiūros stilius"), + ("Default Scroll Style", "Numatytasis slinkties stilius"), + ("Default Image Quality", "Numatytoji vaizdo kokybė"), + ("Default Codec", "Numatytasis kodekas"), + ("Bitrate", "Sparta"), + ("FPS", "FPS"), + ("Auto", "Automatinis"), + ("Other Default Options", "Kitos numatytosios parinktys"), + ("Voice call", "Balso skambutis"), + ("Text chat", "Tekstinis pokalbis"), + ("Stop voice call", "Sustabdyti balso skambutį"), + ("relay_hint_tip", "Tiesioginis ryšys gali būti neįmanomas. Tokiu atveju galite pabandyti prisijungti per perdavimo serverį. \nArba, jei norite iš karto naudoti perdavimo serverį, prie ID galite pridėti priesagą \"/r\" arba nuotolinio pagrindinio kompiuterio nustatymuose įgalinti \"Visada prisijungti per relę\"."), + ("Reconnect", "Prisijungti iš naujo"), + ("Codec", "Kodekas"), + ("Resolution", "Rezoliucija"), + ("No transfers in progress", "Nevyksta jokių perdavimų"), + ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), + ("idd_driver_tip", "Įdiekite virtualaus ekrano tvarkyklę (naudojama, kai nėra fizinių ekranų)"), + ("confirm_idd_driver_tip", "Įjungta virtualaus ekrano tvarkyklės diegimo funkcija. Atminkite, kad bus įdiegtas bandomasis sertifikatas, kad būtų galima pasitikėti tvarkykle. Šis sertifikatas bus naudojamas tik pasitikėjimui Rustdesk tvarkyklėmis patikrinti."), + ("RDP Settings", "RDP nustatymai"), + ("Sort by", "Rūšiuoti pagal"), + ("New Connection", "Naujas ryšys"), + ("Restore", "Atkurti"), + ("Minimize", "Sumažinti"), + ("Maximize", "Padidinti"), + ("Your Device", "Jūsų įrenginys"), + ("empty_recent_tip", "Nėra paskutinių seansų!\nLaikas suplanuoti naują."), + ("empty_favorite_tip", "Dar neturite parankinių nuotolinių seansų."), + ("empty_lan_tip", "Nuotolinių mazgų nerasta."), + ("empty_address_book_tip", "Adresų knygelėje nėra nuotolinių kompiuterių."), + ("eg: admin", "pvz.: administratorius"), + ("Empty Username", "Tuščias naudotojo vardas"), + ("Empty Password", "Tuščias slaptažodis"), + ("Me", "Aš"), + ("identical_file_tip", "Failas yra identiškas nuotoliniame kompiuteryje esančiam failui."), + ("show_monitors_tip", "Rodyti monitorius įrankių juostoje"), + ("View Mode", "Peržiūros režimas"), + ("login_linux_tip", "Norėdami įjungti X darbalaukio seansą, turite būti prisijungę prie nuotolinės Linux paskyros."), + ("verify_rustdesk_password_tip", "Patvirtinkite RustDesk slaptažodį"), + ("remember_account_tip", "Prisiminti šią paskyrą"), + ("os_account_desk_tip", "Ši paskyra naudojama norint prisijungti prie nuotolinės OS ir įgalinti darbalaukio seansą režimu headless"), + ("OS Account", "OS paskyra"), + ("another_user_login_title_tip", "Kitas vartotojas jau yra prisijungęs"), + ("another_user_login_text_tip", "Atjungti"), + ("xorg_not_found_title_tip", "Xorg nerastas"), + ("xorg_not_found_text_tip", "Prašom įdiegti Xorg"), + ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), + ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), + ].iter().cloned().collect(); +} \ No newline at end of file From 6c78e7bf7427223abdbd91585704af2f4a7cec8d Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 09:51:41 +0800 Subject: [PATCH 084/112] fix mobile show chat window Signed-off-by: fufesou --- flutter/lib/common/widgets/overlay.dart | 9 +++++++++ flutter/lib/desktop/pages/remote_page.dart | 7 +------ flutter/lib/mobile/pages/remote_page.dart | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index c67f0f7fb..8c44e7ee4 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -388,6 +388,15 @@ class BlockableOverlayState extends OverlayKeyState { _middleBlocked.value = blocked; } } + + void applyFfi(FFI ffi) { + ffi.dialogManager.setOverlayState(this); + ffi.chatModel.setOverlayState(this); + // make remote page penetrable automatically, effective for chat over remote + onMiddleBlockedClick = () { + setMiddleBlocked(false); + }; + } } class BlockableOverlay extends StatelessWidget { diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 3bc450345..312f9bbed 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -158,12 +158,7 @@ class _RemotePageState extends State // _isCustomCursorInited = true; // } - _ffi.dialogManager.setOverlayState(_blockableOverlayState); - _ffi.chatModel.setOverlayState(_blockableOverlayState); - // make remote page penetrable automatically, effective for chat over remote - _blockableOverlayState.onMiddleBlockedClick = () { - _blockableOverlayState.setMiddleBlocked(false); - }; + _blockableOverlayState.applyFfi(_ffi); } @override diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 776aa5455..04852c98c 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -20,7 +20,6 @@ import '../../models/input_model.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import '../../utils/image.dart'; -import '../widgets/dialog.dart'; import '../widgets/gestures.dart'; final initText = '\1' * 1024; @@ -43,6 +42,8 @@ class _RemotePageState extends State { double _mouseScrollIntegral = 0; // mouse scroll speed controller Orientation? _currentOrientation; + final _blockableOverlayState = BlockableOverlayState(); + final keyboardVisibilityController = KeyboardVisibilityController(); late final StreamSubscription keyboardSubscription; final FocusNode _mobileFocusNode = FocusNode(); @@ -67,6 +68,7 @@ class _RemotePageState extends State { gFFI.qualityMonitorModel.checkShowQualityMonitor(widget.id); keyboardSubscription = keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged); + _blockableOverlayState.applyFfi(gFFI); } @override From f083c0f9fe16dc62cfc889a63755648dcd2b54ff Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 3 Apr 2023 20:35:09 +0800 Subject: [PATCH 085/112] opt: simplify release names --- .github/workflows/flutter-build.yml | 66 +++++++++++++---------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index f94e2dc9c..5e230f20f 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -34,7 +34,7 @@ jobs: job: # - { target: i686-pc-windows-msvc , os: windows-2019 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: x86_64-pc-windows-msvc, os: windows-2019 } + - { target: x86_64-pc-windows-msvc, os: windows-2019, arch: x86_64 } # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } steps: - name: Checkout source code @@ -107,14 +107,7 @@ jobs: python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe popd mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe - - # - name: Rename rustdesk - # shell: bash - # run: | - # for name in rustdesk*??-install.exe; do - # mv "$name" ./SignOutput/"${name%%-install.exe}-${{ matrix.job.target }}.exe" - # done + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.exe - name: Sign rustdesk self-extracted file uses: GermanBluefox/code-sign-action@v7 @@ -148,7 +141,7 @@ jobs: job: # - { target: i686-pc-windows-msvc , os: windows-2019 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: i686-pc-windows-msvc, os: windows-2019 } + - { target: i686-pc-windows-msvc, os: windows-2019, arch: x86 } # - { target: aarch64-pc-windows-msvc, os: windows-2019 } steps: - name: Checkout source code @@ -218,7 +211,7 @@ jobs: python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe popd mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.exe - name: Sign rustdesk self-extracted file uses: GermanBluefox/code-sign-action@v7 @@ -250,6 +243,7 @@ jobs: target: x86_64-apple-darwin, os: macos-latest, extra-build-args: "", + arch: x86_64 } steps: - name: Checkout source code @@ -358,7 +352,7 @@ jobs: - name: Rename rustdesk run: | for name in rustdesk*??.dmg; do - mv "$name" "${name%%.dmg}-${{ matrix.job.target }}.dmg" + mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}.dmg" done - name: Publish DMG package @@ -368,7 +362,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk*-${{ matrix.job.target }}.dmg + rustdesk*-${{ matrix.job.arch }}.dmg build-vcpkg-deps-linux: uses: ./.github/workflows/vcpkg-deps-linux.yml @@ -385,14 +379,14 @@ jobs: matrix: job: - { - arch: x86_64, + arch: aarch64, target: aarch64-linux-android, os: ubuntu-20.04, extra-build-features: "", openssl-arch: android-arm64 } - { - arch: x86_64, + arch: armv7, target: armv7-linux-androideabi, os: ubuntu-20.04, extra-build-features: "", @@ -481,7 +475,7 @@ jobs: # build flutter pushd flutter flutter build apk --release --target-platform android-arm64 --split-per-abi - mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ;; armv7-linux-androideabi) mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a @@ -490,12 +484,12 @@ jobs: # build flutter pushd flutter flutter build apk --release --target-platform android-arm --split-per-abi - mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ;; esac popd mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk . + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk . - uses: r0adkll/sign-android-release@v1 name: Sign app APK @@ -515,7 +509,7 @@ jobs: if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' uses: actions/upload-artifact@master with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - name: Publish signed apk package @@ -534,7 +528,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk build-rustdesk-lib-linux-amd64: needs: [generate-bridge-linux, build-vcpkg-deps-linux] @@ -986,7 +980,7 @@ jobs: run: | for name in rustdesk*??.deb; do # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb" + cp "$name" "${name%%.deb}--${{ matrix.job.arch }}-sciter.deb" done - name: Publish debian package @@ -996,14 +990,14 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb - name: Upload Artifact uses: actions/upload-artifact@master if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb build-rustdesk-linux-arm: needs: [build-rustdesk-lib-linux-arm] @@ -1159,7 +1153,7 @@ jobs: shell: bash run: | for name in rustdesk*??.deb; do - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + cp "$name" "${name%%.deb}-${{ matrix.job.arch }}.deb" done - name: Publish debian package @@ -1169,7 +1163,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Build appimage package if: ${{ matrix.job.extra-build-features == 'appimage' }} @@ -1198,8 +1192,8 @@ jobs: uses: actions/upload-artifact@master if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Patch archlinux PKGBUILD if: ${{ matrix.job.extra-build-features == '' }} @@ -1379,7 +1373,7 @@ jobs: run: | for name in rustdesk*??.deb; do # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + cp "$name" "${name%%.deb}-${{ matrix.job.arch }}.deb" done - name: Publish debian package @@ -1389,14 +1383,14 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Upload Artifact uses: actions/upload-artifact@master if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Patch archlinux PKGBUILD if: ${{ matrix.job.extra-build-features == '' }} @@ -1559,12 +1553,12 @@ jobs: - name: Download Binary uses: actions/download-artifact@master with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb path: . - name: Rename Binary run: | - mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb + mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb rustdesk-${{ env.VERSION }}.deb - uses: Kingtous/run-on-arch-action@amd64-support name: Build rustdesk flatpak package for ${{ matrix.job.arch }} @@ -1596,7 +1590,7 @@ jobs: pushd flatpak git clone https://github.com/flathub/shared-modules.git --depth=1 flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json - flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak com.rustdesk.RustDesk + flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk - name: Publish flatpak package uses: softprops/action-gh-release@v1 @@ -1605,4 +1599,4 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak + flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak From f56adbb56e9edbe419409ec94a09bc9a0356ff41 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 18:35:01 +0800 Subject: [PATCH 086/112] fix wayland map mode Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 33 +++++++++++++---------------- src/flutter_ffi.rs | 6 +++--- src/ui_session_interface.rs | 16 +++++++------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 95f0f2634..f18f63647 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -117,44 +117,41 @@ class InputModel { } void mapKeyboardMode(RawKeyEvent e) { - int scanCode; - int keyCode; + int positionCode = -1; + int platformCode = -1; bool down; if (e.data is RawKeyEventDataMacOs) { RawKeyEventDataMacOs newData = e.data as RawKeyEventDataMacOs; - scanCode = newData.keyCode; - keyCode = newData.keyCode; + positionCode = newData.keyCode; + platformCode = newData.keyCode; } else if (e.data is RawKeyEventDataWindows) { RawKeyEventDataWindows newData = e.data as RawKeyEventDataWindows; - scanCode = newData.scanCode; - keyCode = newData.keyCode; + positionCode = newData.scanCode; + platformCode = newData.keyCode; } else if (e.data is RawKeyEventDataLinux) { RawKeyEventDataLinux newData = e.data as RawKeyEventDataLinux; // scanCode and keyCode of RawKeyEventDataLinux are incorrect. // 1. scanCode means keycode // 2. keyCode means keysym - scanCode = 0; - keyCode = newData.scanCode; + positionCode = newData.scanCode; + platformCode = newData.keyCode; } else if (e.data is RawKeyEventDataAndroid) { RawKeyEventDataAndroid newData = e.data as RawKeyEventDataAndroid; - scanCode = newData.scanCode + 8; - keyCode = newData.keyCode; - } else { - scanCode = -1; - keyCode = -1; - } + positionCode = newData.scanCode + 8; + platformCode = newData.keyCode; + } else {} if (e is RawKeyDownEvent) { down = true; } else { down = false; } - inputRawKey(e.character ?? '', keyCode, scanCode, down); + inputRawKey(e.character ?? '', platformCode, positionCode, down); } /// Send raw Key Event - void inputRawKey(String name, int keyCode, int scanCode, bool down) { + void inputRawKey(String name, int platformCode, int positionCode, bool down) { const capslock = 1; const numlock = 2; const scrolllock = 3; @@ -174,8 +171,8 @@ class InputModel { bind.sessionHandleFlutterKeyEvent( id: id, name: name, - keycode: keyCode, - scancode: scanCode, + platformCode: platformCode, + positionCode: positionCode, lockModes: lockModes, downOrUp: down); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 6d4e468ab..a8fe66c8a 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -332,13 +332,13 @@ pub fn session_switch_display(id: String, value: i32) { pub fn session_handle_flutter_key_event( id: String, name: String, - keycode: i32, - scancode: i32, + platform_code: i32, + position_code: i32, lock_modes: i32, down_or_up: bool, ) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.handle_flutter_key_event(&name, keycode, scancode, lock_modes, down_or_up); + session.handle_flutter_key_event(&name, platform_code, position_code, lock_modes, down_or_up); } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 796e13246..f7a4a7334 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -529,19 +529,19 @@ impl Session { pub fn handle_flutter_key_event( &self, _name: &str, - keycode: i32, - scancode: i32, + platform_code: i32, + position_code: i32, lock_modes: i32, down_or_up: bool, ) { - if scancode < 0 || keycode < 0 { + if position_code < 0 || platform_code < 0 { return; } - let keycode: KeyCode = keycode as _; - let scancode: u32 = scancode as _; + let platform_code: KeyCode = platform_code as _; + let position_code: u32 = position_code as _; #[cfg(not(target_os = "windows"))] - let key = rdev::key_from_code(keycode) as rdev::Key; + let key = rdev::key_from_code(position_code) as rdev::Key; // Windows requires special handling #[cfg(target_os = "windows")] let key = rdev::get_win_key(keycode, scancode); @@ -554,8 +554,8 @@ impl Session { let event = Event { time: SystemTime::now(), unicode: None, - platform_code: keycode as _, - position_code: scancode as _, + platform_code: platform_code as _, + position_code: position_code as _, event_type: event_type, }; keyboard::client::process_event(&event, Some(lock_modes)); From f2aeff974cbc4e9cc7ce3e53e5064e96b2110c0f Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 4 Apr 2023 20:35:04 +0800 Subject: [PATCH 087/112] disable hwcodec if causing crash Signed-off-by: 21pages --- libs/hbb_common/src/platform/mod.rs | 10 ++++++++++ libs/scrap/src/common/codec.rs | 23 +++++++++++++---------- src/ui_session_interface.rs | 2 +- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index c37c8aaf5..c1c766753 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -29,6 +29,16 @@ extern "C" fn breakdown_signal_handler(sig: i32) { info = "Always use software rendering will be set.".to_string(); log::info!("{}", info); } + if stack.iter().any(|s| { + s.to_lowercase().contains("nvidia") + || s.to_lowercase().contains("amf") + || s.to_lowercase().contains("mfx") + || s.contains("cuProfilerStop") + }) { + Config::set_option("enable-hwcodec".to_string(), "N".to_string()); + info = "Perhaps hwcodec causing the crash, disable it first".to_string(); + log::info!("{}", info); + } log::error!( "Got signal {} and exit. stack:\n{}", sig, diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 3209933b4..a3d8a0696 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -109,6 +109,7 @@ impl Encoder { }), Err(e) => { check_config_process(true); + *CODEC_NAME.lock().unwrap() = CodecName::VP9; Err(e) } }, @@ -144,16 +145,18 @@ impl Encoder { let mut h265_name = None; #[cfg(feature = "hwcodec")] { - let best = HwEncoder::best(); - let h264_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); - let h265_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); - if h264_useable { - h264_name = best.h264.map_or(None, |c| Some(c.name)); - } - if h265_useable { - h265_name = best.h265.map_or(None, |c| Some(c.name)); + if enable_hwcodec_option() { + let best = HwEncoder::best(); + let h264_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); + let h265_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); + if h264_useable { + h264_name = best.h264.map_or(None, |c| Some(c.name)); + } + if h265_useable { + h265_name = best.h265.map_or(None, |c| Some(c.name)); + } } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f7a4a7334..18ef27cc0 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -544,7 +544,7 @@ impl Session { let key = rdev::key_from_code(position_code) as rdev::Key; // Windows requires special handling #[cfg(target_os = "windows")] - let key = rdev::get_win_key(keycode, scancode); + let key = rdev::get_win_key(platform_code, position_code); let event_type = if down_or_up { KeyPress(key) From be2e26e758a3770c4da4176330ec8a863450f3bc Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 4 Apr 2023 21:21:00 +0800 Subject: [PATCH 088/112] delete RustDesk_hwcodec.toml on every check Signed-off-by: 21pages --- libs/scrap/src/common/codec.rs | 2 +- libs/scrap/src/common/hwcodec.rs | 8 +++----- src/server.rs | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index a3d8a0696..a4b7c27f8 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -108,7 +108,7 @@ impl Encoder { codec: Box::new(hw), }), Err(e) => { - check_config_process(true); + check_config_process(); *CODEC_NAME.lock().unwrap() = CodecName::VP9; Err(e) } diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index f4de4bf84..8daa6551c 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -199,7 +199,7 @@ impl HwDecoder { } } if fail { - check_config_process(true); + check_config_process(); } HwDecoders { h264, h265 } } @@ -323,13 +323,11 @@ pub fn check_config() { log::error!("Failed to serialize codec info"); } -pub fn check_config_process(force_reset: bool) { +pub fn check_config_process() { use hbb_common::sysinfo::{ProcessExt, System, SystemExt}; std::thread::spawn(move || { - if force_reset { - HwCodecConfig::remove(); - } + HwCodecConfig::remove(); if let Ok(exe) = std::env::current_exe() { if let Some(file_name) = exe.file_name().to_owned() { let s = System::new_all(); diff --git a/src/server.rs b/src/server.rs index 681e7bed1..9c7fb2260 100644 --- a/src/server.rs +++ b/src/server.rs @@ -389,7 +389,7 @@ pub async fn start_server(is_server: bool) { use std::sync::Once; static ONCE: Once = Once::new(); ONCE.call_once(|| { - scrap::hwcodec::check_config_process(false); + scrap::hwcodec::check_config_process(); }) } From c4fec2c19ef13c60a50ca33957cff243f9e58524 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 22:52:33 +0800 Subject: [PATCH 089/112] fix build macos Signed-off-by: fufesou --- src/ui_session_interface.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 18ef27cc0..678db0326 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -537,8 +537,8 @@ impl Session { if position_code < 0 || platform_code < 0 { return; } - let platform_code: KeyCode = platform_code as _; - let position_code: u32 = position_code as _; + let platform_code: u32 = platform_code as _; + let position_code: KeyCode = position_code as _; #[cfg(not(target_os = "windows"))] let key = rdev::key_from_code(position_code) as rdev::Key; @@ -554,7 +554,7 @@ impl Session { let event = Event { time: SystemTime::now(), unicode: None, - platform_code: platform_code as _, + platform_code: platform_code, position_code: position_code as _, event_type: event_type, }; From c4d883c0831a899fc2c9dd8a4c4c78f1b8364b0a Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 23:04:09 +0800 Subject: [PATCH 090/112] trivial changes Signed-off-by: fufesou --- src/ui_session_interface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 678db0326..3a0ae6598 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -554,9 +554,9 @@ impl Session { let event = Event { time: SystemTime::now(), unicode: None, - platform_code: platform_code, + platform_code, position_code: position_code as _, - event_type: event_type, + event_type, }; keyboard::client::process_event(&event, Some(lock_modes)); } From 23732eb8f37d4cc0a533f0afc122b7ea89ecc0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=85=C3=BC?= <53787985+LaiYueTing@users.noreply.github.com> Date: Wed, 5 Apr 2023 01:32:19 +0800 Subject: [PATCH 091/112] Update tw.rs --- src/lang/tw.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index bff471b37..1eb74d7b2 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -480,16 +480,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此檔案與對方的檔案一致"), ("show_monitors_tip", "在工具列中顯示顯示器"), ("View Mode", "瀏覽模式"), - ("login_linux_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("login_linux_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面"), + ("verify_rustdesk_password_tip", "驗證 RustDesk 密碼"), + ("remember_account_tip", "記住此使用者帳戶"), + ("os_account_desk_tip", "此使用者帳戶將用於登入遠端作業系統並啟用無頭模式的桌面連線"), + ("OS Account", "作業系統使用者帳戶"), + ("another_user_login_title_tip", "另一個使用者已經登入"), + ("another_user_login_text_tip", "斷開連線"), + ("xorg_not_found_title_tip", "未找到 Xorg"), + ("xorg_not_found_text_tip", "請安裝 Xorg"), + ("no_desktop_title_tip", "沒有可用的桌面"), + ("no_desktop_text_tip", "請安裝 GNOME 桌面"), ].iter().cloned().collect(); } From 2c25be346c59da4fddacfb288df4e74019345711 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 5 Apr 2023 13:51:59 +0800 Subject: [PATCH 092/112] fix press/release single meta key Signed-off-by: fufesou --- src/lib.rs | 1 + src/server/input_service.rs | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5dcd6389c..a702e5f11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ /// cbindgen:ignore pub mod platform; mod keyboard; +pub use keyboard::keycode_to_rdev_key; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; #[cfg(not(any(target_os = "ios")))] diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 7d3229c12..fb04107ca 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1371,6 +1371,35 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { allow_err!(rdev::simulate_code(Some(keycode), None, down)); } +#[cfg(any(target_os = "android", target_os = "ios"))] +fn is_meta_key(_evt: &KeyEvent) -> bool { + false +} + +#[cfg(not(any(target_os = "android", target_os = "ios")))] +fn is_meta_key(evt: &KeyEvent) -> bool { + match evt.mode.unwrap() { + KeyboardMode::Map | KeyboardMode::Translate => match &evt.union { + Some(key_event::Union::ControlKey(ck)) => { + return *ck == ControlKey::Meta.into(); + } + Some(key_event::Union::Chr(code)) => { + let key = crate::keycode_to_rdev_key(*code); + return key == RdevKey::MetaLeft || key == RdevKey::MetaRight; + } + _ => {} + }, + KeyboardMode::Legacy => match &evt.union { + Some(key_event::Union::ControlKey(ck)) => { + return *ck == ControlKey::Meta.into(); + } + _ => {} + }, + _ => {} + } + false +} + pub fn handle_key_(evt: &KeyEvent) { if EXITING.load(Ordering::SeqCst) { return; @@ -1381,8 +1410,8 @@ pub fn handle_key_(evt: &KeyEvent) { Some(LockModesHandler::new(&evt)) } _ => { - if evt.down { - Some(LockModesHandler::new(&evt)) + if evt.down && !is_meta_key(evt) { + Some(LockModesHandler::new(evt)) } else { None } From acac3054410a1885593273c18f39b41f46c28124 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 5 Apr 2023 14:30:47 +0800 Subject: [PATCH 093/112] win, linux, single meta key Signed-off-by: fufesou --- src/server/input_service.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index fb04107ca..7ea12c965 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1371,13 +1371,13 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { allow_err!(rdev::simulate_code(Some(keycode), None, down)); } -#[cfg(any(target_os = "android", target_os = "ios"))] -fn is_meta_key(_evt: &KeyEvent) -> bool { +#[cfg(not(any(target_os = "windows", target_os = "linux")))] +fn is_win_linux_meta_key(_evt: &KeyEvent) -> bool { false } -#[cfg(not(any(target_os = "android", target_os = "ios")))] -fn is_meta_key(evt: &KeyEvent) -> bool { +#[cfg(any(target_os = "windows", target_os = "linux"))] +fn is_win_linux_meta_key(evt: &KeyEvent) -> bool { match evt.mode.unwrap() { KeyboardMode::Map | KeyboardMode::Translate => match &evt.union { Some(key_event::Union::ControlKey(ck)) => { @@ -1410,7 +1410,7 @@ pub fn handle_key_(evt: &KeyEvent) { Some(LockModesHandler::new(&evt)) } _ => { - if evt.down && !is_meta_key(evt) { + if evt.down && !is_win_linux_meta_key(evt) { Some(LockModesHandler::new(evt)) } else { None From d279588a64589bce9e5a364cf2003f32036f81f6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 5 Apr 2023 14:54:23 +0800 Subject: [PATCH 094/112] fix build Signed-off-by: fufesou --- src/lib.rs | 1 + src/server/input_service.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a702e5f11..45b4c63f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ /// cbindgen:ignore pub mod platform; mod keyboard; +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub use keyboard::keycode_to_rdev_key; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 7ea12c965..5fb63a597 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1410,6 +1410,9 @@ pub fn handle_key_(evt: &KeyEvent) { Some(LockModesHandler::new(&evt)) } _ => { + // LockModesHandler should not be created when single meta is pressing and releasing. + // Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. + // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 if evt.down && !is_win_linux_meta_key(evt) { Some(LockModesHandler::new(evt)) } else { From df73fde6bcbd34b0190e1ddb1b39c97c958d08c3 Mon Sep 17 00:00:00 2001 From: mehdi-song Date: Wed, 5 Apr 2023 07:21:27 +0000 Subject: [PATCH 095/112] Update fa.rs --- src/lang/fa.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lang/fa.rs b/src/lang/fa.rs index d78cc8b37..79f39154a 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -479,17 +479,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "من"), ("identical_file_tip", "این فایل با فایل همتا یکسان است."), ("show_monitors_tip", "نمایش مانیتورها در نوار ابزار"), - ("View Mode", ""), - ("login_linux_tip", ""), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("View Mode", "حالت مشاهده"), + ("login_linux_tip", "برای فعال کردن دسکتاپ X، باید به حساب لینوکس راه دور وارد شوید"), + ("verify_rustdesk_password_tip", "رمز عبور RustDesk را تأیید کنید"), + ("remember_account_tip", "این حساب را به خاطر بسپارید"), + ("os_account_desk_tip", "این حساب برای ورود به سیستم عامل راه دور و فعال کردن جلسه دسکتاپ در هدلس استفاده می شود"), + ("OS Account", "حساب کاربری سیستم عامل"), + ("another_user_login_title_tip", "کاربر دیگری قبلاً وارد شده است"), + ("another_user_login_text_tip", "قطع شدن"), + ("xorg_not_found_title_tip", "پیدا نشد Xorg"), + ("xorg_not_found_text_tip", "لطفا Xorg را نصب کنید"), + ("no_desktop_title_tip", "هیچ دسکتاپی در دسترس نیست"), + ("no_desktop_text_tip", "لطفا دسکتاپ گنوم را نصب کنید"), ].iter().cloned().collect(); } From f6189455ac6324e14c3cd010541bf86773af39da Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 6 Apr 2023 09:31:36 +0800 Subject: [PATCH 096/112] fix: arm architecture env --- .github/workflows/flutter-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 5e230f20f..1bc519f56 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -980,7 +980,7 @@ jobs: run: | for name in rustdesk*??.deb; do # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}--${{ matrix.job.arch }}-sciter.deb" + cp "$name" "${name%%.deb}-${{ matrix.job.arch }}-sciter.deb" done - name: Publish debian package @@ -1103,11 +1103,11 @@ jobs: # edit to corresponding arch case ${{ matrix.job.arch }} in aarch64) - sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py + export ARCH=arm64 sed -i "s/x64\/release/arm64\/release/g" ./build.py ;; armv7) - sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py + export ARCH=armhf sed -i "s/x64\/release/arm\/release/g" ./build.py ;; esac From bd1993e034a51ce3fef017df35e0d35e186536c9 Mon Sep 17 00:00:00 2001 From: junbinhe Date: Thu, 6 Apr 2023 11:39:14 +0800 Subject: [PATCH 097/112] Update devcontainer.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同步安卓ndk到23版本 --- .devcontainer/devcontainer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cd82c75e3..953196eb3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,7 +10,7 @@ "features": { "ghcr.io/devcontainers/features/java:1": {}, "ghcr.io/akhildevelops/devcontainer-features/android-cli:latest": { - "PACKAGES": "platform-tools,ndk;22.1.7171670" + "PACKAGES": "platform-tools,ndk;23.2.8568313" } }, "customizations": { @@ -31,4 +31,4 @@ } } } -} \ No newline at end of file +} From a65611da48a2c1630f6a5f79b92fdbf0c978e5af Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 6 Apr 2023 12:02:01 +0800 Subject: [PATCH 098/112] do not handle signal on debug Signed-off-by: fufesou --- libs/hbb_common/src/platform/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index c1c766753..b109ac683 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -65,6 +65,7 @@ pub fn register_breakdown_handler(callback: T) where T: Fn() + 'static, { + #[cfg(not(debug_assertions))] unsafe { GLOBAL_CALLBACK = Some(Box::new(callback)); libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); From 83cfa24bae0edcfcd0ac85a50bbcc8745c86103e Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 6 Apr 2023 12:02:49 +0800 Subject: [PATCH 099/112] suppress warn Signed-off-by: fufesou --- libs/hbb_common/src/platform/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index b109ac683..fd57476c0 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -61,13 +61,13 @@ extern "C" fn breakdown_signal_handler(sig: i32) { exit(0); } -pub fn register_breakdown_handler(callback: T) +pub fn register_breakdown_handler(_callback: T) where T: Fn() + 'static, { #[cfg(not(debug_assertions))] unsafe { - GLOBAL_CALLBACK = Some(Box::new(callback)); + GLOBAL_CALLBACK = Some(Box::new(_callback)); libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } From 15ea1366d414e8bbdec1b5fd33d370fe96964a04 Mon Sep 17 00:00:00 2001 From: junbinhe Date: Thu, 6 Apr 2023 12:57:42 +0800 Subject: [PATCH 100/112] Update Dockerfile ndk23 --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 32a440b28..08fd65544 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -38,7 +38,7 @@ RUN sudo apt-get install -y libclang-dev RUN sudo apt install -y gcc-multilib WORKDIR $WORKDIR -ENV ANDROID_NDK_HOME=/opt/android/ndk/22.1.7171670 +ENV ANDROID_NDK_HOME=/opt/android/ndk/23.2.8568313 # Somehow try to automate flutter pub get # https://rustdesk.com/docs/en/dev/build/android/ From ddf740ab0dc73dad8d6311aced793ba3f32b0c71 Mon Sep 17 00:00:00 2001 From: youhers <106003909+youhers@users.noreply.github.com> Date: Thu, 6 Apr 2023 13:32:09 +0800 Subject: [PATCH 101/112] =?UTF-8?q?Update=20and=20rename=20src/lang/cn.rs?= =?UTF-8?q?=20to=20=E6=9D=A5=E6=BA=90/=E6=9C=97/cn.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {src/lang => 来源/朗}/cn.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) rename {src/lang => 来源/朗}/cn.rs (98%) diff --git a/src/lang/cn.rs b/来源/朗/cn.rs similarity index 98% rename from src/lang/cn.rs rename to 来源/朗/cn.rs index 11b3144c7..2c058d2ab 100644 --- a/src/lang/cn.rs +++ b/来源/朗/cn.rs @@ -459,26 +459,26 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Codec", "编解码"), ("Resolution", "分辨率"), ("No transfers in progress", ""), - ("Set one-time password length", ""), + ("Set one-time password length", "设置一次性密码长度"), ("idd_driver_tip", "安装虚拟显示器驱动,以便在没有连接显示器的情况下启动虚拟显示器进行控制。"), ("confirm_idd_driver_tip", "安装虚拟显示器驱动的选项已勾选。请注意,测试证书将被安装以信任虚拟显示器驱动。测试证书仅会用于信任Rustdesk的驱动。"), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), + ("RDP Settings", "RDP 设置"), + ("Sort by", "排序依据"), + ("New Connection", "添加新的连接"), + ("Restore", "恢复"), + ("Minimize", "最小化"), + ("Maximize", "最大化"), + ("Your Device", "您的设备"), ("empty_recent_tip", ""), ("empty_favorite_tip", ""), ("empty_lan_tip", ""), ("empty_address_book_tip", ""), ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), + ("Empty Username", "空用户名"), + ("Empty Password", "空密码"), ("Me", ""), ("identical_file_tip", "此文件与对方的一致"), - ("show_monitors_tip", ""), + ("show_monitors_tip", "显示监视器提示"), ("View Mode", "浏览模式"), ("login_linux_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), From e4fb82b5798615625cfe6c4b5e0f70c42bfd483b Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 6 Apr 2023 13:50:17 +0800 Subject: [PATCH 102/112] =?UTF-8?q?Revert=20"Update=20and=20rename=20src/l?= =?UTF-8?q?ang/cn.rs=20to=20=E6=9D=A5=E6=BA=90/=E6=9C=97/cn.rs"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {来源/朗 => src/lang}/cn.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) rename {来源/朗 => src/lang}/cn.rs (98%) diff --git a/来源/朗/cn.rs b/src/lang/cn.rs similarity index 98% rename from 来源/朗/cn.rs rename to src/lang/cn.rs index 2c058d2ab..11b3144c7 100644 --- a/来源/朗/cn.rs +++ b/src/lang/cn.rs @@ -459,26 +459,26 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Codec", "编解码"), ("Resolution", "分辨率"), ("No transfers in progress", ""), - ("Set one-time password length", "设置一次性密码长度"), + ("Set one-time password length", ""), ("idd_driver_tip", "安装虚拟显示器驱动,以便在没有连接显示器的情况下启动虚拟显示器进行控制。"), ("confirm_idd_driver_tip", "安装虚拟显示器驱动的选项已勾选。请注意,测试证书将被安装以信任虚拟显示器驱动。测试证书仅会用于信任Rustdesk的驱动。"), - ("RDP Settings", "RDP 设置"), - ("Sort by", "排序依据"), - ("New Connection", "添加新的连接"), - ("Restore", "恢复"), - ("Minimize", "最小化"), - ("Maximize", "最大化"), - ("Your Device", "您的设备"), + ("RDP Settings", ""), + ("Sort by", ""), + ("New Connection", ""), + ("Restore", ""), + ("Minimize", ""), + ("Maximize", ""), + ("Your Device", ""), ("empty_recent_tip", ""), ("empty_favorite_tip", ""), ("empty_lan_tip", ""), ("empty_address_book_tip", ""), ("eg: admin", ""), - ("Empty Username", "空用户名"), - ("Empty Password", "空密码"), + ("Empty Username", ""), + ("Empty Password", ""), ("Me", ""), ("identical_file_tip", "此文件与对方的一致"), - ("show_monitors_tip", "显示监视器提示"), + ("show_monitors_tip", ""), ("View Mode", "浏览模式"), ("login_linux_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), From ac74ed1914dbdbf0566673bb7aeeacfc6387c8a2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 6 Apr 2023 17:53:07 +0800 Subject: [PATCH 103/112] legacy mode, win, fix layout code simulation Signed-off-by: fufesou --- libs/enigo/src/win/win_impl.rs | 95 +++++++++++++++++++---------- libs/hbb_common/src/platform/mod.rs | 10 ++- src/core_main.rs | 2 + 3 files changed, 73 insertions(+), 34 deletions(-) diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs index 115cb9789..068f3463a 100644 --- a/libs/enigo/src/win/win_impl.rs +++ b/libs/enigo/src/win/win_impl.rs @@ -156,7 +156,7 @@ impl MouseControllable for Enigo { match button { MouseButton::Back => XBUTTON1 as _, MouseButton::Forward => XBUTTON2 as _, - _ => 0, + _ => 0, }, 0, 0, @@ -186,7 +186,7 @@ impl MouseControllable for Enigo { match button { MouseButton::Back => XBUTTON1 as _, MouseButton::Forward => XBUTTON2 as _, - _ => 0, + _ => 0, }, 0, 0, @@ -215,7 +215,7 @@ impl KeyboardControllable for Enigo { fn as_mut_any(&mut self) -> &mut dyn std::any::Any { self } - + fn key_sequence(&mut self, sequence: &str) { let mut buffer = [0; 2]; @@ -247,15 +247,51 @@ impl KeyboardControllable for Enigo { } fn key_down(&mut self, key: Key) -> crate::ResultType { - let code = self.key_to_keycode(key); - if code == 0 || code == 65535 { - return Err("".into()); - } - let res = keybd_event(0, code, 0); - if res == 0 { - let err = get_error(); - if !err.is_empty() { - return Err(err.into()); + match &key { + Key::Layout(c) => { + // to-do: dup code + // https://github.com/rustdesk/rustdesk/blob/1bc0dd791ed8344997024dc46626bd2ca7df73d2/src/server/input_service.rs#L1348 + let code = self.get_layoutdependent_keycode(*c); + if code as u16 != 0xFFFF { + let vk = code & 0x00FF; + let flag = code >> 8; + let modifiers = [Key::Shift, Key::Control, Key::Alt]; + let mod_len = modifiers.len(); + for pos in 0..mod_len { + if flag & (0x0001 << pos) != 0 { + self.key_down(modifiers[pos])?; + } + } + + let res = keybd_event(0, vk, 0); + let err = if res == 0 { get_error() } else { "".to_owned() }; + + for pos in 0..mod_len { + let rpos = mod_len - 1 - pos; + if flag & (0x0001 << rpos) != 0 { + self.key_up(modifiers[pos]); + } + } + + if !err.is_empty() { + return Err(err.into()); + } + } else { + return Err(format!("Failed to get keycode of {}", c).into()); + } + } + _ => { + let code = self.key_to_keycode(key); + if code == 0 || code == 65535 { + return Err("".into()); + } + let res = keybd_event(0, code, 0); + if res == 0 { + let err = get_error(); + if !err.is_empty() { + return Err(err.into()); + } + } } } Ok(()) @@ -411,30 +447,27 @@ impl Enigo { Key::RightAlt => EVK_RMENU, Key::Raw(raw_keycode) => raw_keycode, - Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()), Key::Super | Key::Command | Key::Windows | Key::Meta => EVK_LWIN, + Key::Layout(..) => { + // unreachable + 0 + } } } - fn get_layoutdependent_keycode(&self, string: String) -> u16 { - // get the first char from the string ignore the rest - // ensure its not a multybyte char - if let Some(chr) = string.chars().nth(0) { - // NOTE VkKeyScanW uses the current keyboard LAYOUT - // to specify a LAYOUT use VkKeyScanExW and GetKeyboardLayout - // or load one with LoadKeyboardLayoutW - let current_window_thread_id = - unsafe { GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()) }; - unsafe { LAYOUT = GetKeyboardLayout(current_window_thread_id) }; - let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) }; - if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' { - // a workaround of italian keyboard shift + '.' issue - EVK_PERIOD as _ - } else { - keycode_and_shiftstate as _ - } + fn get_layoutdependent_keycode(&self, chr: char) -> u16 { + // NOTE VkKeyScanW uses the current keyboard LAYOUT + // to specify a LAYOUT use VkKeyScanExW and GetKeyboardLayout + // or load one with LoadKeyboardLayoutW + let current_window_thread_id = + unsafe { GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()) }; + unsafe { LAYOUT = GetKeyboardLayout(current_window_thread_id) }; + let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) }; + if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' { + // a workaround of italian keyboard shift + '.' issue + EVK_PERIOD as _ } else { - 0 + keycode_and_shiftstate as _ } } } diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index fd57476c0..137868e12 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -4,11 +4,15 @@ pub mod linux; #[cfg(target_os = "macos")] pub mod macos; +#[cfg(not(debug_assertions))] use crate::{config::Config, log}; +#[cfg(not(debug_assertions))] use std::process::exit; +#[cfg(not(debug_assertions))] static mut GLOBAL_CALLBACK: Option> = None; +#[cfg(not(debug_assertions))] extern "C" fn breakdown_signal_handler(sig: i32) { let mut stack = vec![]; backtrace::trace(|frame| { @@ -61,13 +65,13 @@ extern "C" fn breakdown_signal_handler(sig: i32) { exit(0); } -pub fn register_breakdown_handler(_callback: T) +#[cfg(not(debug_assertions))] +pub fn register_breakdown_handler(callback: T) where T: Fn() + 'static, { - #[cfg(not(debug_assertions))] unsafe { - GLOBAL_CALLBACK = Some(Box::new(_callback)); + GLOBAL_CALLBACK = Some(Box::new(callback)); libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } diff --git a/src/core_main.rs b/src/core_main.rs index 05a5ad769..a3e11a49e 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,5 +1,6 @@ use crate::platform::breakdown_callback; use hbb_common::log; +#[cfg(not(debug_assertions))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::platform::register_breakdown_handler; @@ -39,6 +40,7 @@ pub fn core_main() -> Option> { } i += 1; } + #[cfg(not(debug_assertions))] #[cfg(not(any(target_os = "android", target_os = "ios")))] register_breakdown_handler(breakdown_callback); #[cfg(target_os = "linux")] From 66dcd80299da3e1afc7fdec26bd16d50951652a9 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Thu, 6 Apr 2023 20:42:17 +0300 Subject: [PATCH 104/112] Update lt.rs --- src/lang/lt.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/lang/lt.rs b/src/lang/lt.rs index e21ff677d..9355a95a2 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -16,7 +16,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), ("Transfer File", "Perkelti failą"), ("Connect", "Prisijungti"), - ("Recent Sessions", "Naujausios sesijos"), + ("Recent Sessions", "Seansų istorija"), ("Address Book", "Adresų knyga"), ("Confirmation", "Patvirtinimas"), ("TCP Tunneling", "TCP tuneliavimas"), @@ -52,7 +52,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Home", "Namai"), ("Audio Input", "Garso įvestis"), ("Enhancements", "Patobulinimai"), - ("Hardware Codec", "Aparatūros kodekas"), + ("Hardware Codec", "Aparatinės įrangos paspartinimas"), ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), ("ID Server", "ID serveris"), ("Relay Server", "Perdavimo serveris"), @@ -93,7 +93,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Local", "Vietinis"), ("Remote", "Nuotolinis"), ("Remote Computer", "Nuotolinis kompiuteris"), - ("Local Computer", "Vietinis kompiuteris"), + ("Local Computer", "Šis kompiuteris"), ("Confirm Delete", "Patvirtinti ištrynimą"), ("Delete", "Ištrinti"), ("Properties", "Ypatybės"), @@ -145,7 +145,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), ("Set Password", "Nustatyti slaptažodį"), ("OS Password", "OS slaptažodis"), - ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami išvengti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk savo sistemoje."), + ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami apeiti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk į savo kompiuterį."), ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), ("Click to download", "Spustelėkite norėdami atsisiųsti"), ("Click to update", "Spustelėkite norėdami atnaujinti"), @@ -210,7 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Settings", "Nustatymai"), ("Username", "Vartotojo vardas"), ("Invalid port", "Netinkamas prievadas"), - ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), + ("Closed manually by the peer", "Partneris atmetė prašymą prisijungti"), ("Enable remote configuration modification", "Įgalinti nuotolinį konfigūracijos modifikavimą"), ("Run without install", "Vykdyti be diegimo"), ("Connect via relay", "Prisijungti per relę"), @@ -234,15 +234,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Password missed", "Slaptažodis praleistas"), ("Wrong credentials", "Klaidingi kredencialai"), ("Edit Tag", "Redaguoti žymą"), - ("Unremember Password", "Neprisiminti slaptažodžio"), - ("Favorites", "Mėgstamiausi"), + ("Unremember Password", "Nebeprisiminti slaptažodžio"), + ("Favorites", "Parankiniai"), ("Add to Favorites", "Įtraukti į parankinius"), ("Remove from Favorites", "Pašalinti iš parankinių"), ("Empty", "Tuščia"), ("Invalid folder name", "Neteisingas aplanko pavadinimas"), ("Socks5 Proxy", "Socks5 Proxy"), ("Hostname", "Pagrindinio kompiuterio pavadinimas"), - ("Discovered", "Atrasta"), + ("Discovered", "Aptikta tinkle"), ("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"), ("Remote ID", "Nuotolinis ID"), ("Paste", "Įklijuoti"), @@ -349,22 +349,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Theme", "Tema"), ("Dark Theme", "Tamsioji tema"), ("Light Theme", "Šviesi tema"), - ("Dark", "Tamsiai"), - ("Light", "Šviesiai"), - ("Follow System", "Sekti sistemą"), - ("Enable hardware codec", "Įgalinti aparatūrinį kodavimą"), + ("Dark", "Tamsi"), + ("Light", "Šviesi"), + ("Follow System", "Kaip sistemos"), + ("Enable hardware codec", "Įgalinti"), ("Unlock Security Settings", "Atrakinti saugos nustatymus"), ("Enable Audio", "Įgalinti garsą"), ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), ("Server", "Serveris"), ("Direct IP Access", "Tiesioginė IP prieiga"), - ("Proxy", "Įgaliotinis serveris"), + ("Proxy", "Tarpinis serveris"), ("Apply", "Taikyti"), ("Disconnect all devices?", "Atjungti visus įrenginius?"), ("Clear", "Išvalyti"), - ("Audio Input Device", "Garso įvesties įrenginys"), + ("Audio Input Device", "Garso įvestis"), ("Deny remote access", "Uždrausti nuotolinę prieigą"), - ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), + ("Use IP Whitelisting", "Naudoti patikimą IP sąrašą"), ("Network", "Tinklas"), ("Enable RDP", "Įgalinti RDP"), ("Pin menubar", "Prisegti meniu juostą"), @@ -401,12 +401,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Zoom cursor", "Mastelio keitimo žymeklis"), ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), ("Accept sessions via click", "Priimti seansus spustelėjus"), - ("Accept sessions via both", "Priimti seansus per abu"), + ("Accept sessions via both", "Priimti seansus abiem variantais"), ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), ("One-time Password", "Vienkartinis slaptažodis"), ("Use one-time password", "Naudoti vienkartinį slaptažodį"), ("One-time password length", "Vienkartinio slaptažodžio ilgis"), - ("Request access to your device", "Prašyti prieigos prie įrenginio"), + ("Request access to your device", "Prašo leidimo valdyti jūsų įrenginį"), ("Hide connection management window", "Slėpti ryšio valdymo langą"), ("hide_cm_tip", "Leisti paslėpti didžiąją ir mažąją raidę, jei priimamos slaptažodžio sesijos arba naudojamas nuolatinis slaptažodis"), ("wayland_experiment_tip", "Wayland palaikymas yra eksperimentinis, naudokite X11, jei jums reikalingas automatinis prisijungimas."), @@ -481,7 +481,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Rodyti monitorius įrankių juostoje"), ("View Mode", "Peržiūros režimas"), ("login_linux_tip", "Norėdami įjungti X darbalaukio seansą, turite būti prisijungę prie nuotolinės Linux paskyros."), - ("verify_rustdesk_password_tip", "Patvirtinkite RustDesk slaptažodį"), + ("verify_rustdesk_password_tip", "Įveskite kliento RustDesk slaptažodį"), ("remember_account_tip", "Prisiminti šią paskyrą"), ("os_account_desk_tip", "Ši paskyra naudojama norint prisijungti prie nuotolinės OS ir įgalinti darbalaukio seansą režimu headless"), ("OS Account", "OS paskyra"), @@ -492,4 +492,4 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), ].iter().cloned().collect(); -} \ No newline at end of file +} From bc5c6e9a06128adfd3ab2d8cf3714be1f442e285 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 6 Apr 2023 16:48:29 +0800 Subject: [PATCH 105/112] tmp fix video qos reset Signed-off-by: 21pages --- src/server/video_qos.rs | 4 +++- src/server/video_service.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index 47bf49707..5bb687473 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -221,7 +221,9 @@ impl VideoQoS { } pub fn reset(&mut self) { - *self = Default::default(); + self.fps = FPS; + self.user_fps = FPS; + self.updated = true; } pub fn check_abr_config(&mut self) -> bool { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 691ca4abe..4abeafff5 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -547,7 +547,7 @@ fn run(sp: GenericService) -> ResultType<()> { check_uac_switch(c.privacy_mode_id, c._capturer_privacy_mode_id)?; let mut video_qos = VIDEO_QOS.lock().unwrap(); - if video_qos.check_if_updated() { + if video_qos.check_if_updated() && video_qos.target_bitrate > 0 { log::debug!( "qos is updated, target_bitrate:{}, fps:{}", video_qos.target_bitrate, From d6c8fb2b281bca6b17ec41d0423d56be93e04732 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 7 Apr 2023 14:46:18 +0800 Subject: [PATCH 106/112] refact is_peer_version_ge, and fix version comparation > to >= Signed-off-by: fufesou --- src/common.rs | 18 ++++++++++++++++++ src/keyboard.rs | 20 ++------------------ src/server/connection.rs | 6 +++++- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/common.rs b/src/common.rs index d07c7bbc6..3cf10eaba 100644 --- a/src/common.rs +++ b/src/common.rs @@ -818,3 +818,21 @@ pub async fn get_key(sync: bool) -> String { } key } + +pub fn is_peer_version_ge(v: &str) -> bool { + #[cfg(not(any(feature = "flutter", feature = "cli")))] + if let Some(session) = crate::ui::CUR_SESSION.lock().unwrap().as_ref() { + return session.get_peer_version() >= hbb_common::get_version_number(v); + } + + #[cfg(feature = "flutter")] + if let Some(session) = crate::flutter::SESSIONS + .read() + .unwrap() + .get(&*crate::flutter::CUR_SESSION_ID.read().unwrap()) + { + return session.get_peer_version() >= hbb_common::get_version_number(v); + } + + false +} diff --git a/src/keyboard.rs b/src/keyboard.rs index 7f1f7e5f8..15629bb00 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -339,24 +339,8 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { "translate" => KeyboardMode::Translate, "legacy" => KeyboardMode::Legacy, _ => { - // Set "map" as default mode if version > 1.2.0. - let mut is_peer_version_gt_1_2_0 = false; - - #[cfg(not(any(feature = "flutter", feature = "cli")))] - if let Some(session) = CUR_SESSION.lock().unwrap().as_ref() { - is_peer_version_gt_1_2_0 = - session.get_peer_version() > hbb_common::get_version_number("1.2.0"); - } - #[cfg(feature = "flutter")] - if let Some(session) = SESSIONS - .read() - .unwrap() - .get(&*CUR_SESSION_ID.read().unwrap()) - { - is_peer_version_gt_1_2_0 = - session.get_peer_version() > hbb_common::get_version_number("1.2.0"); - } - if is_peer_version_gt_1_2_0 { + // Set "map" as default mode if version >= 1.2.0. + if crate::is_peer_version_ge("1.2.0") { KeyboardMode::Map } else { KeyboardMode::Legacy diff --git a/src/server/connection.rs b/src/server/connection.rs index 53dccc3ca..cf962d983 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -43,13 +43,15 @@ use sha2::{Digest, Sha256}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::sync::atomic::Ordering; use std::{ - collections::HashSet, num::NonZeroI64, sync::{atomic::AtomicI64, mpsc as std_mpsc}, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use system_shutdown; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use std::collections::HashSet; + pub type Sender = mpsc::UnboundedSender<(Instant, Arc)>; lazy_static::lazy_static! { @@ -153,6 +155,7 @@ pub struct Connection { voice_call_request_timestamp: Option, audio_input_device_before_voice_call: Option, options_in_login: Option, + #[cfg(not(any(target_os = "android", target_os = "ios")))] pressed_modifiers: HashSet, #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: mpsc::Receiver<()>, @@ -273,6 +276,7 @@ impl Connection { voice_call_request_timestamp: None, audio_input_device_before_voice_call: None, options_in_login: None, + #[cfg(not(any(target_os = "android", target_os = "ios")))] pressed_modifiers: Default::default(), #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: _rx_cm_stream_ready, From 1d4772af1846d02fd9fbc926d0d23ebc43f6c259 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 7 Apr 2023 16:03:18 +0800 Subject: [PATCH 107/112] fix android physical keyboard input Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index f18f63647..ff0faf7a4 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -64,7 +64,7 @@ class InputModel { InputModel(this.parent); KeyEventResult handleRawKeyEvent(FocusNode data, RawKeyEvent e) { - if (!stateGlobal.grabKeyboard) { + if (isDesktop && !stateGlobal.grabKeyboard) { return KeyEventResult.handled; } From 850f48abb8c2486b764954c679fd85e5f060c3fb Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 7 Apr 2023 16:28:34 +0800 Subject: [PATCH 108/112] remove unused code from https://github.com/fufesou/ustdsk/comit/b526bf4a67f7e05b2d3f739864ecc31carembc18a85 Signed-off-by: fufesou --- libs/enigo/src/win/win_impl.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs index 068f3463a..a2839621c 100644 --- a/libs/enigo/src/win/win_impl.rs +++ b/libs/enigo/src/win/win_impl.rs @@ -462,12 +462,6 @@ impl Enigo { let current_window_thread_id = unsafe { GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()) }; unsafe { LAYOUT = GetKeyboardLayout(current_window_thread_id) }; - let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) }; - if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' { - // a workaround of italian keyboard shift + '.' issue - EVK_PERIOD as _ - } else { - keycode_and_shiftstate as _ - } + unsafe { VkKeyScanExW(chr as _, LAYOUT) as _ } } } From b79f14af12507fe183cf0629b1658fa28aa16e16 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 6 Apr 2023 21:36:37 +0800 Subject: [PATCH 109/112] client side fps control for reduce delay Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 4 +- .../lib/desktop/widgets/remote_toolbar.dart | 6 +- libs/hbb_common/src/config.rs | 2 +- src/client.rs | 64 +++++++++++--- src/client/io_loop.rs | 86 ++++++++++++++++++- src/server/video_qos.rs | 2 +- src/ui_session_interface.rs | 3 +- 7 files changed, 143 insertions(+), 24 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 74d51407c..fe5b2fbf4 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1228,9 +1228,9 @@ class _DisplayState extends State<_Display> { children: [ Slider( value: fpsValue.value, - min: 10.0, + min: 5.0, max: 120.0, - divisions: 22, + divisions: 23, onChanged: (double value) async { fpsValue.value = value; await bind.mainSetUserDefaultOption( diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 75bae3d08..9627b107b 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1237,7 +1237,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { final fpsOption = await bind.sessionGetOption(id: widget.id, arg: 'custom-fps'); fpsInitValue = fpsOption == null ? 30 : double.tryParse(fpsOption) ?? 30; - if (fpsInitValue < 10 || fpsInitValue > 120) { + if (fpsInitValue < 5 || fpsInitValue > 120) { fpsInitValue = 30; } final RxDouble fpsSliderValue = RxDouble(fpsInitValue); @@ -1260,9 +1260,9 @@ class _DisplayMenuState extends State<_DisplayMenu> { children: [ Obx((() => Slider( value: fpsSliderValue.value, - min: 10, + min: 5, max: 120, - divisions: 22, + divisions: 23, onChanged: (double value) { fpsSliderValue.value = value; debouncerFps.value = value; diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 4c60e1eb3..369920982 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1363,7 +1363,7 @@ impl UserDefaultConfig { "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), - "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), + "custom-fps" => self.get_double_string(key, 30.0, 5.0, 120.0), _ => self .options .get(key) diff --git a/src/client.rs b/src/client.rs index a5e51065f..a24c531c1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,7 +3,10 @@ use std::{ net::SocketAddr, ops::Deref, str::FromStr, - sync::{mpsc, Arc, Mutex, RwLock}, + sync::{ + atomic::{AtomicUsize, Ordering}, + mpsc, Arc, Mutex, RwLock, + }, }; pub use async_trait::async_trait; @@ -1291,11 +1294,12 @@ impl LoginConfigHandler { config.custom_image_quality[0] }; msg.custom_image_quality = quality << 8; + #[cfg(feature = "flutter")] + if let Some(custom_fps) = self.options.get("custom-fps") { + msg.custom_fps = custom_fps.parse().unwrap_or(30); + } n += 1; } - if let Some(custom_fps) = self.options.get("custom-fps") { - msg.custom_fps = custom_fps.parse().unwrap_or(30); - } let view_only = self.get_toggle_option("view-only"); if view_only { msg.disable_keyboard = BoolOption::Yes.into(); @@ -1677,7 +1681,12 @@ pub type MediaSender = mpsc::Sender; /// * `video_callback` - The callback for video frame. Being called when a video frame is ready. pub fn start_video_audio_threads( video_callback: F, -) -> (MediaSender, MediaSender, Arc>) +) -> ( + MediaSender, + MediaSender, + Arc>, + Arc, +) where F: 'static + FnMut(&mut Vec) + Send, { @@ -1685,21 +1694,48 @@ where let video_queue = Arc::new(ArrayQueue::::new(VIDEO_QUEUE_SIZE)); let video_queue_cloned = video_queue.clone(); let mut video_callback = video_callback; + let mut duration = std::time::Duration::ZERO; + let mut count = 0; + let fps = Arc::new(AtomicUsize::new(0)); + let decode_fps = fps.clone(); + let mut skip_beginning = 0; std::thread::spawn(move || { let mut video_handler = VideoHandler::new(); loop { if let Ok(data) = video_receiver.recv() { match data { - MediaData::VideoFrame(vf) => { - if let Ok(true) = video_handler.handle_frame(*vf) { + MediaData::VideoFrame(_) | MediaData::VideoQueue => { + let vf = if let MediaData::VideoFrame(vf) = data { + *vf + } else { + if let Some(vf) = video_queue.pop() { + vf + } else { + continue; + } + }; + let start = std::time::Instant::now(); + if let Ok(true) = video_handler.handle_frame(vf) { video_callback(&mut video_handler.rgb); - } - } - MediaData::VideoQueue => { - if let Some(vf) = video_queue.pop() { - if let Ok(true) = video_handler.handle_frame(vf) { - video_callback(&mut video_handler.rgb); + // fps calculation + // The first frame will be very slow + if skip_beginning < 5 { + skip_beginning += 1; + continue; + } + duration += start.elapsed(); + count += 1; + if count % 10 == 0 { + fps.store( + (count * 1000 / duration.as_millis()) as usize, + Ordering::Relaxed, + ); + } + // Clear to get real-time fps + if count > 300 { + count = 0; + duration = Duration::ZERO; } } } @@ -1718,7 +1754,7 @@ where log::info!("Video decoder loop exits"); }); let audio_sender = start_audio_thread(); - return (video_sender, audio_sender, video_queue_cloned); + return (video_sender, audio_sender, video_queue_cloned, decode_fps); } /// Start an audio thread diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index bc6ad8a80..f7530cc37 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -65,6 +65,8 @@ pub struct Remote { frame_count: Arc, video_format: CodecFormat, elevation_requested: bool, + fps_control: FpsControl, + decode_fps: Arc, } impl Remote { @@ -76,6 +78,7 @@ impl Remote { receiver: mpsc::UnboundedReceiver, sender: mpsc::UnboundedSender, frame_count: Arc, + decode_fps: Arc, ) -> Self { Self { handler, @@ -100,6 +103,8 @@ impl Remote { stop_voice_call_sender: None, voice_call_request_timestamp: None, elevation_requested: false, + fps_control: Default::default(), + decode_fps, } } @@ -147,6 +152,7 @@ impl Remote { let mut rx_clip_client = rx_clip_client_lock.lock().await; let mut status_timer = time::interval(Duration::new(1, 0)); + let mut fps_instant = Instant::now(); loop { tokio::select! { @@ -224,9 +230,18 @@ impl Remote { } } _ = status_timer.tick() => { - let speed = self.data_count.swap(0, Ordering::Relaxed); + self.fps_control(); + let elapsed = fps_instant.elapsed().as_millis(); + if elapsed < 1000 { + continue; + } + fps_instant = Instant::now(); + let mut speed = self.data_count.swap(0, Ordering::Relaxed); + speed = speed * 1000 / elapsed as usize; let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); - let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; + let mut fps = self.frame_count.swap(0, Ordering::Relaxed) as _; + // Correcting the inaccuracy of status_timer + fps = fps * 1000 / elapsed as i32; self.handler.update_quality_status(QualityStatus { speed:Some(speed), fps:Some(fps), @@ -826,6 +841,53 @@ impl Remote { None => false, } } + #[inline] + fn fps_control(&mut self) { + let len = self.video_queue.len(); + let ctl = &mut self.fps_control; + // Current full speed decoding fps + let decode_fps = self.decode_fps.load(std::sync::atomic::Ordering::Relaxed); + // 500ms + let debounce = if decode_fps > 10 { decode_fps / 2 } else { 5 }; + if len < debounce || decode_fps == 0 { + return; + } + let mut refresh = false; + // First setting , or the length of the queue still increases after setting, or exceed the size of the last setting again + if ctl.set_times < 10 // enough + && (ctl.set_times == 0 + || (len > ctl.last_queue_size && ctl.last_set_instant.elapsed().as_secs() > 30)) + { + // 80% fps to ensure decoding is faster than encoding + let mut custom_fps = decode_fps as i32 * 4 / 5; + if custom_fps < 1 { + custom_fps = 1; + } + // send custom fps + let mut misc = Misc::new(); + misc.set_option(OptionMessage { + custom_fps, + ..Default::default() + }); + let mut msg = Message::new(); + msg.set_misc(misc); + self.sender.send(Data::Message(msg)).ok(); + ctl.last_queue_size = len; + ctl.set_times += 1; + ctl.last_set_instant = Instant::now(); + refresh = true; + } + // send refresh + if ctl.refresh_times < 10 // enough + && (refresh + || (len > self.video_queue.len() / 2 + && ctl.last_refresh_instant.elapsed().as_secs() > 30)) + { + self.handler.refresh_video(); + ctl.refresh_times += 1; + ctl.last_refresh_instant = Instant::now(); + } + } async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { if let Ok(msg_in) = Message::parse_from_bytes(&data) { @@ -1489,3 +1551,23 @@ impl RemoveJob { } } } + +struct FpsControl { + last_queue_size: usize, + set_times: usize, + refresh_times: usize, + last_set_instant: Instant, + last_refresh_instant: Instant, +} + +impl Default for FpsControl { + fn default() -> Self { + Self { + last_queue_size: Default::default(), + set_times: Default::default(), + refresh_times: Default::default(), + last_set_instant: Instant::now(), + last_refresh_instant: Instant::now(), + } + } +} diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index 5bb687473..d53053691 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -1,7 +1,7 @@ use super::*; use std::time::Duration; pub const FPS: u8 = 30; -pub const MIN_FPS: u8 = 10; +pub const MIN_FPS: u8 = 1; pub const MAX_FPS: u8 = 120; trait Percent { fn as_percent(&self) -> u32; diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 3a0ae6598..504982f50 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1162,7 +1162,7 @@ pub async fn io_loop(handler: Session) { let frame_count = Arc::new(AtomicUsize::new(0)); let frame_count_cl = frame_count.clone(); let ui_handler = handler.ui_handler.clone(); - let (video_sender, audio_sender, video_queue) = + let (video_sender, audio_sender, video_queue, decode_fps) = start_video_audio_threads(move |data: &mut Vec| { frame_count_cl.fetch_add(1, Ordering::Relaxed); ui_handler.on_rgba(data); @@ -1176,6 +1176,7 @@ pub async fn io_loop(handler: Session) { receiver, sender, frame_count, + decode_fps, ); remote.io_loop(&key, &token).await; remote.sync_jobs_status_to_local().await; From cd294b919c691d41da659eedb5b9694e9894c20b Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 8 Apr 2023 09:20:41 +0800 Subject: [PATCH 110/112] Revert "Update Dockerfile" --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 08fd65544..32a440b28 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -38,7 +38,7 @@ RUN sudo apt-get install -y libclang-dev RUN sudo apt install -y gcc-multilib WORKDIR $WORKDIR -ENV ANDROID_NDK_HOME=/opt/android/ndk/23.2.8568313 +ENV ANDROID_NDK_HOME=/opt/android/ndk/22.1.7171670 # Somehow try to automate flutter pub get # https://rustdesk.com/docs/en/dev/build/android/ From 4a8d61ac099b11c8edf44831d8969e10430666c2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 15:35:10 +0800 Subject: [PATCH 111/112] Do not sync led, when Control, Shift, Alt, Tab, Enter are pressed Signed-off-by: fufesou --- libs/enigo/src/win/keycodes.rs | 1 + src/core_main.rs | 2 ++ src/server/input_service.rs | 54 +++++++++++++++++++++------------- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/libs/enigo/src/win/keycodes.rs b/libs/enigo/src/win/keycodes.rs index ea35685c5..500582bf0 100644 --- a/libs/enigo/src/win/keycodes.rs +++ b/libs/enigo/src/win/keycodes.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731 // // JP/KR mapping https://github.com/TigerVNC/tigervnc/blob/1a008c1380305648ab50f1d99e73439747e9d61d/vncviewer/win32.c#L267 diff --git a/src/core_main.rs b/src/core_main.rs index a3e11a49e..d6c3bbc59 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,3 +1,5 @@ +#[cfg(not(debug_assertions))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::platform::breakdown_callback; use hbb_common::log; #[cfg(not(debug_assertions))] diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 5fb63a597..c9a92e432 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1372,29 +1372,41 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } #[cfg(not(any(target_os = "windows", target_os = "linux")))] -fn is_win_linux_meta_key(_evt: &KeyEvent) -> bool { +fn skip_led_sync(_evt: &KeyEvent) -> bool { false } #[cfg(any(target_os = "windows", target_os = "linux"))] -fn is_win_linux_meta_key(evt: &KeyEvent) -> bool { - match evt.mode.unwrap() { - KeyboardMode::Map | KeyboardMode::Translate => match &evt.union { - Some(key_event::Union::ControlKey(ck)) => { - return *ck == ControlKey::Meta.into(); - } - Some(key_event::Union::Chr(code)) => { - let key = crate::keycode_to_rdev_key(*code); - return key == RdevKey::MetaLeft || key == RdevKey::MetaRight; - } - _ => {} - }, - KeyboardMode::Legacy => match &evt.union { - Some(key_event::Union::ControlKey(ck)) => { - return *ck == ControlKey::Meta.into(); - } - _ => {} - }, +fn skip_led_sync(evt: &KeyEvent) -> bool { + match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { + (Some(key_event::Union::ControlKey(ck)), _) => { + let key = ck.enum_value_or(ControlKey::Unknown); + return [ + ControlKey::Control, + ControlKey::Meta, + ControlKey::Shift, + ControlKey::Alt, + ControlKey::Tab, + ControlKey::Return, + ] + .contains(&key); + } + (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { + let key = crate::keycode_to_rdev_key(*code); + return [ + RdevKey::ControlLeft, + RdevKey::ControlRight, + RdevKey::MetaLeft, + RdevKey::MetaRight, + RdevKey::ShiftRight, + RdevKey::ShiftRight, + RdevKey::Alt, + RdevKey::AltGr, + RdevKey::Tab, + RdevKey::Return, + ] + .contains(&key); + } _ => {} } false @@ -1413,7 +1425,9 @@ pub fn handle_key_(evt: &KeyEvent) { // LockModesHandler should not be created when single meta is pressing and releasing. // Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 - if evt.down && !is_win_linux_meta_key(evt) { + // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500415822 + // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473 + if evt.down && !skip_led_sync(evt) { Some(LockModesHandler::new(evt)) } else { None From 072da85f09ec230bb6705b52dff06d430ea83cd4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 17:56:04 +0800 Subject: [PATCH 112/112] simple refact Signed-off-by: fufesou --- src/server/input_service.rs | 100 +++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index c9a92e432..0be6f0d84 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1372,44 +1372,48 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } #[cfg(not(any(target_os = "windows", target_os = "linux")))] -fn skip_led_sync(_evt: &KeyEvent) -> bool { +fn skip_led_sync_control_key(_evt: &KeyEvent) -> bool { + false +} + +// LockModesHandler should not be created when single meta is pressing and releasing. +// Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. +// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 +// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500415822 +// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473 +#[cfg(any(target_os = "windows", target_os = "linux"))] +fn skip_led_sync_control_key(key: &ControlKey) -> bool { + [ + ControlKey::Control, + ControlKey::Meta, + ControlKey::Shift, + ControlKey::Alt, + ControlKey::Tab, + ControlKey::Return, + ] + .contains(key) +} + +#[cfg(not(any(target_os = "windows", target_os = "linux")))] +fn skip_led_sync_rdev_key(_evt: &KeyEvent) -> bool { false } #[cfg(any(target_os = "windows", target_os = "linux"))] -fn skip_led_sync(evt: &KeyEvent) -> bool { - match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { - (Some(key_event::Union::ControlKey(ck)), _) => { - let key = ck.enum_value_or(ControlKey::Unknown); - return [ - ControlKey::Control, - ControlKey::Meta, - ControlKey::Shift, - ControlKey::Alt, - ControlKey::Tab, - ControlKey::Return, - ] - .contains(&key); - } - (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { - let key = crate::keycode_to_rdev_key(*code); - return [ - RdevKey::ControlLeft, - RdevKey::ControlRight, - RdevKey::MetaLeft, - RdevKey::MetaRight, - RdevKey::ShiftRight, - RdevKey::ShiftRight, - RdevKey::Alt, - RdevKey::AltGr, - RdevKey::Tab, - RdevKey::Return, - ] - .contains(&key); - } - _ => {} - } - false +fn skip_led_sync_rdev_key(key: &RdevKey) -> bool { + [ + RdevKey::ControlLeft, + RdevKey::ControlRight, + RdevKey::MetaLeft, + RdevKey::MetaRight, + RdevKey::ShiftRight, + RdevKey::ShiftRight, + RdevKey::Alt, + RdevKey::AltGr, + RdevKey::Tab, + RdevKey::Return, + ] + .contains(key) } pub fn handle_key_(evt: &KeyEvent) { @@ -1417,22 +1421,24 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - let _lock_mode_handler = match &evt.union { - Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)) => { - Some(LockModesHandler::new(&evt)) + let mut _lock_mode_handler = None; + match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { + (Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)), _) => { + _lock_mode_handler = Some(LockModesHandler::new(&evt)); } - _ => { - // LockModesHandler should not be created when single meta is pressing and releasing. - // Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. - // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 - // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500415822 - // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473 - if evt.down && !skip_led_sync(evt) { - Some(LockModesHandler::new(evt)) - } else { - None + (Some(key_event::Union::ControlKey(ck)), _) => { + let key = ck.enum_value_or(ControlKey::Unknown); + if !skip_led_sync_control_key(&key) { + _lock_mode_handler = Some(LockModesHandler::new(&evt)); } } + (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { + let key = crate::keycode_to_rdev_key(*code); + if !skip_led_sync_rdev_key(&key) { + _lock_mode_handler = Some(LockModesHandler::new(evt)); + } + } + _ => {} }; match evt.mode.unwrap() {