From cd3db3a6864ebc2bbc49d6abcd3390420c8add30 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 11 Jun 2024 19:51:11 +0800 Subject: [PATCH 001/335] try to fix https://github.com/rustdesk/rustdesk-server-pro/issues/266 --- libs/hbb_common/src/config.rs | 50 ++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index cd5d4c56589..d2f568fd40e 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -912,7 +912,7 @@ impl Config { #[inline] fn purify_options(v: &mut HashMap) { - v.retain(|k, _| is_option_can_save(&OVERWRITE_SETTINGS, k)); + v.retain(|k, v| is_option_can_save(&OVERWRITE_SETTINGS, k, &DEFAULT_SETTINGS, v)); } pub fn set_options(mut v: HashMap) { @@ -936,7 +936,7 @@ impl Config { } pub fn set_option(k: String, v: String) { - if !is_option_can_save(&OVERWRITE_SETTINGS, &k) { + if !is_option_can_save(&OVERWRITE_SETTINGS, &k, &DEFAULT_SETTINGS, &v) { return; } let mut config = CONFIG2.write().unwrap(); @@ -1449,7 +1449,7 @@ impl LocalConfig { } pub fn set_option(k: String, v: String) { - if !is_option_can_save(&OVERWRITE_LOCAL_SETTINGS, &k) { + if !is_option_can_save(&OVERWRITE_LOCAL_SETTINGS, &k, &DEFAULT_LOCAL_SETTINGS, &v) { return; } let mut config = LOCAL_CONFIG.write().unwrap(); @@ -1636,7 +1636,12 @@ impl UserDefaultConfig { } pub fn set(&mut self, key: String, value: String) { - if !is_option_can_save(&OVERWRITE_DISPLAY_SETTINGS, &key) { + if !is_option_can_save( + &OVERWRITE_DISPLAY_SETTINGS, + &key, + &DEFAULT_DISPLAY_SETTINGS, + &value, + ) { return; } if value.is_empty() { @@ -1959,8 +1964,15 @@ fn get_or( } #[inline] -fn is_option_can_save(overwrite: &RwLock>, k: &str) -> bool { - if overwrite.read().unwrap().contains_key(k) { +fn is_option_can_save( + overwrite: &RwLock>, + k: &str, + defaults: &RwLock>, + v: &str, +) -> bool { + if overwrite.read().unwrap().contains_key(k) + || defaults.read().unwrap().get(k).map_or(false, |x| x == v) + { return false; } true @@ -2100,6 +2112,11 @@ pub mod keys { pub const OPTION_ALLOW_LINUX_HEADLESS: &str = "allow-linux-headless"; pub const OPTION_ENABLE_HWCODEC: &str = "enable-hwcodec"; pub const OPTION_APPROVE_MODE: &str = "approve-mode"; + pub const OPTION_CUSTOM_RENDEZVOUS_SERVER: &str = "custom-rendezvous-server"; + pub const OPTION_API_SERVER: &str = "api-server"; + pub const OPTION_KEY: &str = "key"; + pub const OPTION_PRESET_ADDRESS_BOOK_NAME: &str = "preset-address-book-name"; + pub const OPTION_PRESET_ADDRESS_BOOK_TAG: &str = "preset-address-book-tag"; // flutter local options pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState"; @@ -2117,6 +2134,9 @@ pub mod keys { pub const OPTION_FLOATING_WINDOW_TRANSPARENCY: &str = "floating-window-transparency"; pub const OPTION_FLOATING_WINDOW_SVG: &str = "floating-window-svg"; + pub const OPTION_DISABLE_GROUP_PANEL: &str = "disable-group-panel"; + pub const OPTION_PRE_ELEVATE_SERVICE: &str = "pre-elevate-service"; + // proxy settings // The following options are not real keys, they are just used for custom client advanced settings. // The real keys are in Config2::socks. @@ -2177,6 +2197,8 @@ pub mod keys { OPTION_FLOATING_WINDOW_UNTOUCHABLE, OPTION_FLOATING_WINDOW_TRANSPARENCY, OPTION_FLOATING_WINDOW_SVG, + OPTION_DISABLE_GROUP_PANEL, + OPTION_PRE_ELEVATE_SERVICE, ]; // DEFAULT_SETTINGS, OVERWRITE_SETTINGS pub const KEYS_SETTINGS: &[&str] = &[ @@ -2208,6 +2230,11 @@ pub mod keys { OPTION_PROXY_URL, OPTION_PROXY_USERNAME, OPTION_PROXY_PASSWORD, + OPTION_CUSTOM_RENDEZVOUS_SERVER, + OPTION_API_SERVER, + OPTION_KEY, + OPTION_PRESET_ADDRESS_BOOK_NAME, + OPTION_PRESET_ADDRESS_BOOK_TAG, ]; } @@ -2283,7 +2310,18 @@ mod tests { res.insert("c".to_owned(), "d".to_string()); res.insert("d".to_owned(), "cc".to_string()); Config::purify_options(&mut res); + DEFAULT_SETTINGS + .write() + .unwrap() + .insert("f".to_string(), "c".to_string()); + Config::purify_options(&mut res); assert!(res.len() == 2); + DEFAULT_SETTINGS + .write() + .unwrap() + .insert("f".to_string(), "a".to_string()); + Config::purify_options(&mut res); + assert!(res.len() == 1); let res = Config::get_options(); assert!(res["a"] == "b"); assert!(res["c"] == "f"); From b19d732a3a4503f4c2ca4e58328f74a4dbe81248 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:32:10 +0800 Subject: [PATCH 002/335] fix: audio rechannel len (#8315) * fix: audio rechannel len Signed-off-by: fufesou * comments Signed-off-by: fufesou --------- Signed-off-by: fufesou --- src/server/audio_service.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index 031d4e673c7..538dba04b09 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -310,7 +310,9 @@ mod cpal_impl { let mut encoder = Encoder::new(sample_rate, encode_channel, LowDelay)?; // https://www.opus-codec.org/docs/html_api/group__opusencoder.html#gace941e4ef26ed844879fde342ffbe546 // https://chromium.googlesource.com/chromium/deps/opus/+/1.1.1/include/opus.h - let frame_size = sample_rate as usize / 100; // 10 ms + // Do not set `frame_size = sample_rate as usize / 100;` + // Because we find `sample_rate as usize / 100` will cause encoder error in `encoder.encode_vec_float()`. + let frame_size = sample_rate_0 as usize / 100; // 10 ms let encode_len = frame_size * encode_channel as usize; let rechannel_len = encode_len * device_channel as usize / encode_channel as usize; INPUT_BUFFER.lock().unwrap().clear(); From c27791a9ac2333f088ce3ce6c0db471c059a60f1 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:53:54 +0800 Subject: [PATCH 003/335] comments (#8316) Signed-off-by: fufesou --- src/server/audio_service.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index 538dba04b09..cfe7b457beb 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -311,7 +311,8 @@ mod cpal_impl { // https://www.opus-codec.org/docs/html_api/group__opusencoder.html#gace941e4ef26ed844879fde342ffbe546 // https://chromium.googlesource.com/chromium/deps/opus/+/1.1.1/include/opus.h // Do not set `frame_size = sample_rate as usize / 100;` - // Because we find `sample_rate as usize / 100` will cause encoder error in `encoder.encode_vec_float()`. + // Because we find `sample_rate as usize / 100` will cause encoder error in `encoder.encode_vec_float()` sometimes. + // https://github.com/xiph/opus/blob/2554a89e02c7fc30a980b4f7e635ceae1ecba5d6/src/opus_encoder.c#L725 let frame_size = sample_rate_0 as usize / 100; // 10 ms let encode_len = frame_size * encode_channel as usize; let rechannel_len = encode_len * device_channel as usize / encode_channel as usize; From 47143318ba9e827d2ba1d277627d53a6e1e612de Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 12 Jun 2024 01:40:54 +0800 Subject: [PATCH 004/335] ensure nextRgba called no matter if image created --- flutter/lib/utils/image.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flutter/lib/utils/image.dart b/flutter/lib/utils/image.dart index e6223950133..6280555ba4f 100644 --- a/flutter/lib/utils/image.dart +++ b/flutter/lib/utils/image.dart @@ -13,13 +13,14 @@ Future decodeImageFromPixels( int? rowBytes, int? targetWidth, int? targetHeight, - VoidCallback? onPixelsCopied, + VoidCallback? onPixelsCopied, // must ensure onPixelsCopied is called no matter this function succeeds bool allowUpscaling = true, }) async { if (targetWidth != null) { assert(allowUpscaling || targetWidth <= width); if (!(allowUpscaling || targetWidth <= width)) { print("not allow upscaling but targetWidth > width"); + onPixelsCopied?.call(); return null; } } @@ -27,6 +28,7 @@ Future decodeImageFromPixels( assert(allowUpscaling || targetHeight <= height); if (!(allowUpscaling || targetHeight <= height)) { print("not allow upscaling but targetHeight > height"); + onPixelsCopied?.call(); return null; } } @@ -36,6 +38,7 @@ Future decodeImageFromPixels( buffer = await ui.ImmutableBuffer.fromUint8List(pixels); onPixelsCopied?.call(); } catch (e) { + onPixelsCopied?.call(); return null; } From 50aa5880de323a45ac9567d5f03e6b059b543ffd Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 12 Jun 2024 02:34:15 +0800 Subject: [PATCH 005/335] always call platformFFI.nextRgba no matter what to avoid dead lock because of unknown reason --- flutter/lib/models/model.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 54ba4e595c5..1c03c3c26ff 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -2504,12 +2504,15 @@ class FFI { // Fetch the image buffer from rust codes. final sz = platformFFI.getRgbaSize(sessionId, display); if (sz == 0) { + platformFFI.nextRgba(sessionId, display); return; } final rgba = platformFFI.getRgba(sessionId, display, sz); if (rgba != null) { onEvent2UIRgba(); imageModel.onRgba(display, rgba); + } else { + platformFFI.nextRgba(sessionId, display); } } } else if (message is EventToUI_Texture) { From 60049c8cc5540e59a420cded5771d80a2a015b24 Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Wed, 12 Jun 2024 12:20:29 +0300 Subject: [PATCH 006/335] Update lv.rs (#8323) --- src/lang/lv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 409a678d9ad..cfa463fb00a 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -615,7 +615,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_new_voice_call_tip", "Tika saņemts jauns balss zvana pieprasījums. Ja piekrītat, audio pārslēgsies uz balss saziņu."), ("texture_render_tip", "Izmantojiet tekstūras renderēšanu, lai attēli būtu vienmērīgāki. Varat mēģināt atspējot šo opciju, ja rodas renderēšanas problēmas."), ("Use texture rendering", "Izmantot tekstūras renderēšanu"), - ("Floating window", ""), - ("floating_window_tip", ""), + ("Floating window", "Peldošs logs"), + ("floating_window_tip", "Tas palīdz uzturēt RustDesk fona servisu"), ].iter().cloned().collect(); } From 0f10a88b235ddcc50f47676c63e4ed9965e868d4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 12 Jun 2024 20:35:04 +0800 Subject: [PATCH 007/335] remove elevation/installation requirement for --get-id --- src/core_main.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/core_main.rs b/src/core_main.rs index 468ab56dfc5..e91b5571025 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -303,11 +303,7 @@ pub fn core_main() -> Option> { } return None; } else if args[0] == "--get-id" { - if crate::platform::is_installed() && is_root() { - println!("{}", crate::ipc::get_id()); - } else { - println!("Installation and administrative privileges required!"); - } + println!("{}", crate::ipc::get_id()); return None; } else if args[0] == "--set-id" { if args.len() == 2 { From 610009528bc651c5d80187e9462c89b9edc365bc Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 12 Jun 2024 20:40:35 +0800 Subject: [PATCH 008/335] hwcodec, only process that start ipc server start check process (#8325) check process send config to ipc server, other process get config from ipc server. Process will save config to toml, and the toml will be used if the config is none. when start check process: ipc server process start or option changed from disable to enable when get config: main window start or option changed from disable to enable, start_video_audio_threads. Only windows implements signature, which is used to mark whether the gpu software and hardware information changes. After reboot, the signature doesn't change. https://asawicki.info/news_1773_how_to_programmatically_check_graphics_driver_version, use dxgi way to get software version, it's not consistent with the visible driver version, after updating intel driver with small version change, the signature doesn't change. Linux doesn't use toml file. Signed-off-by: 21pages --- Cargo.lock | 4 +- libs/hbb_common/src/config.rs | 46 ++--- libs/scrap/src/common/codec.rs | 7 +- libs/scrap/src/common/hwcodec.rs | 345 +++++++++++++++++++------------ libs/scrap/src/common/vram.rs | 35 ++-- src/client.rs | 22 ++ src/core_main.rs | 2 +- src/ipc.rs | 104 +++++++++- src/server.rs | 9 +- src/ui_interface.rs | 21 +- 10 files changed, 389 insertions(+), 206 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06763278a9d..48d488151ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3037,8 +3037,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.4.15" -source = "git+https://github.com/21pages/hwcodec#1d504ee590c15472813fecc22cee4b8149b2b8cd" +version = "0.4.16" +source = "git+https://github.com/21pages/hwcodec#0973290faddc4e22936859dd10f05610eb8d1619" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index d2f568fd40e..b9533407a2a 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1553,40 +1553,6 @@ impl LanPeers { } } -#[derive(Debug, Default, Serialize, Deserialize, Clone)] -pub struct HwCodecConfig { - #[serde(default, deserialize_with = "deserialize_string")] - pub ram: String, - #[serde(default, deserialize_with = "deserialize_string")] - pub vram: String, -} - -impl HwCodecConfig { - pub fn load() -> HwCodecConfig { - Config::load_::("_hwcodec") - } - - pub fn store(&self) { - Config::store_(self, "_hwcodec"); - } - - pub fn clear() { - HwCodecConfig::default().store(); - } - - pub fn clear_ram() { - let mut c = Self::load(); - c.ram = Default::default(); - c.store(); - } - - pub fn clear_vram() { - let mut c = Self::load(); - c.vram = Default::default(); - c.store(); - } -} - #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct UserDefaultConfig { #[serde(default, deserialize_with = "deserialize_hashmap_string_string")] @@ -2238,6 +2204,18 @@ pub mod keys { ]; } +pub fn common_load< + T: serde::Serialize + serde::de::DeserializeOwned + Default + std::fmt::Debug, +>( + suffix: &str, +) -> T { + Config::load_::(suffix) +} + +pub fn common_store(config: &T, suffix: &str) { + Config::store_(config, suffix); +} + #[cfg(test)] mod tests { use super::*; diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 098daa9985e..343b1997baf 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -144,10 +144,7 @@ impl Encoder { }), Err(e) => { log::error!("new hw encoder failed: {e:?}, clear config"); - #[cfg(target_os = "android")] - crate::android::ffi::clear_codec_info(); - #[cfg(not(target_os = "android"))] - hbb_common::config::HwCodecConfig::clear_ram(); + HwCodecConfig::clear(false, true); Self::update(EncodingUpdate::Check); *ENCODE_CODEC_FORMAT.lock().unwrap() = CodecFormat::VP9; Err(e) @@ -160,7 +157,7 @@ impl Encoder { }), Err(e) => { log::error!("new vram encoder failed: {e:?}, clear config"); - hbb_common::config::HwCodecConfig::clear_vram(); + HwCodecConfig::clear(true, true); Self::update(EncodingUpdate::Check); *ENCODE_CODEC_FORMAT.lock().unwrap() = CodecFormat::VP9; Err(e) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 8c50d12c7b2..ed713887ac2 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -8,7 +8,6 @@ use crate::{ use hbb_common::{ anyhow::{anyhow, bail, Context}, bytes::Bytes, - config::HwCodecConfig, log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, VideoFrame}, serde_derive::{Deserialize, Serialize}, @@ -16,7 +15,7 @@ use hbb_common::{ }; use hwcodec::{ common::{ - DataFormat, + get_gpu_signature, DataFormat, Quality::{self, *}, RateControl::{self, *}, }, @@ -35,6 +34,12 @@ const DEFAULT_HW_QUALITY: Quality = Quality_Default; crate::generate_call_macro!(call_yuv, false); +#[cfg(not(target_os = "android"))] +lazy_static::lazy_static! { + static ref CONFIG: std::sync::Arc>> = Default::default(); + static ref CONFIG_SET_BY_IPC: std::sync::Arc> = Default::default(); +} + #[derive(Debug, Clone)] pub struct HwRamEncoderConfig { pub name: String, @@ -210,21 +215,19 @@ impl EncoderApi for HwRamEncoder { impl HwRamEncoder { pub fn try_get(format: CodecFormat) -> Option { let mut info = None; - if let Ok(hw) = get_config().map(|c| c.e) { - let best = CodecInfo::prioritized(hw); - match format { - CodecFormat::H264 => { - if let Some(v) = best.h264 { - info = Some(v); - } + let best = CodecInfo::prioritized(HwCodecConfig::get().ram_encode); + match format { + CodecFormat::H264 => { + if let Some(v) = best.h264 { + info = Some(v); } - CodecFormat::H265 => { - if let Some(v) = best.h265 { - info = Some(v); - } + } + CodecFormat::H265 => { + if let Some(v) = best.h265 { + info = Some(v); } - _ => {} } + _ => {} } info } @@ -313,21 +316,19 @@ impl HwRamDecoder { _ => {} } if enable_hwcodec_option() { - if let Ok(hw) = get_config().map(|c| c.d) { - let best = CodecInfo::prioritized(hw); - match format { - CodecFormat::H264 => { - if let Some(v) = best.h264 { - info = Some(v); - } + let best = CodecInfo::prioritized(HwCodecConfig::get().ram_decode); + match format { + CodecFormat::H264 => { + if let Some(v) = best.h264 { + info = Some(v); } - CodecFormat::H265 => { - if let Some(v) = best.h265 { - info = Some(v); - } + } + CodecFormat::H265 => { + if let Some(v) = best.h265 { + info = Some(v); } - _ => {} } + _ => {} } } info @@ -347,10 +348,7 @@ impl HwRamDecoder { match Decoder::new(ctx) { Ok(decoder) => Ok(HwRamDecoder { decoder, info }), Err(_) => { - #[cfg(target_os = "android")] - crate::android::ffi::clear_codec_info(); - #[cfg(not(target_os = "android"))] - hbb_common::config::HwCodecConfig::clear_ram(); + HwCodecConfig::clear(false, false); Err(anyhow!(format!("Failed to create decoder"))) } } @@ -467,97 +465,192 @@ impl HwRamDecoderImage<'_> { } } -#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] -struct Available { - e: Vec, - d: Vec, +#[cfg(target_os = "android")] +fn get_mime_type(codec: DataFormat) -> &'static str { + match codec { + DataFormat::VP8 => "video/x-vnd.on2.vp8", + DataFormat::VP9 => "video/x-vnd.on2.vp9", + DataFormat::AV1 => "video/av01", + DataFormat::H264 => "video/avc", + DataFormat::H265 => "video/hevc", + } } -fn get_config() -> ResultType { - #[cfg(target_os = "android")] - { - let info = crate::android::ffi::get_codec_info(); - log::info!("all codec info: {info:?}"); - struct T { - name_prefix: &'static str, - data_format: DataFormat, - } - let ts = vec![ - T { - name_prefix: "h264", - data_format: DataFormat::H264, - }, - T { - name_prefix: "hevc", - data_format: DataFormat::H265, - }, - ]; - let mut e = vec![]; - if let Some(info) = info { - ts.iter().for_each(|t| { - let codecs: Vec<_> = info - .codecs - .iter() - .filter(|c| { - c.is_encoder - && c.mime_type.as_str() == get_mime_type(t.data_format) - && c.nv12 - && c.hw == Some(true) //only use hardware codec - }) - .collect(); - log::debug!("available {:?} encoders: {codecs:?}", t.data_format); - let screen_wh = std::cmp::max(info.w, info.h); - let mut best = None; - if let Some(codec) = codecs - .iter() - .find(|c| c.max_width >= screen_wh && c.max_height >= screen_wh) - { - best = Some(codec.name.clone()); - } else { - // find the max resolution - let mut max_area = 0; - for codec in codecs.iter() { - if codec.max_width * codec.max_height > max_area { - best = Some(codec.name.clone()); - max_area = codec.max_width * codec.max_height; +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct HwCodecConfig { + #[serde(default)] + pub signature: u64, + #[serde(default)] + pub ram_encode: Vec, + #[serde(default)] + pub ram_decode: Vec, + #[cfg(feature = "vram")] + #[serde(default)] + pub vram_encode: Vec, + #[cfg(feature = "vram")] + #[serde(default)] + pub vram_decode: Vec, +} + +// ipc server process start check process once, other process get from ipc server once +// install: --server start check process, check process send to --server, ui get from --server +// portable: ui start check process, check process send to ui +// sciter and unilink: get from ipc server +impl HwCodecConfig { + #[cfg(any(target_os = "windows", target_os = "linux"))] + pub fn set(config: String) { + let config = serde_json::from_str(&config).unwrap_or_default(); + log::info!("set hwcodec config"); + log::debug!("{config:?}"); + #[cfg(windows)] + hbb_common::config::common_store(&config, "_hwcodec"); + *CONFIG.lock().unwrap() = Some(config); + *CONFIG_SET_BY_IPC.lock().unwrap() = true; + } + + pub fn get() -> HwCodecConfig { + #[cfg(target_os = "android")] + { + let info = crate::android::ffi::get_codec_info(); + log::info!("all codec info: {info:?}"); + struct T { + name_prefix: &'static str, + data_format: DataFormat, + } + let ts = vec![ + T { + name_prefix: "h264", + data_format: DataFormat::H264, + }, + T { + name_prefix: "hevc", + data_format: DataFormat::H265, + }, + ]; + let mut e = vec![]; + if let Some(info) = info { + ts.iter().for_each(|t| { + let codecs: Vec<_> = info + .codecs + .iter() + .filter(|c| { + c.is_encoder + && c.mime_type.as_str() == get_mime_type(t.data_format) + && c.nv12 + && c.hw == Some(true) //only use hardware codec + }) + .collect(); + let screen_wh = std::cmp::max(info.w, info.h); + let mut best = None; + if let Some(codec) = codecs + .iter() + .find(|c| c.max_width >= screen_wh && c.max_height >= screen_wh) + { + best = Some(codec.name.clone()); + } else { + // find the max resolution + let mut max_area = 0; + for codec in codecs.iter() { + if codec.max_width * codec.max_height > max_area { + best = Some(codec.name.clone()); + max_area = codec.max_width * codec.max_height; + } } } + if let Some(best) = best { + e.push(CodecInfo { + name: format!("{}_mediacodec", t.name_prefix), + mc_name: Some(best), + format: t.data_format, + hwdevice: hwcodec::ffmpeg::AVHWDeviceType::AV_HWDEVICE_TYPE_NONE, + priority: 0, + }); + } + }); + } + log::debug!("e: {e:?}"); + HwCodecConfig { + ram_encode: e, + ..Default::default() + } + } + #[cfg(windows)] + { + let config = CONFIG.lock().unwrap().clone(); + match config { + Some(c) => c, + None => { + log::info!("try load cached hwcodec config"); + let c = hbb_common::config::common_load::("_hwcodec"); + let new_signature = get_gpu_signature(); + if c.signature == new_signature { + log::debug!("load cached hwcodec config: {c:?}"); + *CONFIG.lock().unwrap() = Some(c.clone()); + c + } else { + log::info!( + "gpu signature changed, {} -> {}", + c.signature, + new_signature + ); + HwCodecConfig::default() + } } - if let Some(best) = best { - e.push(CodecInfo { - name: format!("{}_mediacodec", t.name_prefix), - mc_name: Some(best), - format: t.data_format, - hwdevice: hwcodec::ffmpeg::AVHWDeviceType::AV_HWDEVICE_TYPE_NONE, - priority: 0, - }); - } - }); + } + } + #[cfg(target_os = "linux")] + { + CONFIG.lock().unwrap().clone().unwrap_or_default() + } + #[cfg(any(target_os = "macos", target_os = "ios"))] + { + HwCodecConfig::default() } - log::debug!("e: {e:?}"); - Ok(Available { e, d: vec![] }) } - #[cfg(not(target_os = "android"))] - { - match serde_json::from_str(&HwCodecConfig::load().ram) { - Ok(v) => Ok(v), - Err(e) => Err(anyhow!("Failed to get config:{e:?}")), + + #[cfg(any(target_os = "windows", target_os = "linux"))] + pub fn get_set_value() -> Option { + let set = CONFIG_SET_BY_IPC.lock().unwrap().clone(); + if set { + CONFIG.lock().unwrap().clone() + } else { + None } } -} -#[cfg(target_os = "android")] -fn get_mime_type(codec: DataFormat) -> &'static str { - match codec { - DataFormat::VP8 => "video/x-vnd.on2.vp8", - DataFormat::VP9 => "video/x-vnd.on2.vp9", - DataFormat::AV1 => "video/av01", - DataFormat::H264 => "video/avc", - DataFormat::H265 => "video/hevc", + #[cfg(any(target_os = "windows", target_os = "linux"))] + pub fn already_set() -> bool { + CONFIG_SET_BY_IPC.lock().unwrap().clone() + } + + pub fn clear(vram: bool, encode: bool) { + log::info!("clear hwcodec config, vram: {vram}, encode: {encode}"); + #[cfg(target_os = "android")] + crate::android::ffi::clear_codec_info(); + #[cfg(not(target_os = "android"))] + { + let mut c = CONFIG.lock().unwrap(); + if let Some(c) = c.as_mut() { + if vram { + #[cfg(feature = "vram")] + if encode { + c.vram_encode = vec![]; + } else { + c.vram_decode = vec![]; + } + } else { + if encode { + c.ram_encode = vec![]; + } else { + c.ram_decode = vec![]; + } + } + } + } } } -pub fn check_available_hwcodec() { +pub fn check_available_hwcodec() -> String { let ctx = EncodeContext { name: String::from(""), mc_name: None, @@ -575,29 +668,31 @@ pub fn check_available_hwcodec() { }; #[cfg(feature = "vram")] let vram = crate::vram::check_available_vram(); + #[cfg(feature = "vram")] + let vram_string = vram.2; #[cfg(not(feature = "vram"))] - let vram = "".to_owned(); - let ram = Available { - e: Encoder::available_encoders(ctx, Some(vram.clone())), - d: Decoder::available_decoders(Some(vram.clone())), + let vram_string = "".to_owned(); + let c = HwCodecConfig { + ram_encode: Encoder::available_encoders(ctx, Some(vram_string.clone())), + ram_decode: Decoder::available_decoders(Some(vram_string)), + #[cfg(feature = "vram")] + vram_encode: vram.0, + #[cfg(feature = "vram")] + vram_decode: vram.1, + signature: get_gpu_signature(), }; - if let Ok(ram) = serde_json::to_string_pretty(&ram) { - HwCodecConfig { ram, vram }.store(); - } + log::debug!("{c:?}"); + serde_json::to_string(&c).unwrap_or_default() } #[cfg(any(target_os = "windows", target_os = "linux"))] -pub fn start_check_process(force: bool) { - if !force && !enable_hwcodec_option() { +pub fn start_check_process() { + if !enable_hwcodec_option() || HwCodecConfig::already_set() { return; } use hbb_common::allow_err; use std::sync::Once; let f = || { - // Clear to avoid checking process errors - // But when the program is just started, the configuration file has not been updated, and the new connection will read an empty configuration - // TODO: --server start multi times on windows startup, which will clear the last config and cause concurrent file writing - HwCodecConfig::clear(); if let Ok(exe) = std::env::current_exe() { if let Some(_) = exe.file_name().to_owned() { let arg = "--check-hwcodec-config"; @@ -631,11 +726,7 @@ pub fn start_check_process(force: bool) { }; }; static ONCE: Once = Once::new(); - if force && ONCE.is_completed() { + ONCE.call_once(|| { std::thread::spawn(f); - } else { - ONCE.call_once(|| { - std::thread::spawn(f); - }); - } + }); } diff --git a/libs/scrap/src/common/vram.rs b/libs/scrap/src/common/vram.rs index d99a6bc5971..8baf6ea699e 100644 --- a/libs/scrap/src/common/vram.rs +++ b/libs/scrap/src/common/vram.rs @@ -6,6 +6,7 @@ use std::{ use crate::{ codec::{base_bitrate, enable_vram_option, EncoderApi, EncoderCfg, Quality}, + hwcodec::HwCodecConfig, AdapterDevice, CodecFormat, EncodeInput, EncodeYuvFormat, Pixfmt, }; use hbb_common::{ @@ -228,9 +229,8 @@ impl VRamEncoder { CodecFormat::H265 => DataFormat::H265, _ => return vec![], }; - let v: Vec<_> = get_available_config() - .map(|c| c.e) - .unwrap_or_default() + let v: Vec<_> = crate::hwcodec::HwCodecConfig::get() + .vram_encode .drain(..) .filter(|c| c.data_format == data_format) .collect(); @@ -339,9 +339,8 @@ impl VRamDecoder { CodecFormat::H265 => DataFormat::H265, _ => return vec![], }; - get_available_config() - .map(|c| c.d) - .unwrap_or_default() + crate::hwcodec::HwCodecConfig::get() + .vram_decode .drain(..) .filter(|c| c.data_format == data_format && c.luid == luid && luid != 0) .collect() @@ -351,7 +350,7 @@ impl VRamDecoder { if !enable_vram_option() { return (false, false); } - let v = get_available_config().map(|c| c.d).unwrap_or_default(); + let v = crate::hwcodec::HwCodecConfig::get().vram_decode; ( v.iter().any(|d| d.data_format == DataFormat::H264), v.iter().any(|d| d.data_format == DataFormat::H265), @@ -364,7 +363,7 @@ impl VRamDecoder { match Decoder::new(ctx) { Ok(decoder) => Ok(Self { decoder }), Err(_) => { - hbb_common::config::HwCodecConfig::clear_vram(); + HwCodecConfig::clear(true, false); Err(anyhow!(format!( "Failed to create decoder, format: {:?}", format @@ -386,15 +385,7 @@ pub struct VRamDecoderImage<'a> { impl VRamDecoderImage<'_> {} -fn get_available_config() -> ResultType { - let available = hbb_common::config::HwCodecConfig::load().vram; - match Available::deserialize(&available) { - Ok(v) => Ok(v), - Err(_) => Err(anyhow!("Failed to deserialize:{}", available)), - } -} - -pub(crate) fn check_available_vram() -> String { +pub(crate) fn check_available_vram() -> (Vec, Vec, String) { let d = DynamicContext { device: None, width: 1280, @@ -406,8 +397,12 @@ pub(crate) fn check_available_vram() -> String { let encoders = encode::available(d); let decoders = decode::available(); let available = Available { - e: encoders, - d: decoders, + e: encoders.clone(), + d: decoders.clone(), }; - available.serialize().unwrap_or_default() + ( + encoders, + decoders, + available.serialize().unwrap_or_default(), + ) } diff --git a/src/client.rs b/src/client.rs index b3f790d67a7..41f290bf07d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2132,6 +2132,7 @@ where std::thread::spawn(move || { #[cfg(windows)] sync_cpu_usage(); + get_hwcodec_config(); let mut handler_controller_map = HashMap::new(); // let mut count = Vec::new(); // let mut duration = std::time::Duration::ZERO; @@ -2333,6 +2334,27 @@ pub fn start_audio_thread() -> MediaSender { audio_sender } +fn get_hwcodec_config() { + // for sciter and unilink + #[cfg(feature = "hwcodec")] + #[cfg(any(target_os = "windows", target_os = "linux"))] + { + use std::sync::Once; + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + let start = std::time::Instant::now(); + if let Err(e) = crate::ipc::get_hwcodec_config_from_server() { + log::error!( + "failed to get hwcodec config: {e:?}, elapsed: {:?}", + start.elapsed() + ); + } else { + log::info!("{:?} used to get hwcodec config", start.elapsed()); + } + }); + } +} + #[cfg(windows)] fn sync_cpu_usage() { use std::sync::Once; diff --git a/src/core_main.rs b/src/core_main.rs index e91b5571025..bf7e536f6b2 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -412,7 +412,7 @@ pub fn core_main() -> Option> { return None; } else if args[0] == "--check-hwcodec-config" { #[cfg(feature = "hwcodec")] - scrap::hwcodec::check_available_hwcodec(); + crate::ipc::hwcodec_process(); return None; } else if args[0] == "--cm" { // call connection manager to establish connections diff --git a/src/ipc.rs b/src/ipc.rs index a10a8f955bb..47cac05a6f8 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -239,6 +239,7 @@ pub enum Data { VideoConnCount(Option), // Although the key is not neccessary, it is used to avoid hardcoding the key. WaylandScreencastRestoreToken((String, String)), + HwCodecConfig(Option), } #[tokio::main(flavor = "current_thread")] @@ -523,12 +524,26 @@ async fn handle(data: Data, stream: &mut Connection) { .await ); } - Data::CheckHwcodec => - { - #[cfg(feature = "hwcodec")] - #[cfg(any(target_os = "windows", target_os = "linux"))] - if crate::platform::is_root() { - scrap::hwcodec::start_check_process(true); + #[cfg(feature = "hwcodec")] + #[cfg(any(target_os = "windows", target_os = "linux"))] + Data::CheckHwcodec => { + scrap::hwcodec::start_check_process(); + } + #[cfg(feature = "hwcodec")] + #[cfg(any(target_os = "windows", target_os = "linux"))] + Data::HwCodecConfig(c) => { + match c { + None => { + let v = match scrap::hwcodec::HwCodecConfig::get_set_value() { + Some(v) => Some(serde_json::to_string(&v).unwrap_or_default()), + None => None, + }; + allow_err!(stream.send(&Data::HwCodecConfig(v)).await); + } + Some(v) => { + // --server and portable + scrap::hwcodec::HwCodecConfig::set(v); + } } } Data::WaylandScreencastRestoreToken((key, value)) => { @@ -1025,6 +1040,83 @@ pub async fn notify_server_to_check_hwcodec() -> ResultType<()> { Ok(()) } +#[cfg(feature = "hwcodec")] +#[cfg(any(target_os = "windows", target_os = "linux"))] +#[tokio::main(flavor = "current_thread")] +pub async fn get_hwcodec_config_from_server() -> ResultType<()> { + if !scrap::codec::enable_hwcodec_option() || scrap::hwcodec::HwCodecConfig::already_set() { + return Ok(()); + } + let mut c = connect(50, "").await?; + c.send(&Data::HwCodecConfig(None)).await?; + if let Some(Data::HwCodecConfig(v)) = c.next_timeout(50).await? { + match v { + Some(v) => { + scrap::hwcodec::HwCodecConfig::set(v); + return Ok(()); + } + None => { + bail!("hwcodec config is none"); + } + } + } + bail!("failed to get hwcodec config"); +} + +#[cfg(feature = "hwcodec")] +#[cfg(any(target_os = "windows", target_os = "linux"))] +pub fn client_get_hwcodec_config_thread(wait_sec: u64) { + static ONCE: std::sync::Once = std::sync::Once::new(); + if !crate::platform::is_installed() + || !scrap::codec::enable_hwcodec_option() + || scrap::hwcodec::HwCodecConfig::already_set() + { + return; + } + ONCE.call_once(move || { + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_secs(1)); + let mut intervals: Vec = vec![wait_sec, 3, 3, 6, 9]; + for i in intervals.drain(..) { + if i > 0 { + std::thread::sleep(std::time::Duration::from_secs(i)); + } + if get_hwcodec_config_from_server().is_ok() { + break; + } + } + }); + }); +} + +#[cfg(feature = "hwcodec")] +#[tokio::main(flavor = "current_thread")] +pub async fn hwcodec_process() { + let s = scrap::hwcodec::check_available_hwcodec(); + for _ in 0..5 { + match crate::ipc::connect(1000, "").await { + Ok(mut conn) => { + match conn + .send(&crate::ipc::Data::HwCodecConfig(Some(s.clone()))) + .await + { + Ok(()) => { + log::info!("send ok"); + break; + } + Err(e) => { + log::error!("send failed: {e:?}"); + } + } + } + Err(e) => { + log::error!("connect failed: {e:?}"); + } + } + std::thread::sleep(std::time::Duration::from_secs(1)); + } +} + #[tokio::main(flavor = "current_thread")] pub async fn get_wayland_screencast_restore_token(key: String) -> ResultType { let v = handle_wayland_screencast_restore_token(key, "get".to_owned()).await?; diff --git a/src/server.rs b/src/server.rs index 81b5090347d..43edaea0e4f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -459,9 +459,6 @@ pub async fn start_server(is_server: bool) { log::info!("DISPLAY={:?}", std::env::var("DISPLAY")); log::info!("XAUTHORITY={:?}", std::env::var("XAUTHORITY")); } - #[cfg(feature = "hwcodec")] - #[cfg(any(target_os = "windows", target_os = "linux"))] - scrap::hwcodec::start_check_process(false); #[cfg(windows)] hbb_common::platform::windows::start_cpu_performance_monitor(); @@ -489,6 +486,9 @@ pub async fn start_server(is_server: bool) { tokio::spawn(async { sync_and_watch_config_dir().await }); #[cfg(target_os = "windows")] crate::platform::try_kill_broker(); + #[cfg(feature = "hwcodec")] + #[cfg(any(target_os = "windows", target_os = "linux"))] + scrap::hwcodec::start_check_process(); crate::RendezvousMediator::start_all().await; } else { match crate::ipc::connect(1000, "").await { @@ -509,6 +509,9 @@ pub async fn start_server(is_server: bool) { } } } + #[cfg(feature = "hwcodec")] + #[cfg(any(target_os = "windows", target_os = "linux"))] + crate::ipc::client_get_hwcodec_config_thread(0); } Err(err) => { log::info!("server not started (will try to start): {}", err); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index b58ed6bb3de..6c95d4c4ab3 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -172,15 +172,13 @@ pub fn get_option>(key: T) -> String { #[inline] #[cfg(target_os = "macos")] pub fn use_texture_render() -> bool { - cfg!(feature = "flutter") - && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y" + cfg!(feature = "flutter") && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y" } #[inline] #[cfg(any(target_os = "windows", target_os = "linux"))] pub fn use_texture_render() -> bool { - cfg!(feature = "flutter") - && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N" + cfg!(feature = "flutter") && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N" } #[inline] @@ -1398,9 +1396,16 @@ pub fn check_hwcodec() { #[cfg(feature = "hwcodec")] #[cfg(any(target_os = "windows", target_os = "linux"))] { - scrap::hwcodec::start_check_process(true); - if crate::platform::is_installed() { - ipc::notify_server_to_check_hwcodec().ok(); - } + use std::sync::Once; + static ONCE: Once = Once::new(); + + ONCE.call_once(|| { + if crate::platform::is_installed() { + ipc::notify_server_to_check_hwcodec().ok(); + ipc::client_get_hwcodec_config_thread(3); + } else { + scrap::hwcodec::start_check_process(); + } + }) } } From f559e9c74afc8c5e6a359ed1381a1da03817ea88 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 12 Jun 2024 23:37:51 +0800 Subject: [PATCH 009/335] disable hardware encoding if encoding fails too many times (#8327) Signed-off-by: 21pages --- libs/scrap/src/common/aom.rs | 2 ++ libs/scrap/src/common/codec.rs | 4 ++-- libs/scrap/src/common/hwcodec.rs | 11 ++++++++--- libs/scrap/src/common/vpxcodec.rs | 2 ++ libs/scrap/src/common/vram.rs | 4 ++++ src/server/video_service.rs | 33 +++++++++++++++++++++++++------ 6 files changed, 45 insertions(+), 11 deletions(-) diff --git a/libs/scrap/src/common/aom.rs b/libs/scrap/src/common/aom.rs index 2469ecd3ab4..00d6fe50681 100644 --- a/libs/scrap/src/common/aom.rs +++ b/libs/scrap/src/common/aom.rs @@ -308,6 +308,8 @@ impl EncoderApi for AomEncoder { fn is_hardware(&self) -> bool { false } + + fn disable(&self) {} } impl AomEncoder { diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 343b1997baf..a095cdd6cca 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -76,6 +76,8 @@ pub trait EncoderApi { fn latency_free(&self) -> bool; fn is_hardware(&self) -> bool; + + fn disable(&self); } pub struct Encoder { @@ -145,7 +147,6 @@ impl Encoder { Err(e) => { log::error!("new hw encoder failed: {e:?}, clear config"); HwCodecConfig::clear(false, true); - Self::update(EncodingUpdate::Check); *ENCODE_CODEC_FORMAT.lock().unwrap() = CodecFormat::VP9; Err(e) } @@ -158,7 +159,6 @@ impl Encoder { Err(e) => { log::error!("new vram encoder failed: {e:?}, clear config"); HwCodecConfig::clear(true, true); - Self::update(EncodingUpdate::Check); *ENCODE_CODEC_FORMAT.lock().unwrap() = CodecFormat::VP9; Err(e) } diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index ed713887ac2..589396ede82 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -15,7 +15,7 @@ use hbb_common::{ }; use hwcodec::{ common::{ - get_gpu_signature, DataFormat, + DataFormat, Quality::{self, *}, RateControl::{self, *}, }, @@ -210,6 +210,10 @@ impl EncoderApi for HwRamEncoder { fn is_hardware(&self) -> bool { true } + + fn disable(&self) { + HwCodecConfig::clear(false, true); + } } impl HwRamEncoder { @@ -582,7 +586,7 @@ impl HwCodecConfig { None => { log::info!("try load cached hwcodec config"); let c = hbb_common::config::common_load::("_hwcodec"); - let new_signature = get_gpu_signature(); + let new_signature = hwcodec::common::get_gpu_signature(); if c.signature == new_signature { log::debug!("load cached hwcodec config: {c:?}"); *CONFIG.lock().unwrap() = Some(c.clone()); @@ -647,6 +651,7 @@ impl HwCodecConfig { } } } + crate::codec::Encoder::update(crate::codec::EncodingUpdate::Check); } } @@ -679,7 +684,7 @@ pub fn check_available_hwcodec() -> String { vram_encode: vram.0, #[cfg(feature = "vram")] vram_decode: vram.1, - signature: get_gpu_signature(), + signature: hwcodec::common::get_gpu_signature(), }; log::debug!("{c:?}"); serde_json::to_string(&c).unwrap_or_default() diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 64969ba312f..11b497fb3bc 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -246,6 +246,8 @@ impl EncoderApi for VpxEncoder { fn is_hardware(&self) -> bool { false } + + fn disable(&self) {} } impl VpxEncoder { diff --git a/libs/scrap/src/common/vram.rs b/libs/scrap/src/common/vram.rs index 8baf6ea699e..8de6658a433 100644 --- a/libs/scrap/src/common/vram.rs +++ b/libs/scrap/src/common/vram.rs @@ -194,6 +194,10 @@ impl EncoderApi for VRamEncoder { fn is_hardware(&self) -> bool { true } + + fn disable(&self) { + HwCodecConfig::clear(true, true); + } } impl VRamEncoder { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 899a4cd30db..7cdaa521fac 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -481,6 +481,7 @@ fn run(vs: VideoService) -> ResultType<()> { let mut mid_data = Vec::new(); let mut repeat_encode_counter = 0; let repeat_encode_max = 10; + let mut encode_fail_counter = 0; while sp.ok() { #[cfg(windows)] @@ -568,6 +569,7 @@ fn run(vs: VideoService) -> ResultType<()> { ms, &mut encoder, recorder.clone(), + &mut encode_fail_counter, )?; frame_controller.set_send(now, send_conn_ids); } @@ -622,6 +624,7 @@ fn run(vs: VideoService) -> ResultType<()> { ms, &mut encoder, recorder.clone(), + &mut encode_fail_counter, )?; frame_controller.set_send(now, send_conn_ids); } @@ -876,6 +879,7 @@ fn handle_one_frame( ms: i64, encoder: &mut Encoder, recorder: Arc>>, + encode_fail_counter: &mut usize, ) -> ResultType> { sp.snapshot(|sps| { // so that new sub and old sub share the same encoder after switch @@ -889,6 +893,7 @@ fn handle_one_frame( let mut send_conn_ids: HashSet = Default::default(); match encoder.encode_to_message(frame, ms) { Ok(mut vf) => { + *encode_fail_counter = 0; vf.display = display as _; let mut msg = Message::new(); msg.set_video_frame(vf); @@ -899,13 +904,29 @@ fn handle_one_frame( .map(|r| r.write_message(&msg)); send_conn_ids = sp.send_video_frame(msg); } - Err(e) => match e.to_string().as_str() { - scrap::codec::ENCODE_NEED_SWITCH => { - log::info!("switch due to encoder need switch"); - bail!("SWITCH"); + Err(e) => { + let max_fail_times = if cfg!(target_os = "android") && encoder.is_hardware() { + 12 + } else { + 6 + }; + *encode_fail_counter += 1; + if *encode_fail_counter >= max_fail_times { + *encode_fail_counter = 0; + if encoder.is_hardware() { + encoder.disable(); + log::error!("switch due to encoding fails more than {max_fail_times} times"); + bail!("SWITCH"); + } } - _ => {} - }, + match e.to_string().as_str() { + scrap::codec::ENCODE_NEED_SWITCH => { + log::error!("switch due to encoder need switch"); + bail!("SWITCH"); + } + _ => {} + } + } } Ok(send_conn_ids) } From 964d4f1f87e656620ac58207c6d62f1084ead6e6 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 13 Jun 2024 00:34:23 +0800 Subject: [PATCH 010/335] try fix cursor id, int.parse, exceeds limit (#8333) Signed-off-by: fufesou --- flutter/lib/models/model.dart | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 1c03c3c26ff..437e6c61909 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -973,7 +973,9 @@ class FfiModel with ChangeNotifier { } updateLastCursorId(Map evt) { - parent.target?.cursorModel.id = int.parse(evt['id']); + // int.parse(evt['id']) may cause FormatException + // Unhandled Exception: FormatException: Positive input exceeds the limit of integer 18446744071749110741 + parent.target?.cursorModel.id = evt['id']; } handleCursorId(Map evt) { @@ -1619,7 +1621,7 @@ class CanvasModel with ChangeNotifier { // data for cursor class CursorData { final String peerId; - final int id; + final String id; final img2.Image image; double scale; Uint8List? data; @@ -1699,12 +1701,12 @@ const _forbiddenCursorPng = const _defaultCursorPng = 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAFmSURBVFiF7dWxSlxREMbx34QFDRowYBchZSxSCWlMCOwD5FGEFHap06UI7KPsAyyEEIQFqxRaCqYTsqCJFsKkuAeRXb17wrqV918dztw55zszc2fo6Oh47MR/e3zO1/iAHWmznHKGQwx9ip/LEbCfazbsoY8j/JLOhcC6sCW9wsjEwJf483AC9nPNc1+lFRwI13d+l3rYFS799rFGxJMqARv2pBXh+72XQ7gWvklPS7TmMl9Ak/M+DqrENvxAv/guKKApuKPWl0/TROK4+LbSqzhuB+OZ3fRSeFPWY+Fkyn56Y29hfgTSpnQ+s98cvorVey66uPlNFxKwZOYLCGfCs5n9NMYVrsp6mvXSoFqpqYFDvMBkStgJJe93dZOwVXxbqUnBENulydSReqUrDhcX0PT2EXarBYS3GNXMhboinBgIl9K71kg0L3+PvyYGdVpruT2MwrF0iotiXfIwus0Dj+OOjo6Of+e7ab74RkpgAAAAAElFTkSuQmCC'; -const kPreForbiddenCursorId = -2; +const kPreForbiddenCursorId = "-2"; final preForbiddenCursor = PredefinedCursor( png: _forbiddenCursorPng, id: kPreForbiddenCursorId, ); -const kPreDefaultCursorId = -1; +const kPreDefaultCursorId = "-1"; final preDefaultCursor = PredefinedCursor( png: _defaultCursorPng, id: kPreDefaultCursorId, @@ -1717,7 +1719,7 @@ class PredefinedCursor { img2.Image? _image2; CursorData? _cache; String png; - int id; + String id; double Function(double)? hotxGetter; double Function(double)? hotyGetter; @@ -1775,13 +1777,15 @@ class PredefinedCursor { class CursorModel with ChangeNotifier { ui.Image? _image; - final _images = >{}; + final _images = >{}; CursorData? _cache; - final _cacheMap = {}; + final _cacheMap = {}; final _cacheKeys = {}; double _x = -10000; double _y = -10000; - int _id = -1; + // int.parse(evt['id']) may cause FormatException + // So we use String here. + String _id = "-1"; double _hotx = 0; double _hoty = 0; double _displayOriginX = 0; @@ -1808,7 +1812,7 @@ class CursorModel with ChangeNotifier { double get hotx => _hotx; double get hoty => _hoty; - set id(int id) => _id = id; + set id(String id) => _id = id; bool get isPeerControlProtected => DateTime.now().difference(_lastPeerMouse).inMilliseconds < @@ -1986,7 +1990,7 @@ class CursorModel with ChangeNotifier { } updateCursorData(Map evt) async { - final id = int.parse(evt['id']); + final id = evt['id']; final hotx = double.parse(evt['hotx']); final hoty = double.parse(evt['hoty']); final width = int.parse(evt['width']); @@ -2011,7 +2015,7 @@ class CursorModel with ChangeNotifier { Future _updateCache( Uint8List rgba, ui.Image image, - int id, + String id, double hotx, double hoty, int w, From 65c2ccdc93bcc43bfce2162616417463070e875a Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 13 Jun 2024 10:42:50 +0800 Subject: [PATCH 011/335] fix: try fix, macos, flutter, focus changed, no reponse (#8338) Signed-off-by: fufesou --- flutter/pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 68629a565c7..6851fca377d 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "3535741662c5b7529e182227a277a8551aed3398" + resolved-ref: "60773827434eefe6d01eefa814dca9a032b970b3" url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From 77204127f26296ff60d6062a1873da75db108e70 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 13 Jun 2024 13:05:35 +0800 Subject: [PATCH 012/335] use latest rust for non-windows, and upgrade tokio to 3.18 which fix a mpsc channel bug --- .github/workflows/flutter-build.yml | 5 +++-- Cargo.lock | 8 ++++---- libs/hbb_common/Cargo.toml | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 897cfbbba26..1c222b8ba92 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -11,7 +11,8 @@ on: default: "nightly" env: - RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 + WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 + RUST_VERSION: "1.78" CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" FLUTTER_VERSION: "3.19.6" @@ -84,7 +85,7 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 with: - toolchain: ${{ env.RUST_VERSION }} + toolchain: ${{ env.WIN_RUST_VERSION }} targets: ${{ matrix.job.target }} components: "rustfmt" diff --git a/Cargo.lock b/Cargo.lock index 48d488151ee..a3e77f8ec22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6359,9 +6359,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -6378,9 +6378,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2 1.0.79", "quote 1.0.35", diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index e0a97e61b63..8ae3ebcd571 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] flexi_logger = { version = "0.27", features = ["async"] } protobuf = { version = "3.4", features = ["with-bytes"] } -tokio = { version = "1.37", features = ["full"] } +tokio = { version = "1.38", features = ["full"] } tokio-util = { version = "0.7", features = ["full"] } futures = "0.3" bytes = { version = "1.6", features = ["serde"] } From 8e12a34634595b9955850f339785288a3e1ea2ba Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 13 Jun 2024 13:17:34 +0800 Subject: [PATCH 013/335] upgrade our ipc-parity and tokio-socks crate to tokio 1.38 --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3e77f8ec22..058e6cc2405 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2917,7 +2917,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tokio-rustls 0.26.0", - "tokio-socks 0.5.2", + "tokio-socks 0.5.2-1", "tokio-util", "toml 0.7.8", "url", @@ -4383,8 +4383,8 @@ dependencies = [ [[package]] name = "parity-tokio-ipc" -version = "0.7.3-3" -source = "git+https://github.com/rustdesk-org/parity-tokio-ipc#e8448ade10d6d972d0b2307646424b36ab202ff5" +version = "0.7.3-4" +source = "git+https://github.com/rustdesk-org/parity-tokio-ipc#3623ec9ebef50c9b118e03b03df831008a4d1441" dependencies = [ "futures", "libc", @@ -6432,8 +6432,8 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.2" -source = "git+https://github.com/rustdesk-org/tokio-socks#51037c93f8be34196fd2b6de9f674e8dfae3d01e" +version = "0.5.2-1" +source = "git+https://github.com/rustdesk-org/tokio-socks#94e97c6d7c93b0bcbfa54f2dc397c1da0a6e43d3" dependencies = [ "bytes", "either", From bc875a35b02c9e159229601560411212d06c3f1e Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 13 Jun 2024 18:03:41 +0800 Subject: [PATCH 014/335] Refact/multi window soft rendering (#8343) * refact: multi_window_soft_rendering Signed-off-by: fufesou * fix: window pos, potential wait for image Signed-off-by: fufesou * comments Signed-off-by: fufesou * remove debug print Signed-off-by: fufesou * explicitly set rgba_data.size_got to false after init Signed-off-by: fufesou * refact: multi window, merge images, render with texture Signed-off-by: fufesou * revert, flutter.rs, rgba valid Signed-off-by: fufesou * Add displays index before sending capture msg Signed-off-by: fufesou * refact: multi window, soft rendering Signed-off-by: fufesou * fix: build Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/common.dart | 4 +- .../lib/common/widgets/setting_widgets.dart | 4 +- flutter/lib/common/widgets/toolbar.dart | 5 +- flutter/lib/desktop/pages/remote_page.dart | 34 ++++---- .../lib/desktop/pages/remote_tab_page.dart | 2 +- .../lib/desktop/widgets/remote_toolbar.dart | 13 +--- flutter/lib/models/model.dart | 62 +++++++++------ flutter/lib/web/bridge.dart | 13 ++-- src/flutter.rs | 77 ++++++++++++++++--- src/flutter_ffi.rs | 28 +++++-- 10 files changed, 160 insertions(+), 82 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index a349fb3bdfe..0e1aca9d070 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2924,10 +2924,10 @@ openMonitorInNewTabOrWindow(int i, String peerId, PeerInfo pi, kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args)); } -setNewConnectWindowFrame(int windowId, String peerId, Rect? screenRect) async { +setNewConnectWindowFrame(int windowId, String peerId, int? display, Rect? screenRect) async { if (screenRect == null) { await restoreWindowPosition(WindowType.RemoteDesktop, - windowId: windowId, peerId: peerId); + windowId: windowId, display: display, peerId: peerId); } else { await tryMoveToScreenAndSetFullscreen(screenRect); } diff --git a/flutter/lib/common/widgets/setting_widgets.dart b/flutter/lib/common/widgets/setting_widgets.dart index c91a25164fd..5bcb73a4c5e 100644 --- a/flutter/lib/common/widgets/setting_widgets.dart +++ b/flutter/lib/common/widgets/setting_widgets.dart @@ -234,12 +234,12 @@ List<(String, String)> otherDefaultSettings() { ('True color (4:4:4)', kOptionI444), ('Reverse mouse wheel', kKeyReverseMouseWheel), ('swap-left-right-mouse', kOptionSwapLeftRightMouse), - if (isDesktop && bind.mainGetUseTextureRender()) + if (isDesktop) ( 'Show displays as individual windows', kKeyShowDisplaysAsIndividualWindows ), - if (isDesktop && bind.mainGetUseTextureRender()) + if (isDesktop) ( 'Use all my displays for the remote session', kKeyUseAllMyDisplaysForTheRemoteSession diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index 699aaa143d2..0c2494c944a 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -586,7 +586,6 @@ Future> toolbarDisplayToggle( if (pi.isSupportMultiDisplay && PrivacyModeState.find(id).isEmpty && pi.displaysCount.value > 1 && - bind.mainGetUseTextureRender() && bind.mainGetUserDefaultOption(key: kKeyShowMonitorsToolbar) == 'Y') { final value = bind.sessionGetDisplaysAsIndividualWindows(sessionId: ffi.sessionId) == @@ -602,9 +601,7 @@ Future> toolbarDisplayToggle( } final isMultiScreens = !isWeb && (await getScreenRectList()).length > 1; - if (bind.mainGetUseTextureRender() && - pi.isSupportMultiDisplay && - isMultiScreens) { + if (pi.isSupportMultiDisplay && isMultiScreens) { final value = bind.sessionGetUseAllMyDisplaysForTheRemoteSession( sessionId: ffi.sessionId) == 'Y'; diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index a1266dc47b1..5e3d15130d2 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -617,10 +617,11 @@ class _ImagePaintState extends State { final paintWidth = c.getDisplayWidth() * s; final paintHeight = c.getDisplayHeight() * s; final paintSize = Size(paintWidth, paintHeight); - final paintWidget = m.useTextureRender - ? _BuildPaintTextureRender( - c, s, Offset.zero, paintSize, isViewOriginal()) - : _buildScrollbarNonTextureRender(m, paintSize, s); + final paintWidget = + m.useTextureRender || widget.ffi.ffiModel.pi.forceTextureRender + ? _BuildPaintTextureRender( + c, s, Offset.zero, paintSize, isViewOriginal()) + : _buildScrollbarNonTextureRender(m, paintSize, s); return NotificationListener( onNotification: (notification) { c.updateScrollPercent(); @@ -638,17 +639,18 @@ class _ImagePaintState extends State { )); } else { if (c.size.width > 0 && c.size.height > 0) { - final paintWidget = m.useTextureRender - ? _BuildPaintTextureRender( - c, - s, - Offset( - isLinux ? c.x.toInt().toDouble() : c.x, - isLinux ? c.y.toInt().toDouble() : c.y, - ), - c.size, - isViewOriginal()) - : _buildScrollAuthNonTextureRender(m, c, s); + final paintWidget = + m.useTextureRender || widget.ffi.ffiModel.pi.forceTextureRender + ? _BuildPaintTextureRender( + c, + s, + Offset( + isLinux ? c.x.toInt().toDouble() : c.x, + isLinux ? c.y.toInt().toDouble() : c.y, + ), + c.size, + isViewOriginal()) + : _buildScrollAutoNonTextureRender(m, c, s); return mouseRegion(child: _buildListener(paintWidget)); } else { return Container(); @@ -664,7 +666,7 @@ class _ImagePaintState extends State { ); } - Widget _buildScrollAuthNonTextureRender( + Widget _buildScrollAutoNonTextureRender( ImageModel m, CanvasModel c, double s) { return CustomPaint( size: Size(c.size.width, c.size.height), diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 4affd7b0714..2468121ba97 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -420,7 +420,7 @@ class _ConnectionTabPageState extends State { await WindowController.fromWindowId(windowId()).setFullscreen(false); stateGlobal.setFullscreen(false, procWnd: false); } - await setNewConnectWindowFrame(windowId(), id!, screenRect); + await setNewConnectWindowFrame(windowId(), id!, display, screenRect); Future.delayed(Duration(milliseconds: isWindows ? 100 : 0), () async { await windowOnTop(windowId()); }); diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index e3d2f72a144..2e1ca67b0e9 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -643,15 +643,12 @@ class _MonitorMenu extends StatelessWidget { } Widget buildMonitorSubmenuWidget(BuildContext context) { - final m = Provider.of(context); return Column( mainAxisSize: MainAxisSize.min, children: [ Row(children: buildMonitorList(context, false)), - supportIndividualWindows && m.useTextureRender ? Divider() : Offstage(), - supportIndividualWindows && m.useTextureRender - ? chooseDisplayBehavior() - : Offstage(), + supportIndividualWindows ? Divider() : Offstage(), + supportIndividualWindows ? chooseDisplayBehavior() : Offstage(), ], ); } @@ -737,10 +734,7 @@ class _MonitorMenu extends StatelessWidget { for (int i = 0; i < pi.displays.length; i++) { monitorList.add(buildMonitorButton(i)); } - final m = Provider.of(context); - if (supportIndividualWindows && - m.useTextureRender && - pi.displays.length > 1) { + if (supportIndividualWindows && pi.displays.length > 1) { monitorList.add(buildMonitorButton(kAllDisplayValue)); } return monitorList; @@ -824,7 +818,6 @@ class _MonitorMenu extends StatelessWidget { RxInt display = CurrentDisplayState.find(id); if (display.value != i) { final isChooseDisplayToOpenInNewWindow = pi.isSupportMultiDisplay && - bind.mainGetUseTextureRender() && bind.sessionGetDisplaysAsIndividualWindows( sessionId: ffi.sessionId) == 'Y'; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 437e6c61909..bfadc3fa5f3 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1187,10 +1187,11 @@ class ImageModel with ChangeNotifier { onRgba(int display, Uint8List rgba) { final pid = parent.target?.id; + final rect = parent.target?.ffiModel.pi.getDisplayRect(display); img.decodeImageFromPixels( rgba, - parent.target?.ffiModel.rect?.width.toInt() ?? 0, - parent.target?.ffiModel.rect?.height.toInt() ?? 0, + rect?.width.toInt() ?? 0, + rect?.height.toInt() ?? 0, isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888, onPixelsCopied: () { // Unlock the rgba memory from rust codes. @@ -2397,9 +2398,10 @@ class FFI { cursorModel.peerId = id; } + final isNewPeer = tabWindowId == null; // If tabWindowId != null, this session is a "tab -> window" one. // Else this session is a new one. - if (tabWindowId == null) { + if (isNewPeer) { // ignore: unused_local_variable final addRes = bind.sessionAddSync( sessionId: sessionId, @@ -2424,14 +2426,25 @@ class FFI { 'Unreachable, failed to add existed session to $id, $addRes'); return; } - bind.sessionTryAddDisplay( - sessionId: sessionId, displays: Int32List.fromList(displays)); ffiModel.pi.currentDisplay = display; } if (isDesktop && connType == ConnType.defaultConn) { textureModel.updateCurrentDisplay(display ?? 0); } - final stream = bind.sessionStart(sessionId: sessionId, id: id); + + // CAUTION: `sessionStart()` and `sessionStartWithDisplays()` are an async functions. + // Though the stream is returned immediately, the stream may not be ready. + // Any operations that depend on the stream should be carefully handled. + late final Stream stream; + if (isNewPeer || display == null || displays == null) { + stream = bind.sessionStart(sessionId: sessionId, id: id); + } else { + // We have to put displays in `sessionStart()` to make sure the stream is ready + // and then the displays' capturing requests can be sent. + stream = bind.sessionStartWithDisplays( + sessionId: sessionId, id: id, displays: Int32List.fromList(displays)); + } + if (isWeb) { platformFFI.setRgbaCallback((int display, Uint8List data) { onEvent2UIRgba(); @@ -2442,14 +2455,6 @@ class FFI { final cb = ffiModel.startEventListener(sessionId, id); - // Force refresh displays. - // The controlled side may not refresh the image when the (peer,display) is already subscribed. - if (displays != null) { - for (final display in displays) { - bind.sessionRefresh(sessionId: sessionId, display: display); - } - } - imageModel.updateUserTextureRender(); final hasGpuTextureRender = bind.mainHasGpuTextureRender(); final SimpleWrapper isToNewWindowNotified = SimpleWrapper(false); @@ -2500,8 +2505,8 @@ class FFI { } } else if (message is EventToUI_Rgba) { final display = message.field0; - if (imageModel.useTextureRender) { - debugPrint("EventToUI_Rgba display:$display"); + if (imageModel.useTextureRender || ffiModel.pi.forceTextureRender) { + //debugPrint("EventToUI_Rgba display:$display"); textureModel.setTextureType(display: display, gpuTexture: false); onEvent2UIRgba(); } else { @@ -2692,6 +2697,7 @@ class PeerInfo with ChangeNotifier { bool get isSupportMultiDisplay => (isDesktop || isWebDesktop) && isSupportMultiUiSession; + bool get forceTextureRender => currentDisplay == kAllDisplayValue; bool get cursorEmbedded => tryGetDisplay()?.cursorEmbedded ?? false; @@ -2700,30 +2706,32 @@ class PeerInfo with ChangeNotifier { bool get isAmyuniIdd => platformAdditions[kPlatformAdditionsIddImpl] == 'amyuni_idd'; - Display? tryGetDisplay() { + Display? tryGetDisplay({int? display}) { if (displays.isEmpty) { return null; } - if (currentDisplay == kAllDisplayValue) { + display ??= currentDisplay; + if (display == kAllDisplayValue) { return displays[0]; } else { - if (currentDisplay > 0 && currentDisplay < displays.length) { - return displays[currentDisplay]; + if (display > 0 && display < displays.length) { + return displays[display]; } else { return displays[0]; } } } - Display? tryGetDisplayIfNotAllDisplay() { + Display? tryGetDisplayIfNotAllDisplay({int? display}) { if (displays.isEmpty) { return null; } - if (currentDisplay == kAllDisplayValue) { + display ??= currentDisplay; + if (display == kAllDisplayValue) { return null; } - if (currentDisplay >= 0 && currentDisplay < displays.length) { - return displays[currentDisplay]; + if (display >= 0 && display < displays.length) { + return displays[display]; } else { return null; } @@ -2747,6 +2755,12 @@ class PeerInfo with ChangeNotifier { } return 1.0; } + + Rect? getDisplayRect(int display) { + final d = tryGetDisplayIfNotAllDisplay(display: display); + if (d == null) return null; + return Rect.fromLTWH(d.x, d.y, d.width.toDouble(), d.height.toDouble()); + } } const canvasKey = 'canvas'; diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 272171a14f8..fddf1473774 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -63,11 +63,6 @@ class RustdeskImpl { return ''; } - void sessionTryAddDisplay( - {required UuidValue sessionId, - required Int32List displays, - dynamic hint}) {} - String sessionAddSync( {required UuidValue sessionId, required String id, @@ -94,6 +89,14 @@ class RustdeskImpl { return Stream.empty(); } + Stream sessionStartWithDisplays( + {required UuidValue sessionId, + required String id, + required Int32List displays, + dynamic hint}) { + throw UnimplementedError(); + } + Future sessionGetRemember( {required UuidValue sessionId, dynamic hint}) { return Future( diff --git a/src/flutter.rs b/src/flutter.rs index 2d17944bcc3..d176bb52f24 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -408,6 +408,25 @@ impl VideoRenderer { } } + fn add_displays(&self, displays: &[i32]) { + let mut sessions_lock = self.map_display_sessions.write().unwrap(); + for display in displays { + let d = *display as usize; + if !sessions_lock.contains_key(&d) { + sessions_lock.insert( + d, + DisplaySessionInfo { + texture_rgba_ptr: 0, + size: (0, 0), + #[cfg(feature = "vram")] + gpu_output_ptr: usize::default(), + notify_render_type: None, + }, + ); + } + } + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn on_rgba(&self, display: usize, rgba: &scrap::ImageRgb) -> bool { let mut write_lock = self.map_display_sessions.write().unwrap(); @@ -768,9 +787,9 @@ impl InvokeUiSession for FlutterHandler { #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] fn on_rgba(&self, display: usize, rgba: &mut scrap::ImageRgb) { - if self.use_texture_render.load(Ordering::Relaxed) { - self.on_rgba_flutter_texture_render(display, rgba); - } else { + let use_texture_render = self.use_texture_render.load(Ordering::Relaxed); + self.on_rgba_flutter_texture_render(use_texture_render, display, rgba); + if !use_texture_render { self.on_rgba_soft_render(display, rgba); } } @@ -1039,22 +1058,43 @@ impl FlutterHandler { } drop(rgba_write_lock); - // Non-texture-render UI does not support multiple displays in the one UI session. - // It's Ok to notify each session for now. for h in self.session_handlers.read().unwrap().values() { - if let Some(stream) = &h.event_stream { - stream.add(EventToUI::Rgba(display)); + // `map_display_sessions` stores the display indices that are used by the video renderer. + let map_display_sessions = h.renderer.map_display_sessions.read().unwrap(); + // The soft renderer does not support multi ui session for now. + if map_display_sessions.len() > 1 { + continue; + } + if h.renderer + .map_display_sessions + .read() + .unwrap() + .contains_key(&display) + { + if map_display_sessions.contains_key(&display) { + if let Some(stream) = &h.event_stream { + stream.add(EventToUI::Rgba(display)); + } + } } } } #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn on_rgba_flutter_texture_render(&self, display: usize, rgba: &mut scrap::ImageRgb) { + fn on_rgba_flutter_texture_render( + &self, + use_texture_render: bool, + display: usize, + rgba: &mut scrap::ImageRgb, + ) { for (_, session) in self.session_handlers.read().unwrap().iter() { - if session.renderer.on_rgba(display, rgba) { - if let Some(stream) = &session.event_stream { - stream.add(EventToUI::Rgba(display)); + if use_texture_render || session.renderer.map_display_sessions.read().unwrap().len() > 1 + { + if session.renderer.on_rgba(display, rgba) { + if let Some(stream) = &session.event_stream { + stream.add(EventToUI::Rgba(display)); + } } } } @@ -1522,6 +1562,21 @@ pub fn session_register_gpu_texture(_session_id: SessionID, _display: usize, _ou } } +pub fn session_add_displays(session_id: &SessionID, displays: &[i32]) { + for s in sessions::get_sessions() { + if let Some(h) = s + .ui_handler + .session_handlers + .read() + .unwrap() + .get(session_id) + { + h.renderer.add_displays(displays); + break; + } + } +} + #[inline] #[cfg(not(feature = "vram"))] pub fn get_adapter_luid() -> Option { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 8e99a780a20..d6012a04b66 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -110,13 +110,6 @@ pub fn session_add_existed_sync(id: String, session_id: SessionID) -> SyncReturn } } -pub fn session_try_add_display(session_id: SessionID, displays: Vec) -> SyncReturn<()> { - if let Some(session) = sessions::get_session_by_session_id(&session_id) { - session.capture_displays(displays, vec![], vec![]); - } - SyncReturn(()) -} - pub fn session_add_sync( session_id: SessionID, id: String, @@ -153,6 +146,27 @@ pub fn session_start( session_start_(&session_id, &id, events2ui) } +pub fn session_start_with_displays( + events2ui: StreamSink, + session_id: SessionID, + id: String, + displays: Vec, +) -> ResultType<()> { + session_start_(&session_id, &id, events2ui)?; + + // `session_add_displays` is used for software rendering, multi-ui sessions. + // We need to add displays to the session first, then capture the displays. + // Otherwise, the session may not send video frames to the flutter side. + super::flutter::session_add_displays(&session_id, &displays); + if let Some(session) = sessions::get_session_by_session_id(&session_id) { + session.capture_displays(displays.clone(), vec![], vec![]); + for display in displays { + session.refresh_video(display as _); + } + } + Ok(()) +} + pub fn session_get_remember(session_id: SessionID) -> Option { if let Some(session) = sessions::get_session_by_session_id(&session_id) { Some(session.get_remember()) From ab451b90564cadd7240171034e39008fc1492280 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 13 Jun 2024 18:30:29 +0800 Subject: [PATCH 015/335] android keep screen on option (#8344) * android keep screen on option Keep screen on option relays on floating window. Three options: Never, During controlled(default), During service is on Signed-off-by: 21pages * When rustdesk is in forground, be consistent with the settings Signed-off-by: 21pages --------- Signed-off-by: 21pages --- .../flutter_hbb/FloatingWindowService.kt | 60 +++++++++++++++-- flutter/lib/consts.dart | 2 + flutter/lib/mobile/pages/settings_page.dart | 65 ++++++++++++++++++- flutter/lib/models/server_model.dart | 29 ++++++++- libs/hbb_common/src/config.rs | 4 ++ src/lang/ar.rs | 4 ++ src/lang/bg.rs | 4 ++ src/lang/ca.rs | 4 ++ src/lang/cn.rs | 4 ++ src/lang/cs.rs | 4 ++ src/lang/da.rs | 4 ++ src/lang/de.rs | 4 ++ src/lang/el.rs | 4 ++ src/lang/eo.rs | 4 ++ src/lang/es.rs | 4 ++ src/lang/et.rs | 4 ++ src/lang/fa.rs | 4 ++ src/lang/fr.rs | 4 ++ src/lang/he.rs | 4 ++ src/lang/hr.rs | 4 ++ src/lang/hu.rs | 4 ++ src/lang/id.rs | 4 ++ src/lang/it.rs | 4 ++ src/lang/ja.rs | 4 ++ src/lang/ko.rs | 4 ++ src/lang/kz.rs | 4 ++ src/lang/lt.rs | 4 ++ src/lang/lv.rs | 4 ++ src/lang/nb.rs | 4 ++ src/lang/nl.rs | 4 ++ src/lang/pl.rs | 4 ++ src/lang/pt_PT.rs | 4 ++ src/lang/ptbr.rs | 4 ++ src/lang/ro.rs | 4 ++ src/lang/ru.rs | 4 ++ src/lang/sk.rs | 4 ++ src/lang/sl.rs | 4 ++ src/lang/sq.rs | 4 ++ src/lang/sr.rs | 4 ++ src/lang/sv.rs | 4 ++ src/lang/template.rs | 4 ++ src/lang/th.rs | 4 ++ src/lang/tr.rs | 4 ++ src/lang/tw.rs | 4 ++ src/lang/ua.rs | 4 ++ src/lang/vn.rs | 4 ++ 46 files changed, 315 insertions(+), 9 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt index a16e90e957f..e117f5b9fda 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt @@ -11,7 +11,9 @@ import android.graphics.PixelFormat import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.os.Build +import android.os.Handler import android.os.IBinder +import android.os.Looper import android.util.Log import android.view.Gravity import android.view.MotionEvent @@ -20,6 +22,7 @@ import android.view.WindowManager import android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL +import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON import android.widget.ImageView import android.widget.PopupMenu import com.caverock.androidsvg.SVG @@ -39,6 +42,7 @@ class FloatingWindowService : Service(), View.OnTouchListener { private var lastDownX = 0f private var lastDownY = 0f private var viewCreated = false; + private var keepScreenOn = KeepScreenOn.DURING_CONTROLLED companion object { private val logTag = "floatingService" @@ -69,12 +73,21 @@ class FloatingWindowService : Service(), View.OnTouchListener { } Log.d(logTag, "floating window size: $viewWidth x $viewHeight, transparency: $viewTransparency, lastLayoutX: $lastLayoutX, lastLayoutY: $lastLayoutY, customSvg: $customSvg") createView(windowManager) + handler.postDelayed(runnable, 1000) Log.d(logTag, "onCreate success") } catch (e: Exception) { Log.d(logTag, "onCreate failed: $e") } } + override fun onDestroy() { + super.onDestroy() + if (viewCreated) { + windowManager.removeView(floatingView) + } + handler.removeCallbacks(runnable) + } + @SuppressLint("ClickableViewAccessibility") private fun createView(windowManager: WindowManager) { floatingView = ImageView(this) @@ -151,6 +164,15 @@ class FloatingWindowService : Service(), View.OnTouchListener { layoutParams.x = lastLayoutX layoutParams.y = lastLayoutY + val keepScreenOnOption = FFI.getLocalOption("keep-screen-on").lowercase() + keepScreenOn = when (keepScreenOnOption) { + "never" -> KeepScreenOn.NEVER + "service-on" -> KeepScreenOn.SERVICE_ON + else -> KeepScreenOn.DURING_CONTROLLED + } + Log.d(logTag, "keepScreenOn option: $keepScreenOnOption, value: $keepScreenOn") + updateKeepScreenOnLayoutParams() + windowManager.addView(floatingView, layoutParams) moveToScreenSide() } @@ -200,12 +222,7 @@ class FloatingWindowService : Service(), View.OnTouchListener { lastOrientation = resources.configuration.orientation } - override fun onDestroy() { - super.onDestroy() - if (viewCreated) { - windowManager.removeView(floatingView) - } - } + private fun performClick() { showPopupMenu() @@ -326,5 +343,36 @@ class FloatingWindowService : Service(), View.OnTouchListener { private fun stopMainService() { MainActivity.flutterMethodChannel?.invokeMethod("stop_service", null) } + + enum class KeepScreenOn { + NEVER, + DURING_CONTROLLED, + SERVICE_ON, + } + + private val handler = Handler(Looper.getMainLooper()) + private val runnable = object : Runnable { + override fun run() { + if (updateKeepScreenOnLayoutParams()) { + windowManager.updateViewLayout(floatingView, layoutParams) + } + handler.postDelayed(this, 1000) // 1000 milliseconds = 1 second + } + } + + private fun updateKeepScreenOnLayoutParams(): Boolean { + val oldOn = layoutParams.flags and FLAG_KEEP_SCREEN_ON != 0 + val newOn = keepScreenOn == KeepScreenOn.SERVICE_ON || (keepScreenOn == KeepScreenOn.DURING_CONTROLLED && MainService.isStart) + if (oldOn != newOn) { + Log.d(logTag, "change keep screen on to $newOn") + if (newOn) { + layoutParams.flags = layoutParams.flags or FLAG_KEEP_SCREEN_ON + } else { + layoutParams.flags = layoutParams.flags and FLAG_KEEP_SCREEN_ON.inv() + } + return true + } + return false + } } diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index ae3aef0b9c0..9cbfd265ed8 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -139,6 +139,8 @@ const String kOptionToggleViewOnly = "view-only"; const String kOptionDisableFloatingWindow = "disable-floating-window"; +const String kOptionKeepScreenOn = "keep-screen-on"; + const String kUrlActionClose = "close"; const String kTabLabelHomePage = "Home"; diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 1b402f6a1d5..5e3dcdee9d0 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -35,12 +35,41 @@ class SettingsPage extends StatefulWidget implements PageShape { const url = 'https://rustdesk.com/'; +enum KeepScreenOn { + never, + duringControlled, + serviceOn, +} + +String _keepScreenOnToOption(KeepScreenOn value) { + switch (value) { + case KeepScreenOn.never: + return 'never'; + case KeepScreenOn.duringControlled: + return 'during-controlled'; + case KeepScreenOn.serviceOn: + return 'service-on'; + } +} + +KeepScreenOn optionToKeepScreenOn(String value) { + switch (value) { + case 'never': + return KeepScreenOn.never; + case 'service-on': + return KeepScreenOn.serviceOn; + default: + return KeepScreenOn.duringControlled; + } +} + class _SettingsState extends State with WidgetsBindingObserver { final _hasIgnoreBattery = false; //androidVersion >= 26; // remove because not work on every device var _ignoreBatteryOpt = false; var _enableStartOnBoot = false; var _floatingWindowDisabled = false; + var _keepScreenOn = KeepScreenOn.duringControlled; // relay on floating window var _enableAbr = false; var _denyLANDiscovery = false; var _onlyWhiteList = false; @@ -96,6 +125,15 @@ class _SettingsState extends State with WidgetsBindingObserver { _floatingWindowDisabled = floatingWindowDisabled; } + final keepScreenOn = _floatingWindowDisabled + ? KeepScreenOn.never + : optionToKeepScreenOn( + bind.mainGetLocalOption(key: kOptionKeepScreenOn)); + if (keepScreenOn != _keepScreenOn) { + update = true; + _keepScreenOn = keepScreenOn; + } + final enableAbrRes = option2bool( kOptionEnableAbr, await bind.mainGetOption(key: kOptionEnableAbr)); if (enableAbrRes != _enableAbr) { @@ -524,6 +562,29 @@ class _SettingsState extends State with WidgetsBindingObserver { ? null : onFloatingWindowChanged)); + enhancementsTiles.add(_getPopupDialogRadioEntry( + title: 'Keep screen on', + list: [ + _RadioEntry('Never', _keepScreenOnToOption(KeepScreenOn.never)), + _RadioEntry('During controlled', + _keepScreenOnToOption(KeepScreenOn.duringControlled)), + _RadioEntry('During service is on', + _keepScreenOnToOption(KeepScreenOn.serviceOn)), + ], + getter: () => _keepScreenOnToOption(_floatingWindowDisabled + ? KeepScreenOn.never + : optionToKeepScreenOn( + bind.mainGetLocalOption(key: kOptionKeepScreenOn))), + asyncSetter: isOptionFixed(kOptionKeepScreenOn) || _floatingWindowDisabled + ? null + : (value) async { + await bind.mainSetLocalOption( + key: kOptionKeepScreenOn, value: value); + setState(() => _keepScreenOn = optionToKeepScreenOn(value)); + gFFI.serverModel.androidUpdatekeepScreenOn(); + }, + )); + final disabledSettings = bind.isDisableSettings(); final settings = SettingsList( sections: [ @@ -947,7 +1008,7 @@ class _RadioEntry { typedef _RadioEntryGetter = String Function(); typedef _RadioEntrySetter = Future Function(String); -_getPopupDialogRadioEntry({ +SettingsTile _getPopupDialogRadioEntry({ required String title, required List<_RadioEntry> list, required _RadioEntryGetter getter, @@ -1001,7 +1062,7 @@ _getPopupDialogRadioEntry({ return SettingsTile( title: Text(translate(title)), - onPressed: (context) => showDialog(), + onPressed: asyncSetter == null ? null : (context) => showDialog(), value: Padding( padding: EdgeInsets.symmetric(vertical: 8), child: Obx(() => Text(translate(valueText.value))), diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 2aa961cecc0..fc169c96388 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/main.dart'; +import 'package:flutter_hbb/mobile/pages/settings_page.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; @@ -421,7 +422,7 @@ class ServerModel with ChangeNotifier { await bind.mainStartService(); updateClientState(); if (isAndroid) { - WakelockPlus.enable(); + androidUpdatekeepScreenOn(); } } @@ -514,6 +515,7 @@ class ServerModel with ChangeNotifier { } if (_clients.length != oldClientLenght) { notifyListeners(); + if (isAndroid) androidUpdatekeepScreenOn(); } } @@ -548,6 +550,7 @@ class ServerModel with ChangeNotifier { scrollToBottom(); notifyListeners(); if (isAndroid && !client.authorized) showLoginDialog(client); + if (isAndroid) androidUpdatekeepScreenOn(); } catch (e) { debugPrint("Failed to call loginRequest,error:$e"); } @@ -668,6 +671,7 @@ class ServerModel with ChangeNotifier { final index = _clients.indexOf(client); tabController.remove(index); _clients.remove(client); + if (isAndroid) androidUpdatekeepScreenOn(); } } @@ -691,6 +695,7 @@ class ServerModel with ChangeNotifier { if (desktopType == DesktopType.cm && _clients.isEmpty) { hideCmWindow(); } + if (isAndroid) androidUpdatekeepScreenOn(); notifyListeners(); } catch (e) { debugPrint("onClientRemove failed,error:$e"); @@ -702,6 +707,7 @@ class ServerModel with ChangeNotifier { _clients.map((client) => bind.cmCloseConnection(connId: client.id))); _clients.clear(); tabController.state.value.tabs.clear(); + if (isAndroid) androidUpdatekeepScreenOn(); } void jumpTo(int id) { @@ -739,6 +745,27 @@ class ServerModel with ChangeNotifier { debugPrint("updateVoiceCallState failed: $e"); } } + + void androidUpdatekeepScreenOn() async { + if (!isAndroid) return; + var floatingWindowDisabled = + bind.mainGetLocalOption(key: kOptionDisableFloatingWindow) == "Y" || + !await AndroidPermissionManager.check(kSystemAlertWindow); + final keepScreenOn = floatingWindowDisabled + ? KeepScreenOn.never + : optionToKeepScreenOn( + bind.mainGetLocalOption(key: kOptionKeepScreenOn)); + final on = ((keepScreenOn == KeepScreenOn.serviceOn) && _isStart) || + (keepScreenOn == KeepScreenOn.duringControlled && + _clients.map((e) => !e.disconnected).isNotEmpty); + if (on != await WakelockPlus.enabled) { + if (on) { + WakelockPlus.enable(); + } else { + WakelockPlus.disable(); + } + } + } } enum ClientType { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index b9533407a2a..bc7c9673218 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2100,6 +2100,9 @@ pub mod keys { pub const OPTION_FLOATING_WINDOW_TRANSPARENCY: &str = "floating-window-transparency"; pub const OPTION_FLOATING_WINDOW_SVG: &str = "floating-window-svg"; + // android keep screen on + pub const OPTION_KEEP_SCREEN_ON: &str = "keep-screen-on"; + pub const OPTION_DISABLE_GROUP_PANEL: &str = "disable-group-panel"; pub const OPTION_PRE_ELEVATE_SERVICE: &str = "pre-elevate-service"; @@ -2163,6 +2166,7 @@ pub mod keys { OPTION_FLOATING_WINDOW_UNTOUCHABLE, OPTION_FLOATING_WINDOW_TRANSPARENCY, OPTION_FLOATING_WINDOW_SVG, + OPTION_KEEP_SCREEN_ON, OPTION_DISABLE_GROUP_PANEL, OPTION_PRE_ELEVATE_SERVICE, ]; diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 35ebdf33205..03483141017 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 7fb752ca654..42da650783b 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 24ac262964c..0e86032d71e 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 2763e53ce40..11da24d72e8 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "使用纹理渲染"), ("Floating window", "悬浮窗"), ("floating_window_tip", "有助于保持RustDesk后台服务"), + ("Keep screen on", "保持屏幕开启"), + ("Never", "从不"), + ("During controlled", "被控期间"), + ("During service is on", "服务开启期间"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 12ab464d81a..8edfb8c88bf 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Použít vykreslování textur"), ("Floating window", "Plovoucí okno"), ("floating_window_tip", "Pomáhá udržovat službu RustDesk na pozadí"), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 5c21fe99edf..42f9c751c6e 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 09237a31381..05992da2b8f 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Textur-Rendering verwenden"), ("Floating window", "Schwebendes Fenster"), ("floating_window_tip", "Es hilft dabei, RustDesk im Hintergrund auszuführen."), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 1f9244bb432..35b2e7c351b 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index df8fc4558c8..49ce149bc2f 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index fd691a0874b..a452f5478f9 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Usar renderizado de texturas"), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index d81976a9b4a..e96eaa2760b 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 4b9152a8846..923aca2e235 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index d8580188e20..54894c667a9 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Utiliser le rendu de texture"), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 7b41ac6e594..6ef587819b1 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 7df05e51878..34ade848903 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index e505fb015af..9ddeb3f9fef 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index c7eab6322c2..5dbd233eccf 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 048ad34e030..3d3cfc7da23 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Usa rendering texture"), ("Floating window", "Finestra galleggiante"), ("floating_window_tip", "Aiuta a mantenere il servizio di RustDesk in background"), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 32272835cf6..0ee19694103 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index a52c147ed6e..756a5a04cf8 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index f8f864e574f..3d4793d67ed 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 03438827381..7fb256f3364 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index cfa463fb00a..ef2a087ed45 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Izmantot tekstūras renderēšanu"), ("Floating window", "Peldošs logs"), ("floating_window_tip", "Tas palīdz uzturēt RustDesk fona servisu"), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 90d9666fcb8..098fa82bac3 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index aaf6b130800..8ba24d86250 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Textuurrendering gebruiken"), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index d0cd3a7d94f..0b9e1ba6e0c 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 11b974547dd..b77af7dc16b 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 8a654340c86..1e4ad748e38 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 4e2998dc816..e81f5ed32cb 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index f653cbe6626..44d16987e16 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Визуализация текстур"), ("Floating window", "Плавающее окно"), ("floating_window_tip", "Помогает поддерживать фоновую службу RustDesk"), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index cb6d0517d19..8d8fb2bf0d5 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Použiť vykresľovanie textúr"), ("Floating window", "Plávajúce okno"), ("floating_window_tip", "Pomáha udržiavať službu RustDesk na pozadí"), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 1401a227935..97554bb9759 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 740579e7c09..94d560c9e19 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 079f5ec33b2..550f73e5c02 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 7cbd89036c6..1ebac149bba 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 757e9417367..d15af13d8d2 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 99caddf5929..b2dfe9a25de 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 8537198285e..103c5c83363 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index f85bbc72d0d..03702aaeac2 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "使用紋理渲染"), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 1ca769a078f..8412a69594d 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Використовувати візуалізацію текстур"), ("Floating window", "Рухоме вікно"), ("floating_window_tip", "Допомагає зберегти фонову службу Rustdesk"), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 3e4b887f868..46ee58c67c6 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -617,5 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), + ("Keep screen on", ""), + ("Never", ""), + ("During controlled", ""), + ("During service is on", ""), ].iter().cloned().collect(); } From 2e4fafcf465a1796f48e967e057b9e3f8be604ca Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 13 Jun 2024 19:24:38 +0800 Subject: [PATCH 016/335] add missing call of androidUpdatekeepScreenOn (#8345) Signed-off-by: 21pages --- flutter/lib/mobile/pages/settings_page.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 5e3dcdee9d0..3610b94e137 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -549,6 +549,7 @@ class _SettingsState extends State with WidgetsBindingObserver { key: kOptionDisableFloatingWindow, value: disable ? 'Y' : defaultOptionNo); setState(() => _floatingWindowDisabled = disable); + gFFI.serverModel.androidUpdatekeepScreenOn(); } enhancementsTiles.add(SettingsTile.switchTile( From d33fa3f0734451d415e435021ca5b4cf0f0e6203 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:01:24 +0200 Subject: [PATCH 017/335] Update de.rs (#8346) --- src/lang/de.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 05992da2b8f..c950648baf2 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -617,9 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Textur-Rendering verwenden"), ("Floating window", "Schwebendes Fenster"), ("floating_window_tip", "Es hilft dabei, RustDesk im Hintergrund auszuführen."), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Keep screen on", "Bildschirm eingeschaltet lassen"), + ("Never", "Niemals"), + ("During controlled", "Wenn kontrolliert"), + ("During service is on", "Wenn der Dienst läuft"), ].iter().cloned().collect(); } From 60f47cb54931d030b3f2d1c9918262db79a1cc13 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 13 Jun 2024 21:04:00 +0800 Subject: [PATCH 018/335] fix: desktop, remote toolbar autohide (#8347) Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 18 +++++++++++++++--- .../lib/desktop/widgets/remote_toolbar.dart | 9 ++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 5e3d15130d2..6503fe10810 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -85,6 +85,9 @@ class _RemotePageState extends State final FocusNode _rawKeyFocusNode = FocusNode(debugLabel: "rawkeyFocusNode"); + // We need `_instanceIdOnEnterOrLeaveImage4Toolbar` together with `_onEnterOrLeaveImage4Toolbar` + // to identify the toolbar instance and its callback function. + int? _instanceIdOnEnterOrLeaveImage4Toolbar; Function(bool)? _onEnterOrLeaveImage4Toolbar; late FFI _ffi; @@ -268,9 +271,18 @@ class _RemotePageState extends State id: widget.id, ffi: _ffi, state: widget.toolbarState, - onEnterOrLeaveImageSetter: (func) => - _onEnterOrLeaveImage4Toolbar = func, - onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Toolbar = null, + onEnterOrLeaveImageSetter: (id, func) { + _instanceIdOnEnterOrLeaveImage4Toolbar = id; + _onEnterOrLeaveImage4Toolbar = func; + }, + onEnterOrLeaveImageCleaner: (id) { + // If _instanceIdOnEnterOrLeaveImage4Toolbar != id + // it means `_onEnterOrLeaveImage4Toolbar` is not set or it has been changed to another toolbar. + if (_instanceIdOnEnterOrLeaveImage4Toolbar == id) { + _instanceIdOnEnterOrLeaveImage4Toolbar = null; + _onEnterOrLeaveImage4Toolbar = null; + } + }, setRemoteState: setState, ); diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 2e1ca67b0e9..869fe87a72f 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -332,8 +332,8 @@ class RemoteToolbar extends StatefulWidget { final String id; final FFI ffi; final ToolbarState state; - final Function(Function(bool)) onEnterOrLeaveImageSetter; - final VoidCallback onEnterOrLeaveImageCleaner; + final Function(int, Function(bool)) onEnterOrLeaveImageSetter; + final Function(int) onEnterOrLeaveImageCleaner; final Function(VoidCallback) setRemoteState; RemoteToolbar({ @@ -393,7 +393,7 @@ class _RemoteToolbarState extends State { initialValue: 0, ); - widget.onEnterOrLeaveImageSetter((enter) { + widget.onEnterOrLeaveImageSetter(identityHashCode(this), (enter) { if (enter) { triggerAutoHide(); _isCursorOverImage = true; @@ -413,12 +413,11 @@ class _RemoteToolbarState extends State { dispose() { super.dispose(); - widget.onEnterOrLeaveImageCleaner(); + widget.onEnterOrLeaveImageCleaner(identityHashCode(this)); } @override Widget build(BuildContext context) { - // No need to use future builder here. return Align( alignment: Alignment.topCenter, child: Obx(() => show.value From 12f7fc3d33f1482bcca166f3c954081c08ee5c32 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 13 Jun 2024 23:22:03 +0800 Subject: [PATCH 019/335] fix: push rgba only on desktop (#8348) Signed-off-by: fufesou --- src/flutter.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index d176bb52f24..79f25430f99 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1058,6 +1058,7 @@ impl FlutterHandler { } drop(rgba_write_lock); + let is_multi_sessions = self.session_handlers.read().unwrap().len() > 1; for h in self.session_handlers.read().unwrap().values() { // `map_display_sessions` stores the display indices that are used by the video renderer. let map_display_sessions = h.renderer.map_display_sessions.read().unwrap(); @@ -1065,18 +1066,17 @@ impl FlutterHandler { if map_display_sessions.len() > 1 { continue; } - if h.renderer - .map_display_sessions - .read() - .unwrap() - .contains_key(&display) - { - if map_display_sessions.contains_key(&display) { - if let Some(stream) = &h.event_stream { - stream.add(EventToUI::Rgba(display)); - } + // If there're multiple ui sessions, we only notify the ui session that has the display. + // We must make sure that the display is in the `map_display_sessions`. + // `session_start_with_displays()` can guarantee that. + if is_multi_sessions { + if !map_display_sessions.contains_key(&display) { + continue; } } + if let Some(stream) = &h.event_stream { + stream.add(EventToUI::Rgba(display)); + } } } From 07e0b5ac10c23fdb8d1b8645f1dd35b323a070f5 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 14 Jun 2024 00:28:59 +0800 Subject: [PATCH 020/335] fix: desktop, remote toolbar, remember collapse (#8349) Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 1 + .../lib/desktop/pages/remote_tab_page.dart | 14 ++--- .../lib/desktop/widgets/remote_toolbar.dart | 53 +++++++++---------- libs/hbb_common/src/config.rs | 1 + 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 6503fe10810..47a158682f3 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -496,6 +496,7 @@ class _RemotePageState extends State () => _ffi.ffiModel.pi.isSet.isFalse ? Container(color: Colors.transparent) : Obx(() { + widget.toolbarState.initShow(sessionId); _ffi.textureModel.updateCurrentDisplay(peerDisplay.value); return ImagePaint( id: widget.id, diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 2468121ba97..f07b9538259 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -46,7 +46,6 @@ class _ConnectionTabPageState extends State { static const IconData selectedIcon = Icons.desktop_windows_sharp; static const IconData unselectedIcon = Icons.desktop_windows_outlined; - late ToolbarState _toolbarState; String? peerId; bool _isScreenRectSet = false; int? _display; @@ -54,7 +53,6 @@ class _ConnectionTabPageState extends State { var connectionMap = RxList.empty(growable: true); _ConnectionTabPageState(Map params) { - _toolbarState = ToolbarState(); RemoteCountState.init(); peerId = params['id']; final sessionId = params['session_id']; @@ -91,7 +89,7 @@ class _ConnectionTabPageState extends State { display: display, displays: displays?.cast(), password: params['password'], - toolbarState: _toolbarState, + toolbarState: ToolbarState(), tabController: tabController, switchUuid: params['switch_uuid'], forceRelay: params['forceRelay'], @@ -126,7 +124,6 @@ class _ConnectionTabPageState extends State { @override void dispose() { super.dispose(); - _toolbarState.save(); } @override @@ -251,15 +248,16 @@ class _ConnectionTabPageState extends State { final pi = ffi.ffiModel.pi; final perms = ffi.ffiModel.permissions; final sessionId = ffi.sessionId; + final toolbarState = remotePage.toolbarState; menu.addAll([ MenuEntryButton( childBuilder: (TextStyle? style) => Obx(() => Text( translate( - _toolbarState.show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'), + toolbarState.show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'), style: style, )), proc: () { - _toolbarState.switchShow(); + toolbarState.switchShow(sessionId); cancelFunc(); }, padding: padding, @@ -426,8 +424,6 @@ class _ConnectionTabPageState extends State { }); }); ConnectionTypeState.init(id); - _toolbarState.setShow( - bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y'); tabController.add(TabInfo( key: id, label: id, @@ -442,7 +438,7 @@ class _ConnectionTabPageState extends State { display: display, displays: displays?.cast(), password: args['password'], - toolbarState: _toolbarState, + toolbarState: ToolbarState(), tabController: tabController, switchUuid: switchUuid, forceRelay: args['forceRelay'], diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 869fe87a72f..1c90e25d75d 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -26,45 +26,42 @@ import './popup_menu.dart'; import './kb_layout_type_chooser.dart'; class ToolbarState { - late RxBool show; late RxBool _pin; + bool isShowInited = false; + RxBool show = false.obs; + ToolbarState() { + _pin = RxBool(false); final s = bind.getLocalFlutterOption(k: kOptionRemoteMenubarState); if (s.isEmpty) { - _initSet(false, false); return; } try { final m = jsonDecode(s); - if (m == null) { - _initSet(false, false); - } else { - _initSet(m['pin'] ?? false, m['pin'] ?? false); + if (m != null) { + _pin = RxBool(m['pin'] ?? false); } } catch (e) { debugPrint('Failed to decode toolbar state ${e.toString()}'); - _initSet(false, false); } } - _initSet(bool s, bool p) { - // Show remubar when connection is established. - show = RxBool( - bind.mainGetUserDefaultOption(key: kOptionCollapseToolbar) != 'Y'); - _pin = RxBool(p); - } - bool get pin => _pin.value; - switchShow() async { + switchShow(SessionID sessionId) async { + bind.sessionToggleOption( + sessionId: sessionId, value: kOptionCollapseToolbar); show.value = !show.value; } - setShow(bool v) async { - if (show.value != v) { - show.value = v; + initShow(SessionID sessionId) async { + if (!isShowInited) { + show.value = !(await bind.sessionGetToggleOption( + sessionId: sessionId, arg: kOptionCollapseToolbar) ?? + false); + isShowInited = true; } } @@ -86,10 +83,6 @@ class ToolbarState { bind.setLocalFlutterOption( k: kOptionRemoteMenubarState, v: jsonEncode({'pin': _pin.value})); } - - save() async { - await _savePin(); - } } class _ToolbarTheme { @@ -446,7 +439,7 @@ class _RemoteToolbarState extends State { sessionId: widget.ffi.sessionId, dragging: _dragging, fractionX: _fractionX, - show: show, + toolbarState: widget.state, setFullscreen: _setFullscreen, setMinimize: _minimize, borderRadius: borderRadius, @@ -2343,7 +2336,7 @@ class _DraggableShowHide extends StatefulWidget { final SessionID sessionId; final RxDouble fractionX; final RxBool dragging; - final RxBool show; + final ToolbarState toolbarState; final BorderRadius borderRadius; final Function(bool) setFullscreen; @@ -2354,7 +2347,7 @@ class _DraggableShowHide extends StatefulWidget { required this.sessionId, required this.fractionX, required this.dragging, - required this.show, + required this.toolbarState, required this.setFullscreen, required this.setMinimize, required this.borderRadius, @@ -2370,6 +2363,8 @@ class _DraggableShowHideState extends State<_DraggableShowHide> { double left = 0.0; double right = 1.0; + RxBool get show => widget.toolbarState.show; + @override initState() { super.initState(); @@ -2473,13 +2468,13 @@ class _DraggableShowHideState extends State<_DraggableShowHide> { )), TextButton( onPressed: () => setState(() { - widget.show.value = !widget.show.value; + widget.toolbarState.switchShow(widget.sessionId); }), child: Obx((() => Tooltip( - message: translate( - widget.show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'), + message: + translate(show.isTrue ? 'Hide Toolbar' : 'Show Toolbar'), child: Icon( - widget.show.isTrue ? Icons.expand_less : Icons.expand_more, + show.isTrue ? Icons.expand_less : Icons.expand_more, size: iconSize, ), ))), diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index bc7c9673218..3c37bd8e93a 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1284,6 +1284,7 @@ impl PeerConfig { keys::OPTION_TOUCH_MODE, keys::OPTION_I444, keys::OPTION_SWAP_LEFT_RIGHT_MOUSE, + keys::OPTION_COLLAPSE_TOOLBAR, ] .map(|key| { mp.insert(key.to_owned(), UserDefaultConfig::read(key)); From 70c20fc76f3c5fca05c9123f0b42917a03b0da6d Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:39:24 +0200 Subject: [PATCH 021/335] Update Italian language (#8352) --- src/lang/it.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 3d3cfc7da23..d1499b8f77d 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -617,9 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Usa rendering texture"), ("Floating window", "Finestra galleggiante"), ("floating_window_tip", "Aiuta a mantenere il servizio di RustDesk in background"), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Keep screen on", "Mantieni schermo acceso"), + ("Never", "Mai"), + ("During controlled", "Durante il controllo"), + ("During service is on", "Quando il servizio è attivo"), ].iter().cloned().collect(); } From 92dd0ee1dde39394058075787f38bb32b9c990ba Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:55:03 +0800 Subject: [PATCH 022/335] fix: non texture, multi window, switch display (#8353) * fix: non texture, multi window, switch display Signed-off-by: fufesou * fix build Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/common.dart | 3 +- flutter/lib/models/model.dart | 3 +- flutter/lib/web/bridge.dart | 5 +- src/flutter.rs | 92 ++++++++++++++++------------------- src/flutter_ffi.rs | 12 ++--- 5 files changed, 57 insertions(+), 58 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 0e1aca9d070..4536a806dd6 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2924,7 +2924,8 @@ openMonitorInNewTabOrWindow(int i, String peerId, PeerInfo pi, kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args)); } -setNewConnectWindowFrame(int windowId, String peerId, int? display, Rect? screenRect) async { +setNewConnectWindowFrame( + int windowId, String peerId, int? display, Rect? screenRect) async { if (screenRect == null) { await restoreWindowPosition(WindowType.RemoteDesktop, windowId: windowId, display: display, peerId: peerId); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index bfadc3fa5f3..c7b3debea5d 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -2420,7 +2420,8 @@ class FFI { 'Unreachable, failed to add existed session to $id, the displays is null while display is $display'); return; } - final addRes = bind.sessionAddExistedSync(id: id, sessionId: sessionId); + final addRes = bind.sessionAddExistedSync( + id: id, sessionId: sessionId, displays: Int32List.fromList(displays)); if (addRes != '') { debugPrint( 'Unreachable, failed to add existed session to $id, $addRes'); diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index fddf1473774..60d4aa732b9 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -59,7 +59,10 @@ class RustdeskImpl { } String sessionAddExistedSync( - {required String id, required UuidValue sessionId, dynamic hint}) { + {required String id, + required UuidValue sessionId, + required Int32List displays, + dynamic hint}) { return ''; } diff --git a/src/flutter.rs b/src/flutter.rs index 79f25430f99..5f506588139 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -168,6 +168,9 @@ pub unsafe extern "C" fn get_rustdesk_app_name(buffer: *mut u16, length: i32) -> #[derive(Default)] struct SessionHandler { event_stream: Option>, + // displays of current session. + // We need this variable to check if the display is in use before pushing rgba to flutter. + displays: Vec, renderer: VideoRenderer, } @@ -408,25 +411,6 @@ impl VideoRenderer { } } - fn add_displays(&self, displays: &[i32]) { - let mut sessions_lock = self.map_display_sessions.write().unwrap(); - for display in displays { - let d = *display as usize; - if !sessions_lock.contains_key(&d) { - sessions_lock.insert( - d, - DisplaySessionInfo { - texture_rgba_ptr: 0, - size: (0, 0), - #[cfg(feature = "vram")] - gpu_output_ptr: usize::default(), - notify_render_type: None, - }, - ); - } - } - } - #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn on_rgba(&self, display: usize, rgba: &scrap::ImageRgb) -> bool { let mut write_lock = self.map_display_sessions.write().unwrap(); @@ -598,6 +582,7 @@ impl FlutterHandler { pub fn update_use_texture_render(&self) { self.use_texture_render .store(crate::ui_interface::use_texture_render(), Ordering::Relaxed); + self.display_rgbas.write().unwrap().clear(); } } @@ -1058,26 +1043,39 @@ impl FlutterHandler { } drop(rgba_write_lock); - let is_multi_sessions = self.session_handlers.read().unwrap().len() > 1; + let mut is_sent = false; + let is_multi_sessions = self.is_multi_ui_session(); for h in self.session_handlers.read().unwrap().values() { - // `map_display_sessions` stores the display indices that are used by the video renderer. - let map_display_sessions = h.renderer.map_display_sessions.read().unwrap(); - // The soft renderer does not support multi ui session for now. - if map_display_sessions.len() > 1 { + // The soft renderer does not support multi-displays session for now. + if h.displays.len() > 1 { continue; } // If there're multiple ui sessions, we only notify the ui session that has the display. - // We must make sure that the display is in the `map_display_sessions`. - // `session_start_with_displays()` can guarantee that. if is_multi_sessions { - if !map_display_sessions.contains_key(&display) { + if !h.displays.contains(&display) { continue; } } if let Some(stream) = &h.event_stream { stream.add(EventToUI::Rgba(display)); + is_sent = true; } } + // We need `is_sent` here. Because we use texture render for multi-displays session. + // + // Eg. We have to windows, one is display 1, the other is displays 0&1. + // When image of display 0 is received, we will not send the event. + // + // 1. "display 1" will not send the event. + // 2. "displays 0&1" will not send the event. Because it uses texutre render for now. + if !is_sent { + self.display_rgbas + .write() + .unwrap() + .get_mut(&display) + .unwrap() + .valid = false; + } } #[inline] @@ -1089,8 +1087,7 @@ impl FlutterHandler { rgba: &mut scrap::ImageRgb, ) { for (_, session) in self.session_handlers.read().unwrap().iter() { - if use_texture_render || session.renderer.map_display_sessions.read().unwrap().len() > 1 - { + if use_texture_render || session.displays.len() > 1 { if session.renderer.on_rgba(display, rgba) { if let Some(stream) = &session.event_stream { stream.add(EventToUI::Rgba(display)); @@ -1102,8 +1099,12 @@ impl FlutterHandler { } // This function is only used for the default connection session. -pub fn session_add_existed(peer_id: String, session_id: SessionID) -> ResultType<()> { - sessions::insert_peer_session_id(peer_id, ConnType::DEFAULT_CONN, session_id); +pub fn session_add_existed( + peer_id: String, + session_id: SessionID, + displays: Vec, +) -> ResultType<()> { + sessions::insert_peer_session_id(peer_id, ConnType::DEFAULT_CONN, session_id, displays); Ok(()) } @@ -1523,6 +1524,11 @@ pub fn session_set_size(session_id: SessionID, display: usize, width: usize, hei .unwrap() .get_mut(&session_id) { + // If the session is the first connection, displays is not set yet. + // `displays`` is set while switching displays or adding a new session. + if !h.displays.contains(&display) { + h.displays.push(display); + } h.renderer.set_size(display, width, height); break; } @@ -1562,21 +1568,6 @@ pub fn session_register_gpu_texture(_session_id: SessionID, _display: usize, _ou } } -pub fn session_add_displays(session_id: &SessionID, displays: &[i32]) { - for s in sessions::get_sessions() { - if let Some(h) = s - .ui_handler - .session_handlers - .read() - .unwrap() - .get(session_id) - { - h.renderer.add_displays(displays); - break; - } - } -} - #[inline] #[cfg(not(feature = "vram"))] pub fn get_adapter_luid() -> Option { @@ -1898,8 +1889,9 @@ pub mod sessions { pub fn session_switch_display(is_desktop: bool, session_id: SessionID, value: Vec) { for s in SESSIONS.read().unwrap().values() { - let read_lock = s.ui_handler.session_handlers.read().unwrap(); - if read_lock.contains_key(&session_id) { + let mut write_lock = s.ui_handler.session_handlers.write().unwrap(); + if let Some(h) = write_lock.get_mut(&session_id) { + h.displays = value.iter().map(|x| *x as usize).collect::<_>(); if value.len() == 1 { // Switch display. // This operation will also cause the peer to send a switch display message. @@ -1915,7 +1907,7 @@ pub mod sessions { Some(value[0] as _), &session_id, &s, - &read_lock, + &write_lock, ); } } @@ -1947,9 +1939,11 @@ pub mod sessions { peer_id: String, conn_type: ConnType, session_id: SessionID, + displays: Vec, ) -> bool { if let Some(s) = SESSIONS.read().unwrap().get(&(peer_id, conn_type)) { let mut h = SessionHandler::default(); + h.displays = displays.iter().map(|x| *x as usize).collect::<_>(); #[cfg(not(any(target_os = "android", target_os = "ios")))] let is_support_multi_ui_session = crate::common::is_support_multi_ui_session( &s.ui_handler.peer_info.read().unwrap().version, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index d6012a04b66..4acae739328 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -102,8 +102,12 @@ pub fn peer_get_default_sessions_count(id: String) -> SyncReturn { SyncReturn(sessions::get_session_count(id, ConnType::DEFAULT_CONN)) } -pub fn session_add_existed_sync(id: String, session_id: SessionID) -> SyncReturn { - if let Err(e) = session_add_existed(id.clone(), session_id) { +pub fn session_add_existed_sync( + id: String, + session_id: SessionID, + displays: Vec, +) -> SyncReturn { + if let Err(e) = session_add_existed(id.clone(), session_id, displays) { SyncReturn(format!("Failed to add session with id {}, {}", &id, e)) } else { SyncReturn("".to_owned()) @@ -154,10 +158,6 @@ pub fn session_start_with_displays( ) -> ResultType<()> { session_start_(&session_id, &id, events2ui)?; - // `session_add_displays` is used for software rendering, multi-ui sessions. - // We need to add displays to the session first, then capture the displays. - // Otherwise, the session may not send video frames to the flutter side. - super::flutter::session_add_displays(&session_id, &displays); if let Some(session) = sessions::get_session_by_session_id(&session_id) { session.capture_displays(displays.clone(), vec![], vec![]); for display in displays { From 5cf2d5f0623bf7b55835335c97ec78d85f7f0cd9 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 15 Jun 2024 14:03:33 +0800 Subject: [PATCH 023/335] change back to 1.75 since sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html --- .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 1c222b8ba92..171061198e2 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -12,7 +12,7 @@ on: env: WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 - RUST_VERSION: "1.78" + RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" FLUTTER_VERSION: "3.19.6" From f224d8872e05e292322bffcdbec1d4d001ba725a Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 15 Jun 2024 08:03:42 +0000 Subject: [PATCH 024/335] update cs.rs (#8365) --- src/lang/cs.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 8edfb8c88bf..1fbc0bf8cf9 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -617,9 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Použít vykreslování textur"), ("Floating window", "Plovoucí okno"), ("floating_window_tip", "Pomáhá udržovat službu RustDesk na pozadí"), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Keep screen on", "Ponechat obrazovku zapnutou"), + ("Never", "Nikdy"), + ("During controlled", "Během řízeného"), + ("During service is on", "Během služby je v provozu"), ].iter().cloned().collect(); } From 12ff1319f1b3610ae959e6f2980717186a1b44af Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 15 Jun 2024 08:03:56 +0000 Subject: [PATCH 025/335] update sk.rs (#8364) --- src/lang/sk.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 8d8fb2bf0d5..3f085251e00 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -617,9 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Použiť vykresľovanie textúr"), ("Floating window", "Plávajúce okno"), ("floating_window_tip", "Pomáha udržiavať službu RustDesk na pozadí"), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Keep screen on", "Ponechať obrazovku zapnutú"), + ("Never", "Nikdy"), + ("During controlled", "Počas kontrolovaného"), + ("During service is on", "Počas služby je v prevádzke"), ].iter().cloned().collect(); } From 60ea8d2c2b6d30c70e5252646b499b984e853dc5 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 16 Jun 2024 12:01:41 +0800 Subject: [PATCH 026/335] mac scale factor of each screen (#8368) Signed-off-by: 21pages --- libs/scrap/src/quartz/display.rs | 2 +- libs/scrap/src/quartz/ffi.rs | 2 +- src/platform/macos.mm | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libs/scrap/src/quartz/display.rs b/libs/scrap/src/quartz/display.rs index b7d45488013..137d648cf01 100644 --- a/libs/scrap/src/quartz/display.rs +++ b/libs/scrap/src/quartz/display.rs @@ -71,7 +71,7 @@ impl Display { } pub fn scale(self) -> f64 { - let s = unsafe { BackingScaleFactor() as _ }; + let s = unsafe { BackingScaleFactor(self.0) as _ }; if s > 1. { let enable_retina = super::ENABLE_RETINA.lock().unwrap().clone(); if enable_retina { diff --git a/libs/scrap/src/quartz/ffi.rs b/libs/scrap/src/quartz/ffi.rs index 7cc57e29e82..c55bc8c699a 100644 --- a/libs/scrap/src/quartz/ffi.rs +++ b/libs/scrap/src/quartz/ffi.rs @@ -193,7 +193,7 @@ extern "C" { pub fn CGDisplayIsOnline(display: u32) -> i32; pub fn CGDisplayBounds(display: u32) -> CGRect; - pub fn BackingScaleFactor() -> f32; + pub fn BackingScaleFactor(display: u32) -> f32; // IOSurface diff --git a/src/platform/macos.mm b/src/platform/macos.mm index d487098b617..cf35b5e1a65 100644 --- a/src/platform/macos.mm +++ b/src/platform/macos.mm @@ -109,9 +109,13 @@ return Elevate(NULL, NULL); } -extern "C" float BackingScaleFactor() { - NSScreen* s = [NSScreen mainScreen]; - if (s) return [s backingScaleFactor]; +extern "C" float BackingScaleFactor(uint32_t display) { + display -= 1; + NSArray *screens = [NSScreen screens]; + if (display >= 0 && display < [screens count]) { + NSScreen* s = [screens objectAtIndex:display]; + if (s) return [s backingScaleFactor]; + } return 1; } From f016d453fa992366b4247e4ccec3b044e6074546 Mon Sep 17 00:00:00 2001 From: solokot Date: Sun, 16 Jun 2024 10:14:45 +0300 Subject: [PATCH 027/335] Update ru.rs (#8370) --- src/lang/ru.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 44d16987e16..2f6b4a1a626 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -507,7 +507,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Exit", "Выход"), ("Open", "Открыть"), ("logout_tip", "Вы действительно хотите выйти?"), - ("Service", "Сервис"), + ("Service", "Служба"), ("Start", "Запустить"), ("Stop", "Остановить"), ("exceed_max_devices", "Достигнуто максимальное количество управляемых устройств."), @@ -617,9 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Визуализация текстур"), ("Floating window", "Плавающее окно"), ("floating_window_tip", "Помогает поддерживать фоновую службу RustDesk"), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Keep screen on", "Держать экран включённым"), + ("Never", "Нет"), + ("During controlled", "При управлении"), + ("During service is on", "При запущенной службе"), ].iter().cloned().collect(); } From db108d964bf829b464a7867ef89daf96acdc13f1 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 16 Jun 2024 22:37:14 +0800 Subject: [PATCH 028/335] fix: build https://github.com/1wilkens/pam-sys/commit/09f452b055928f70cfbcdb301a049265bbadf511 (#8373) Signed-off-by: fufesou --- Cargo.lock | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 058e6cc2405..f26b2ad3061 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4327,7 +4327,7 @@ dependencies = [ [[package]] name = "pam" version = "0.7.0" -source = "git+https://github.com/fufesou/pam#10da2cbbabe32cbc9de22a66abe44738e7ec0ea0" +source = "git+https://github.com/fufesou/pam#3a2aaa6e07b176d8e2d66a5eec38d2ddb45f009f" dependencies = [ "libc", "pam-macros", @@ -4349,8 +4349,7 @@ dependencies = [ [[package]] name = "pam-sys" version = "1.0.0-alpha4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9dfd42858f6a6bb1081079fd9dc259ca3e2aaece6cb689fd36b1058046c969" +source = "git+https://github.com/fufesou/pam-sys?branch=fix/v1.0.0-alpha4_gnuc_va_list#3337c9bb9a9c68d7497ec8c93cad2368c26091b7" dependencies = [ "bindgen 0.59.2", "libc", From 8d6de9ca59f9718c3a6658bba941eb8ba78720d1 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 16 Jun 2024 23:13:46 +0800 Subject: [PATCH 029/335] opt android ab ui (#8374) * multiline error banner * mobile remove ab permission icons due to hard to press * center ab dropdown button text Signed-off-by: 21pages --- flutter/lib/common.dart | 3 -- flutter/lib/common/widgets/address_book.dart | 49 ++++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 4536a806dd6..407e2ed03d4 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2747,12 +2747,10 @@ Widget buildErrorBanner(BuildContext context, required RxString err, required Function? retry, required Function close}) { - const double height = 25; return Obx(() => Offstage( offstage: !(!loading.value && err.value.isNotEmpty), child: Center( child: Container( - height: height, color: MyTheme.color(context).errorBannerBg, child: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -2771,7 +2769,6 @@ Widget buildErrorBanner(BuildContext context, message: translate(err.value), child: Text( translate(err.value), - overflow: TextOverflow.ellipsis, ), )).marginSymmetric(vertical: 2), ), diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 1128f1aa58f..d316d4fa2f6 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -130,7 +130,6 @@ class _AddressBookState extends State { width: double.infinity, child: _buildTags(), ), - _buildAbPermission(), ], ), ), @@ -198,24 +197,28 @@ class _AddressBookState extends State { if (contains) { names.insert(0, personalAddressBookName); } + + Row buildItem(String e, {bool button = false}) { + return Row( + children: [ + Expanded( + child: Tooltip( + waitDuration: Duration(milliseconds: 500), + message: gFFI.abModel.translatedName(e), + child: Text( + gFFI.abModel.translatedName(e), + style: button ? null : TextStyle(fontSize: 14.0), + maxLines: 1, + overflow: TextOverflow.ellipsis, + textAlign: button ? TextAlign.center : null, + )), + ), + ], + ); + } + final items = names - .map((e) => DropdownMenuItem( - value: e, - child: Row( - children: [ - Expanded( - child: Tooltip( - waitDuration: Duration(milliseconds: 500), - message: gFFI.abModel.translatedName(e), - child: Text( - gFFI.abModel.translatedName(e), - style: TextStyle(fontSize: 14.0), - maxLines: 1, - overflow: TextOverflow.ellipsis, - )), - ), - ], - ))) + .map((e) => DropdownMenuItem(value: e, child: buildItem(e))) .toList(); var menuItemStyleData = MenuItemStyleData(height: 36); if (contains && items.length > 1) { @@ -237,14 +240,22 @@ class _AddressBookState extends State { bind.setLocalFlutterOption(k: kOptionCurrentAbName, v: value); } }, + customButton: Container( + height: isDesktop ? 48 : 40, + child: Row(children: [ + Expanded( + child: buildItem(gFFI.abModel.currentName.value, button: true)), + Icon(Icons.arrow_drop_down), + ]), + ), underline: Container( height: 0.7, color: Theme.of(context).dividerColor.withOpacity(0.1), ), - buttonStyleData: ButtonStyleData(height: 48), menuItemStyleData: menuItemStyleData, items: items, isExpanded: true, + isDense: true, dropdownSearchData: DropdownSearchData( searchController: textEditingController, searchInnerWidgetHeight: 50, From e2a6d66805bcf08d5af315854bf3084b7b371bf4 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 16 Jun 2024 23:41:40 +0800 Subject: [PATCH 030/335] make mobile ab dropdown button text vertical center (#8376) Signed-off-by: 21pages --- flutter/lib/common/widgets/address_book.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index d316d4fa2f6..50bb35dcab4 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -110,6 +110,7 @@ class _AddressBookState extends State { } Widget _buildAddressBookMobile() { + const padding = 8.0; return Column( children: [ Offstage( @@ -120,7 +121,8 @@ class _AddressBookState extends State { border: Border.all( color: Theme.of(context).colorScheme.background)), child: Container( - padding: const EdgeInsets.all(8.0), + padding: + const EdgeInsets.fromLTRB(padding, 0, padding, padding), child: Column( mainAxisSize: MainAxisSize.min, children: [ From 2e0eaed322f13119bae739f097f4a60d013dda2c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 16 Jun 2024 23:50:39 +0800 Subject: [PATCH 031/335] call _ffiBind.mainStartPa only for --cm --- flutter/lib/models/native_model.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 3f81ba422f8..5790026c78f 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -134,7 +134,9 @@ class PlatformFFI { if (isLinux) { // Start a dbus service, no need to await _ffiBind.mainStartDbusServer(); - _ffiBind.mainStartPa(); + if (appType == kAppTypeConnectionManager) { + _ffiBind.mainStartPa(); + } } else if (isMacOS && isMain) { // Start ipc service for uri links. _ffiBind.mainStartIpcUrlServer(); From ed0cba281f4a897558e6edbf8cc664eea54d4f3e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 16 Jun 2024 23:59:09 +0800 Subject: [PATCH 032/335] start dbus only for main --- flutter/lib/models/native_model.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 5790026c78f..5883416ef0b 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -132,8 +132,10 @@ class PlatformFFI { _ffiBind = RustdeskImpl(dylib); if (isLinux) { - // Start a dbus service, no need to await - _ffiBind.mainStartDbusServer(); + if (isMain) { + // Start a dbus service for uri links, no need to await + _ffiBind.mainStartDbusServer(); + } if (appType == kAppTypeConnectionManager) { _ffiBind.mainStartPa(); } From 237d2342774b2a88db74156ce2d33afd85d8f0e0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 17 Jun 2024 09:44:36 +0800 Subject: [PATCH 033/335] add rustc 1.78 ABI change comment --- .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 171061198e2..deb51f5389b 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -11,7 +11,7 @@ on: default: "nightly" env: - WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 + WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" From 5b52742cf708a09bf3f85c6a56ec939e6eb18f97 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 17 Jun 2024 10:35:57 +0800 Subject: [PATCH 034/335] fix mobile ab/group not update when login with 2fa/email (#8378) Signed-off-by: 21pages --- flutter/lib/common/widgets/login.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index a6b8048a8e9..92528d5e3f3 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -455,7 +455,7 @@ Future loginDialog() async { } if (isEmailVerification != null) { if (isMobile) { - if (close != null) close(false); + if (close != null) close(null); verificationCodeDialog( resp.user, resp.secret, isEmailVerification); } else { @@ -712,6 +712,11 @@ Future verificationCodeDialog( dialogButton("Verify", onPressed: getOnSubmit()), ]); }); + // For verification code, desktop update other models in login dialog, mobile need to close login dialog first, + // otherwise the soft keyboard will jump out on each key press, so mobile update in verification code dialog. + if (isMobile && res == true) { + await UserModel.updateOtherModels(); + } return res; } From 4a648f006879088b634ad8408959cd7a92950176 Mon Sep 17 00:00:00 2001 From: flusheDData <116861809+flusheDData@users.noreply.github.com> Date: Mon, 17 Jun 2024 04:36:10 +0200 Subject: [PATCH 035/335] New terms added (#8377) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update es.rs New term and tip added * Update es.rs change representación por renderizado (render) * Update es.rs New terms added --- src/lang/es.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lang/es.rs b/src/lang/es.rs index a452f5478f9..c9ceba5d042 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -615,11 +615,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_new_voice_call_tip", "Se ha recibido una nueva solicitud de llamada de voz. Si aceptas el audio cambiará a comunicación de voz."), ("texture_render_tip", "Usar renderizado de texturas para hacer las imágenes más suaves."), ("Use texture rendering", "Usar renderizado de texturas"), - ("Floating window", ""), - ("floating_window_tip", ""), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Floating window", "Ventana flotante"), + ("floating_window_tip", "Ayuda a mantener el servicio de RustDesk de fondo"), + ("Keep screen on", "Mantener la pantalla encendida"), + ("Never", "Nunca"), + ("During controlled", "Mientras está siendo controlado"), + ("During service is on", "Mientras el servicio está activo"), ].iter().cloned().collect(); } From f2a612c3d95bfd75b94ec4295ce61224b44edcdb Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 17 Jun 2024 10:57:25 +0800 Subject: [PATCH 036/335] add voice_call start_pa --- flutter/lib/models/native_model.dart | 3 --- flutter/lib/web/bridge.dart | 4 ---- src/flutter_ffi.rs | 5 ----- src/ui_session_interface.rs | 2 ++ 4 files changed, 2 insertions(+), 12 deletions(-) diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 5883416ef0b..0b70e30c8c0 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -136,9 +136,6 @@ class PlatformFFI { // Start a dbus service for uri links, no need to await _ffiBind.mainStartDbusServer(); } - if (appType == kAppTypeConnectionManager) { - _ffiBind.mainStartPa(); - } } else if (isMacOS && isMain) { // Start ipc service for uri links. _ffiBind.mainStartIpcUrlServer(); diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 60d4aa732b9..2cb7a573424 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -1412,10 +1412,6 @@ class RustdeskImpl { return false; } - Future mainStartPa({dynamic hint}) { - throw UnimplementedError(); - } - bool mainHideDocker({dynamic hint}) { throw UnimplementedError(); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 4acae739328..a44c3d09519 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1839,11 +1839,6 @@ pub fn main_is_login_wayland() -> SyncReturn { SyncReturn(is_login_wayland()) } -pub fn main_start_pa() { - #[cfg(target_os = "linux")] - std::thread::spawn(crate::ipc::start_pa); -} - pub fn main_hide_docker() -> SyncReturn { #[cfg(target_os = "macos")] crate::platform::macos::hide_dock(); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 2df18ae42ee..1c56b97d111 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1268,6 +1268,8 @@ impl Session { #[inline] pub fn request_voice_call(&self) { + #[cfg(target_os = "linux")] + std::thread::spawn(crate::ipc::start_pa); self.send(Data::NewVoiceCall); } From e933f0baf2b62c0a5491d5fb40205d1f0ec23b6e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 17 Jun 2024 11:16:33 +0800 Subject: [PATCH 037/335] build 41 --- flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 93fc9aadf19..17c3db5067f 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.2.6+40 +version: 1.2.6+41 environment: sdk: '^3.1.0' From 70151e3dd813946852e4e29a8acc8a15a9c4e483 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 17 Jun 2024 12:50:29 +0800 Subject: [PATCH 038/335] add Push Notifications capability though we do not use it explictly, but our dep used it, have to enable it, otherwise review will refuse us --- flutter/ios/Runner/Runner.entitlements | 2 ++ flutter/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter/ios/Runner/Runner.entitlements b/flutter/ios/Runner/Runner.entitlements index ba21fbdaf29..75e36a143e4 100644 --- a/flutter/ios/Runner/Runner.entitlements +++ b/flutter/ios/Runner/Runner.entitlements @@ -2,6 +2,8 @@ + aps-environment + development com.apple.developer.networking.wifi-info diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 17c3db5067f..20ef15d3f8f 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.2.6+41 +version: 1.2.6+42 environment: sdk: '^3.1.0' From 46bf552afce81195d56d3a0653ad62b193654ddc Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Mon, 17 Jun 2024 12:31:38 +0300 Subject: [PATCH 039/335] Update lv.rs (#8383) --- src/lang/lv.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/lv.rs b/src/lang/lv.rs index ef2a087ed45..b1db124c268 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -617,9 +617,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Izmantot tekstūras renderēšanu"), ("Floating window", "Peldošs logs"), ("floating_window_tip", "Tas palīdz uzturēt RustDesk fona servisu"), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Keep screen on", "Turēt ekrānu ieslēgtu"), + ("Never", "Nekad"), + ("During controlled", "Lietošanas laikā"), + ("During service is on", "Kamēr pakalpojums ir ieslēgts"), ].iter().cloned().collect(); } From 37ebac2a9e01b0a4c7e17645109521bdfcdc6b7b Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 17 Jun 2024 23:53:43 +0800 Subject: [PATCH 040/335] update hwcodec, remove AVCodecParserContext (#8389) It was used to decode different resolution with same decoder, but may cause crash. Signed-off-by: 21pages --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f26b2ad3061..ff6e44c2f50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3037,8 +3037,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.4.16" -source = "git+https://github.com/21pages/hwcodec#0973290faddc4e22936859dd10f05610eb8d1619" +version = "0.4.17" +source = "git+https://github.com/21pages/hwcodec#38eb9bfc4730986ebeb519ab39027d654356ce1a" dependencies = [ "bindgen 0.59.2", "cc", From e23a9da1a8f1a79fe7906cfd7d8db1f29504fd32 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 18 Jun 2024 08:29:10 +0800 Subject: [PATCH 041/335] sync get option in android setting (#8393) Signed-off-by: 21pages --- flutter/ios_arm64.sh | 2 +- .../desktop/pages/desktop_setting_page.dart | 4 +- flutter/lib/mobile/pages/settings_page.dart | 106 ++++-------------- src/flutter_ffi.rs | 4 +- 4 files changed, 28 insertions(+), 88 deletions(-) diff --git a/flutter/ios_arm64.sh b/flutter/ios_arm64.sh index 5a629175596..579baaa6dda 100755 --- a/flutter/ios_arm64.sh +++ b/flutter/ios_arm64.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -cargo build --features flutter --release --target aarch64-apple-ios --lib +cargo build --features flutter,hwcodec --release --target aarch64-apple-ios --lib diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 6cb2b2329bb..04385ce5075 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -504,9 +504,9 @@ class _GeneralState extends State<_General> { Widget record(BuildContext context) { final showRootDir = isWindows && bind.mainIsInstalled(); return futureBuilder(future: () async { - String user_dir = await bind.mainVideoSaveDirectory(root: false); + String user_dir = bind.mainVideoSaveDirectory(root: false); String root_dir = - showRootDir ? await bind.mainVideoSaveDirectory(root: true) : ''; + showRootDir ? bind.mainVideoSaveDirectory(root: true) : ''; bool user_dir_exists = await Directory(user_dir).exists(); bool root_dir_exists = showRootDir ? await Directory(root_dir).exists() : false; diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 3610b94e137..d1e2ff55abd 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -89,6 +89,27 @@ class _SettingsState extends State with WidgetsBindingObserver { super.initState(); WidgetsBinding.instance.addObserver(this); + _enableAbr = option2bool( + kOptionEnableAbr, bind.mainGetOptionSync(key: kOptionEnableAbr)); + _denyLANDiscovery = !option2bool(kOptionEnableLanDiscovery, + bind.mainGetOptionSync(key: kOptionEnableLanDiscovery)); + _onlyWhiteList = (bind.mainGetOptionSync(key: kOptionWhitelist)) != + defaultOptionWhitelist; + _enableDirectIPAccess = option2bool( + kOptionDirectServer, bind.mainGetOptionSync(key: kOptionDirectServer)); + _enableRecordSession = option2bool(kOptionEnableRecordSession, + bind.mainGetOptionSync(key: kOptionEnableRecordSession)); + _enableHardwareCodec = option2bool(kOptionEnableHwcodec, + bind.mainGetOptionSync(key: kOptionEnableHwcodec)); + _autoRecordIncomingSession = option2bool(kOptionAllowAutoRecordIncoming, + bind.mainGetOptionSync(key: kOptionAllowAutoRecordIncoming)); + _localIP = bind.mainGetOptionSync(key: 'local-ip-addr'); + _directAccessPort = bind.mainGetOptionSync(key: kOptionDirectAccessPort); + _allowAutoDisconnect = option2bool(kOptionAllowAutoDisconnect, + bind.mainGetOptionSync(key: kOptionAllowAutoDisconnect)); + _autoDisconnectTimeout = + bind.mainGetOptionSync(key: kOptionAutoDisconnectTimeout); + () async { var update = false; @@ -134,69 +155,6 @@ class _SettingsState extends State with WidgetsBindingObserver { _keepScreenOn = keepScreenOn; } - final enableAbrRes = option2bool( - kOptionEnableAbr, await bind.mainGetOption(key: kOptionEnableAbr)); - if (enableAbrRes != _enableAbr) { - update = true; - _enableAbr = enableAbrRes; - } - - final denyLanDiscovery = !option2bool(kOptionEnableLanDiscovery, - await bind.mainGetOption(key: kOptionEnableLanDiscovery)); - if (denyLanDiscovery != _denyLANDiscovery) { - update = true; - _denyLANDiscovery = denyLanDiscovery; - } - - final onlyWhiteList = (await bind.mainGetOption(key: kOptionWhitelist)) != - defaultOptionWhitelist; - if (onlyWhiteList != _onlyWhiteList) { - update = true; - _onlyWhiteList = onlyWhiteList; - } - - final enableDirectIPAccess = option2bool(kOptionDirectServer, - await bind.mainGetOption(key: kOptionDirectServer)); - if (enableDirectIPAccess != _enableDirectIPAccess) { - update = true; - _enableDirectIPAccess = enableDirectIPAccess; - } - - final enableRecordSession = option2bool(kOptionEnableRecordSession, - await bind.mainGetOption(key: kOptionEnableRecordSession)); - if (enableRecordSession != _enableRecordSession) { - update = true; - _enableRecordSession = enableRecordSession; - } - - final enableHardwareCodec = option2bool(kOptionEnableHwcodec, - await bind.mainGetOption(key: kOptionEnableHwcodec)); - if (_enableHardwareCodec != enableHardwareCodec) { - update = true; - _enableHardwareCodec = enableHardwareCodec; - } - - final autoRecordIncomingSession = option2bool( - kOptionAllowAutoRecordIncoming, - await bind.mainGetOption(key: kOptionAllowAutoRecordIncoming)); - if (autoRecordIncomingSession != _autoRecordIncomingSession) { - update = true; - _autoRecordIncomingSession = autoRecordIncomingSession; - } - - final localIP = await bind.mainGetOption(key: 'local-ip-addr'); - if (localIP != _localIP) { - update = true; - _localIP = localIP; - } - - final directAccessPort = - await bind.mainGetOption(key: kOptionDirectAccessPort); - if (directAccessPort != _directAccessPort) { - update = true; - _directAccessPort = directAccessPort; - } - final fingerprint = await bind.mainGetFingerprint(); if (_fingerprint != fingerprint) { update = true; @@ -208,21 +166,6 @@ class _SettingsState extends State with WidgetsBindingObserver { update = true; _buildDate = buildDate; } - - final allowAutoDisconnect = option2bool(kOptionAllowAutoDisconnect, - await bind.mainGetOption(key: kOptionAllowAutoDisconnect)); - if (allowAutoDisconnect != _allowAutoDisconnect) { - update = true; - _allowAutoDisconnect = allowAutoDisconnect; - } - - final autoDisconnectTimeout = - await bind.mainGetOption(key: kOptionAutoDisconnectTimeout); - if (autoDisconnectTimeout != _autoDisconnectTimeout) { - update = true; - _autoDisconnectTimeout = autoDisconnectTimeout; - } - if (update) { setState(() {}); } @@ -661,11 +604,8 @@ class _SettingsState extends State with WidgetsBindingObserver { title: Text(translate('Automatically record incoming sessions')), leading: Icon(Icons.videocam), - description: FutureBuilder( - builder: (ctx, data) => Offstage( - offstage: !data.hasData, - child: Text("${translate("Directory")}: ${data.data}")), - future: bind.mainVideoSaveDirectory(root: false)), + description: Text( + "${translate("Directory")}: ${bind.mainVideoSaveDirectory(root: false)}"), initialValue: _autoRecordIncomingSession, onToggle: isOptionFixed(kOptionAllowAutoRecordIncoming) ? null diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index a44c3d09519..d34ecb14d47 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1194,8 +1194,8 @@ pub fn main_change_language(lang: String) { send_to_cm(&crate::ipc::Data::Language(lang)); } -pub fn main_video_save_directory(root: bool) -> String { - video_save_directory(root) +pub fn main_video_save_directory(root: bool) -> SyncReturn { + SyncReturn(video_save_directory(root)) } pub fn main_set_user_default_option(key: String, value: String) { From 818439db481f72adb5fcb6115ff2183910e95bda Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jun 2024 08:43:19 +0800 Subject: [PATCH 042/335] fix ci --- flutter/lib/web/bridge.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index 2cb7a573424..ce541469193 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -943,7 +943,7 @@ class RustdeskImpl { throw UnimplementedError(); } - Future mainVideoSaveDirectory({required bool root, dynamic hint}) { + String mainVideoSaveDirectory({required bool root, dynamic hint}) { throw UnimplementedError(); } From 32b26e4ad3706e06d9de5b2bd6141b339ebc83f0 Mon Sep 17 00:00:00 2001 From: XLion Date: Tue, 18 Jun 2024 09:54:03 +0800 Subject: [PATCH 043/335] Update translation (#8394) * Update tw.rs * Update cn.rs Add spacing --- src/lang/cn.rs | 2 +- src/lang/tw.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 11da24d72e8..ad4026dc350 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -616,7 +616,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("texture_render_tip", "使用纹理渲染,使图片更加流畅。 如果您遭遇渲染问题,可尝试关闭此选项。"), ("Use texture rendering", "使用纹理渲染"), ("Floating window", "悬浮窗"), - ("floating_window_tip", "有助于保持RustDesk后台服务"), + ("floating_window_tip", "有助于保持 RustDesk 后台服务"), ("Keep screen on", "保持屏幕开启"), ("Never", "从不"), ("During controlled", "被控期间"), diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 03702aaeac2..459e45c8293 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -615,11 +615,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_new_voice_call_tip", "收到新的語音通話請求。如果您接受,音訊將切換為語音通訊。"), ("texture_render_tip", "使用紋理渲染,讓圖片更加順暢。 如果您遭遇渲染問題,可嘗試關閉此選項。"), ("Use texture rendering", "使用紋理渲染"), - ("Floating window", ""), - ("floating_window_tip", ""), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Floating window", "懸浮視窗"), + ("floating_window_tip", "有助於保持 RustDesk 後台服務"), + ("Keep screen on", "保持螢幕開啟"), + ("Never", "從不"), + ("During controlled", "被控期間"), + ("During service is on", "服務開啟期間"), ].iter().cloned().collect(); } From d25670c79ab5814a50f5734815e477515d6f0d46 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jun 2024 14:39:56 +0800 Subject: [PATCH 044/335] fix https://github.com/rustdesk/rustdesk/issues/2680 --- flutter/lib/common.dart | 35 ++++++++++++------- .../lib/desktop/pages/desktop_tab_page.dart | 27 +++++++++++--- .../lib/desktop/widgets/tabbar_widget.dart | 11 +----- 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 407e2ed03d4..48951c3ec09 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2674,23 +2674,34 @@ Future start_service(bool is_start) async { } } +Future canBeBlocked() async { + var access_mode = await bind.mainGetOption(key: kOptionAccessMode); + var option = option2bool(kOptionAllowRemoteConfigModification, + await bind.mainGetOption(key: kOptionAllowRemoteConfigModification)); + return access_mode == 'view' || (access_mode.isEmpty && !option); +} + +Future shouldBeBlocked(RxBool block, WhetherUseRemoteBlock? use) async { + if (use != null && !await use()) { + block.value = false; + return; + } + var time0 = DateTime.now().millisecondsSinceEpoch; + await bind.mainCheckMouseTime(); + Timer(const Duration(milliseconds: 120), () async { + var d = time0 - await bind.mainGetMouseTime(); + if (d < 120) { + block.value = true; + } + }); +} + typedef WhetherUseRemoteBlock = Future Function(); Widget buildRemoteBlock({required Widget child, WhetherUseRemoteBlock? use}) { var block = false.obs; return Obx(() => MouseRegion( onEnter: (_) async { - if (use != null && !await use()) { - block.value = false; - return; - } - var time0 = DateTime.now().millisecondsSinceEpoch; - await bind.mainCheckMouseTime(); - Timer(const Duration(milliseconds: 120), () async { - var d = time0 - await bind.mainGetMouseTime(); - if (d < 120) { - block.value = true; - } - }); + await shouldBeBlocked(block, use); }, onExit: (event) => block.value = false, child: Stack(children: [ diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 4c4b8b0f53d..e611aca44f9 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -36,12 +36,24 @@ class DesktopTabPage extends StatefulWidget { } } -class _DesktopTabPageState extends State { +class _DesktopTabPageState extends State + with WidgetsBindingObserver { final tabController = DesktopTabController(tabType: DesktopTabType.main); + final RxBool _block = false.obs; + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + shouldBeBlocked(_block, canBeBlocked); + } else if (state == AppLifecycleState.inactive) {} + } + @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); Get.put(tabController); RemoteCountState.init(); tabController.add(TabInfo( @@ -68,8 +80,10 @@ class _DesktopTabPageState extends State { @override void dispose() { - super.dispose(); + WidgetsBinding.instance.removeObserver(this); Get.delete(); + + super.dispose(); } @override @@ -89,12 +103,17 @@ class _DesktopTabPageState extends State { ), ), ))); + widget() => MouseRegion( + onEnter: (_) async { + await shouldBeBlocked(_block, canBeBlocked); + }, + child: FocusScope(child: tabWidget, canRequestFocus: !_block.value)); return isMacOS || kUseCompatibleUiMode - ? tabWidget + ? Obx(() => widget()) : Obx( () => DragToResizeArea( resizeEdgeSize: stateGlobal.resizeEdgeSize.value, - child: tabWidget, + child: widget(), ), ); } diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 69ba2ecf1fb..fe851984707 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -320,16 +320,7 @@ class DesktopTab extends StatelessWidget { if (tabType != DesktopTabType.main) { return child; } - return buildRemoteBlock( - child: child, - use: () async { - var access_mode = await bind.mainGetOption(key: kOptionAccessMode); - var option = option2bool( - kOptionAllowRemoteConfigModification, - await bind.mainGetOption( - key: kOptionAllowRemoteConfigModification)); - return access_mode == 'view' || (access_mode.isEmpty && !option); - }); + return buildRemoteBlock(child: child, use: canBeBlocked); } List _tabWidgets = []; From bf6a3a7067dc5560f3907b7a8b0e3949f65ba196 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jun 2024 15:06:43 +0800 Subject: [PATCH 045/335] fix stupid flutter --- flutter/lib/desktop/pages/desktop_tab_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index e611aca44f9..9f9f4fd5be7 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -107,7 +107,10 @@ class _DesktopTabPageState extends State onEnter: (_) async { await shouldBeBlocked(_block, canBeBlocked); }, - child: FocusScope(child: tabWidget, canRequestFocus: !_block.value)); + child: _block + .value // FocusScope cause id input not working when closing remote + ? tabWidget + : FocusScope(child: tabWidget, canRequestFocus: false)); return isMacOS || kUseCompatibleUiMode ? Obx(() => widget()) : Obx( From e3ca82945febcb0f1575773435522d23f58c111d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jun 2024 16:30:56 +0800 Subject: [PATCH 046/335] fix https://github.com/rustdesk/rustdesk/issues/2680 --- flutter/lib/common.dart | 2 ++ flutter/lib/desktop/pages/desktop_tab_page.dart | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 48951c3ec09..6fe8d59461a 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2692,6 +2692,8 @@ Future shouldBeBlocked(RxBool block, WhetherUseRemoteBlock? use) async { var d = time0 - await bind.mainGetMouseTime(); if (d < 120) { block.value = true; + } else { + block.value = false; } }); } diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 9f9f4fd5be7..e611aca44f9 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -107,10 +107,7 @@ class _DesktopTabPageState extends State onEnter: (_) async { await shouldBeBlocked(_block, canBeBlocked); }, - child: _block - .value // FocusScope cause id input not working when closing remote - ? tabWidget - : FocusScope(child: tabWidget, canRequestFocus: false)); + child: FocusScope(child: tabWidget, canRequestFocus: !_block.value)); return isMacOS || kUseCompatibleUiMode ? Obx(() => widget()) : Obx( From b0042f29fbc7bec4f021e1ed9e02797069895d0b Mon Sep 17 00:00:00 2001 From: Stas Solovey Date: Tue, 18 Jun 2024 17:08:44 +0800 Subject: [PATCH 047/335] Update ru.rs (#8398) * Update ru.rs * Update ru.rs * Update ru.rs --- src/lang/ru.rs | 70 +++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 2f6b4a1a626..36d58bd778d 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -3,7 +3,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ("Status", "Статус"), ("Your Desktop", "Ваш рабочий стол"), - ("desk_tip", "Ваш рабочий стол доступен с этим ID и паролем"), + ("desk_tip", "Ваш рабочий стол доступен с этим ID и паролем."), ("Password", "Пароль"), ("Ready", "Готов"), ("Established", "Установлено"), @@ -43,7 +43,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("allowed characters", "допустимые символы"), ("id_change_tip", "Допускаются только символы a-z, A-Z, 0-9 и _ (подчёркивание). Первой должна быть буква a-z, A-Z. Длина от 6 до 16."), ("Website", "Сайт"), - ("About", "О программе"), + ("About", "О приложении"), ("Slogan_tip", "Сделано с душой в этом безумном мире!"), ("Privacy Statement", "Заявление о конфиденциальности"), ("Mute", "Отключить звук"), @@ -97,7 +97,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Confirm Delete", "Подтвердить удаление"), ("Delete", "Удалить"), ("Properties", "Свойства"), - ("Multi Select", "Многоэлементный выбор"), + ("Multi Select", "Множественный выбор"), ("Select All", "Выбрать все"), ("Unselect All", "Снять все"), ("Empty Directory", "Пустая папка"), @@ -114,41 +114,41 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Speed", "Скорость"), ("Custom Image Quality", "Пользовательское качество изображения"), ("Privacy mode", "Режим конфиденциальности"), - ("Block user input", "Блокировать пользовательский ввод"), - ("Unblock user input", "Разблокировать пользовательский ввод"), + ("Block user input", "Заблокировать ввод на удалённом устройстве"), + ("Unblock user input", "Разблокировать ввод на удалённом устройстве"), ("Adjust Window", "Настроить окно"), ("Original", "Оригинал"), ("Shrink", "Уменьшить"), ("Stretch", "Растянуть"), ("Scrollbar", "Полоса прокрутки"), ("ScrollAuto", "Автопрокрутка"), - ("Good image quality", "Хорошее качество изображения"), - ("Balanced", "Сбалансировано"), - ("Optimize reaction time", "Оптимальное время реакции"), - ("Custom", "Своё"), + ("Good image quality", "Лучшее качество изображения"), + ("Balanced", "Баланс между качеством и откликом"), + ("Optimize reaction time", "Лучшее время отклика"), + ("Custom", "Заданное пользователем"), ("Show remote cursor", "Показывать удалённый курсор"), - ("Show quality monitor", "Показать качество"), + ("Show quality monitor", "Показывать монитор качества"), ("Disable clipboard", "Отключить буфер обмена"), - ("Lock after session end", "Выход из учётной записи после завершения сеанса"), + ("Lock after session end", "Заблокировать учётную запись после сеанса"), ("Insert", "Вставить"), - ("Insert Lock", "Установить замок"), + ("Insert Lock", "Заблокировать учётную запись"), ("Refresh", "Обновить"), ("ID does not exist", "ID не существует"), ("Failed to connect to rendezvous server", "Невозможно подключиться к промежуточному серверу"), ("Please try later", "Попробуйте позже"), - ("Remote desktop is offline", "Удалённый рабочий стол не в сети"), + ("Remote desktop is offline", "Удалённое устройство не в сети"), ("Key mismatch", "Несоответствие ключей"), ("Timeout", "Истекло время ожидания"), ("Failed to connect to relay server", "Невозможно подключиться к ретранслятору"), ("Failed to connect via rendezvous server", "Невозможно подключиться через промежуточный сервер"), ("Failed to connect via relay server", "Невозможно подключиться через ретранслятор"), - ("Failed to make direct connection to remote desktop", "Невозможно установить прямое подключение к удалённому рабочему столу"), + ("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", "Нажмите для обновления"), + ("install_tip", "В некоторых случаях из-за UAC RustDesk может работать неправильно на удалённом узле. Чтобы избежать возможных проблем с UAC, нажмите кнопку ниже для установки RustDesk в системе."), + ("Click to upgrade", "Нажмите, чтобы обновить"), + ("Click to download", "Нажмите, чтобы загрузить"), + ("Click to update", "Нажмите, чтобы обновить"), ("Configure", "Настроить"), ("config_acc", "Чтобы удалённо управлять своим рабочим столом, вы должны предоставить RustDesk права \"доступа\""), ("config_screen", "Для удалённого доступа к рабочему столу вы должны предоставить RustDesk права \"снимок экрана\""), @@ -156,14 +156,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Install", "Установить"), ("Installation", "Установка"), ("Installation Path", "Путь установки"), - ("Create start menu shortcuts", "Создать ярлыки меню \"Пуск\""), + ("Create start menu shortcuts", "Создать ярлыки в меню \"Пуск\""), ("Create desktop icon", "Создать значок на рабочем столе"), - ("agreement_tip", "Начиная установку, вы принимаете условия лицензионного соглашения"), + ("agreement_tip", "Начиная установку, вы принимаете условия лицензионного соглашения."), ("Accept and Install", "Принять и установить"), ("End-user license agreement", "Лицензионное соглашение с конечным пользователем"), ("Generating ...", "Генерация..."), ("Your installation is lower version.", "Установлена более ранняя версия"), - ("not_close_tcp_tip", "Не закрывать это окно при использовании туннеля"), + ("not_close_tcp_tip", "Не закрывать это окно при использовании туннеля."), ("Listening ...", "Ожидание..."), ("Remote Host", "Удалённый узел"), ("Remote Port", "Удалённый порт"), @@ -172,8 +172,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Local Port", "Локальный порт"), ("Local Address", "Локальный адрес"), ("Change Local Port", "Изменить локальный порт"), - ("setup_server_tip", "Для более быстрого подключения настройте собственный сервер"), - ("Too short, at least 6 characters.", "Слишком короткий, минимум 6 символов"), + ("setup_server_tip", "Для более быстрого подключения настройте собственный сервер."), + ("Too short, at least 6 characters.", "Слишком короткий, минимум 6 символов."), ("The confirmation is not identical.", "Подтверждение не совпадает"), ("Permissions", "Разрешения"), ("Accept", "Принять"), @@ -189,7 +189,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter your password", "Введите пароль"), ("Logging in...", "Вход..."), ("Enable RDP session sharing", "Использовать общий доступ к сеансу RDP"), - ("Auto Login", "Автоматический вход (действителен только если вы установили \"Завершение пользовательского сеанса после завершения удалённого подключения\""), + ("Auto Login", "Автоматический вход в учётную запись"), ("Enable direct IP access", "Использовать прямой IP-доступ"), ("Rename", "Переименовать"), ("Space", "Место"), @@ -205,14 +205,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("x11 expected", "Ожидается X11"), ("Port", "Порт"), ("Settings", "Настройки"), - ("Username", "Пользователь"), + ("Username", "Имя пользователя"), ("Invalid port", "Неправильный порт"), ("Closed manually by the peer", "Закрыто удалённым узлом вручную"), ("Enable remote configuration modification", "Разрешить удалённое изменение конфигурации"), ("Run without install", "Запустить без установки"), ("Connect via relay", "Подключится через ретранслятор"), ("Always connect via relay", "Всегда подключаться через ретранслятор"), - ("whitelist_tip", "Только IP-адреса из белого списка могут получить доступ ко мне"), + ("whitelist_tip", "Только IP-адреса из белого списка могут получить доступ к моему устройству."), ("Login", "Войти"), ("Verify", "Проверить"), ("Remember me", "Запомнить"), @@ -222,7 +222,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logout", "Выйти"), ("Tags", "Метки"), ("Search ID", "Поиск по ID"), - ("whitelist_sep", "Разделение запятой, точкой с запятой, пробелом или новой строкой"), + ("whitelist_sep", "Разделение запятой, точкой с запятой, пробелом или новой строкой."), ("Add ID", "Добавить ID"), ("Add Tag", "Добавить ключевое слово"), ("Unselect all tags", "Отменить выбор всех меток"), @@ -248,7 +248,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Are you sure to close the connection?", "Завершить подключение?"), ("Download new version", "Скачать новую версию"), ("Touch mode", "Сенсорный режим"), - ("Mouse mode", "Режим мыши"), + ("Mouse mode", "Режим мыши/тачпада"), ("One-Finger Tap", "Нажатие одним пальцем"), ("Left Mouse", "Левая кнопка мыши"), ("One-Long Tap", "Долгое нажатие одним пальцем"), @@ -258,12 +258,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Double Tap & Move", "Двойное нажатие и перемещение"), ("Mouse Drag", "Перетаскивание мышью"), ("Three-Finger vertically", "Тремя пальцами по вертикали"), - ("Mouse Wheel", "Колёсико мыши"), + ("Mouse Wheel", "Колесо мыши"), ("Two-Finger Move", "Перемещение двумя пальцами"), ("Canvas Move", "Перемещение холста"), ("Pinch to Zoom", "Масштабирование щипком"), ("Canvas Zoom", "Масштаб холста"), - ("Reset canvas", "Сбросить холст"), + ("Reset canvas", "Сбросить масштаб холста"), ("No permission of file transfer", "Нет разрешения на передачу файлов"), ("Note", "Заметка"), ("Connection", "Подключение"), @@ -304,9 +304,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Отключён"), ("Language", "Язык"), ("Keep RustDesk background service", "Держать в фоне службу RustDesk"), - ("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"), + ("Ignore Battery Optimizations", "Игнорировать оптимизацию потребления батареи"), ("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек"), - ("Start on boot", "Начинать при загрузке"), + ("Start on boot", "Запускать при загрузке"), ("Start the screen sharing service on boot, requires special permissions", "Запускать службу демонстрации экрана при загрузке (требуются специальные разрешения)"), ("Connection not allowed", "Подключение не разрешено"), ("Legacy mode", "Устаревший режим"), @@ -315,9 +315,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Использовать постоянный пароль"), ("Use both passwords", "Использовать оба пароля"), ("Set permanent password", "Установить постоянный пароль"), - ("Enable remote restart", "Использовать удалённый перезапуск"), + ("Enable remote restart", "Разшешить удалённую перезагрузку"), ("Restart remote device", "Перезапустить удалённое устройство"), - ("Are you sure you want to restart", "Вы уверены, что хотите выполнить перезапуск?"), + ("Are you sure you want to restart", "Вы уверены, что хотите выполнить перезагрузку?"), ("Restarting remote device", "Перезагрузка удалённого устройства"), ("remote_restarting_tip", "Удалённое устройство перезапускается. Закройте это сообщение и через некоторое время переподключитесь, используя постоянный пароль."), ("Copied", "Скопировано"), @@ -399,7 +399,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("One-time password length", "Длина одноразового пароля"), ("Request access to your device", "Запрос доступа к вашему устройству"), ("Hide connection management window", "Скрывать окно управления подключениями"), - ("hide_cm_tip", "Разрешать скрытие случае, если принимаются сеансы по паролю или используется постоянный пароль"), + ("hide_cm_tip", "Разрешать скрытие в случае, если принимаются сеансы по паролю или используется постоянный пароль"), ("wayland_experiment_tip", "Поддержка Wayland находится на экспериментальной стадии, используйте X11, если вам требуется автоматический доступ."), ("Right click to select tabs", "Выбор вкладок щелчком правой кнопки мыши"), ("Skipped", "Пропущено"), From 97aa739d69c67a9cd9d40da8ed7308224ffae875 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jun 2024 18:21:29 +0800 Subject: [PATCH 048/335] revert https://github.com/rustdesk/rustdesk/pull/8368 --- src/platform/macos.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform/macos.mm b/src/platform/macos.mm index cf35b5e1a65..bce2a6b5b90 100644 --- a/src/platform/macos.mm +++ b/src/platform/macos.mm @@ -110,12 +110,16 @@ } extern "C" float BackingScaleFactor(uint32_t display) { + NSScreen* s = [NSScreen mainScreen]; + if (s) return [s backingScaleFactor]; + /* on my mini mac, display==2, but only 1 screen display -= 1; NSArray *screens = [NSScreen screens]; if (display >= 0 && display < [screens count]) { NSScreen* s = [screens objectAtIndex:display]; if (s) return [s backingScaleFactor]; } + */ return 1; } From 2c38648e39601a9c195fc034947d2771c9db24c2 Mon Sep 17 00:00:00 2001 From: Stas Solovey Date: Tue, 18 Jun 2024 19:36:32 +0800 Subject: [PATCH 049/335] update ru.rs (#8400) * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs --------- Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> --- src/lang/ru.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 36d58bd778d..9364ebbfa57 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -14,7 +14,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Service is not running", "Служба не запущена"), ("not_ready_status", "Не подключено. Проверьте соединение."), ("Control Remote Desktop", "Управление удалённым рабочим столом"), - ("Transfer file", "Передача файла"), + ("Transfer file", "Передать файлы"), ("Connect", "Подключиться"), ("Recent sessions", "Последние сеансы"), ("Address book", "Адресная книга"), @@ -53,7 +53,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Audio Input", "Аудиовход"), ("Enhancements", "Улучшения"), ("Hardware Codec", "Аппаратный кодек"), - ("Adaptive bitrate", "Адаптивная скорость потока"), + ("Adaptive bitrate", "Адаптивный битрейт"), ("ID Server", "Сервер ID"), ("Relay Server", "Ретранслятор"), ("API Server", "Сервер API"), @@ -112,7 +112,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Waiting", "Ожидание"), ("Finished", "Завершено"), ("Speed", "Скорость"), - ("Custom Image Quality", "Пользовательское качество изображения"), + ("Custom Image Quality", "Заданное пользователем качество изображения"), ("Privacy mode", "Режим конфиденциальности"), ("Block user input", "Заблокировать ввод на удалённом устройстве"), ("Unblock user input", "Разблокировать ввод на удалённом устройстве"), @@ -144,7 +144,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Failed to connect via relay server", "Невозможно подключиться через ретранслятор"), ("Failed to make direct connection to remote desktop", "Невозможно установить прямое подключение к удалённому устройству"), ("Set Password", "Установить пароль"), - ("OS Password", "Пароль ОС"), + ("OS Password", "Пароль входа в ОС"), ("install_tip", "В некоторых случаях из-за UAC RustDesk может работать неправильно на удалённом узле. Чтобы избежать возможных проблем с UAC, нажмите кнопку ниже для установки RustDesk в системе."), ("Click to upgrade", "Нажмите, чтобы обновить"), ("Click to download", "Нажмите, чтобы загрузить"), @@ -275,8 +275,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Screen Capture", "Захват экрана"), ("Input Control", "Управление вводом"), ("Audio Capture", "Захват аудио"), - ("File Connection", "Файловое подключение"), - ("Screen Connection", "Подключение экрана"), + ("File Connection", "Подключение передачи файлов"), + ("Screen Connection", "Подключение просмотра/управления экраном"), ("Do you accept?", "Вы согласны?"), ("Open System Setting", "Открыть настройки системы"), ("How to get Android input permission?", "Как получить разрешение на ввод Android?"), @@ -348,7 +348,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Системная"), ("Enable hardware codec", "Использовать аппаратный кодек"), ("Unlock Security Settings", "Разблокировать настройки безопасности"), - ("Enable audio", "Включить звук"), + ("Enable audio", "Включить передачу звука"), ("Unlock Network Settings", "Разблокировать сетевые настройки"), ("Server", "Сервер"), ("Direct IP Access", "Прямой IP-доступ"), @@ -376,7 +376,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("elevated_foreground_window_tip", "Текущее окно удалённого рабочего стола требует более высоких привилегий для работы, поэтому временно невозможно использовать мышь и клавиатуру. Можно попросить удалённого пользователя свернуть текущее окно или нажать кнопку повышения прав в окне управления подключением. Чтобы избежать этой проблемы в дальнейшем, рекомендуется выполнить установку программного обеспечения на удалённом устройстве."), ("Disconnected", "Отключено"), ("Other", "Другое"), - ("Confirm before closing multiple tabs", "Подтверждать закрытие несколько вкладок"), + ("Confirm before closing multiple tabs", "Подтверждать закрытие нескольких вкладок"), ("Keyboard Settings", "Настройки клавиатуры"), ("Full Access", "Полный доступ"), ("Screen Share", "Демонстрация экрана"), @@ -466,13 +466,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Пустое имя пользователя"), ("Empty Password", "Пустой пароль"), ("Me", "Я"), - ("identical_file_tip", "Файл идентичен файлу на удалённом узле."), + ("identical_file_tip", "Файл идентичен файлу на удалённом узле"), ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), ("login_linux_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), ("verify_rustdesk_password_tip", "Подтвердить пароль RustDesk"), ("remember_account_tip", "Запомнить этот аккаунт"), - ("os_account_desk_tip", "Этот аккаунт используется для входа в удалённую ОС и включения сеанса рабочего стола в режиме headless"), + ("os_account_desk_tip", "Этот аккаунт используется для входа в удалённую ОС и включения сеанса рабочего стола в режиме headless."), ("OS Account", "Аккаунт ОС"), ("another_user_login_title_tip", "Другой пользователь уже вошёл в систему"), ("another_user_login_text_tip", "Отключить"), @@ -547,7 +547,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("upgrade_rustdesk_server_pro_to_{}_tip", "Обновите RustDesk Server Pro до версии {} или новее!"), ("pull_group_failed_tip", "Невозможно обновить группу"), ("Filter by intersection", "Фильтровать по пересечению"), - ("Remove wallpaper during incoming sessions", "Удалять обои в сеансе"), + ("Remove wallpaper during incoming sessions", "Скрывать обои рабочего стола при входящем сеансе"), ("Test", "Тест"), ("display_is_plugged_out_msg", "Дисплей отключён, переключитесь на первый дисплей."), ("No displays", "Нет дисплеев"), @@ -563,7 +563,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Virtual display", "Виртуальный дисплей"), ("Plug out all", "Отключить все"), ("True color (4:4:4)", "True color (4:4:4)"), - ("Enable blocking user input", "Блокировать ввод пользователя"), + ("Enable blocking user input", "Разрешить блокировать ввод на устройстве"), ("id_input_tip", "Можно ввести идентификатор, прямой IP-адрес или домен с портом (<домен>:<порт>).\nЕсли необходимо получить доступ к устройству на другом сервере, добавьте адрес сервера (@<адрес_сервера>?key=<ключ_значение>), например:\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЕсли необходимо получить доступ к устройству на общедоступном сервере, введите \"@public\", ключ для публичного сервера не требуется."), ("privacy_mode_impl_mag_tip", "Режим 1"), ("privacy_mode_impl_virtual_display_tip", "Режим 2"), @@ -574,9 +574,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("input_source_1_tip", "Источник ввода 1"), ("input_source_2_tip", "Источник ввода 2"), ("capture_display_elevated_connections_tip", "Захват экрана нескольких дисплеев не поддерживается в режиме повышенных прав. Повторите попытку после установки, если хотите управлять несколькими дисплеями."), - ("Swap control-command key", "Поменять кнопки управления и команд"), - ("swap-left-right-mouse", "Поменять левую и правую кнопки мыши"), - ("2FA code", "Код 2FA"), + ("Swap control-command key", "Поменять местами значения кнопок Ctrl и Command"), + ("swap-left-right-mouse", "Поменять местами значения левой и правой кнопок мыши"), + ("2FA code", "Код двухфакторной аутентификации"), ("More", "Ещё"), ("enable-2fa-title", "Использовать двухфакторную аутентификацию"), ("enable-2fa-desc", "Настройте приложение аутентификации. Используйте, например, Authy, Microsoft или Google Authenticator, на телефоне или компьютере.\n\nОтсканируйте QR-код с помощью приложения аутентификации и введите код, который отобразит это приложение, чтобы включить двухфакторную аутентификацию."), From 8c39979848e1251043b65f1ceaf5ea9b4be2ac23 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 18 Jun 2024 19:37:15 +0800 Subject: [PATCH 050/335] fix get mac display scale, find screen from display id (#8401) --- src/platform/macos.mm | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/platform/macos.mm b/src/platform/macos.mm index bce2a6b5b90..0f963b97f7b 100644 --- a/src/platform/macos.mm +++ b/src/platform/macos.mm @@ -109,17 +109,17 @@ return Elevate(NULL, NULL); } +// https://gist.github.com/briankc/025415e25900750f402235dbf1b74e42 extern "C" float BackingScaleFactor(uint32_t display) { - NSScreen* s = [NSScreen mainScreen]; - if (s) return [s backingScaleFactor]; - /* on my mini mac, display==2, but only 1 screen - display -= 1; NSArray *screens = [NSScreen screens]; - if (display >= 0 && display < [screens count]) { - NSScreen* s = [screens objectAtIndex:display]; - if (s) return [s backingScaleFactor]; + for (NSScreen *screen in screens) { + NSDictionary *deviceDescription = [screen deviceDescription]; + NSNumber *screenNumber = [deviceDescription objectForKey:@"NSScreenNumber"]; + CGDirectDisplayID screenDisplayID = [screenNumber unsignedIntValue]; + if (screenDisplayID == display) { + return [screen backingScaleFactor]; + } } - */ return 1; } From e57854422a660b4397fe55b0c8ade9fac8cd2681 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jun 2024 22:04:34 +0800 Subject: [PATCH 051/335] fix kill main window in --server --- src/ipc.rs | 28 +++------------------------- src/server.rs | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/ipc.rs b/src/ipc.rs index 47cac05a6f8..5c926829ce5 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -699,6 +699,9 @@ async fn check_pid(postfix: &str) { } } } + // if not remove old ipc file, the new ipc creation will fail + // if we remove a ipc file, but the old ipc process is still running, + // new connection to the ipc will connect to new ipc, old connection to old ipc still keep alive std::fs::remove_file(&Config::ipc_path(postfix)).ok(); } @@ -985,31 +988,6 @@ pub async fn test_rendezvous_server() -> ResultType<()> { Ok(()) } -#[cfg(windows)] -pub fn is_ipc_file_exist(suffix: &str) -> ResultType { - // Not change this to std::path::Path::exists, unless it can be ensured that it can find the ipc which occupied by a process that taskkill can't kill. - let prefix = "\\\\.\\pipe\\"; - let file_name = Config::ipc_path(suffix).replace(prefix, ""); - let mut err = None; - for entry in std::fs::read_dir(prefix)? { - match entry { - Ok(entry) => { - if entry.file_name().into_string().unwrap_or_default() == file_name { - return Ok(true); - } - } - Err(e) => { - err = Some(e); - } - } - } - if let Some(e) = err { - Err(e.into()) - } else { - Ok(false) - } -} - #[tokio::main(flavor = "current_thread")] pub async fn send_url_scheme(url: String) -> ResultType<()> { connect(1_000, "_url") diff --git a/src/server.rs b/src/server.rs index 43edaea0e4f..e1f5d634cf8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -467,12 +467,9 @@ pub async fn start_server(is_server: bool) { std::thread::spawn(move || { if let Err(err) = crate::ipc::start("") { log::error!("Failed to start ipc: {}", err); - #[cfg(windows)] - if crate::is_server() && crate::ipc::is_ipc_file_exist("").unwrap_or(false) { + if crate::is_server() { log::error!("ipc is occupied by another process, try kill it"); - if let Err(e) = crate::platform::try_kill_rustdesk_main_window_process() { - log::error!("kill failed: {}", e); - } + std::thread::spawn(stop_main_window_process).join().ok(); } std::process::exit(-1); } @@ -636,3 +633,19 @@ async fn sync_and_watch_config_dir() { } log::warn!("skipped config sync"); } + +#[tokio::main(flavor = "current_thread")] +pub async fn stop_main_window_process() { + // this may also kill another --server process, + // but --server usually can be auto restarted by --service, so it is ok + if let Ok(mut conn) = crate::ipc::connect(1000, "").await { + conn.send(&crate::ipc::Data::Close).await.ok(); + } + #[cfg(windows)] + { + // in case above failure, e.g. zombie process + if let Err(e) = crate::platform::try_kill_rustdesk_main_window_process() { + log::error!("kill failed: {}", e); + } + } +} From 1fcc7001bd204889ac857cd05f087d9562293bed Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 18 Jun 2024 22:42:42 +0800 Subject: [PATCH 052/335] use exit(-1) in Data::Close to make sure --server can restart --- src/ipc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ipc.rs b/src/ipc.rs index 5c926829ce5..d76f51820e4 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -358,7 +358,7 @@ async fn handle(data: Data, stream: &mut Connection) { if is_server() { let _ = privacy_mode::turn_off_privacy(0, Some(PrivacyModeState::OffByPeer)); } - std::process::exit(0); + std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used } } Data::OnlineStatus(_) => { From 5a740e891e38dd8dcd5d0d280d24a788785f3fbb Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 09:27:29 +0800 Subject: [PATCH 053/335] make main window can be reopen if killed by --server for creating ipc --- src/common.rs | 6 ++++++ src/ipc.rs | 12 ++++++++++-- src/platform/mod.rs | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/common.rs b/src/common.rs index 60cbd06b364..9be677e182a 100644 --- a/src/common.rs +++ b/src/common.rs @@ -197,6 +197,7 @@ lazy_static::lazy_static! { static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned()); // Is server logic running. The server code can invoked to run by the main process if --server is not running. static ref SERVER_RUNNING: Arc> = Default::default(); + static ref IS_MAIN: bool = std::env::args().nth(1).map_or(true, |arg| !arg.starts_with("--")); } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -250,6 +251,11 @@ pub fn is_server() -> bool { *IS_SERVER } +#[inline] +pub fn is_main() -> bool { + *IS_MAIN +} + // Is server logic running. #[inline] pub fn is_server_running() -> bool { diff --git a/src/ipc.rs b/src/ipc.rs index d76f51820e4..2e566feff89 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -358,7 +358,15 @@ async fn handle(data: Data, stream: &mut Connection) { if is_server() { let _ = privacy_mode::turn_off_privacy(0, Some(PrivacyModeState::OffByPeer)); } - std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used + #[cfg(target_os = "linux")] + if crate::is_main() { + // below part is for main windows can be reopen during rustdesk installation and installing service from UI + // this make new ipc server (domain socket) can be created. + std::fs::remove_file(&Config::ipc_path("")).ok(); + hbb_common::sleep(crate::platform::SERVICE_INTERVAL * 2 as _).await; + crate::run_me::<&str>(vec![]).ok(); + } + std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used } } Data::OnlineStatus(_) => { @@ -700,7 +708,7 @@ async fn check_pid(postfix: &str) { } } // if not remove old ipc file, the new ipc creation will fail - // if we remove a ipc file, but the old ipc process is still running, + // if we remove a ipc file, but the old ipc process is still running, // new connection to the ipc will connect to new ipc, old connection to old ipc still keep alive std::fs::remove_file(&Config::ipc_path(postfix)).ok(); } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index a01362872c8..65d047cff13 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -27,7 +27,7 @@ pub mod linux_desktop_manager; use hbb_common::{message_proto::CursorData, ResultType}; use std::sync::{Arc, Mutex}; #[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))] -const SERVICE_INTERVAL: u64 = 300; +pub const SERVICE_INTERVAL: u64 = 300; lazy_static::lazy_static! { static ref INSTALLING_SERVICE: Arc>= Default::default(); From 4eafa5a585759219931653c4df396d390e7eea8f Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 19 Jun 2024 10:12:10 +0800 Subject: [PATCH 054/335] fix ci (#8407) Signed-off-by: 21pages --- src/ipc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ipc.rs b/src/ipc.rs index 2e566feff89..f7398112601 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -363,7 +363,7 @@ async fn handle(data: Data, stream: &mut Connection) { // below part is for main windows can be reopen during rustdesk installation and installing service from UI // this make new ipc server (domain socket) can be created. std::fs::remove_file(&Config::ipc_path("")).ok(); - hbb_common::sleep(crate::platform::SERVICE_INTERVAL * 2 as _).await; + hbb_common::sleep((crate::platform::SERVICE_INTERVAL * 2) as f32).await; crate::run_me::<&str>(vec![]).ok(); } std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used From 841c33198137461ce317a76b9ab27dd09db7b782 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 19 Jun 2024 14:02:13 +0800 Subject: [PATCH 055/335] fix sleep duration when receive ipc close (#8410) unit is second Signed-off-by: 21pages --- src/ipc.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ipc.rs b/src/ipc.rs index f7398112601..2aff25a1597 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -363,7 +363,8 @@ async fn handle(data: Data, stream: &mut Connection) { // below part is for main windows can be reopen during rustdesk installation and installing service from UI // this make new ipc server (domain socket) can be created. std::fs::remove_file(&Config::ipc_path("")).ok(); - hbb_common::sleep((crate::platform::SERVICE_INTERVAL * 2) as f32).await; + hbb_common::sleep((crate::platform::SERVICE_INTERVAL * 2) as f32 / 1000.0) + .await; crate::run_me::<&str>(vec![]).ok(); } std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used From 60dc40f47f76b4bf6d8be6a8040e7ee282d007d3 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 15:42:53 +0800 Subject: [PATCH 056/335] try hide docker in tao delegate because hide in rustdesk side a bit late so that still seeing it sometimes refactor service to make it restart after login to avoid delegate caught for seconds after login also make main windows Close event restart itself for above case --- Cargo.lock | 4 ++-- src/ipc.rs | 21 +++++++++++++++++---- src/platform/macos.rs | 20 +++++++++----------- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff6e44c2f50..4cb4a06349d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6127,7 +6127,7 @@ dependencies = [ [[package]] name = "tao" version = "0.25.0" -source = "git+https://github.com/rustdesk-org/tao?branch=dev#1cad16b200485bbccc67dcee2d339eac6e1c16ad" +source = "git+https://github.com/rustdesk-org/tao?branch=dev#8a014cdb4dd20f35d64bf992374fa40263ee86a4" dependencies = [ "bitflags 1.3.2", "cc", @@ -6167,7 +6167,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.2" -source = "git+https://github.com/rustdesk-org/tao?branch=dev#1cad16b200485bbccc67dcee2d339eac6e1c16ad" +source = "git+https://github.com/rustdesk-org/tao?branch=dev#8a014cdb4dd20f35d64bf992374fa40263ee86a4" dependencies = [ "proc-macro2 1.0.79", "quote 1.0.35", diff --git a/src/ipc.rs b/src/ipc.rs index 2aff25a1597..cae959a1957 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -358,14 +358,27 @@ async fn handle(data: Data, stream: &mut Connection) { if is_server() { let _ = privacy_mode::turn_off_privacy(0, Some(PrivacyModeState::OffByPeer)); } - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "macos", target_os = "linux"))] if crate::is_main() { // below part is for main windows can be reopen during rustdesk installation and installing service from UI // this make new ipc server (domain socket) can be created. std::fs::remove_file(&Config::ipc_path("")).ok(); - hbb_common::sleep((crate::platform::SERVICE_INTERVAL * 2) as f32 / 1000.0) - .await; - crate::run_me::<&str>(vec![]).ok(); + #[cfg(target_os = "linux")] + { + hbb_common::sleep((crate::platform::SERVICE_INTERVAL * 2) as f32 / 1000.0) + .await; + crate::run_me::<&str>(vec![]).ok(); + } + #[cfg(target_os = "macos")] + { + // our launchagent interval is 1 second + hbb_common::sleep(1.5).await; + std::process::Command::new("open") + .arg("-n") + .arg(&format!("/Applications/{}.app", crate::get_app_name())) + .spawn() + .ok(); + } } std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used } diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 92991cdcf90..0658fb889d2 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -18,7 +18,6 @@ use core_graphics::{ window::{kCGWindowName, kCGWindowOwnerPID}, }; use hbb_common::{ - allow_err, anyhow::anyhow, bail, log, message_proto::{DisplayInfo, Resolution}, @@ -203,15 +202,6 @@ pub fn is_installed_daemon(prompt: bool) -> bool { .args(&["load", "-w", &agent_plist_file]) .status() .ok(); - std::process::Command::new("sh") - .arg("-c") - .arg(&format!( - "sleep 0.5; open -n /Applications/{}.app", - crate::get_app_name(), - )) - .spawn() - .ok(); - quit_gui(); } } } @@ -516,8 +506,16 @@ pub fn start_os_service() { .unwrap_or_default() as i64; log::info!("Startime: {my_start_time} vs {:?}", server); + let uname = get_active_username(); std::thread::spawn(move || loop { std::thread::sleep(std::time::Duration::from_secs(1)); + let tmp = get_active_username(); + // restart my self after login to make sure --server started earlier + // so that not forbid user start main window because of delegate caught by --service + if uname != tmp { + log::info!("Console user changed from {uname} to {tmp}"); + std::process::exit(-1); + } if server.is_none() { server = get_server_start_time(&mut sys, &path); } @@ -527,7 +525,7 @@ pub fn start_os_service() { log::info!( "Agent start later, {my_start_time} vs {start_time}, will restart --service to make delegate work", ); - std::process::exit(0); + std::process::exit(-1); } // only refresh this pid and check if valid, no need to refresh all processes since refreshing all is expensive, about 10ms on my machine if !sys.refresh_process_specifics(pid, ProcessRefreshKind::new()) { From e50b72622c547e705f161a72e8d6e8ce50d01c9f Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:58:23 +0800 Subject: [PATCH 057/335] fix: android, touch mode, soft keyboard, no pointer events (#8409) * fix: android, touch mode, soft keyboard, no pointer events Signed-off-by: fufesou * Reset lastIsBlocked on touch mode toggled Signed-off-by: fufesou * refact: reset lastIsBlocked when updating keyHelpToolsRect Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/common/widgets/remote_input.dart | 19 ++++++-- flutter/lib/mobile/pages/remote_page.dart | 25 ++++++++++ flutter/lib/models/model.dart | 49 +++++++++++++++++++- 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index 1e7523e54b9..ad08736c802 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -95,8 +95,9 @@ class _RawTouchGestureDetectorRegionState } if (handleTouch) { // Desktop or mobile "Touch mode" - ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy); - inputModel.tapDown(MouseButtons.left); + if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) { + inputModel.tapDown(MouseButtons.left); + } } } @@ -105,8 +106,9 @@ class _RawTouchGestureDetectorRegionState return; } if (handleTouch) { - ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy); - inputModel.tapUp(MouseButtons.left); + if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) { + inputModel.tapUp(MouseButtons.left); + } } } @@ -134,6 +136,9 @@ class _RawTouchGestureDetectorRegionState if (lastDeviceKind != PointerDeviceKind.touch) { return; } + if (ffiModel.touchMode && ffi.cursorModel.lastIsBlocked) { + return; + } inputModel.tap(MouseButtons.left); inputModel.tap(MouseButtons.left); } @@ -222,6 +227,9 @@ class _RawTouchGestureDetectorRegionState return; } if (handleTouch) { + if (ffi.cursorModel.shouldBlock(d.localPosition.dx, d.localPosition.dy)) { + return; + } if (isDesktop) { ffi.cursorModel.trySetRemoteWindowCoords(); } @@ -244,6 +252,9 @@ class _RawTouchGestureDetectorRegionState if (lastDeviceKind != PointerDeviceKind.touch) { return; } + if (ffi.cursorModel.shouldBlock(d.localPosition.dx, d.localPosition.dy)) { + return; + } ffi.cursorModel.updatePan(d.delta, d.localPosition, handleTouch); } diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index ca7b909e804..7d449f84b2b 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -679,6 +679,7 @@ class _KeyHelpToolsState extends State { var _fn = false; var _pin = false; final _keyboardVisibilityController = KeyboardVisibilityController(); + final _key = GlobalKey(); InputModel get inputModel => gFFI.inputModel; @@ -703,6 +704,24 @@ class _KeyHelpToolsState extends State { onPressed: onPressed); } + @override + void initState() { + super.initState(); + } + + _updateRect() { + RenderObject? renderObject = _key.currentContext?.findRenderObject(); + if (renderObject == null) { + return; + } + if (renderObject is RenderBox) { + final size = renderObject.size; + Offset pos = renderObject.localToGlobal(Offset.zero); + gFFI.cursorModel.keyHelpToolsRect = + Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height); + } + } + @override Widget build(BuildContext context) { final hasModifierOn = inputModel.ctrl || @@ -711,6 +730,7 @@ class _KeyHelpToolsState extends State { inputModel.command; if (!_pin && !hasModifierOn && !widget.requestShow) { + gFFI.cursorModel.keyHelpToolsRect = null; return Offstage(); } final size = MediaQuery.of(context).size; @@ -821,7 +841,12 @@ class _KeyHelpToolsState extends State { }), ]; final space = size.width > 320 ? 4.0 : 2.0; + // 500 ms is long enough for this widget to be built! + Future.delayed(Duration(milliseconds: 500), () { + _updateRect(); + }); return Container( + key: _key, color: Color(0xAA000000), padding: EdgeInsets.only( top: _keyboardVisibilityController.isVisible ? 24 : 4, bottom: 8), diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index c7b3debea5d..bdbad349f24 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1800,6 +1800,32 @@ class CursorModel with ChangeNotifier { String peerId = ''; WeakReference parent; + // Only for mobile, touch mode + // To block touch event above the KeyHelpTools + // + // A better way is to not listen events from the KeyHelpTools. + // But we're now using a Container(child: Stack(...)) to wrap the KeyHelpTools, + // and the listener is on the Container. + Rect? _keyHelpToolsRect; + bool _lastIsBlocked = false; + + set keyHelpToolsRect(Rect? r) { + _keyHelpToolsRect = r; + if (r == null) { + _lastIsBlocked = false; + } else { + // `lastIsBlocked` is only used in common/widgets/remote_input.dart -> _RawTouchGestureDetectorRegionState -> onDoubleTap() + // Because onDoubleTap() doesn't have the `event` parameter, we can't get the touch event's position. + // + // Block the touch event is safe here. + // `lastIsBlocked` is only used in onDoubleTap() to block the touch event from the KeyHelpTools. + // `lastIsBlocked` will be set when the cursor is moving or touch somewhere else. + _lastIsBlocked = true; + } + } + + get lastIsBlocked => _lastIsBlocked; + ui.Image? get image => _image; CursorData? get cache => _cache; @@ -1844,9 +1870,10 @@ class CursorModel with ChangeNotifier { return Rect.fromLTWH(x0, y0, size.width / scale, size.height / scale); } + get keyboardHeight => MediaQueryData.fromWindow(ui.window).viewInsets.bottom; + double adjustForKeyboard() { final m = MediaQueryData.fromWindow(ui.window); - var keyboardHeight = m.viewInsets.bottom; final size = m.size; if (keyboardHeight < 100) return 0; final s = parent.target?.canvasModel.scale ?? 1.0; @@ -1855,9 +1882,29 @@ class CursorModel with ChangeNotifier { return h - thresh; } + // mobile Soft keyboard, block touch event from the KeyHelpTools + shouldBlock(double x, double y) { + if (!(parent.target?.ffiModel.touchMode ?? false)) { + return false; + } + if (_keyHelpToolsRect == null) { + return false; + } + if (isPointInRect(Offset(x, y), _keyHelpToolsRect!)) { + return true; + } + return false; + } + move(double x, double y) { + if (shouldBlock(x, y)) { + _lastIsBlocked = true; + return false; + } + _lastIsBlocked = false; moveLocal(x, y); parent.target?.inputModel.moveMouse(_x, _y); + return true; } moveLocal(double x, double y) { From 99edab4b61d6d7e6bba534055da812ddbfa45a43 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 16:24:58 +0800 Subject: [PATCH 058/335] hide docker from tao, this may fix https://github.com/rustdesk/rustdesk/issues/8399 --- Cargo.lock | 4 ++-- src/tray.rs | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4cb4a06349d..9b9101bb66d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6127,7 +6127,7 @@ dependencies = [ [[package]] name = "tao" version = "0.25.0" -source = "git+https://github.com/rustdesk-org/tao?branch=dev#8a014cdb4dd20f35d64bf992374fa40263ee86a4" +source = "git+https://github.com/rustdesk-org/tao?branch=dev#288c219cb0527e509590c2b2d8e7072aa9feb2d3" dependencies = [ "bitflags 1.3.2", "cc", @@ -6167,7 +6167,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.2" -source = "git+https://github.com/rustdesk-org/tao?branch=dev#8a014cdb4dd20f35d64bf992374fa40263ee86a4" +source = "git+https://github.com/rustdesk-org/tao?branch=dev#288c219cb0527e509590c2b2d8e7072aa9feb2d3" dependencies = [ "proc-macro2 1.0.79", "quote 1.0.35", diff --git a/src/tray.rs b/src/tray.rs index 567f43c83ad..8251d2505c9 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -41,7 +41,7 @@ pub fn make_tray() -> hbb_common::ResultType<()> { let icon = tray_icon::Icon::from_rgba(icon_rgba, icon_width, icon_height) .context("Failed to open icon")?; - let event_loop = EventLoopBuilder::new().build(); + let mut event_loop = EventLoopBuilder::new().build(); let tray_menu = Menu::new(); let quit_i = MenuItem::new(translate("Exit".to_owned()), true, None); @@ -77,7 +77,6 @@ pub fn make_tray() -> hbb_common::ResultType<()> { let tray_channel = TrayEvent::receiver(); #[cfg(windows)] let (ipc_sender, ipc_receiver) = std::sync::mpsc::channel::(); - let mut docker_hiden = false; let open_func = move || { if cfg!(not(feature = "flutter")) { @@ -110,12 +109,12 @@ pub fn make_tray() -> hbb_common::ResultType<()> { }); #[cfg(windows)] let mut last_click = std::time::Instant::now(); + #[cfg(target_os = "macos")] + { + use tao::platform::macos::EventLoopExtMacOS; + event_loop.set_activation_policy(tao::platform::macos::ActivationPolicy::Accessory); + } event_loop.run(move |_event, _, control_flow| { - if !docker_hiden { - #[cfg(target_os = "macos")] - crate::platform::macos::hide_dock(); - docker_hiden = true; - } *control_flow = ControlFlow::WaitUntil( std::time::Instant::now() + std::time::Duration::from_millis(100), ); From 7c45a68870e142cee19600b5d5a03aebb554a1a0 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 19 Jun 2024 16:29:40 +0800 Subject: [PATCH 059/335] linux install service, stop service before start (#8414) If the stop-service option before installation is "", after installation --sever is also started up. When clicking to start service, restart --server to make it read the config file, otherwise the service can't be started util --server is restarted. Signed-off-by: 21pages --- 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 a7fb22c4822..cc0192fbf0a 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1392,7 +1392,7 @@ pub fn install_service() -> bool { let cp = switch_service(false); let app_name = crate::get_app_name().to_lowercase(); if !run_cmds_pkexec(&format!( - "{cp} systemctl enable {app_name}; systemctl start {app_name};" + "{cp} systemctl enable {app_name}; systemctl stop {app_name}; systemctl start {app_name};" )) { Config::set_option("stop-service".into(), "Y".into()); return true; From 137f58a84aab54e487846b12e16164f60b9028a0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 18:49:49 +0800 Subject: [PATCH 060/335] refactor macos service for delegate again, remove runme in install service of linux --- src/platform/linux.rs | 2 -- src/platform/macos.rs | 15 +++++---------- src/platform/privileges_scripts/daemon.plist | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index cc0192fbf0a..7e8c158805d 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1397,8 +1397,6 @@ pub fn install_service() -> bool { Config::set_option("stop-service".into(), "Y".into()); return true; } - run_me_with(2); - std::process::exit(0); } fn check_if_stop_service() { diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 0658fb889d2..ca3f0f75df3 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -500,29 +500,24 @@ pub fn start_os_service() { let path = std::fs::canonicalize(std::env::current_exe().unwrap_or_default()).unwrap_or_default(); let mut server = get_server_start_time(&mut sys, &path); + if server.is_none() { + log::error!("Agent not started yet, will restart --service to make delegate work",); + std::process::exit(-1); + } let my_start_time = sys .process((std::process::id() as usize).into()) .map(|p| p.start_time()) .unwrap_or_default() as i64; log::info!("Startime: {my_start_time} vs {:?}", server); - let uname = get_active_username(); std::thread::spawn(move || loop { std::thread::sleep(std::time::Duration::from_secs(1)); - let tmp = get_active_username(); - // restart my self after login to make sure --server started earlier - // so that not forbid user start main window because of delegate caught by --service - if uname != tmp { - log::info!("Console user changed from {uname} to {tmp}"); - std::process::exit(-1); - } if server.is_none() { server = get_server_start_time(&mut sys, &path); } if let Some((start_time, pid)) = server { if my_start_time <= start_time { - // I tried add delegate (using tao and with its main loop0, but it works in normal mode, but not work as daemon - log::info!( + log::error!( "Agent start later, {my_start_time} vs {start_time}, will restart --service to make delegate work", ); std::process::exit(-1); diff --git a/src/platform/privileges_scripts/daemon.plist b/src/platform/privileges_scripts/daemon.plist index 004957e364d..e1609d1030c 100644 --- a/src/platform/privileges_scripts/daemon.plist +++ b/src/platform/privileges_scripts/daemon.plist @@ -12,7 +12,7 @@ /bin/sh -c - sleep 1.5; /Applications/RustDesk.app/Contents/MacOS/RustDesk --service + sleep 3; /Applications/RustDesk.app/Contents/MacOS/RustDesk --service RunAtLoad From adf0226641e0eb3b96084f0d1b0e64f19afa4612 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 19:10:44 +0800 Subject: [PATCH 061/335] fix ci and make macos service time check more aggressive --- src/platform/linux.rs | 2 +- src/platform/macos.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 7e8c158805d..0810bb2460b 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1395,8 +1395,8 @@ pub fn install_service() -> bool { "{cp} systemctl enable {app_name}; systemctl stop {app_name}; systemctl start {app_name};" )) { Config::set_option("stop-service".into(), "Y".into()); - return true; } + true } fn check_if_stop_service() { diff --git a/src/platform/macos.rs b/src/platform/macos.rs index ca3f0f75df3..923d7d76298 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -516,7 +516,7 @@ pub fn start_os_service() { server = get_server_start_time(&mut sys, &path); } if let Some((start_time, pid)) = server { - if my_start_time <= start_time { + if my_start_time <= start_time + 1 { log::error!( "Agent start later, {my_start_time} vs {start_time}, will restart --service to make delegate work", ); From d75caad71fff4aeb1ae5fbaaf6ec39bfb9103941 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 19:54:30 +0800 Subject: [PATCH 062/335] move --server check into daemon.plist --- src/platform/macos.rs | 4 ++-- src/platform/privileges_scripts/daemon.plist | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 923d7d76298..53327b0481a 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -501,7 +501,7 @@ pub fn start_os_service() { std::fs::canonicalize(std::env::current_exe().unwrap_or_default()).unwrap_or_default(); let mut server = get_server_start_time(&mut sys, &path); if server.is_none() { - log::error!("Agent not started yet, will restart --service to make delegate work",); + log::error!("Agent not started yet, please restart --server first to make delegate work",); std::process::exit(-1); } let my_start_time = sys @@ -518,7 +518,7 @@ pub fn start_os_service() { if let Some((start_time, pid)) = server { if my_start_time <= start_time + 1 { log::error!( - "Agent start later, {my_start_time} vs {start_time}, will restart --service to make delegate work", + "Agent start later, {my_start_time} vs {start_time}, please start --server first to make delegate work", ); std::process::exit(-1); } diff --git a/src/platform/privileges_scripts/daemon.plist b/src/platform/privileges_scripts/daemon.plist index e1609d1030c..61efc25eca4 100644 --- a/src/platform/privileges_scripts/daemon.plist +++ b/src/platform/privileges_scripts/daemon.plist @@ -12,7 +12,7 @@ /bin/sh -c - sleep 3; /Applications/RustDesk.app/Contents/MacOS/RustDesk --service + sleep 3; if pgrep -f '/Applications/RustDesk.app/Contents/MacOS/RustDesk --server' > /dev/null; then /Applications/RustDesk.app/Contents/MacOS/RustDesk --service; fi RunAtLoad From 1f25a8af86f90f007a078fc5aadb393541f7bf13 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 20:23:05 +0800 Subject: [PATCH 063/335] fix macos stop service on gui not restart --- src/platform/macos.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 53327b0481a..58678b00791 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -258,12 +258,9 @@ pub fn uninstall_service(show_new_window: bool, sync: bool) -> bool { .status() .ok(); if show_new_window { - std::process::Command::new("sh") - .arg("-c") - .arg(&format!( - "sleep 0.5; open /Applications/{}.app", - crate::get_app_name(), - )) + std::process::Command::new("open") + .arg("-n") + .arg(&format!("/Applications/{}.app", crate::get_app_name())) .spawn() .ok(); } From 27478946ea741389b30d9201a07e48bcaa906fe5 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 21:17:26 +0800 Subject: [PATCH 064/335] open new window not always work, so give it a little time before exit --- src/ipc.rs | 2 ++ src/platform/macos.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/ipc.rs b/src/ipc.rs index cae959a1957..b33264d3e6b 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -379,6 +379,8 @@ async fn handle(data: Data, stream: &mut Connection) { .spawn() .ok(); } + // leave above open a little time + hbb_common::sleep(0.3).await; } std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used } diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 58678b00791..65e7cf97dce 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -263,6 +263,8 @@ pub fn uninstall_service(show_new_window: bool, sync: bool) -> bool { .arg(&format!("/Applications/{}.app", crate::get_app_name())) .spawn() .ok(); + // leave open a little time + std::thread::sleep(std::time::Duration::from_millis(300)); } quit_gui(); } From 77f1c7e74c2fcb8f68086293ad9355e8dde93094 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 21:21:51 +0800 Subject: [PATCH 065/335] add crate::platform::quit_gui(); for double sure --- src/ipc.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ipc.rs b/src/ipc.rs index b33264d3e6b..f1141e041db 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -381,6 +381,8 @@ async fn handle(data: Data, stream: &mut Connection) { } // leave above open a little time hbb_common::sleep(0.3).await; + // in case below exit failed + crate::platform::quit_gui(); } std::process::exit(-1); // to make sure --server luauchagent process can restart because SuccessfulExit used } From 0bf9de8256eeada6c22ea41fce15c0dfd7345a66 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 19 Jun 2024 21:29:54 +0800 Subject: [PATCH 066/335] also check --server in loop --- src/platform/macos.rs | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 65e7cf97dce..a6887c2791a 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -514,27 +514,31 @@ pub fn start_os_service() { if server.is_none() { server = get_server_start_time(&mut sys, &path); } - if let Some((start_time, pid)) = server { - if my_start_time <= start_time + 1 { - log::error!( + let Some((start_time, pid)) = server else { + log::error!( + "Agent not started yet, please restart --server first to make delegate work", + ); + std::process::exit(-1); + }; + if my_start_time <= start_time + 1 { + log::error!( "Agent start later, {my_start_time} vs {start_time}, please start --server first to make delegate work", ); - std::process::exit(-1); - } - // only refresh this pid and check if valid, no need to refresh all processes since refreshing all is expensive, about 10ms on my machine - if !sys.refresh_process_specifics(pid, ProcessRefreshKind::new()) { - server = None; - continue; - } - if let Some(p) = sys.process(pid.into()) { - if let Some(p) = get_server_start_time_of(p, &path) { - server = Some((p, pid)); - } else { - server = None; - } + std::process::exit(-1); + } + // only refresh this pid and check if valid, no need to refresh all processes since refreshing all is expensive, about 10ms on my machine + if !sys.refresh_process_specifics(pid, ProcessRefreshKind::new()) { + server = None; + continue; + } + if let Some(p) = sys.process(pid.into()) { + if let Some(p) = get_server_start_time_of(p, &path) { + server = Some((p, pid)); } else { server = None; } + } else { + server = None; } }); From dcba4615a21f734d2845a0ac285eb78a293df361 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 20 Jun 2024 08:29:07 +0800 Subject: [PATCH 067/335] fix: android, touch mode, move cursor (#8419) Signed-off-by: fufesou --- flutter/lib/common/widgets/remote_input.dart | 4 +- flutter/lib/mobile/pages/remote_page.dart | 10 +-- flutter/lib/models/model.dart | 81 ++++++++++++++++---- 3 files changed, 74 insertions(+), 21 deletions(-) diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index ad08736c802..2ffd314e2db 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -95,7 +95,7 @@ class _RawTouchGestureDetectorRegionState } if (handleTouch) { // Desktop or mobile "Touch mode" - if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) { + if (ffi.cursorModel.moveTapDown(d.localPosition.dx, d.localPosition.dy)) { inputModel.tapDown(MouseButtons.left); } } @@ -106,7 +106,7 @@ class _RawTouchGestureDetectorRegionState return; } if (handleTouch) { - if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) { + if (ffi.cursorModel.moveTapUp(d.localPosition.dx, d.localPosition.dy)) { inputModel.tapUp(MouseButtons.left); } } diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 7d449f84b2b..5b0a1dddd09 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -717,8 +717,8 @@ class _KeyHelpToolsState extends State { if (renderObject is RenderBox) { final size = renderObject.size; Offset pos = renderObject.localToGlobal(Offset.zero); - gFFI.cursorModel.keyHelpToolsRect = - Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height); + gFFI.cursorModel.keyHelpToolsVisibilityChanged( + Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height)); } } @@ -730,7 +730,7 @@ class _KeyHelpToolsState extends State { inputModel.command; if (!_pin && !hasModifierOn && !widget.requestShow) { - gFFI.cursorModel.keyHelpToolsRect = null; + gFFI.cursorModel.keyHelpToolsVisibilityChanged(null); return Offstage(); } final size = MediaQuery.of(context).size; @@ -867,7 +867,7 @@ class ImagePaint extends StatelessWidget { Widget build(BuildContext context) { final m = Provider.of(context); final c = Provider.of(context); - final adjust = gFFI.cursorModel.adjustForKeyboard(); + final adjust = gFFI.cursorModel.adjustForKeyboard; var s = c.scale; return CustomPaint( painter: ImagePainter( @@ -881,7 +881,7 @@ class CursorPaint extends StatelessWidget { Widget build(BuildContext context) { final m = Provider.of(context); final c = Provider.of(context); - final adjust = gFFI.cursorModel.adjustForKeyboard(); + final adjust = gFFI.cursorModel.adjustForKeyboard; var s = c.scale; double hotx = m.hotx; double hoty = m.hoty; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index bdbad349f24..2c1a043cbaf 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1807,21 +1807,30 @@ class CursorModel with ChangeNotifier { // But we're now using a Container(child: Stack(...)) to wrap the KeyHelpTools, // and the listener is on the Container. Rect? _keyHelpToolsRect; + // `lastIsBlocked` is only used in common/widgets/remote_input.dart -> _RawTouchGestureDetectorRegionState -> onDoubleTap() + // Because onDoubleTap() doesn't have the `event` parameter, we can't get the touch event's position. bool _lastIsBlocked = false; - - set keyHelpToolsRect(Rect? r) { + // This is the flag for the touch mode. + // See `moveTapDown` and `moveTapUp` for more details. + bool _inTapDown = false; + // This is current adjust value for the touch mode. + double _adjustForKeyboard = 0.0; + // This is the last two adjust value for the touch mode. + // This value is used to avoid the cursor jumping in one tapDown and tapUp event. + double _adjustForKeyboard2 = 0.0; + + keyHelpToolsVisibilityChanged(Rect? r) { _keyHelpToolsRect = r; if (r == null) { _lastIsBlocked = false; } else { - // `lastIsBlocked` is only used in common/widgets/remote_input.dart -> _RawTouchGestureDetectorRegionState -> onDoubleTap() - // Because onDoubleTap() doesn't have the `event` parameter, we can't get the touch event's position. - // // Block the touch event is safe here. - // `lastIsBlocked` is only used in onDoubleTap() to block the touch event from the KeyHelpTools. + // `lastIsBlocked` is only used in onDoubleTap() to block the touch event from the KeyHelpTools. // `lastIsBlocked` will be set when the cursor is moving or touch somewhere else. _lastIsBlocked = true; } + _adjustForKeyboard = 0.0; + _adjustForKeyboard2 = 0.0; } get lastIsBlocked => _lastIsBlocked; @@ -1871,15 +1880,18 @@ class CursorModel with ChangeNotifier { } get keyboardHeight => MediaQueryData.fromWindow(ui.window).viewInsets.bottom; + get scale => parent.target?.canvasModel.scale ?? 1.0; - double adjustForKeyboard() { + get adjustForKeyboard => _adjustForKeyboard; + _updateAdjustForKeyboard() { final m = MediaQueryData.fromWindow(ui.window); final size = m.size; if (keyboardHeight < 100) return 0; - final s = parent.target?.canvasModel.scale ?? 1.0; final thresh = (size.height - keyboardHeight) / 2; - var h = (_y - getVisibleRect().top) * s; // local physical display height - return h - thresh; + final h = + (_y - getVisibleRect().top) * scale; // local physical display height + _adjustForKeyboard2 = _adjustForKeyboard; + _adjustForKeyboard = h - thresh; } // mobile Soft keyboard, block touch event from the KeyHelpTools @@ -1896,23 +1908,64 @@ class CursorModel with ChangeNotifier { return false; } + // mobile touch mode + // We do not update adjustForKeyboard here. + // Because `moveTapUp` will also trigger `moveLocal` to update the cursor position. + // And `moveLocal` depends on `adjustForKeyboard`. + // If we update `adjustForKeyboard` here, the cursor will move to the wrong position. + // + // We can trigger the update in `moveTapUp` and use `_inTapDown` to control the update. + // If `_inTapDown` is timeout, we suppose the touch event is a long press event, and do not update `adjustForKeyboard`. + moveTapDown(double x, double y) { + if (shouldBlock(x, y)) { + _lastIsBlocked = true; + return false; + } + _lastIsBlocked = false; + moveLocal(x, y, adjust: _adjustForKeyboard); + parent.target?.inputModel.moveMouse(_x, _y); + _inTapDown = true; + Future.delayed(Duration(milliseconds: 500), () { + _inTapDown = false; + }); + return true; + } + + // mobile touch mode + moveTapUp(double x, double y) { + if (shouldBlock(x, y)) { + _lastIsBlocked = true; + return false; + } + _lastIsBlocked = false; + + final wasInTapDown = _inTapDown; + if (_inTapDown) { + _updateAdjustForKeyboard(); + _inTapDown = false; + } + parent.target?.inputModel.moveMouse(_x, _y); + moveLocal(x, y, + adjust: wasInTapDown ? _adjustForKeyboard2 : _adjustForKeyboard); + return true; + } + move(double x, double y) { if (shouldBlock(x, y)) { _lastIsBlocked = true; return false; } _lastIsBlocked = false; - moveLocal(x, y); parent.target?.inputModel.moveMouse(_x, _y); + moveLocal(x, y, adjust: _adjustForKeyboard); return true; } - moveLocal(double x, double y) { - final scale = parent.target?.canvasModel.scale ?? 1.0; + moveLocal(double x, double y, {double adjust = 0}) { final xoffset = parent.target?.canvasModel.x ?? 0; final yoffset = parent.target?.canvasModel.y ?? 0; _x = (x - xoffset) / scale + _displayOriginX; - _y = (y - yoffset) / scale + _displayOriginY; + _y = (y - yoffset + adjust) / scale + _displayOriginY; notifyListeners(); } From 7956953669a4756955e31c824dccea0821a1b3f8 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:22:36 +0800 Subject: [PATCH 068/335] Revert "fix: android, touch mode, move cursor (#8419)" (#8421) This reverts commit dcba4615a21f734d2845a0ac285eb78a293df361. --- flutter/lib/common/widgets/remote_input.dart | 4 +- flutter/lib/mobile/pages/remote_page.dart | 10 +-- flutter/lib/models/model.dart | 81 ++++---------------- 3 files changed, 21 insertions(+), 74 deletions(-) diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index 2ffd314e2db..ad08736c802 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -95,7 +95,7 @@ class _RawTouchGestureDetectorRegionState } if (handleTouch) { // Desktop or mobile "Touch mode" - if (ffi.cursorModel.moveTapDown(d.localPosition.dx, d.localPosition.dy)) { + if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) { inputModel.tapDown(MouseButtons.left); } } @@ -106,7 +106,7 @@ class _RawTouchGestureDetectorRegionState return; } if (handleTouch) { - if (ffi.cursorModel.moveTapUp(d.localPosition.dx, d.localPosition.dy)) { + if (ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy)) { inputModel.tapUp(MouseButtons.left); } } diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 5b0a1dddd09..7d449f84b2b 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -717,8 +717,8 @@ class _KeyHelpToolsState extends State { if (renderObject is RenderBox) { final size = renderObject.size; Offset pos = renderObject.localToGlobal(Offset.zero); - gFFI.cursorModel.keyHelpToolsVisibilityChanged( - Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height)); + gFFI.cursorModel.keyHelpToolsRect = + Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height); } } @@ -730,7 +730,7 @@ class _KeyHelpToolsState extends State { inputModel.command; if (!_pin && !hasModifierOn && !widget.requestShow) { - gFFI.cursorModel.keyHelpToolsVisibilityChanged(null); + gFFI.cursorModel.keyHelpToolsRect = null; return Offstage(); } final size = MediaQuery.of(context).size; @@ -867,7 +867,7 @@ class ImagePaint extends StatelessWidget { Widget build(BuildContext context) { final m = Provider.of(context); final c = Provider.of(context); - final adjust = gFFI.cursorModel.adjustForKeyboard; + final adjust = gFFI.cursorModel.adjustForKeyboard(); var s = c.scale; return CustomPaint( painter: ImagePainter( @@ -881,7 +881,7 @@ class CursorPaint extends StatelessWidget { Widget build(BuildContext context) { final m = Provider.of(context); final c = Provider.of(context); - final adjust = gFFI.cursorModel.adjustForKeyboard; + final adjust = gFFI.cursorModel.adjustForKeyboard(); var s = c.scale; double hotx = m.hotx; double hoty = m.hoty; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 2c1a043cbaf..bdbad349f24 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1807,30 +1807,21 @@ class CursorModel with ChangeNotifier { // But we're now using a Container(child: Stack(...)) to wrap the KeyHelpTools, // and the listener is on the Container. Rect? _keyHelpToolsRect; - // `lastIsBlocked` is only used in common/widgets/remote_input.dart -> _RawTouchGestureDetectorRegionState -> onDoubleTap() - // Because onDoubleTap() doesn't have the `event` parameter, we can't get the touch event's position. bool _lastIsBlocked = false; - // This is the flag for the touch mode. - // See `moveTapDown` and `moveTapUp` for more details. - bool _inTapDown = false; - // This is current adjust value for the touch mode. - double _adjustForKeyboard = 0.0; - // This is the last two adjust value for the touch mode. - // This value is used to avoid the cursor jumping in one tapDown and tapUp event. - double _adjustForKeyboard2 = 0.0; - - keyHelpToolsVisibilityChanged(Rect? r) { + + set keyHelpToolsRect(Rect? r) { _keyHelpToolsRect = r; if (r == null) { _lastIsBlocked = false; } else { + // `lastIsBlocked` is only used in common/widgets/remote_input.dart -> _RawTouchGestureDetectorRegionState -> onDoubleTap() + // Because onDoubleTap() doesn't have the `event` parameter, we can't get the touch event's position. + // // Block the touch event is safe here. - // `lastIsBlocked` is only used in onDoubleTap() to block the touch event from the KeyHelpTools. + // `lastIsBlocked` is only used in onDoubleTap() to block the touch event from the KeyHelpTools. // `lastIsBlocked` will be set when the cursor is moving or touch somewhere else. _lastIsBlocked = true; } - _adjustForKeyboard = 0.0; - _adjustForKeyboard2 = 0.0; } get lastIsBlocked => _lastIsBlocked; @@ -1880,18 +1871,15 @@ class CursorModel with ChangeNotifier { } get keyboardHeight => MediaQueryData.fromWindow(ui.window).viewInsets.bottom; - get scale => parent.target?.canvasModel.scale ?? 1.0; - get adjustForKeyboard => _adjustForKeyboard; - _updateAdjustForKeyboard() { + double adjustForKeyboard() { final m = MediaQueryData.fromWindow(ui.window); final size = m.size; if (keyboardHeight < 100) return 0; + final s = parent.target?.canvasModel.scale ?? 1.0; final thresh = (size.height - keyboardHeight) / 2; - final h = - (_y - getVisibleRect().top) * scale; // local physical display height - _adjustForKeyboard2 = _adjustForKeyboard; - _adjustForKeyboard = h - thresh; + var h = (_y - getVisibleRect().top) * s; // local physical display height + return h - thresh; } // mobile Soft keyboard, block touch event from the KeyHelpTools @@ -1908,64 +1896,23 @@ class CursorModel with ChangeNotifier { return false; } - // mobile touch mode - // We do not update adjustForKeyboard here. - // Because `moveTapUp` will also trigger `moveLocal` to update the cursor position. - // And `moveLocal` depends on `adjustForKeyboard`. - // If we update `adjustForKeyboard` here, the cursor will move to the wrong position. - // - // We can trigger the update in `moveTapUp` and use `_inTapDown` to control the update. - // If `_inTapDown` is timeout, we suppose the touch event is a long press event, and do not update `adjustForKeyboard`. - moveTapDown(double x, double y) { - if (shouldBlock(x, y)) { - _lastIsBlocked = true; - return false; - } - _lastIsBlocked = false; - moveLocal(x, y, adjust: _adjustForKeyboard); - parent.target?.inputModel.moveMouse(_x, _y); - _inTapDown = true; - Future.delayed(Duration(milliseconds: 500), () { - _inTapDown = false; - }); - return true; - } - - // mobile touch mode - moveTapUp(double x, double y) { - if (shouldBlock(x, y)) { - _lastIsBlocked = true; - return false; - } - _lastIsBlocked = false; - - final wasInTapDown = _inTapDown; - if (_inTapDown) { - _updateAdjustForKeyboard(); - _inTapDown = false; - } - parent.target?.inputModel.moveMouse(_x, _y); - moveLocal(x, y, - adjust: wasInTapDown ? _adjustForKeyboard2 : _adjustForKeyboard); - return true; - } - move(double x, double y) { if (shouldBlock(x, y)) { _lastIsBlocked = true; return false; } _lastIsBlocked = false; + moveLocal(x, y); parent.target?.inputModel.moveMouse(_x, _y); - moveLocal(x, y, adjust: _adjustForKeyboard); return true; } - moveLocal(double x, double y, {double adjust = 0}) { + moveLocal(double x, double y) { + final scale = parent.target?.canvasModel.scale ?? 1.0; final xoffset = parent.target?.canvasModel.x ?? 0; final yoffset = parent.target?.canvasModel.y ?? 0; _x = (x - xoffset) / scale + _displayOriginX; - _y = (y - yoffset + adjust) / scale + _displayOriginY; + _y = (y - yoffset) / scale + _displayOriginY; notifyListeners(); } From 30bd4e1cef96c492d0cf541a59fb976e24dbbb54 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 20 Jun 2024 13:57:56 +0800 Subject: [PATCH 069/335] update hwcodec, use ms as pts like vpx (#8422) Signed-off-by: 21pages --- Cargo.lock | 4 ++-- libs/scrap/examples/benchmark.rs | 6 +++++- libs/scrap/src/common/hwcodec.rs | 16 ++++++++-------- libs/scrap/src/common/record.rs | 2 +- libs/scrap/src/common/vram.rs | 13 ++++++++----- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b9101bb66d..54bef444475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3037,8 +3037,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.4.17" -source = "git+https://github.com/21pages/hwcodec#38eb9bfc4730986ebeb519ab39027d654356ce1a" +version = "0.4.18" +source = "git+https://github.com/21pages/hwcodec#b84d5bbefa949194d1fc51a5c7e9d7988e315ce6" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index 3ef7916a95c..803a4343ce2 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -287,13 +287,17 @@ mod hw { let mut mid_data = Vec::new(); let mut counter = 0; let mut time_sum = Duration::ZERO; + let start = std::time::Instant::now(); loop { match c.frame(std::time::Duration::from_millis(30)) { Ok(frame) => { let tmp_timer = Instant::now(); let frame = frame.to(encoder.yuvfmt(), &mut yuv, &mut mid_data).unwrap(); let yuv = frame.yuv().unwrap(); - for ref frame in encoder.encode(&yuv).unwrap() { + for ref frame in encoder + .encode(&yuv, start.elapsed().as_millis() as _) + .unwrap() + { size += frame.data.len(); h26xs.push(frame.data.to_vec()); diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 589396ede82..c387afcd7f1 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -28,7 +28,7 @@ use hwcodec::{ }; const DEFAULT_PIXFMT: AVPixelFormat = AVPixelFormat::AV_PIX_FMT_NV12; -pub const DEFAULT_TIME_BASE: [i32; 2] = [1, 30]; +pub const DEFAULT_FPS: i32 = 30; const DEFAULT_GOP: i32 = i32::MAX; const DEFAULT_HW_QUALITY: Quality = Quality_Default; @@ -82,7 +82,7 @@ impl EncoderApi for HwRamEncoder { pixfmt: DEFAULT_PIXFMT, align: HW_STRIDE_ALIGN as _, kbs: bitrate as i32, - timebase: DEFAULT_TIME_BASE, + fps: DEFAULT_FPS, gop, quality: DEFAULT_HW_QUALITY, rc, @@ -113,16 +113,16 @@ impl EncoderApi for HwRamEncoder { } } - fn encode_to_message(&mut self, input: EncodeInput, _ms: i64) -> ResultType { + fn encode_to_message(&mut self, input: EncodeInput, ms: i64) -> ResultType { let mut vf = VideoFrame::new(); let mut frames = Vec::new(); for frame in self - .encode(input.yuv()?) + .encode(input.yuv()?, ms) .with_context(|| "Failed to encode")? { frames.push(EncodedVideoFrame { data: Bytes::from(frame.data), - pts: frame.pts as _, + pts: frame.pts, key: frame.key == 1, ..Default::default() }); @@ -236,8 +236,8 @@ impl HwRamEncoder { info } - pub fn encode(&mut self, yuv: &[u8]) -> ResultType> { - match self.encoder.encode(yuv) { + pub fn encode(&mut self, yuv: &[u8], ms: i64) -> ResultType> { + match self.encoder.encode(yuv, ms) { Ok(v) => { let mut data = Vec::::new(); data.append(v); @@ -664,7 +664,7 @@ pub fn check_available_hwcodec() -> String { pixfmt: DEFAULT_PIXFMT, align: HW_STRIDE_ALIGN as _, kbs: 0, - timebase: DEFAULT_TIME_BASE, + fps: DEFAULT_FPS, gop: DEFAULT_GOP, quality: DEFAULT_HW_QUALITY, rc: RC_CBR, diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 54c9c186489..52973c2b244 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -331,7 +331,7 @@ impl RecorderApi for HwRecorder { width: ctx.width, height: ctx.height, is265: ctx.format == CodecFormat::H265, - framerate: crate::hwcodec::DEFAULT_TIME_BASE[1] as _, + framerate: crate::hwcodec::DEFAULT_FPS as _, }) .map_err(|_| anyhow!("Failed to create hardware muxer"))?; Ok(HwRecorder { diff --git a/libs/scrap/src/common/vram.rs b/libs/scrap/src/common/vram.rs index 8de6658a433..241908b74af 100644 --- a/libs/scrap/src/common/vram.rs +++ b/libs/scrap/src/common/vram.rs @@ -99,15 +99,18 @@ impl EncoderApi for VRamEncoder { fn encode_to_message( &mut self, frame: EncodeInput, - _ms: i64, + ms: i64, ) -> ResultType { let texture = frame.texture()?; let mut vf = VideoFrame::new(); let mut frames = Vec::new(); - for frame in self.encode(texture).with_context(|| "Failed to encode")? { + for frame in self + .encode(texture, ms) + .with_context(|| "Failed to encode")? + { frames.push(EncodedVideoFrame { data: Bytes::from(frame.data), - pts: frame.pts as _, + pts: frame.pts, key: frame.key == 1, ..Default::default() }); @@ -266,8 +269,8 @@ impl VRamEncoder { } } - pub fn encode(&mut self, texture: *mut c_void) -> ResultType> { - match self.encoder.encode(texture) { + pub fn encode(&mut self, texture: *mut c_void, ms: i64) -> ResultType> { + match self.encoder.encode(texture, ms) { Ok(v) => { let mut data = Vec::::new(); data.append(v); From 3c5810cc01205456c4129d04a59384b9361eadc1 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 19:16:51 +0800 Subject: [PATCH 070/335] prepare android old version test --- .github/workflows/build-macos-arm64.yml | 307 +++++++++++------------- 1 file changed, 141 insertions(+), 166 deletions(-) diff --git a/.github/workflows/build-macos-arm64.yml b/.github/workflows/build-macos-arm64.yml index a196ee6133d..fb3c1401b6c 100644 --- a/.github/workflows/build-macos-arm64.yml +++ b/.github/workflows/build-macos-arm64.yml @@ -31,200 +31,175 @@ env: SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}" jobs: - build-appimage: - name: Build image ${{ matrix.job.target }} - runs-on: ubuntu-20.04 + build-rustdesk-android: + name: build rustdesk android apk ${{ matrix.job.target }} + runs-on: ${{ matrix.job.os }} strategy: fail-fast: false matrix: job: - { - target: x86_64-unknown-linux-gnu, - arch: x86_64, - } - - { - target: aarch64-unknown-linux-gnu, arch: aarch64, + target: aarch64-linux-android, + os: ubuntu-20.04, + openssl-arch: android-arm64, + ref: abc123 } steps: - name: Checkout source code uses: actions/checkout@v3 + with: + ref: ac79c45529138e63cae893ea9dfacae3858477d3 - - name: Rename Binary - run: | - sudo apt-get update -y - sudo apt-get install -y wget libarchive-tools - wget https://github.com/rustdesk/rustdesk/releases/download/nightly/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb rustdesk-${{ env.VERSION }}.deb - - - name: Patch archlinux PKGBUILD - if: matrix.job.arch == 'x86_64' + - name: Install dependencies run: | - sed -i "s/x86_64/${{ matrix.job.arch }}/g" res/PKGBUILD - if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then - sed -i "s/linux\/x64/linux\/arm64/g" ./res/PKGBUILD - fi - bsdtar -zxvf rustdesk-${{ env.VERSION }}.deb - tar -xvf ./data.tar.xz - case ${{ matrix.job.arch }} in - aarch64) - mkdir -p flutter/build/linux/arm64/release/bundle - cp -rf usr/lib/rustdesk/* flutter/build/linux/arm64/release/bundle/ - ;; - x86_64) - mkdir -p flutter/build/linux/x64/release/bundle - cp -rf usr/lib/rustdesk/* flutter/build/linux/x64/release/bundle/ - ;; - esac + sudo apt-get update + sudo apt-get install -y \ + clang \ + cmake \ + curl \ + gcc-multilib \ + git \ + g++ \ + g++-multilib \ + libappindicator3-dev \ + libasound2-dev \ + libc6-dev \ + libclang-10-dev \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libgtk-3-dev \ + libpam0g-dev \ + libpulse-dev \ + libva-dev \ + libvdpau-dev \ + libxcb-randr0-dev \ + libxcb-shape0-dev \ + libxcb-xfixes0-dev \ + libxdo-dev \ + libxfixes-dev \ + llvm-10-dev \ + nasm \ + ninja-build \ + openjdk-11-jdk-headless \ + pkg-config \ + tree \ + wget - - name: Build archlinux package - if: matrix.job.arch == 'x86_64' - uses: rustdesk-org/arch-makepkg-action@master + - name: Install flutter + uses: subosito/flutter-action@v2 with: - packages: > - llvm - clang - libva - libvdpau - rust - gstreamer - unzip - git - cmake - gcc - curl - wget - nasm - zip - make - pkg-config - clang - gtk3 - xdotool - libxcb - libxfixes - alsa-lib - pipewire - python - ttf-arphic-uming - libappindicator-gtk3 - pam - gst-plugins-base - gst-plugin-pipewire - scripts: | - cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} - - name: Publish archlinux package - if: matrix.job.arch == 'x86_64' - uses: softprops/action-gh-release@v1 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@v1 with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - res/rustdesk-${{ env.VERSION }}*.zst + toolchain: ${{ env.RUST_VERSION }} + components: "rustfmt" - - name: Build appimage package - shell: bash + - name: Install flutter rust bridge deps 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 + git config --global core.longpaths true + cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" + 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: Publish appimage package - uses: softprops/action-gh-release@v1 + - uses: nttld/setup-ndk@v1 + id: setup-ndk with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage + ndk-version: ${{ env.NDK_VERSION }} + add-to-path: true - build-flatpak: - name: Build Flatpak ${{ matrix.job.target }} - runs-on: ${{ matrix.job.on }} - strategy: - fail-fast: false - matrix: - job: - - { - target: x86_64-unknown-linux-gnu, - distro: ubuntu18.04, - on: ubuntu-20.04, - arch: x86_64, - } - - { - target: aarch64-unknown-linux-gnu, - # try out newer flatpak since error of "error: Nothing matches org.freedesktop.Platform in remote flathub" - distro: ubuntu22.04, - on: [self-hosted, Linux, ARM64], - arch: aarch64, - } - steps: - - name: Checkout source code - uses: actions/checkout@v3 + - name: Setup vcpkg with Github Actions binary cache + uses: lukka/run-vcpkg@v11 + with: + vcpkgDirectory: /opt/artifacts/vcpkg + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + case ${{ matrix.job.target }} in + aarch64-linux-android) + ./flutter/build_android_deps.sh arm64-v8a + ;; + armv7-linux-androideabi) + ./flutter/build_android_deps.sh armeabi-v7a + ;; + esac + shell: bash + + - name: Build rustdesk lib + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + run: | + rustup target add ${{ matrix.job.target }} + cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} + 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: Rename Binary + - name: Build rustdesk + shell: bash + env: + JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64 run: | - sudo apt-get update -y - sudo apt-get install -y wget - wget https://github.com/rustdesk/rustdesk/releases/download/nightly/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb rustdesk-${{ env.VERSION }}.deb + 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 ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.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.arch }}.apk + ;; + armv7-linux-androideabi) + mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a + cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.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.arch }}.apk + ;; + esac + popd + mkdir -p signed-apk; pushd signed-apk + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ./rustdesk-test.apk - - uses: rustdesk-org/run-on-arch-action@amd64-support - name: Build rustdesk flatpak package for ${{ matrix.job.arch }} - id: rpm + - uses: r0adkll/sign-android-release@v1 + name: Sign app APK + if: env.ANDROID_SIGNING_KEY != null + id: sign-rustdesk with: - arch: ${{ matrix.job.arch }} - distro: ${{ matrix.job.distro }} - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - shell: /bin/bash - install: | - apt-get update -y - apt-get install -y \ - curl \ - git \ - rpm \ - wget - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /workspace - # install - apt-get update -y - apt-get install -y \ - cmake \ - curl \ - flatpak \ - flatpak-builder \ - gcc \ - git \ - g++ \ - libgtk-3-dev \ - nasm \ - wget - # 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 }}/23.08 - flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/23.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.arch }}.flatpak com.rustdesk.RustDesk + 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: Publish flatpak package + - 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: | - flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak + ${{steps.sign-rustdesk.outputs.signedReleaseFile}} From a4248308932b67e737f3fe7188e5789ffb48b67c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 19:23:17 +0800 Subject: [PATCH 071/335] fix ci --- .github/workflows/history.yml | 84 ------------------- .../{build-macos-arm64.yml => playground.yml} | 4 +- 2 files changed, 2 insertions(+), 86 deletions(-) delete mode 100644 .github/workflows/history.yml rename .github/workflows/{build-macos-arm64.yml => playground.yml} (98%) diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml deleted file mode 100644 index 50bda4d48a0..00000000000 --- a/.github/workflows/history.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Flutter Windows History Build - -on: [workflow_dispatch] - -env: - LLVM_VERSION: "10.0" - FLUTTER_VERSION: "3.16.9" - TAG_NAME: "tmp" - FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" - VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - VERSION: "1.2.6" - -jobs: - build-for-history-windows: - name: ${{ matrix.job.date }} - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { target: x86_64-pc-windows-msvc, os: windows-2022, arch: x86_64, date: 2023-08-04, ref: 72c198a1e94cc1e0242fce88f92b3f3caedcd0c3 } - steps: - - name: Checkout source code - uses: actions/checkout@v4 - with: - ref: ${{ matrix.job.ref }} - - - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 - with: - version: ${{ env.LLVM_VERSION }} - - - name: Install flutter - uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - components: rustfmt - profile: minimal # minimal component installation (ie, no documentation) - - - name: Install flutter rust bridge deps - run: | - cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" - 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: Setup vcpkg with Github Actions binary cache - uses: lukka/run-vcpkg@v11 - with: - vcpkgDirectory: C:\vcpkg - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - $VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed" - shell: bash - - - name: Build rustdesk - run: python3 .\build.py --portable --hwcodec --flutter - - - name: Build self-extracted executable - shell: bash - 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-${{ matrix.job.date }}-${{ matrix.job.target }}.exe - - - name: Publish Release - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./SignOutput/rustdesk-*.exe diff --git a/.github/workflows/build-macos-arm64.yml b/.github/workflows/playground.yml similarity index 98% rename from .github/workflows/build-macos-arm64.yml rename to .github/workflows/playground.yml index fb3c1401b6c..7bf0dee4825 100644 --- a/.github/workflows/build-macos-arm64.yml +++ b/.github/workflows/playground.yml @@ -1,4 +1,4 @@ -name: Flutter Nightly MacOS Arm64 Build +name: playground on: #schedule: @@ -102,7 +102,7 @@ jobs: run: | git config --global core.longpaths true cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" - Push-Location flutter ; flutter pub get ; Pop-Location + 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 - uses: nttld/setup-ndk@v1 From e424d01f3d0e124ea11a8694b57edb76f54aec1c Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 19:41:43 +0800 Subject: [PATCH 072/335] publish missed --- .github/workflows/playground.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 7bf0dee4825..43b66fbfb39 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -196,7 +196,6 @@ jobs: BUILD_TOOLS_VERSION: "30.0.2" - name: Publish signed apk package - if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true From 67f83bd5dd272d37cc0cbd79c9954e74f7d88826 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 19:48:40 +0800 Subject: [PATCH 073/335] more commits --- .github/workflows/playground.yml | 41 +++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 43b66fbfb39..15f48cf7251 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,13 +43,48 @@ jobs: target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: abc123 + ref: f6612d39bf0985b0c6d91557947bf8397dac114a # apr 30 + } + - { + arch: aarch64, + target: aarch64-linux-android, + os: ubuntu-20.04, + openssl-arch: android-arm64, + ref: 0d8fe1b7b8a3a688c0c445dedc706033565cfdcf # apr 20 + } + - { + arch: aarch64, + target: aarch64-linux-android, + os: ubuntu-20.04, + openssl-arch: android-arm64, + ref: ac79c45529138e63cae893ea9dfacae3858477d3 # apr 10 + } + - { + arch: aarch64, + target: aarch64-linux-android, + os: ubuntu-20.04, + openssl-arch: android-arm64, + ref: c0e5e78d2be72d44512e08692bbe0d8e973925e8 # mar 10 + } + - { + arch: aarch64, + target: aarch64-linux-android, + os: ubuntu-20.04, + openssl-arch: android-arm64, + ref: 633076ddd468a20ac20ef12a7e06b9945ee4fb67 # feb 10 + } + - { + arch: aarch64, + target: aarch64-linux-android, + os: ubuntu-20.04, + openssl-arch: android-arm64, + ref: ebfbc8ce61d2e1a6a5bf9576556e1e4b75f2f2c3 # jan 10 } steps: - name: Checkout source code uses: actions/checkout@v3 with: - ref: ac79c45529138e63cae893ea9dfacae3858477d3 + ref: ${{ matrix.job.ref }} - name: Install dependencies run: | @@ -179,7 +214,7 @@ jobs: esac popd mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ./rustdesk-test.apk + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ./rustdesk-test-${{ matrix.job.ref }}.apk - uses: r0adkll/sign-android-release@v1 name: Sign app APK From c1322b47c3bbc52c4e9e0d81dbb8b15bd8f7b85b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 20:13:57 +0800 Subject: [PATCH 074/335] more commits --- .github/workflows/playground.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 15f48cf7251..7a938a32684 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,21 +43,14 @@ jobs: target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: f6612d39bf0985b0c6d91557947bf8397dac114a # apr 30 + ref: 0d75f71d16b9712b959423f6ae8fe5de7502e8f2 # mar 30 } - { arch: aarch64, target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: 0d8fe1b7b8a3a688c0c445dedc706033565cfdcf # apr 20 - } - - { - arch: aarch64, - target: aarch64-linux-android, - os: ubuntu-20.04, - openssl-arch: android-arm64, - ref: ac79c45529138e63cae893ea9dfacae3858477d3 # apr 10 + ref: 0540e2cf6c4292e06320222acc6c05ef09e677e4 # mar 20 } - { arch: aarch64, From 39e3da1eb07561c64099218ca1eebdef857a096d Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 20 Jun 2024 20:20:32 +0800 Subject: [PATCH 075/335] android, secure keyboard on remote input (#8425) Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 7d449f84b2b..7e78fc298db 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -479,7 +479,11 @@ class _RemotePageState extends State { : TextFormField( textInputAction: TextInputAction.newline, autocorrect: false, - enableSuggestions: false, + // Flutter 3.16.9 Android. + // `enableSuggestions` causes secure keyboard to be shown. + // https://github.com/flutter/flutter/issues/139143 + // https://github.com/flutter/flutter/issues/146540 + // enableSuggestions: false, autofocus: true, focusNode: _mobileFocusNode, maxLines: null, From ae69cbb207277f3880a15bceb4fbc34fc3fba45a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 20:30:31 +0800 Subject: [PATCH 076/335] fix ci --- .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 deb51f5389b..25259e4cc52 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -15,7 +15,7 @@ env: RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.19.6" + FLUTTER_VERSION: "3.16.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" From f5bc136b07d65b011582052cad5ecce5c012d333 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 20:32:00 +0800 Subject: [PATCH 077/335] fix ci --- .github/workflows/flutter-build.yml | 2 +- .github/workflows/playground.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 25259e4cc52..deb51f5389b 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -15,7 +15,7 @@ env: RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.16.9" + FLUTTER_VERSION: "3.19.6" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 7a938a32684..90e87b31167 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.19.6" + FLUTTER_VERSION: "3.16.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" From 93d88f30b4c673b1c64e69fe254719ea0bac7488 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 21:10:43 +0800 Subject: [PATCH 078/335] test more commits --- .github/workflows/playground.yml | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 90e87b31167..6dd5bbaf78b 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,35 +43,21 @@ jobs: target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: 0d75f71d16b9712b959423f6ae8fe5de7502e8f2 # mar 30 + ref: ebb14af488f31eb651a4c3c4c873aed7303cf53a # Dec 11 } - { arch: aarch64, target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: 0540e2cf6c4292e06320222acc6c05ef09e677e4 # mar 20 + ref: e57d07f7d6cd216c14bd35ab5323fcdd371de464 # Nov 10 } - { arch: aarch64, target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: c0e5e78d2be72d44512e08692bbe0d8e973925e8 # mar 10 - } - - { - arch: aarch64, - target: aarch64-linux-android, - os: ubuntu-20.04, - openssl-arch: android-arm64, - ref: 633076ddd468a20ac20ef12a7e06b9945ee4fb67 # feb 10 - } - - { - arch: aarch64, - target: aarch64-linux-android, - os: ubuntu-20.04, - openssl-arch: android-arm64, - ref: ebfbc8ce61d2e1a6a5bf9576556e1e4b75f2f2c3 # jan 10 + ref: bbd7cf306a1937ed5cefa486826b46743e0339f7 # Oct 13 } steps: - name: Checkout source code From ba707d1149335d4441e7f995582e11e83d7aaa84 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 21:28:27 +0800 Subject: [PATCH 079/335] try out different ndk --- .github/workflows/playground.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 6dd5bbaf78b..e918329a11d 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,21 +43,16 @@ jobs: target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: ebb14af488f31eb651a4c3c4c873aed7303cf53a # Dec 11 + ref: ebb14af488f31eb651a4c3c4c873aed7303cf53a, # Dec 11 + ndk: r25c } - { arch: aarch64, target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: e57d07f7d6cd216c14bd35ab5323fcdd371de464 # Nov 10 - } - - { - arch: aarch64, - target: aarch64-linux-android, - os: ubuntu-20.04, - openssl-arch: android-arm64, - ref: bbd7cf306a1937ed5cefa486826b46743e0339f7 # Oct 13 + ref: e57d07f7d6cd216c14bd35ab5323fcdd371de464, # Nov 10 + ndk: r26d } steps: - name: Checkout source code @@ -94,6 +89,7 @@ jobs: libxfixes-dev \ llvm-10-dev \ nasm \ + yasm \ ninja-build \ openjdk-11-jdk-headless \ pkg-config \ @@ -122,7 +118,7 @@ jobs: - uses: nttld/setup-ndk@v1 id: setup-ndk with: - ndk-version: ${{ env.NDK_VERSION }} + ndk-version: ${{ matrix.job.ndk }} add-to-path: true - name: Setup vcpkg with Github Actions binary cache @@ -193,7 +189,7 @@ jobs: esac popd mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ./rustdesk-test-${{ matrix.job.ref }}.apk + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ./rustdesk-test-${{ matrix.job.ref }}-${{ matrix.job.ndk }}.apk - uses: r0adkll/sign-android-release@v1 name: Sign app APK From 3c794045342276bfdcf50f695e484b1931e10c91 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 21:53:43 +0800 Subject: [PATCH 080/335] use my vcpkg --- .github/workflows/playground.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index e918329a11d..d685eafbde6 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -128,6 +128,7 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + if: False run: | case ${{ matrix.job.target }} in aarch64-linux-android) @@ -139,6 +140,12 @@ jobs: esac shell: bash + - name: Clone deps + shell: bash + run: | + pushd /opt + git clone https://github.com/rustdesk-org/rustdesk_thirdparty_lib.git --depth=1 + - name: Build rustdesk lib env: ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} From a679e4a5e350c48a917aed86f50834c3b7b982f4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 22:16:04 +0800 Subject: [PATCH 081/335] vcpkg_root --- .github/workflows/playground.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index d685eafbde6..ed2af510c94 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -150,6 +150,7 @@ jobs: 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 --version ${{ env.CARGO_NDK_VERSION }} From 82bf04da8178d3ad8ed102325a140d37c602b141 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 20 Jun 2024 22:18:57 +0800 Subject: [PATCH 082/335] fix: android, touch mode, correct cursor input, on soft keyboard shows (#8426) Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 6 ++--- flutter/lib/models/model.dart | 28 +++++++++++++---------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 7e78fc298db..a6b51b99e30 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -721,8 +721,8 @@ class _KeyHelpToolsState extends State { if (renderObject is RenderBox) { final size = renderObject.size; Offset pos = renderObject.localToGlobal(Offset.zero); - gFFI.cursorModel.keyHelpToolsRect = - Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height); + gFFI.cursorModel.keyHelpToolsVisibilityChanged( + Rect.fromLTWH(pos.dx, pos.dy, size.width, size.height)); } } @@ -734,7 +734,7 @@ class _KeyHelpToolsState extends State { inputModel.command; if (!_pin && !hasModifierOn && !widget.requestShow) { - gFFI.cursorModel.keyHelpToolsRect = null; + gFFI.cursorModel.keyHelpToolsVisibilityChanged(null); return Offstage(); } final size = MediaQuery.of(context).size; diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index bdbad349f24..afb4f88a94e 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1807,21 +1807,22 @@ class CursorModel with ChangeNotifier { // But we're now using a Container(child: Stack(...)) to wrap the KeyHelpTools, // and the listener is on the Container. Rect? _keyHelpToolsRect; + // `lastIsBlocked` is only used in common/widgets/remote_input.dart -> _RawTouchGestureDetectorRegionState -> onDoubleTap() + // Because onDoubleTap() doesn't have the `event` parameter, we can't get the touch event's position. bool _lastIsBlocked = false; + double _yForKeyboardAdjust = 0; - set keyHelpToolsRect(Rect? r) { + keyHelpToolsVisibilityChanged(Rect? r) { _keyHelpToolsRect = r; if (r == null) { _lastIsBlocked = false; } else { - // `lastIsBlocked` is only used in common/widgets/remote_input.dart -> _RawTouchGestureDetectorRegionState -> onDoubleTap() - // Because onDoubleTap() doesn't have the `event` parameter, we can't get the touch event's position. - // // Block the touch event is safe here. - // `lastIsBlocked` is only used in onDoubleTap() to block the touch event from the KeyHelpTools. + // `lastIsBlocked` is only used in onDoubleTap() to block the touch event from the KeyHelpTools. // `lastIsBlocked` will be set when the cursor is moving or touch somewhere else. _lastIsBlocked = true; } + _yForKeyboardAdjust = _y; } get lastIsBlocked => _lastIsBlocked; @@ -1871,14 +1872,18 @@ class CursorModel with ChangeNotifier { } get keyboardHeight => MediaQueryData.fromWindow(ui.window).viewInsets.bottom; + get scale => parent.target?.canvasModel.scale ?? 1.0; double adjustForKeyboard() { + if (keyboardHeight < 100) { + return 0.0; + } + final m = MediaQueryData.fromWindow(ui.window); final size = m.size; - if (keyboardHeight < 100) return 0; - final s = parent.target?.canvasModel.scale ?? 1.0; final thresh = (size.height - keyboardHeight) / 2; - var h = (_y - getVisibleRect().top) * s; // local physical display height + final h = (_yForKeyboardAdjust - getVisibleRect().top) * + scale; // local physical display height return h - thresh; } @@ -1902,17 +1907,16 @@ class CursorModel with ChangeNotifier { return false; } _lastIsBlocked = false; - moveLocal(x, y); + moveLocal(x, y, adjust: adjustForKeyboard()); parent.target?.inputModel.moveMouse(_x, _y); return true; } - moveLocal(double x, double y) { - final scale = parent.target?.canvasModel.scale ?? 1.0; + moveLocal(double x, double y, {double adjust = 0}) { final xoffset = parent.target?.canvasModel.x ?? 0; final yoffset = parent.target?.canvasModel.y ?? 0; _x = (x - xoffset) / scale + _displayOriginX; - _y = (y - yoffset) / scale + _displayOriginY; + _y = (y - yoffset + adjust) / scale + _displayOriginY; notifyListeners(); } From a91f244f354200de712c1dcfa91325a6bbf2ba06 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 20 Jun 2024 22:21:14 +0800 Subject: [PATCH 083/335] fix: android, touch mode, one finger pan, start pos (#8427) Signed-off-by: fufesou --- flutter/lib/common/widgets/gestures.dart | 2 ++ flutter/lib/common/widgets/remote_input.dart | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/flutter/lib/common/widgets/gestures.dart b/flutter/lib/common/widgets/gestures.dart index aeff150417e..6c269656722 100644 --- a/flutter/lib/common/widgets/gestures.dart +++ b/flutter/lib/common/widgets/gestures.dart @@ -112,6 +112,8 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { }; } + // FIXME: This debounce logic is not working properly. + // If we move our finger very fast, we won't be able to detect the "oneFingerPan" event sometimes. void onOneFingerStartDebounce(ScaleUpdateDetails d) { start(ScaleUpdateDetails d) { _currentState = GestureState.oneFingerPan; diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index ad08736c802..0be75240078 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -69,6 +69,8 @@ class RawTouchGestureDetectorRegion extends StatefulWidget { class _RawTouchGestureDetectorRegionState extends State { Offset _cacheLongPressPosition = Offset(0, 0); + // Timestamp of the last long press event. + int _cacheLongPressPositionTs = 0; double _mouseScrollIntegral = 0; // mouse scroll speed controller double _scale = 1; @@ -151,6 +153,7 @@ class _RawTouchGestureDetectorRegionState if (handleTouch) { ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy); _cacheLongPressPosition = d.localPosition; + _cacheLongPressPositionTs = DateTime.now().millisecondsSinceEpoch; } } @@ -233,6 +236,16 @@ class _RawTouchGestureDetectorRegionState if (isDesktop) { ffi.cursorModel.trySetRemoteWindowCoords(); } + // Workaround for the issue that the first pan event is sent a long time after the start event. + // If the time interval between the start event and the first pan event is less than 500ms, + // we consider to use the long press position as the start position. + // + // TODO: We should find a better way to send the first pan event as soon as possible. + if (DateTime.now().millisecondsSinceEpoch - _cacheLongPressPositionTs < + 500) { + ffi.cursorModel + .move(_cacheLongPressPosition.dx, _cacheLongPressPosition.dy); + } inputModel.sendMouse('down', MouseButtons.left); ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy); } else { From 5f6f1e8d368113277c04333966952300b6ce3f96 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 22:45:07 +0800 Subject: [PATCH 084/335] use prebuilt vcpkg to overwrite --- .github/workflows/playground.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index ed2af510c94..403b3199954 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -128,7 +128,6 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies - if: False run: | case ${{ matrix.job.target }} in aarch64-linux-android) @@ -145,12 +144,12 @@ jobs: run: | pushd /opt git clone https://github.com/rustdesk-org/rustdesk_thirdparty_lib.git --depth=1 + cp -rf /opt/rustdesk_thirdparty_lib/vcpkg/* /opt/artifacts/vcpkg/ - 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 --version ${{ env.CARGO_NDK_VERSION }} From 4338fcc51a25a4ab45d93352872159023b14b6c5 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 23:06:42 +0800 Subject: [PATCH 085/335] check if vpckg overrided --- .github/workflows/playground.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 403b3199954..c3bbca81ed9 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -144,7 +144,9 @@ jobs: run: | pushd /opt git clone https://github.com/rustdesk-org/rustdesk_thirdparty_lib.git --depth=1 + ls -ls /opt/artifacts/vcpkg/installed/arm64-linux/lib/ cp -rf /opt/rustdesk_thirdparty_lib/vcpkg/* /opt/artifacts/vcpkg/ + ls -ls /opt/artifacts/vcpkg/installed/arm64-linux/lib/ - name: Build rustdesk lib env: From 0cab620ba584cd1387899db85a1141de1515aab6 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 23:09:38 +0800 Subject: [PATCH 086/335] try old flutter and vcpkg --- .github/workflows/playground.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index c3bbca81ed9..c81b2e858c3 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,14 +10,14 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.16.9" + FLUTTER_VERSION: "3.13.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" TAG_NAME: "nightly" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.03.25 - VCPKG_COMMIT_ID: "a34c873a9717a888f58dc05268dea15592c2f0ff" + VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50" VERSION: "1.2.6" NDK_VERSION: "r26d" #signing keys env variable checks From 859020583de390e41aace77f1641b5839c19144e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 23:21:08 +0800 Subject: [PATCH 087/335] fix ci --- .github/workflows/playground.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index c81b2e858c3..f0d9d883092 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -144,9 +144,9 @@ jobs: run: | pushd /opt git clone https://github.com/rustdesk-org/rustdesk_thirdparty_lib.git --depth=1 - ls -ls /opt/artifacts/vcpkg/installed/arm64-linux/lib/ + ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/ cp -rf /opt/rustdesk_thirdparty_lib/vcpkg/* /opt/artifacts/vcpkg/ - ls -ls /opt/artifacts/vcpkg/installed/arm64-linux/lib/ + ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/ - name: Build rustdesk lib env: From 58d86acf0dd20f0f60a864422fd28aabb5b449ef Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 20 Jun 2024 23:51:58 +0800 Subject: [PATCH 088/335] change back to 3.16.9 --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index f0d9d883092..7d274b7fc02 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.13.9" + FLUTTER_VERSION: "3.16.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" From 7db9543feeaefa110282550c5b9afe7e7ef2ccae Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 00:15:21 +0800 Subject: [PATCH 089/335] change back to old settings, only use flutter 3.13.9 --- .github/workflows/playground.yml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 7d274b7fc02..b7d3b834063 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,14 +10,14 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.16.9" + FLUTTER_VERSION: "3.13.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" TAG_NAME: "nightly" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.03.25 - VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50" + VCPKG_COMMIT_ID: "a34c873a9717a888f58dc05268dea15592c2f0ff" VERSION: "1.2.6" NDK_VERSION: "r26d" #signing keys env variable checks @@ -44,15 +44,6 @@ jobs: os: ubuntu-20.04, openssl-arch: android-arm64, ref: ebb14af488f31eb651a4c3c4c873aed7303cf53a, # Dec 11 - ndk: r25c - } - - { - arch: aarch64, - target: aarch64-linux-android, - os: ubuntu-20.04, - openssl-arch: android-arm64, - ref: e57d07f7d6cd216c14bd35ab5323fcdd371de464, # Nov 10 - ndk: r26d } steps: - name: Checkout source code @@ -118,7 +109,7 @@ jobs: - uses: nttld/setup-ndk@v1 id: setup-ndk with: - ndk-version: ${{ matrix.job.ndk }} + ndk-version: ${{ env.NDK_VERSION }} add-to-path: true - name: Setup vcpkg with Github Actions binary cache @@ -145,7 +136,7 @@ jobs: pushd /opt git clone https://github.com/rustdesk-org/rustdesk_thirdparty_lib.git --depth=1 ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/ - cp -rf /opt/rustdesk_thirdparty_lib/vcpkg/* /opt/artifacts/vcpkg/ + # cp -rf /opt/rustdesk_thirdparty_lib/vcpkg/* /opt/artifacts/vcpkg/ ls -ls /opt/artifacts/vcpkg/installed/arm64-android/lib/ - name: Build rustdesk lib From 3057396c0259cda0f4000c870ed941188f71638b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 00:19:05 +0800 Subject: [PATCH 090/335] try latest --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index b7d3b834063..937c9885d24 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,7 +43,7 @@ jobs: target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: ebb14af488f31eb651a4c3c4c873aed7303cf53a, # Dec 11 + ref: 30bd4e1cef96c492d0cf541a59fb976e24dbbb54, # latest } steps: - name: Checkout source code From 06fe9726838f6283483762da3112a1cc5fad5860 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 00:45:45 +0800 Subject: [PATCH 091/335] try out 3.22.2 for android --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 937c9885d24..49221b25812 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.13.9" + FLUTTER_VERSION: "3.22.2" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" From e95823f543128424ca29809aeab4e37427f8b879 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 01:00:46 +0800 Subject: [PATCH 092/335] for try out flutter 3.22.2 --- flutter/pubspec.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 20ef15d3f8f..8483e11ea44 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -37,9 +37,9 @@ dependencies: package_info_plus: ^4.2.0 url_launcher: ^6.2.1 toggle_switch: ^2.1.0 - dash_chat_2: - git: - url: https://github.com/rustdesk-org/Dash-Chat-2 + dash_chat_2: ^0.0.21 + #git: + # url: https://github.com/rustdesk-org/Dash-Chat-2 draggable_float_widget: ^0.1.0 settings_ui: ^2.0.2 flutter_breadcrumb: ^1.0.1 From 80c5d599166486ab7b09d992eb7f3badcbc8c6a8 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 01:13:05 +0800 Subject: [PATCH 093/335] fix ci --- .github/workflows/playground.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 49221b25812..93ee96676c4 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -103,6 +103,7 @@ jobs: run: | git config --global core.longpaths true cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" + rm -rf flutter/pubspec.lock 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 From 0b32e741f7a448ff8e680acc09c4cf12da1a7ba2 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 01:29:00 +0800 Subject: [PATCH 094/335] test 3.13.9 on master for android --- .github/workflows/playground.yml | 4 ++-- flutter/pubspec.yaml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 93ee96676c4..3d23656118c 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.22.2" + FLUTTER_VERSION: "3.13.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" @@ -103,7 +103,7 @@ jobs: run: | git config --global core.longpaths true cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" - rm -rf flutter/pubspec.lock + sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml 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 diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 8483e11ea44..20ef15d3f8f 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -37,9 +37,9 @@ dependencies: package_info_plus: ^4.2.0 url_launcher: ^6.2.1 toggle_switch: ^2.1.0 - dash_chat_2: ^0.0.21 - #git: - # url: https://github.com/rustdesk-org/Dash-Chat-2 + dash_chat_2: + git: + url: https://github.com/rustdesk-org/Dash-Chat-2 draggable_float_widget: ^0.1.0 settings_ui: ^2.0.2 flutter_breadcrumb: ^1.0.1 From 42394fcbdd57dc4dffb0c0bc8af3deafca5dfeb4 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:07:32 +0800 Subject: [PATCH 095/335] fix: android, two finger pan, scale (#8429) Signed-off-by: fufesou --- flutter/lib/common/widgets/remote_input.dart | 2 +- flutter/lib/models/model.dart | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index 0be75240078..61bd4dd31bc 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -305,7 +305,7 @@ class _RawTouchGestureDetectorRegionState } } else { // mobile - ffi.canvasModel.updateScale(d.scale / _scale); + ffi.canvasModel.updateScale(d.scale / _scale, d.focalPoint); _scale = d.scale; ffi.canvasModel.panX(d.focalPointDelta.dx); ffi.canvasModel.panY(d.focalPointDelta.dy); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index afb4f88a94e..2ea0376096d 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1576,22 +1576,21 @@ class CanvasModel with ChangeNotifier { notifyListeners(); } - updateScale(double v) { + updateScale(double v, Offset focalPoint) { if (parent.target?.imageModel.image == null) return; - final offset = parent.target?.cursorModel.offset ?? const Offset(0, 0); - var r = parent.target?.cursorModel.getVisibleRect() ?? Rect.zero; - final px0 = (offset.dx - r.left) * _scale; - final py0 = (offset.dy - r.top) * _scale; + final s = _scale; _scale *= v; final maxs = parent.target?.imageModel.maxScale ?? 1; final mins = parent.target?.imageModel.minScale ?? 1; if (_scale > maxs) _scale = maxs; if (_scale < mins) _scale = mins; - r = parent.target?.cursorModel.getVisibleRect() ?? Rect.zero; - final px1 = (offset.dx - r.left) * _scale; - final py1 = (offset.dy - r.top) * _scale; - _x -= px1 - px0; - _y -= py1 - py0; + // (focalPoint.dx - _x_1) / s1 + displayOriginX = (focalPoint.dx - _x_2) / s2 + displayOriginX + // _x_2 = focalPoint.dx - (focalPoint.dx - _x_1) / s1 * s2 + _x = focalPoint.dx - (focalPoint.dx - _x) / s * _scale; + final adjustForKeyboard = parent.target?.cursorModel.adjustForKeyboard() ?? 0.0; + // (focalPoint.dy - _y_1 + adjust) / s1 + displayOriginY = (focalPoint.dy - _y_2 + adjust) / s2 + displayOriginY + // _y_2 = focalPoint.dy + adjust - (focalPoint.dy - _y_1 + adjust) / s1 * s2 + _y = focalPoint.dy + adjustForKeyboard - (focalPoint.dy - _y + adjustForKeyboard) / s * _scale; notifyListeners(); } From 1cb0e1ce7bd1cc6eb362a1c7920f94c31f57acd5 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 12:33:05 +0800 Subject: [PATCH 096/335] try out 3.22.2 --- flutter/lib/desktop/pages/port_forward_page.dart | 7 ++++--- flutter/lib/desktop/widgets/material_mod_popup_menu.dart | 4 ++-- flutter/lib/desktop/widgets/popup_menu.dart | 2 +- flutter/lib/mobile/pages/remote_page.dart | 2 +- flutter/macos/Podfile.lock | 8 ++++---- flutter/pubspec.yaml | 3 +++ 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/flutter/lib/desktop/pages/port_forward_page.dart b/flutter/lib/desktop/pages/port_forward_page.dart index efeeb4cf882..b9a3248facf 100644 --- a/flutter/lib/desktop/pages/port_forward_page.dart +++ b/flutter/lib/desktop/pages/port_forward_page.dart @@ -141,8 +141,9 @@ class _PortForwardPageState extends State child: Text(translate(label)).marginOnly(left: _kTextLeftMargin)); return Theme( - data: Theme.of(context) - .copyWith(backgroundColor: Theme.of(context).colorScheme.background), + data: Theme.of(context).copyWith( + colorScheme: Theme.of(context).colorScheme, + ), child: Obx(() => ListView.builder( controller: ScrollController(), itemCount: pfs.length + 2, @@ -289,7 +290,7 @@ class _PortForwardPageState extends State ).marginOnly(left: _kTextLeftMargin)); return Theme( data: Theme.of(context) - .copyWith(backgroundColor: Theme.of(context).colorScheme.background), + .copyWith(colorScheme: Theme.of(context).colorScheme), child: ListView.builder( controller: ScrollController(), itemCount: 2, diff --git a/flutter/lib/desktop/widgets/material_mod_popup_menu.dart b/flutter/lib/desktop/widgets/material_mod_popup_menu.dart index 69ee882e8f1..0e085a14e19 100644 --- a/flutter/lib/desktop/widgets/material_mod_popup_menu.dart +++ b/flutter/lib/desktop/widgets/material_mod_popup_menu.dart @@ -271,7 +271,7 @@ class PopupMenuItem extends PopupMenuEntry { /// The text style of the popup menu item. /// /// If this property is null, then [PopupMenuThemeData.textStyle] is used. - /// If [PopupMenuThemeData.textStyle] is also null, then [TextTheme.subtitle1] + /// If [PopupMenuThemeData.textStyle] is also null, then [TextTheme.titleMedium] /// of [ThemeData.textTheme] is used. final TextStyle? textStyle; @@ -352,7 +352,7 @@ class PopupMenuItemState> extends State { final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context); TextStyle style = widget.textStyle ?? popupMenuTheme.textStyle ?? - theme.textTheme.subtitle1!; + theme.textTheme.titleMedium!; if (!widget.enabled) style = style.copyWith(color: theme.disabledColor); diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index da1a82835b7..7d8f7c78cb7 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -65,7 +65,7 @@ class MyPopupMenuItemState> final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context); TextStyle style = widget.textStyle ?? popupMenuTheme.textStyle ?? - theme.textTheme.subtitle1!; + theme.textTheme.titleMedium!; return Obx(() => mod_menu.PopupMenuButton( enabled: enabled.value, position: widget.position, diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index a6b51b99e30..3d89cd85f09 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -930,7 +930,7 @@ void showOptions( border: Border.all(color: Theme.of(context).hintColor), borderRadius: BorderRadius.circular(2), color: i == cur - ? Theme.of(context).toggleableActiveColor.withOpacity(0.6) + ? Theme.of(context).primaryColor.withOpacity(0.6) : null), child: Center( child: Text((i + 1).toString(), diff --git a/flutter/macos/Podfile.lock b/flutter/macos/Podfile.lock index a9f3c7388cf..a29674fece3 100644 --- a/flutter/macos/Podfile.lock +++ b/flutter/macos/Podfile.lock @@ -95,17 +95,17 @@ SPEC CHECKSUMS: desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 desktop_multi_window: 566489c048b501134f9d7fb6a2354c60a9126486 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f - file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 + file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 flutter_custom_cursor: 629957115075c672287bd0fa979d863ccf6024f7 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec texture_rgba_renderer: cbed959a3c127122194a364e14b8577bd62dc8f2 uni_links_desktop: 45900fb319df48fcdea2df0756e9c2626696b026 - url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 - video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 + video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 20ef15d3f8f..d43f0843fe4 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -114,6 +114,9 @@ dev_dependencies: flutter_lints: ^2.0.2 ffigen: ^8.0.2 +dependency_overrides: + intl: ^0.19.0 + # rerun: flutter pub run flutter_launcher_icons flutter_icons: image_path: "../res/icon.png" From 3244395bfb37725eba6859302a17a2b4bf36dffb Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 12:34:00 +0800 Subject: [PATCH 097/335] try 3.22.2 playground --- .github/workflows/playground.yml | 2 +- flutter/pubspec.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 3d23656118c..b055e20623b 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.13.9" + FLUTTER_VERSION: "3.22.2" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 6851fca377d..9c37d4c399f 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -790,13 +790,13 @@ packages: source: hosted version: "0.2.1+1" intl: - dependency: transitive + dependency: "direct overridden" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -857,10 +857,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -1613,5 +1613,5 @@ packages: source: hosted version: "0.2.1" sdks: - dart: ">=3.2.0 <4.0.0" + dart: ">=3.3.0-0 <4.0.0" flutter: ">=3.16.0" From 32c4712d5e2d91f848142644941593edc5aea67b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 12:42:27 +0800 Subject: [PATCH 098/335] fix ci --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index b055e20623b..56be43ccfae 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,7 +43,7 @@ jobs: target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: 30bd4e1cef96c492d0cf541a59fb976e24dbbb54, # latest + ref: 3244395bfb37725eba6859302a17a2b4bf36dffb, # latest } steps: - name: Checkout source code From 74cc5abd09f56ac6d8294a4e3c8ff35fb6883a8e Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:43:54 +0800 Subject: [PATCH 099/335] fix: android prompt "Failed to stop voice call" on conn ended (#8434) * fix: android prompt "Failed to stop voice call" on conn ended Signed-off-by: fufesou * Remove invalid comment Signed-off-by: fufesou * Better control of voice call status Signed-off-by: fufesou * Better voice call status control Signed-off-by: fufesou * better end conn for voice call Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .../com/carriez/flutter_hbb/AudioRecordHandle.kt | 14 +++++--------- flutter/lib/mobile/pages/remote_page.dart | 9 ++++----- flutter/lib/models/chat_model.dart | 2 ++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt index 122a5a92b9e..bb8cbffb2fe 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt @@ -119,21 +119,17 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart: if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { return false } - if (isVideoStart() || isAudioStart()) { - if (!switchToVoiceCall(mediaProjection)) { - return false - } - } else { - if (!switchToVoiceCall(mediaProjection)) { - return false - } + // No need to check if video or audio is started here. + if (!switchToVoiceCall(mediaProjection)) { + return false } return true } fun onVoiceCallClosed(mediaProjection: MediaProjection?): Boolean { + // Return true if `Build.VERSION.SDK_INT < Build.VERSION_CODES.R`, because is was not started. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { - return false + return true } if (isVideoStart()) { switchOutVoiceCall(mediaProjection) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 3d89cd85f09..d74dc48399f 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -105,11 +105,10 @@ class _RemotePageState extends State { } await keyboardSubscription.cancel(); removeSharedStates(widget.id); - if (isAndroid) { - // Only one client is considered here for now. - // TODO: take into account the case where there are multiple clients - gFFI.invokeMethod("on_voice_call_closed"); - } + // `on_voice_call_closed` should be called when the connection is ended. + // The inner logic of `on_voice_call_closed` will check if the voice call is active. + // Only one client is considered here for now. + gFFI.chatModel.onVoiceCallClosed("End connetion"); } // to-do: It should be better to use transparent color instead of the bgColor. diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index 2891241045c..778c9435704 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -535,6 +535,8 @@ class ChatModel with ChangeNotifier { void onVoiceCallClosed(String reason) { _voiceCallStatus.value = VoiceCallStatus.notStarted; if (isAndroid) { + // We can always invoke "on_voice_call_closed" + // no matter if the `_voiceCallStatus` was `VoiceCallStatus.notStarted` or not. parent.target?.invokeMethod("on_voice_call_closed"); } } From b2af79a3c5cf34f2651096192ebc8a948cca8e2f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 17:05:51 +0800 Subject: [PATCH 100/335] try out 3.16.0 --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 56be43ccfae..c501851e6ed 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.22.2" + FLUTTER_VERSION: "3.16.0" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" From cdf97f8717db7d9c660494e60d4e55cd090832d2 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 17:42:58 +0800 Subject: [PATCH 101/335] try out 3.13.9 with master --- .github/workflows/playground.yml | 5 +++-- flutter/macos/Podfile.lock | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index c501851e6ed..99bac2bc022 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.16.0" + FLUTTER_VERSION: "3.13.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" @@ -43,7 +43,7 @@ jobs: target: aarch64-linux-android, os: ubuntu-20.04, openssl-arch: android-arm64, - ref: 3244395bfb37725eba6859302a17a2b4bf36dffb, # latest + ref: master, # latest } steps: - name: Checkout source code @@ -104,6 +104,7 @@ jobs: git config --global core.longpaths true cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml + pushd flutter/lib; find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd; 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 diff --git a/flutter/macos/Podfile.lock b/flutter/macos/Podfile.lock index a29674fece3..a9f3c7388cf 100644 --- a/flutter/macos/Podfile.lock +++ b/flutter/macos/Podfile.lock @@ -95,17 +95,17 @@ SPEC CHECKSUMS: desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 desktop_multi_window: 566489c048b501134f9d7fb6a2354c60a9126486 device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f - file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 + file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 flutter_custom_cursor: 629957115075c672287bd0fa979d863ccf6024f7 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec texture_rgba_renderer: cbed959a3c127122194a364e14b8577bd62dc8f2 uni_links_desktop: 45900fb319df48fcdea2df0756e9c2626696b026 - url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 - video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 + url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 + video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 From ff2e055a5aff3c975aa39d35bbb879e001b59f93 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 21 Jun 2024 18:14:58 +0800 Subject: [PATCH 102/335] use flutter 3.13 for android because its video super slow on my phone --- .github/workflows/flutter-build.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index deb51f5389b..c8865233f47 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -16,6 +16,7 @@ env: CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" FLUTTER_VERSION: "3.19.6" + ANDROID_FLUTTER_VERSION: "3.13.9" # >= 3.16 is very slow on my android phone, but work well on most of others. We may switch to new flutter after changing to texture rendering (I believe it can solve my problem). FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" @@ -811,7 +812,7 @@ jobs: uses: subosito/flutter-action@v2 with: channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} + flutter-version: ${{ env.ANDROID_FLUTTER_VERSION }} - uses: nttld/setup-ndk@v1 id: setup-ndk with: @@ -853,6 +854,13 @@ jobs: prefix-key: rustdesk-lib-cache-android # TODO: drop '-android' part after caches are invalidated key: ${{ matrix.job.target }} + - name: fix android for flutter 3.13 + if: $${{ env.ANDROID_FLUTTER_VERSION == '3.13.9' }} + run: | + sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml + cd flutter/lib + find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' + - name: Build rustdesk lib env: ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} From 0f6538c1a766a434f52d06d5186625b6a165bbe5 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 21 Jun 2024 18:54:32 +0800 Subject: [PATCH 103/335] add enable directx option, android software encoding half resolution option (#8435) * add option enable directx capture screen, default true Signed-off-by: 21pages * option android software encoding half scale, check isStart flag Signed-off-by: 21pages --------- Signed-off-by: 21pages --- .../com/carriez/flutter_hbb/MainService.kt | 15 +++--- flutter/lib/consts.dart | 1 + .../desktop/pages/desktop_setting_page.dart | 8 ++- libs/hbb_common/src/config.rs | 5 ++ libs/scrap/src/common/android.rs | 5 ++ libs/scrap/src/common/codec.rs | 50 ++++++++++++------- libs/scrap/src/common/mod.rs | 5 ++ libs/scrap/src/common/vram.rs | 2 +- src/lang/ar.rs | 1 + src/lang/bg.rs | 1 + src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/et.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/he.rs | 1 + src/lang/hr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/lv.rs | 1 + src/lang/nb.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 3 +- src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + src/server/video_service.rs | 29 +++++++++-- 50 files changed, 133 insertions(+), 30 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 57b97b7c24f..1a709747e25 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -103,6 +103,9 @@ class MainService : Service() { put("scale",SCREEN_INFO.scale) }.toString() } + "is_start" -> { + isStart.toString() + } else -> "" } } @@ -172,10 +175,10 @@ class MainService : Service() { Log.d(logTag, "from rust:stop_capture") stopCapture() } - "is_hardware_codec" -> { - val isHwCodec = arg1.toBoolean() - if (isHardwareCodec != isHwCodec) { - isHardwareCodec = isHwCodec + "half_scale" -> { + val halfScale = arg1.toBoolean() + if (isHalfScale != halfScale) { + isHalfScale = halfScale updateScreenInfo(resources.configuration.orientation) } @@ -251,7 +254,7 @@ class MainService : Service() { super.onDestroy() } - private var isHardwareCodec: Boolean? = null; + private var isHalfScale: Boolean? = null; private fun updateScreenInfo(orientation: Int) { var w: Int var h: Int @@ -284,7 +287,7 @@ class MainService : Service() { Log.d(logTag,"updateScreenInfo:w:$w,h:$h") var scale = 1 if (w != 0 && h != 0) { - if (isHardwareCodec == false && (w > MAX_SCREEN_SIZE || h > MAX_SCREEN_SIZE)) { + if (isHalfScale == true && (w > MAX_SCREEN_SIZE || h > MAX_SCREEN_SIZE)) { scale = 2 w /= scale h /= scale diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 9cbfd265ed8..4568a3f02e5 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -134,6 +134,7 @@ const String kOptionEnableCheckUpdate = "enable-check-update"; const String kOptionAllowLinuxHeadless = "allow-linux-headless"; const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper"; const String kOptionStopService = "stop-service"; +const String kOptionDirectxCapture = "enable-directx-capture"; const String kOptionToggleViewOnly = "view-only"; diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 04385ce5075..81717de3b10 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -315,11 +315,11 @@ class _GeneralState extends State<_General> { children: [ service(), theme(), + _Card(title: 'Language', children: [language()]), hwcodec(), audio(context), record(context), WaylandCard(), - _Card(title: 'Language', children: [language()]), other() ], ).marginOnly(bottom: _kListViewBottomMargin)); @@ -413,6 +413,12 @@ class _GeneralState extends State<_General> { 'Check for software update on startup', kOptionEnableCheckUpdate, isServer: false, + ), + if (isWindows && !bind.isOutgoingOnly()) + _OptionCheckBox( + context, + 'Capture screen using DirectX', + kOptionDirectxCapture, ) ], ]; diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 3c37bd8e93a..5846563bb67 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2084,6 +2084,9 @@ pub mod keys { pub const OPTION_KEY: &str = "key"; pub const OPTION_PRESET_ADDRESS_BOOK_NAME: &str = "preset-address-book-name"; pub const OPTION_PRESET_ADDRESS_BOOK_TAG: &str = "preset-address-book-tag"; + pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture"; + pub const OPTION_ENABLE_ANDRIOD_SOFTWARE_ENCODING_HALF_SCALE: &str = + "enable-andriod-software-encoding-half-scale"; // flutter local options pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState"; @@ -2206,6 +2209,8 @@ pub mod keys { OPTION_KEY, OPTION_PRESET_ADDRESS_BOOK_NAME, OPTION_PRESET_ADDRESS_BOOK_TAG, + OPTION_ENABLE_DIRECTX_CAPTURE, + OPTION_ENABLE_ANDRIOD_SOFTWARE_ENCODING_HALF_SCALE, ]; } diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index dffbc9ff7b1..49956fcce61 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -182,3 +182,8 @@ fn get_size() -> Option<(u16, u16, u16)> { } None } + +pub fn is_start() -> Option { + let res = call_main_service_get_by_name("is_start").ok()?; + Some(res == "true") +} diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index a095cdd6cca..9fbcae2cdec 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -21,8 +21,8 @@ use crate::{ use hbb_common::{ anyhow::anyhow, bail, - config::PeerConfig, - log, + config::{keys::OPTION_ENABLE_HWCODEC, option2bool, Config, PeerConfig}, + lazy_static, log, message_proto::{ supported_decoding::PreferCodec, video_frame, Chroma, CodecAbility, EncodedVideoFrames, SupportedDecoding, SupportedEncoding, VideoFrame, @@ -31,8 +31,6 @@ use hbb_common::{ tokio::time::Instant, ResultType, }; -#[cfg(any(feature = "hwcodec", feature = "mediacodec", feature = "vram"))] -use hbb_common::{config::Config2, lazy_static}; lazy_static::lazy_static! { static ref PEER_DECODINGS: Arc>> = Default::default(); @@ -201,7 +199,7 @@ impl Encoder { #[allow(unused_mut)] let mut h265vram_encoding = false; #[cfg(feature = "vram")] - if enable_vram_option() { + if enable_vram_option(true) { if _all_support_h264_decoding { if VRamEncoder::available(CodecFormat::H264).len() > 0 { h264vram_encoding = true; @@ -340,7 +338,7 @@ impl Encoder { encoding.h265 |= HwRamEncoder::try_get(CodecFormat::H265).is_some(); } #[cfg(feature = "vram")] - if enable_vram_option() { + if enable_vram_option(true) { encoding.h264 |= VRamEncoder::available(CodecFormat::H264).len() > 0; encoding.h265 |= VRamEncoder::available(CodecFormat::H265).len() > 0; } @@ -451,7 +449,7 @@ impl Decoder { }; } #[cfg(feature = "vram")] - if enable_vram_option() && _use_texture_render { + if enable_vram_option(false) && _use_texture_render { decoding.ability_h264 |= if VRamDecoder::available(CodecFormat::H264, _luid).len() > 0 { 1 } else { @@ -530,7 +528,7 @@ impl Decoder { } CodecFormat::H264 => { #[cfg(feature = "vram")] - if !valid && enable_vram_option() && _luid.clone().unwrap_or_default() != 0 { + if !valid && enable_vram_option(false) && _luid.clone().unwrap_or_default() != 0 { match VRamDecoder::new(format, _luid) { Ok(v) => h264_vram = Some(v), Err(e) => log::error!("create H264 vram decoder failed: {}", e), @@ -556,7 +554,7 @@ impl Decoder { } CodecFormat::H265 => { #[cfg(feature = "vram")] - if !valid && enable_vram_option() && _luid.clone().unwrap_or_default() != 0 { + if !valid && enable_vram_option(false) && _luid.clone().unwrap_or_default() != 0 { match VRamDecoder::new(format, _luid) { Ok(v) => h265_vram = Some(v), Err(e) => log::error!("create H265 vram decoder failed: {}", e), @@ -839,19 +837,37 @@ impl Decoder { #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] pub fn enable_hwcodec_option() -> bool { if cfg!(windows) || cfg!(target_os = "linux") || cfg!(target_os = "android") { - if let Some(v) = Config2::get().options.get("enable-hwcodec") { - return v != "N"; - } - return true; // default is true + return option2bool( + OPTION_ENABLE_HWCODEC, + &Config::get_option(OPTION_ENABLE_HWCODEC), + ); } false } #[cfg(feature = "vram")] -pub fn enable_vram_option() -> bool { - if let Some(v) = Config2::get().options.get("enable-hwcodec") { - return v != "N"; +pub fn enable_vram_option(encode: bool) -> bool { + if cfg!(windows) { + let enable = option2bool( + OPTION_ENABLE_HWCODEC, + &Config::get_option(OPTION_ENABLE_HWCODEC), + ); + if encode { + enable && enable_directx_capture() + } else { + enable + } + } else { + false } - return true; // default is true +} + +#[cfg(windows)] +pub fn enable_directx_capture() -> bool { + use hbb_common::config::keys::OPTION_ENABLE_DIRECTX_CAPTURE as OPTION; + option2bool( + OPTION, + &Config::get_option(hbb_common::config::keys::OPTION_ENABLE_DIRECTX_CAPTURE), + ) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 13129db55a5..085d1e501fd 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -491,3 +491,8 @@ pub trait GoogleImage { pub fn screen_size() -> (u16, u16, u16) { SCREEN_SIZE.lock().unwrap().clone() } + +#[cfg(target_os = "android")] +pub fn is_start() -> Option { + android::is_start() +} diff --git a/libs/scrap/src/common/vram.rs b/libs/scrap/src/common/vram.rs index 241908b74af..a2b4d348c46 100644 --- a/libs/scrap/src/common/vram.rs +++ b/libs/scrap/src/common/vram.rs @@ -354,7 +354,7 @@ impl VRamDecoder { } pub fn possible_available_without_check() -> (bool, bool) { - if !enable_vram_option() { + if !enable_vram_option(false) { return (false, false); } let v = crate::hwcodec::HwCodecConfig::get().vram_decode; diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 03483141017..ab5e6a62915 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 42da650783b..b72b2454551 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 0e86032d71e..1a59ae9c736 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index ad4026dc350..553a4b27a9d 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "从不"), ("During controlled", "被控期间"), ("During service is on", "服务开启期间"), + ("Capture screen using DirectX", "使用 DirectX 捕获屏幕"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 1fbc0bf8cf9..1ff602be7b2 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nikdy"), ("During controlled", "Během řízeného"), ("During service is on", "Během služby je v provozu"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 42f9c751c6e..3e4a673bddc 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index c950648baf2..dab7a9cc3f4 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Niemals"), ("During controlled", "Wenn kontrolliert"), ("During service is on", "Wenn der Dienst läuft"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 35b2e7c351b..69fa86276c3 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 49ce149bc2f..1afae087014 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index c9ceba5d042..8fc97a9e9e4 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nunca"), ("During controlled", "Mientras está siendo controlado"), ("During service is on", "Mientras el servicio está activo"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index e96eaa2760b..f1d04fe2748 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 923aca2e235..189fd4e3442 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 54894c667a9..0d8525dbf78 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 6ef587819b1..1b415de90ea 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 34ade848903..e919325d40a 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 9ddeb3f9fef..7bb267a8652 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 5dbd233eccf..e77ad450407 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index d1499b8f77d..7e33b6fbe9b 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Mai"), ("During controlled", "Durante il controllo"), ("During service is on", "Quando il servizio è attivo"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 0ee19694103..c0bbf703891 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 756a5a04cf8..c101d151384 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 3d4793d67ed..8d5a214dde0 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 7fb256f3364..ab2dde495bf 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index b1db124c268..1b00e7de2ec 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nekad"), ("During controlled", "Lietošanas laikā"), ("During service is on", "Kamēr pakalpojums ir ieslēgts"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 098fa82bac3..edebfc7af88 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 8ba24d86250..85d1d63a599 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 0b9e1ba6e0c..0f4707ddb94 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index b77af7dc16b..e5b53e55933 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 1e4ad748e38..072426ff269 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index e81f5ed32cb..acdd4a987be 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 9364ebbfa57..364946a3953 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -125,7 +125,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Good image quality", "Лучшее качество изображения"), ("Balanced", "Баланс между качеством и откликом"), ("Optimize reaction time", "Лучшее время отклика"), - ("Custom", "Заданное пользователем"), + ("Custom", "Заданное пользователем"), ("Show remote cursor", "Показывать удалённый курсор"), ("Show quality monitor", "Показывать монитор качества"), ("Disable clipboard", "Отключить буфер обмена"), @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Нет"), ("During controlled", "При управлении"), ("During service is on", "При запущенной службе"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 3f085251e00..542373de236 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nikdy"), ("During controlled", "Počas kontrolovaného"), ("During service is on", "Počas služby je v prevádzke"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 97554bb9759..dcf5192d9b0 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 94d560c9e19..d7abb5b4a61 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 550f73e5c02..8c3cea8b69b 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 1ebac149bba..2aceeba2861 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index d15af13d8d2..3166f919490 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index b2dfe9a25de..2c4e32aaafb 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 103c5c83363..47a7a133a99 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 459e45c8293..7833d574750 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "從不"), ("During controlled", "被控期間"), ("During service is on", "服務開啟期間"), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 8412a69594d..fee60a93a64 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 46ee58c67c6..acefe2fa561 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -621,5 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", ""), ("During controlled", ""), ("During service is on", ""), + ("Capture screen using DirectX", ""), ].iter().cloned().collect(); } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 7cdaa521fac..eb9ec8689e4 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -407,7 +407,11 @@ fn run(vs: VideoService) -> ResultType<()> { let display_idx = vs.idx; let sp = vs.sp; let mut c = get_capturer(display_idx, last_portable_service_running)?; - + #[cfg(windows)] + if !scrap::codec::enable_directx_capture() && !c.is_gdi() { + log::info!("disable dxgi with option, fall back to gdi"); + c.set_gdi(); + } let mut video_qos = VIDEO_QOS.lock().unwrap(); video_qos.refresh(None); let mut spf; @@ -837,16 +841,33 @@ fn get_recorder( #[cfg(target_os = "android")] fn check_change_scale(hardware: bool) -> ResultType<()> { + use hbb_common::config::keys::OPTION_ENABLE_ANDRIOD_SOFTWARE_ENCODING_HALF_SCALE as SCALE_SOFT; + + // isStart flag is set at the end of startCapture() in Android, wait it to be set. + for i in 0..6 { + if scrap::is_start() == Some(true) { + log::info!("start flag is set"); + break; + } + log::info!("wait for start, {i}"); + std::thread::sleep(Duration::from_millis(50)); + if i == 5 { + log::error!("wait for start timeout"); + } + } let screen_size = scrap::screen_size(); - log::info!("hardware: {hardware}, screen_size: {screen_size:?}",); + let scale_soft = hbb_common::config::option2bool(SCALE_SOFT, &Config::get_option(SCALE_SOFT)); + let half_scale = !hardware && scale_soft; + log::info!("hardware: {hardware}, scale_soft: {scale_soft}, screen_size: {screen_size:?}",); scrap::android::call_main_service_set_by_name( - "is_hardware_codec", - Some(hardware.to_string().as_str()), + "half_scale", + Some(half_scale.to_string().as_str()), None, ) .ok(); let old_scale = screen_size.2; let new_scale = scrap::screen_size().2; + log::info!("old_scale: {old_scale}, new_scale: {new_scale}"); if old_scale != new_scale { log::info!("switch due to scale changed, {old_scale} -> {new_scale}"); // switch is not a must, but it is better to do so. From ffed29e6326e306168302c68a6be32189789da0c Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 21 Jun 2024 23:58:00 +0800 Subject: [PATCH 104/335] fix typo (#8436) Signed-off-by: 21pages --- libs/hbb_common/src/config.rs | 6 +++--- src/server/video_service.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 5846563bb67..e1649ddd7bf 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2085,8 +2085,8 @@ pub mod keys { pub const OPTION_PRESET_ADDRESS_BOOK_NAME: &str = "preset-address-book-name"; pub const OPTION_PRESET_ADDRESS_BOOK_TAG: &str = "preset-address-book-tag"; pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture"; - pub const OPTION_ENABLE_ANDRIOD_SOFTWARE_ENCODING_HALF_SCALE: &str = - "enable-andriod-software-encoding-half-scale"; + pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str = + "enable-android-software-encoding-half-scale"; // flutter local options pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState"; @@ -2210,7 +2210,7 @@ pub mod keys { OPTION_PRESET_ADDRESS_BOOK_NAME, OPTION_PRESET_ADDRESS_BOOK_TAG, OPTION_ENABLE_DIRECTX_CAPTURE, - OPTION_ENABLE_ANDRIOD_SOFTWARE_ENCODING_HALF_SCALE, + OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE, ]; } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index eb9ec8689e4..7fd5dee1f82 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -841,7 +841,7 @@ fn get_recorder( #[cfg(target_os = "android")] fn check_change_scale(hardware: bool) -> ResultType<()> { - use hbb_common::config::keys::OPTION_ENABLE_ANDRIOD_SOFTWARE_ENCODING_HALF_SCALE as SCALE_SOFT; + use hbb_common::config::keys::OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE as SCALE_SOFT; // isStart flag is set at the end of startCapture() in Android, wait it to be set. for i in 0..6 { From bbf7d9e08a23107987347ca9bffdb2d9f40743c3 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sat, 22 Jun 2024 08:10:54 +0800 Subject: [PATCH 105/335] fix: android, no voice call under android 11 (#8440) Signed-off-by: fufesou --- .../carriez/flutter_hbb/AudioRecordHandle.kt | 9 +++---- .../com/carriez/flutter_hbb/MainActivity.kt | 11 ++++++++ .../kotlin/com/carriez/flutter_hbb/common.kt | 8 ++++++ flutter/lib/mobile/pages/remote_page.dart | 26 +++++++++++-------- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt index bb8cbffb2fe..db222dc8474 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/AudioRecordHandle.kt @@ -116,7 +116,7 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart: } fun onVoiceCallStarted(mediaProjection: MediaProjection?): Boolean { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + if (!isSupportVoiceCall()) { return false } // No need to check if video or audio is started here. @@ -127,8 +127,8 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart: } fun onVoiceCallClosed(mediaProjection: MediaProjection?): Boolean { - // Return true if `Build.VERSION.SDK_INT < Build.VERSION_CODES.R`, because is was not started. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + // Return true if not supported, because is was not started. + if (!isSupportVoiceCall()) { return true } if (isVideoStart()) { @@ -176,9 +176,6 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart: } fun tryReleaseAudio() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { - return - } if (isAudioStart() || isVideoStart()) { return } diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt index b7050d8a481..10c3d7c2d78 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -233,6 +233,17 @@ class MainActivity : FlutterActivity() { result.success(false) } } + GET_VALUE -> { + if (call.arguments is String) { + if (call.arguments == KEY_IS_SUPPORT_VOICE_CALL) { + result.success(isSupportVoiceCall()) + } else { + result.error("-1", "No such key", null) + } + } else { + result.success(null) + } + } "on_voice_call_started" -> { onVoiceCallStarted() } diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt index 3b41a73a1a0..514d493b9c0 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt @@ -47,6 +47,9 @@ const val START_ACTION = "start_action" const val GET_START_ON_BOOT_OPT = "get_start_on_boot_opt" const val SET_START_ON_BOOT_OPT = "set_start_on_boot_opt" const val SYNC_APP_DIR_CONFIG_PATH = "sync_app_dir" +const val GET_VALUE = "get_value" + +const val KEY_IS_SUPPORT_VOICE_CALL = "KEY_IS_SUPPORT_VOICE_CALL" const val KEY_SHARED_PREFERENCES = "KEY_SHARED_PREFERENCES" const val KEY_START_ON_BOOT_OPT = "KEY_START_ON_BOOT_OPT" @@ -60,6 +63,11 @@ data class Info( var width: Int, var height: Int, var scale: Int, var dpi: Int ) +fun isSupportVoiceCall(): Boolean { + // https://developer.android.com/reference/android/media/MediaRecorder.AudioSource#VOICE_COMMUNICATION + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R +} + fun requestPermission(context: Context, type: String) { XXPermissions.with(context) .permission(type) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index d74dc48399f..4d021609a6e 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -418,17 +418,21 @@ class _RemotePageState extends State { (isWeb ? [] : [ - IconButton( - color: Colors.white, - icon: isAndroid - ? SvgPicture.asset('assets/chat.svg', - colorFilter: ColorFilter.mode( - Colors.white, BlendMode.srcIn)) - : Icon(Icons.message), - onPressed: () => isAndroid - ? showChatOptions(widget.id) - : onPressedTextChat(widget.id), - ) + futureBuilder( + future: gFFI.invokeMethod( + "get_value", "KEY_IS_SUPPORT_VOICE_CALL"), + hasData: (isSupportVoiceCall) => IconButton( + color: Colors.white, + icon: isAndroid && isSupportVoiceCall + ? SvgPicture.asset('assets/chat.svg', + colorFilter: ColorFilter.mode( + Colors.white, BlendMode.srcIn)) + : Icon(Icons.message), + onPressed: () => + isAndroid && isSupportVoiceCall + ? showChatOptions(widget.id) + : onPressedTextChat(widget.id), + )) ]) + [ IconButton( From 1a21dff5d4d4865c1b70cf40c22aa250682ad6af Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Sat, 22 Jun 2024 02:11:09 +0200 Subject: [PATCH 106/335] Update Italian language (#8439) --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 7e33b6fbe9b..27d8f19a049 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -621,6 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Mai"), ("During controlled", "Durante il controllo"), ("During service is on", "Quando il servizio è attivo"), - ("Capture screen using DirectX", ""), + ("Capture screen using DirectX", "Cattura schermo usando DirectX"), ].iter().cloned().collect(); } From 3742b51d58ae28c6b05a097912bf5a05811a69e5 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 22 Jun 2024 09:39:02 +0800 Subject: [PATCH 107/335] quality monitor, delay displays as 0 when fps is 0 (#8441) Signed-off-by: 21pages --- flutter/lib/common/widgets/overlay.dart | 4 +++- src/ui/remote.tis | 12 ++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index 886e9b117f5..b16b4d9ada4 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -498,8 +498,10 @@ class QualityMonitor extends StatelessWidget { children: [ _row("Speed", qualityMonitorModel.data.speed ?? '-'), _row("FPS", qualityMonitorModel.data.fps ?? '-'), + // let delay be 0 if fps is 0 _row( - "Delay", "${qualityMonitorModel.data.delay ?? '-'}ms", + "Delay", + "${qualityMonitorModel.data.delay == null ? '-' : (qualityMonitorModel.data.fps ?? "").replaceAll(' ', '').replaceAll('0', '').isEmpty ? 0 : qualityMonitorModel.data.delay}ms", rightColor: Colors.green), _row("Target Bitrate", "${qualityMonitorModel.data.targetBitrate ?? '-'}kb"), diff --git a/src/ui/remote.tis b/src/ui/remote.tis index 8f106e78eec..00dceac5bde 100644 --- a/src/ui/remote.tis +++ b/src/ui/remote.tis @@ -519,12 +519,12 @@ class QualityMonitor: Reactor.Component $(#quality-monitor).content(); handler.updateQualityStatus = function(speed, fps, delay, bitrate, codec_format, chroma) { - speed ? qualityMonitorData[0] = speed:null; - fps ? qualityMonitorData[1] = fps:null; - delay ? qualityMonitorData[2] = delay:null; - bitrate ? qualityMonitorData[3] = bitrate:null; - codec_format ? qualityMonitorData[4] = codec_format:null; - chroma ? qualityMonitorData[5] = chroma:null; + if (speed !== null) qualityMonitorData[0] = speed; + if (fps !== null) qualityMonitorData[1] = fps; + if (delay !== null) qualityMonitorData[2] = qualityMonitorData[1] === 0 ? 0 : delay; + if (bitrate !== null) qualityMonitorData[3] = bitrate; + if (codec_format !== null) qualityMonitorData[4] = codec_format; + if (chroma !== null) qualityMonitorData[5] = chroma; qualityMonitor.update(); } From 41a20b50ea04aab0e1c7cbe9f9fab3c0c5888d4b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 22 Jun 2024 12:29:20 +0800 Subject: [PATCH 108/335] split web js to v1 and v2 --- .github/workflows/flutter-build.yml | 1 + flutter/web/README.md | 17 - flutter/web/favicon.svg | 1 - flutter/web/icons/Icon-192.png | Bin 6339 -> 0 bytes flutter/web/icons/Icon-512.png | Bin 17282 -> 0 bytes flutter/web/icons/Icon-maskable-192.png | Bin 6339 -> 0 bytes flutter/web/icons/Icon-maskable-512.png | Bin 17282 -> 0 bytes flutter/web/js/src/consts.ts | 19 - flutter/web/js/src/globals.js | 792 ------------------ flutter/web/v1/README.md | 1 + flutter/web/{ => v1}/index.html | 0 flutter/web/{ => v1}/js/.gitattributes | 0 flutter/web/{ => v1}/js/.gitignore | 0 flutter/web/{ => v1}/js/.yarnrc.yml | 0 flutter/web/{ => v1}/js/gen_js_from_hbb.py | 0 flutter/web/{ => v1}/js/index.html | 0 flutter/web/{ => v1}/js/package.json | 6 +- flutter/web/{ => v1}/js/src/codec.js | 2 +- flutter/web/{ => v1}/js/src/common.ts | 0 flutter/web/{ => v1}/js/src/connection.ts | 368 +------- flutter/web/v1/js/src/globals.js | 383 +++++++++ flutter/web/{ => v1}/js/src/main.ts | 0 flutter/web/{ => v1}/js/src/style.css | 0 flutter/web/{ => v1}/js/src/ui.js | 0 flutter/web/{ => v1}/js/src/vite-env.d.ts | 0 flutter/web/{ => v1}/js/src/websock.ts | 0 flutter/web/{ => v1}/js/ts_proto.py | 0 flutter/web/{ => v1}/js/tsconfig.json | 0 flutter/web/{ => v1}/js/vite.config.js | 0 flutter/web/{ => v1}/js/yarn.lock | 0 .../web/{ => v1}/libs/firebase-analytics.js | 0 flutter/web/{ => v1}/libs/firebase-app.js | 0 flutter/web/{ => v1}/manifest.json | 0 flutter/web/{ => v1}/yarn.lock | 0 flutter/web/{ => v1}/yuv.js | 0 flutter/web/{ => v1}/yuv.wasm | Bin flutter/web/v2/README.md | 1 + 37 files changed, 435 insertions(+), 1156 deletions(-) delete mode 100644 flutter/web/README.md delete mode 100644 flutter/web/favicon.svg delete mode 100644 flutter/web/icons/Icon-192.png delete mode 100644 flutter/web/icons/Icon-512.png delete mode 100644 flutter/web/icons/Icon-maskable-192.png delete mode 100644 flutter/web/icons/Icon-maskable-512.png delete mode 100644 flutter/web/js/src/consts.ts delete mode 100644 flutter/web/js/src/globals.js create mode 100644 flutter/web/v1/README.md rename flutter/web/{ => v1}/index.html (100%) rename flutter/web/{ => v1}/js/.gitattributes (100%) rename flutter/web/{ => v1}/js/.gitignore (100%) rename flutter/web/{ => v1}/js/.yarnrc.yml (100%) rename flutter/web/{ => v1}/js/gen_js_from_hbb.py (100%) rename flutter/web/{ => v1}/js/index.html (100%) rename flutter/web/{ => v1}/js/package.json (70%) rename flutter/web/{ => v1}/js/src/codec.js (95%) rename flutter/web/{ => v1}/js/src/common.ts (100%) rename flutter/web/{ => v1}/js/src/connection.ts (64%) create mode 100644 flutter/web/v1/js/src/globals.js rename flutter/web/{ => v1}/js/src/main.ts (100%) rename flutter/web/{ => v1}/js/src/style.css (100%) rename flutter/web/{ => v1}/js/src/ui.js (100%) rename flutter/web/{ => v1}/js/src/vite-env.d.ts (100%) rename flutter/web/{ => v1}/js/src/websock.ts (100%) rename flutter/web/{ => v1}/js/ts_proto.py (100%) rename flutter/web/{ => v1}/js/tsconfig.json (100%) rename flutter/web/{ => v1}/js/vite.config.js (100%) rename flutter/web/{ => v1}/js/yarn.lock (100%) rename flutter/web/{ => v1}/libs/firebase-analytics.js (100%) rename flutter/web/{ => v1}/libs/firebase-app.js (100%) rename flutter/web/{ => v1}/manifest.json (100%) rename flutter/web/{ => v1}/yarn.lock (100%) rename flutter/web/{ => v1}/yuv.js (100%) rename flutter/web/{ => v1}/yuv.wasm (100%) create mode 100644 flutter/web/v2/README.md diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index c8865233f47..70312d3e332 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1573,6 +1573,7 @@ jobs: flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak build-rustdesk-web: + if: False name: build-rustdesk-web runs-on: ubuntu-20.04 strategy: diff --git a/flutter/web/README.md b/flutter/web/README.md deleted file mode 100644 index e605e72c01e..00000000000 --- a/flutter/web/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# RustDesk web - -## Functions - -### Current and planned - -- [x] Outgoing -- [ ] Address book -- [ ] Force relay - -### Unsupported - -1. Incoming -2. LAN -3. Gpu texture render. We use WebGL instead. - -### No plans diff --git a/flutter/web/favicon.svg b/flutter/web/favicon.svg deleted file mode 100644 index 0234ca69ece..00000000000 --- a/flutter/web/favicon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/flutter/web/icons/Icon-192.png b/flutter/web/icons/Icon-192.png deleted file mode 100644 index 2ff3bb45c06170ece6a60c3df422472c6d7b5a61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6339 zcmXY0dpy(M|KDaVV}_}$++!llT+4kL!VF2d--YBlWO7^TV_k^cqp)0}KFIwRO>QBq zLgY4g5sk!@%Wr+YzdzoW$9bIhd0o!y^*Ybz>wVwK;vz5iDQ*A&z-ww^Xw7<3|6W`i zth@E3KPLcycwlO1U_%71KVuJey7>I!%-+K_5~J8$o$nEh!LRaKt@&LnrN2-eQq z;zRw1OphKB)wCjk4!R>KCEjO>RoYFZW-FHDvG+8&g~|_kfd8u z(=dv`9QW8@93A@e0Mi24821N`1YkZG#bGEXL-N>&rr)5RmM{)MBNW_@GuYgM5$sGZ zEA}ue2WWxnvG~=1&Tg-KB=7_rFC08 z%bFBRwSy)EtFkqgj)9)?eJIgEBkF;+Cvc=!w|(T4pb5HE+BA1?>`C$1JF~&c z_=x*Y#{FY6lcQZp@)P_#xvqAC9*=W~UtQb4eyv#&4Y%q>{O)`?#~6QB4%MFSdQcCA z^b|TmcjJ|)s)()Ey1iT73U9}b&*vb^@9hoTV?XS?pk7sK-vIVweK$J#v+S9od4(m- zyJ^1?vFdWnO@f~+!Xf8=ad;^?YR;*ph*!DNC?Ga)pzE=l={F?*4Z5 z19F55M}i5t>V_yjN3Mq7##_T3AjsMoiF-R!u1erg%fYzl&-P#+4J3?5BXot z9VsyY9cV7#3T}=pacpRf7?G!57>*%Hs@p&f^@%qz_M9HZxjAqNL-oD4n{H!g|i^)_uwjFS8OcV6T?(Pz`h8eE!lI5251<4AGilkKk{@ zqom5X@uv;g-J4S5zMCl*oP9t%yULTIbi0=VLNX7kU{aXu$oj6Oq=#Apmc}D_t5-p3 zSoQ@PmDfk}aeWfN9c$Lh#;F3m^^$GVP|X#9G!P)XU!YC-co>t=k6$B?S~YGHbt0G< zfIWD&$k|NrjQ(&Sn2YCPi_`R8n7L?dT{OfTc3hp@7E0sd=gxdq*HrsbaMO=rSp0`ff=+GvBajm+*Y<7g2CUsHzW z_F_zc?>jEKY44CmG5=+4f#lLfQ>$tbm2>S$bpl!eLtF)Hkp1u*r~5bd4blbh6wU8IkK;n4H}e`ha&Mtc>KI zDjRBKi3*l{<@2&)_RVlDzOY-kyR{^BOs5kh#8x zq}Zb}cG=s#lHp9@?Q6*bZkDh5_bz)dK~HPIGUOAx7V-UG48vl#gn`db_OAIz^9y?V znZ*MFS)Oka+S>RW!At@?ILD z%5|jlPudLE>)-!kk02q2fqU@uaG0}7<+F&&@AOx7%7@Ko+7HN=ZJzZp%(0oPG7pm! zmHr*^aIWe4WRhv`qiU{`H#f=#X?v^NedgRvbeR1OQ^HC@_`vsv0v8T&m zahH%Q!>L%?!5hoD>LDc}{lTTLh_2or(?je(WiRp#^+bAxJ649SB-AOozZa9DDt+K*xOGsoT9LpYUDloDwx`8&Dv=T9!RIi}H{Xgy4n|JYk~x8q(1#HFFR zZbIbNm+q@Dj>&yq`>Y@0{>$%pe?<-bjBONzBpWh`Bk#MWjy~f?V76g z`_`Mq>hu3>Mc)5aZm(C>q-z=9-k*CiM&Y&~!$3PNgowu%P1sgMUk~D`NRPQldOPAJ z;3??W2Br*94lRxL21cbt?UM;~%!iUy|D1~!FUDvk%b8kfHyf);HlNODhy5uVY?GvZ zgeCKwk&XeTgDD>Y96_HeBGQ*+Ix zWv*Mlw#u6n%leK<%$o$pDinj6XCQD&FQq?!)oTgDgPr}JGy$EWjjeq_x8bKi8E2?c zQKFp(tBmEp<#Sp^zx#gnLrSnkkw}H}7vdwnuMtY^f=T!L;21Na&lLw&N#V~C`OTOk z@}>0z>wIJM{lXt&)s0fM6^w@f1kY^04BOS_YF|mdRt| z*F(bVSgBf^Mz3jc>~kcwmW$2hZS&w3aRqYZX}uni!#slQM)1h0%n>CAj-v%Ri(b5Z zbB{ZC&D#;e4~KB*pP0A#P#6s5oYSbZoBFPABbWk?(;KzV0}-tWKhvRe8)0qW+si3% zx51Fycglt;DD23rEss?^>)c5RZru`eo3|HImPu-K#P8x6HNURkN|?2cYS}0IshKc{ z(wAaE`(ghq@)X#|=awK%#^Bf?7b_jF$QmUQ2Sl&+Rq-3J$#`lzpLaUI&kVq;sTS<< zR1uHT?XdbBIIC}8axwn!#+vW@^NUi{{SK5zm@ytwph%8wMq!7P&0pb`b$wk4ke5G^ z*G+B#f?a36xQK!5D!dp6Cp$}_ZQxsk z6(x21TgiJ68p$Uo8ltvHyCN*rXr16n@>$ajXI|Wcue_(omt?yVk3Uq>75>Fe%f{9{ z8)xtYABDm^ykuPCNueu)%L>pg_dslS+~R|7k>;OX3j3h_p=e=cm0TqQl4jU=2SiH+ zjtzO=oJx^*_)iMCzV5CoX|4NO2EYuY$chW~lhCefowIk3IJEGZY6Z=C+4KL{l1!XV z8+(nUgB=LIBiA{xA)0A~Zw;mPkOF1$XsypXdFuWhlt&{RqsYvqe=RG6>st6QF9eX4 zNF^f8^F&)BJ2SMd5X?Yw>5vcK$UUT}nd|57%$SwSk=tI^sJ{V42R0|cG5CwH5sgNK z8FqMU<;U9`XB>=oiXZX%-IaRw85 z;q*rJa5^hCXvwzMl+!cyH`uWkjrKo}A&pknkEGT5mDEndWY=sjwyX8Cj@ZIb~B{=?x)nq*0Tb*2Oj z{+Us~tmwCcq@`fr>JFdO1IAX58BEUd@Q1--h~5cq=r?uEQ$iRlEGEWMEgH1n4@Huc z3|?z)w=UYpw=+h{oey^rH{Me$18(r<1AgS@2vT>DT<6Jwl~oXq4#hw4!>mXpE0ce48M@|G7j0HfSQ#3;yKN$8Tl1q5b`<_(L7)Own$UTZdWny>t+)+j=E7XNGfX8fM5it<81)8>46*?Uzhx~%_wFNDde3Ln!OX!fJ z_PjU?Bz#WAO4S(7!LirJ1jG41kIa5Swj1Gcf{URq0C7%Bdw=sFWI_a5JR8)KdhgqM zYf3@!7tL)7ejPA-%x%!+KC?xM^ho5^jOU5s_BVVhU-vF`*4{+kF3F|yMS0F1r`y=D zJVeXFzR*G@%vJw8ZB2HC6yW2Zgg;?$OCX`5-{gR5(_l|OQE1m<=!ml66$?O|7NHci zo^S=w6?bJ3Op|XH7Ozi%V;`R%`}VSYfu-4dcVN4@CqSfW}VTm;D2{w0-hkgotgHKM@P`vy^Aq7%3QiE63=Ea+sHH!iQBW5EJg(OL-yngu&z1l8OuZ zc|o$BqLDo9&iuS{Y_69DBVS072OY;Q^gVVL!fY;HZv$wB`hIoE%JV#bJVGl$f+`deEkL}$OknqHzT1VDy zgDoy0?Lt2l`XIdXYxyn1O64H6)8Htrb%Fq9^V#_730b>%_pZnEPajXnIa;4jNR)4i zbP<}{eDqE_`|2r(OI&RxZ*@rVnSmjH~;Has5_{Aha5VM(8+~mm0dK(z; zNza!@Yj>HEZ5DNB&E=5|-oGr*!8b=nw-2{c%s4R{8?>dm-&XgNE?gGt2P zM+6CiL!eI`fE5G`mo5=M$ zl8pW!=Z4Hk){C@^LQ&W*Gi_$Sb2$wMzgNaWHm0nzVdHB)ST+c#!#BN0Qo8N1DNQ`E zBTEIOzEk!R>5Z!}J6#ec2i*D_TFXP%3ndpbTL2HbIMV@LEUq>ig#WAGHB-H&^Ay-y zhdi)W=t|avARt4MbY_+}LdZ7$hOwr|jkIHrD9H8AN0A|&3n&&e-(I2>*ati{Ys@%J znyU(E1GElM&LxFwj-pt;8@!tJWzbwgrmQ1h>Gq+>@A*M6<{HGUCHS+fD=BXp93-{| z$D49@NE9y|>e_SQA4$rTvCxY?8I?a_k+paw2k^kY1vzJ_n?*k^L?fi15Swt~y+s`6$KRPDCKydyZ#P8@8ShAn8i~7--Q60pU zQcdr-yeZZ0sxy6VdfuE$)`w^96P|P|db(^SZZZ_aBR|c3oI-8>D{XE9naFjHt<&pA zror!RhSWi(O!!kpn%#`k*`C3)__dBcmi)N8_M3!_Op-Lw(g!ouxN zu|b*oSG2ze?9?8~M2rfM`36lTyOH}zw*6TWPc_y2ZMmJf)Y8J;ehKuB;Zvi|ndD6? z)omXhdiRd(ZquEMk{uH#mcTn&#yx5NrJ;L!ISN^L^V4GH65THsar7Cm`v8R!s=ZQO zWC4UauIW@DKL1hrMm&&yD2}Ro(%3<%pOzZ!w@I}o$ShSc?{+ZCHC&ytXFris?{N-y z-tjX7#r;Qpf4+R~?^plvo`9Qcjo15?Pcc!8w9S)xs-OE}lH?=&Z)U%laNjY=LiRXi z=36<24Hx^p?HChQPvkc&OA1!rydd)Xj;eF{J6lJu+q+UGED0C@7cGv z%nN3GD>Y;qYQIh$cIqmj{%QQ;It#z6Pu8o~d32^&0OPu2l8^}fExeuq@B^BlHJ-wq zGz-(xLWJ-L;EwttEbiP8Vn&RLM?3W3Q)p+~sb{pthP~Jjz%bFnR~LWtjezA@J%sz5 zsw{&1`q#f+?-`&OjgXdJiiauVmCxnBqYalt4+Ha*H8gEbL-bm7Zm*k4kzjd2=}wDr zHrS?aUE8S&BET1>B_F1IW3I<#fJ!1Dpf`R-+lIvA|AV;*pGmR)qd9IV43pXGbd(`4Lw<0k z{Z}%w?0(~25h+X*eEJtDmAYW{1KAkt5%1XGbs!#cH5&jsZqj#l6<;vQ85Ga=!6HO< z^%A8KWMbW}pbxG*6-X$xf>bSk(X|Rz78C|H0$M`U2Q~|FD3L!#=xOZvKKN> zJseFh;-kru%C`kn>K6`^Qg3XaHMgxH9J<+amBNa1nMHh?Zvft(dra(8XGcCp zNA1VnI^PJf`{?h%t|eTq}4?2(d5(9?8xVmW@HIDaRh zfk&bt1F?1fBkGBj{NB(D9aKweG+`w^N+sGOp=-rQbf=DJBB#9Yk!w5d66wd!YJ|eY z>Yc^~ae3Wo-S$FPUxEpQ(cOFEt(|+#LN>1ZF{1ezNP^XDM5lBG57xermVab&Sk*WG zIl0;CBt43ZI_bGtbw=^fX&t>x!bzj#5FO+g;PBE%PpI{U%TCsz4l16>JuAFoZ(Y7` zj@YB}iNlKlf+8-V`5hUBBB}8b<-!`h=*qy0_ucGr&#^;*L34p1Q Lg<-`7xA^}9wKTWe diff --git a/flutter/web/icons/Icon-512.png b/flutter/web/icons/Icon-512.png deleted file mode 100644 index 4d94e3123b8c87a2fd8bdc1dafc28a819ae5353b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17282 zcmYMb2{_c>_c(rM45p2lLfJJaOO~?lQw>50Np>YlvXq@LsK^>AOIfmHjglq%RPQKj zvV|~`Jv)(Y{`aNN_xJDVdD82id+yoqIp-b^FX(9=*vqjO0B}I-9LW#>H2e__unh2@ z74NPM0M2S@kxpOqM*Z%go4mnzq+vv9KqJ*js@K@c%qr1Iw8g-VCPE*Pes0zlp@`=tcgxd2dj+}{t4k-Odx#4a^pm+4sZ5E zr^-~nyJx>ztM;>(f}N4^(&M8hdT%=)PHn9GN#|7rho&C8`zgw_>WOlKk1pAzCROsa zsmBh2QQ0Kbb0#^pozn~y$$pWP=cyYp%)B6kI_9p?{|!udfSGZ)}~T&L%c0oK`>odgC- zpkwulHky3YTWa00Y9^1I#{vb^m8C<-R>aQK}sxq&HBniHaw-A2k3F za6UALzCjT@)v3rni6#$xTe9T@oY`LA5>b>;hsSUBlPjlYFyPnmmkMWj0b@1+1?ok< z1)b#r`(Exl2o~1b^!M$z11I8(v;d{PT2`!U4fXn5mFl(^py)bu)G?9Ozj5yY>$JJ^ z5r3Q@ob!1=*{VJ%_9FojQ6+)`W0ud@et!h-_)wr*p?HRlO2L4O^OPbcvO?ZDKwJ%% zu5hHY8C~%}gXv|D{wz^Co4%C*_@6Z^v!Yo9iHr;*cqbm8 z$MvI59V_6na_y6TsDq@ZH|~Jv3H^-Vd1n{+F3y_g4~Vhlul&G?I4gZX^r}f_%X0%< zj{$j3A-@Rq+I~Y31qPRm&1$1juEuCkH4~oCNRf4bLKu{ZEibwQexyOz>f(IVYjZT% zuuTwQpk(d=lQEjjOk|aV;Hz5oQ+7~s9Ha+2KO089KAj9XqMgrj_%nz49IGDl!P|PE z+=(>}+B5t+++Xgc{I=Zd>l?+S=+5E6^SzgRU!8UhFwVKPUH^JsAqlMSBN!SR(ubyO z({{j{nSZiCAa7=m2UpeD_j!97H$s`)GEaHxVD$@>C4{t)-z8uaaZ@ANfzQAJTfse za3Y{m=~KO*lRZYtUEq#;&c18fOpt<|I~gcf{G+Si!_IZw{_c3#^kCakjPo-WXRW27 z?%S!uTqASpr^TPoHYUz}OuQZ0s{G@;*W2ilzQ*s*w`Y{x-9)Qz#eO-fi?Zrr9Y4!y zdp9uWlThbN!PJjwVn5~g^WwEohf~ghyI4H?!Nt1|U!IE&{9X33lY>!}++TRIovJIrxat_7h#DYX|VC(4qt>wv{_BkJIFeq87F-+1{RY*yk>#1k%8u&s5%w6#m+OPND&JU8Ls-di-l~~|q?)T9iSxfp z6AypEH7DR=5B;GoVkpe`Sla-rdQ}4|vyzk5FvrA3-_gh~VJ#VCQl&Gr2T>-wtavjmyOciz5VRn|jq$rFfpn-lV` za>Waab5R{ZeBV>VU9tGSfI{HSg=fdL#ILsxNxL}fxQ<2gHDnR(9uFGpV}#H()j56b z$156=@Y;`0#N%R7F+5_RF6JCvphK}r@`NL&p>nxolfzKRe9?p5Biy~_eyUp4DE?Pm z{rGq3zA)+6^KeHM&Vg?#Ltu?xQ&o*YZ-W3#nW*2&v(BxYeO zJl+BC;~v@q)^`%enwNTdFo(~3#?*O*lN+LdZEUr9^7*o4O& z3I=Obze^L4h|8|icw+K97gcQMD;AG#;{OGj(p3)VTWucQ)Ra zY~q~PgFED>Amt@v{&%mMM|OYVk~#k$s%U|MQjmL^Fu1K=S6P?FnWOn5b3O$MQSu$0 z>U^77-+ldWOtJO+-YyZJ`*NV}Y!vP`*JYlT#RtbCq?$DY3MZ(&+L-fsPq31#sL+9* zOFz8m>)!FV4V@3-AS^k#fb$wzO?Kf-v>#7KcKQs~CjAf=BhO*pDYPtpQ$e7zpM^ejeH( z@XnvVJ+v;_khpYaE?*JNf@rd$Vtw7#I-jHdIG-V=O^O_vOCWHL(hv2;{c3qLH{yjH z-(`k*S6%+)*Tf)O-qk&^B1>dcO`Hf{$x$n6l&F{tO;)JrrIn>^ zl|6-LAn3v!x^)2p|Bb;BrocgUG9>VVm0ZH?z98ON-N+T&aCKE@P;Y|?`>n_7V{R1J zsd@jThA~q>E%X-%I`l-;@2_cvdetP~DRh7{$ULG!@b`SkT#+qbaQ8n=*e99A5KIW&R9(?}?at?hE0@DsPLlN= zBu*OC1pY`KNq%S$Nz?ohWe8zJj4&7f6n3Mk;uIbvp4;sFJ?QbAbFH>++tlr+O%nXG zcwtd`BCBQM`JGNlspAI}A0D+2Z8?8Z$v!M;_WMlP;WD!~(ynT$bDaT!;(b=F=lEMa zYOT_)?$i%+yf=&cBbpU9&MiFU)T!9I@Qe<{N1f-zUikQCbLWpIchdl2Z(i-wEH*s- zk2!J8;mh%HGX48^zVHDI8Q^T@BC|u?2i}XbTIiEdy|=WcSqzAFPWmP;E&26ozcX8d z01lvi9{|UW)3wfakH;e|GoRg^O=W2 z4!|g-IX3p{L5jhiM3}hCd?LB_6Xk$Q_MSJIA z9LS+I;Wsi}*s{<|hlWB1sOl2I?LoN1gT=z%`BGD)e+q&M;9M_g?v36#2G1W*;;!BK zl2v$R4{aKYU?Y;D^7vzvdAKo0z`baIlRPehl|Kor z>VJksM_|F%ZS#H8+qr5x3}E&CuKp>3&d!f{@qn)~H7DZI#I*ynzw557hW}fmenf<9 z`103O!SXlztr@m|=MFU%J!A$4L3(*jLCksGq>f@ogjRsAE6>v<0lsnW^VPnck1g$g zpGJ7|h#?C;3=RXtNWb#Ij^>7Ab_b?bBpBcdowt zQy+N`Vg51GxPAzP&9Bq0sO$CcYq`DIRTLk2@i@SZF4o`3W~Z(HQxe5C5DiYweKYov z+WkV_&;-R>d%)MInWYjQSzBg=?A$=iLQ@(bwp0hYz2^;dyM2c8d7S{D8t0wuA@G0Y zSCqnUN{+(g7X)@cOCe+p#M23``eZdn2AufE{yNX7KH#epb10FqaZM-;-4CF&YPbPB z16gQL85p1ZeP!3=R%yG4c%AfiIqZK?T96QoopeOqC zBm5Bsdpi?e+Fd(v>i^M>V7U7H_*Bk5xBoNetwW#-g+2m#ckgWoW)Ij!u>B8#RO;LA znWO)62*p69U99GcTq#q5y|mN+C#Had3!|-jDkIfUGoRZx?H3k3w(7U`iG^v^&V9vVscQ8=I z6}9nf(PGpPlS`k&Q$!)nSv9j$wffgu7XDcrD=M=7ApqNVuMF*1-GwW{FFe*wfwY0R z)PBo2kNW?Gi){k6vHcjrw!o}@mEv{RCPu`{jZYJ4IcBSI!n3~1Eljn3dZDWF5-;>m zOm-b+^P1L)sHP3px1TsaIlAxQ#@8*TT075bD}`F!iSy3JX^Y1rV8}DF`nw5zIc{|7 zqrQC$;t4oD^ac;-nw;+Z*LpF%=>$ikDhJ0)AJ@iYCe9f=Q*x3Svl!#j9~;-wX0e>r z?lbGT^zB|lFvD!?R9vgW_F7YxYEAlC3DpPpA0+ZquLtr3!Z>15D3sVytb=h6Chz4- zr;R%Kwin(Dczr5csbXR)sJE_^e_)j*`&)UA47XsEuXu5@Y|d);b;Z?b(^t2NWOf9F z&NHsn0LuUFEb@cfPQC+?_X288Wxw}Z>W_bId`G4GjuXAf+ZZv$sn?PT82va*9;QxA39)Za_j zusE^X>@9Zv?@Iwh>*v*;CnWq+yX0EUaix{PF=3T}^z@#Zf^~c1>+I$p%WA2O58v)> z=ocNpAXYp1?&a*;7|uW(l^5$h`0JjySz1Tj__sYn)j5%~4u(cnJDovqbIM2uQEGut z7nupY%kEj!8~R0APDydJG{w&cW=;-R52LMQpl7^dJgsjC}}Z-_|}#eSh;Pz|<@} zyjRqh*cto0V3MTkqnY5MJN09#HHa^LVN92bP=qQ!_ZTo%GjcTPJ1Q%>C}c+i{Jz;GkKjX;+@+ zKBXl^|I?X;f9)4O!jvaaxl1sg1FVx~lr*l}4L<(EjOh|aTdBPNsg|bv zMz}c8I`%6$@3;m<>2Uc}{;cq<-oUhJDOGp)M+uNp-5agpB21vl*0Li~4V$Zk zsoDB+kEbuGIAmVXsAsXXur~KSV@Z{#rdfis?o{%uu*z$R%)VCoGB%4Rhg;`A&y=b~ z1!l6-_VU?K(G*|-)=A7;%qg> zplQEqdAdo)SzORWk4TLt0d!zWxWYJM@!MFG|IwLb<5ynrvA z3AZqP{Bd$XZmv;8*mx%d9^cY;0 zb%}(cBL~#y-e7<@xzHQY=O!OSDeQ2$F!Lu5B^%ZI*l%Tqlk=QR4QxQVN=nhj-_Zas z222+pdPrI|Pv%v*FB@K0$$2X?id<26^XHmUFdguBG#i4-;!8h<`zm?j#c)x})5Vkk zLUMncNquFgUF*pZUJs12x4fVTCn%i`vwZ-M-j8nMPU-3j5qxqOKRUdoKRRzc%STmj z=6F(KXrv>^tW`Llq|QwPu3L)GO(Ij4I4M z=tbQ`6jOb9@VYnJzSJxg>tdc){(58<5v`{Hh^fc@ZO#t?J_{IF-j5xnmX4%wyv@m7 zB4jLTx=qi-Y~7Mt58}58u){4>5AI311IRCbFIsP3_Xi{{SXX4*J~r#VTCf#%gr+(; z>QeLo|21&&D7W}`#cTkIcZQV|C+PTo!rO^ADehZrVFAQ@JcgH6b(&J_>5V9gH^tquS%S)YL6d}`iMvnd~1XY zRCg%tOU0`Ag+L`G_YVNH{+Re&B{gG>!NZ8o;5Gg9fX@?Ne3WBTeg!+NN}Whk5AAZ# zXotPwjdfceI`TOf0#-QUx?XduBXHZD>vAxWbi-J@&M{hHMiC>JlT2tGX8?9mN+9n| zH9L)uz~LufF!Xk7aZQ2;d*t0a11CYv6)zOgn#aXzTN9?`>BC>Mj&Td+ZH3){d4$xn zYaZ5xIM>y>tesQ5}p`X=QB@{)qKUaH}vZnWelu+zRDrTXYFiO#;u7zISN z=f;mbG<)j9V5+gg{=DnI?Z`kJ{(xG*mTd%?k;7<12auU^R934C{I)V-iSX6Y)i8#B zsk4r>GWnv=xAjKcm~{(&q3U{cVlj>mR9$uG*dN%9$Rv6&EM*s$)M)V>9ct9PfPjXg9V_ zlY4T)Va_>U%fD3O+^!Du8fs0YX=Q3tWEhX?BZH4C$4mzq(K}4&fYREtFe?;uao=8) zGm(K>)Yoehn)ilUC8b<)~g zbK;1a9o)HGUWy{im*sz1Z5+1v1UIRwDDXF~l1M5p(;YP&Et4T+9;o%;vo}R60iXEx z{PPTUS~x$vTW)x3Yo00!%bl2Q{;udaZeVKE3a^XQqtv0Z!T=X~r#$EF{K=2jrtlCe zW)FvROus;AfyGUr$KajxDnJ5Lr82U7& z;UXS%`O$VRzK`@*hwDR|*UZ!#58+p9;%25~mdz3Bh`5E&!4#05>e%cHy?OmrA!L)KsnyP(z0#>3vlY=$HD9TY9x30iCkD!(1=7Tc73s&30#Qb*7KS6Ft_S? z^>;7S-U?^@x9EEXOZ*uljBfT@ry-pD_1l?yix|Q5tIeMNX&#z31hc5GO1B|oq&b~W zhGoJFJ{t{lCeUS`cEs|LhfZBEj3h3c&kWu#vYaQayvd%mUpY+_!mf{$dx8u4+**Fq z9*#pW8y4b49S7vol(>)O!4Y9rDiz)P0|fDs{NmW$>Iz6cKPQ*e4lDFU`9M9r_Zp=3 zXB{UTmmq(q(Bq{OfLOaytk2TA5JLWncilKE%&lFySC-&A((=?9kb#?9h$cnCQveWE z;zfKpT&vmraX4A@hYcz(zSb_F00o;C2T&5${Q~WDKxX(J_xPe37R!q&=1RTP_J`#z zX{A{7AR>WW?BxMt3@96l$w|02_`Dbg&G+joLA-&yWvz-Gya(=ulO6vYLlMO$e7@<- zi>`xQSRzrLXr5IkspyybG9PU-&4so0?U+SV=THb+tF{4~~jlR_X(InOPb=fuWVD3F2 zC!EOpQcQ;?D{NYr%CSaU!jF;UlSUS2=HDW@4^8WtJ|+^sW8baW>;+wByj)h&0H9tRrS3$j8>w zeeoUX{-$)W#Nj~8W`~3HOI256#bz4NSS%h@JE!aD^38aaka6uxH@nfZ)5OnMZ>kLk z=+aJOx7d@bF9g!+n zEEj5?`;a>Qp$UwksdBf@4nmSl?sHXyjIg=`RG79w{aPj+1YtLsi#%YFNT%QyIvU-=ok=Tg ze`oplNEqb+>3txhpVsyo@gNG`b)rgWCLye7FjW>RF^HcUXQ>5n-KwW*1ZEx(u-HAQ z>VriYocLFBLWm?GfOT&LiYzql`;EPV)gQ8k!!3-Dn5rug?x^TXBo3jWY>3wa@i_2X z-@BwpVuKNVhDf;|5fL6hTAA(Ef@v$CD@H(D9E6!kc~vt2dVXV=Dm3voyg5JEd!TV9+ck<=`E5m$g{X_tqYKt_+wyle3#f&I{@Sx~PO^x=M8 zN0CGrg@Xvx{6R~=C*nUNjCc$e+G_EfUKu-HW{e9F<^{}_s~Z{~&wM~E={wD*k1_BIw|B!_t z27Q(?e#xYa7jIcDgH%+QNb<6rCm`Qk`x}c`y*#FLLKzYueM7(PAH#v>156-)Fq8p_ z7&f@CGttj2^gVF+D3_LJi0>VxJ{BDS#4^7|ItCyULVlJJMRuEO%~K_0{B2c797_^Y zbKO)F(okHVBQP_45R1j3iv0zOH^Ra$l_wxM2L*cTCoz!TU;CSh7n~-LE9i#gN~{2p zWFA+J1TI*U*S=;qxBuc%GFCG-fNZsR=gao?G;iU<}W=>N|b1@MA8gHX@!2{u$NaA9@B!chadatN80 zY*aI#q&4>)BA^ooa z=}d%l`bI>6q2wW{Ogiun%i7HN9)M@H}pK zxuhE2R>A5->r`;DeyA6@sowKZ@2X^^&3 zjyN}zw*0&hRG38dAaX4*pm>iMT!8C3<~skZ(tB*n9-6h4SAy7#HV!9%9#VdX@NC3< z_<#C*aDP{y%K@++=R1d~`5ja<%}?O);B!G@| zf~j;<@|#_|;&1MUcD4Dz^tyAs!CumFVBE*E=d9#&Z__6W-BRSDv}eTVvC3l{Oc?IIQ^CFY|4`DT5R`9Xa5 zdtUUZOlXoMO30=Zv}u($U;fcotZaM{hK_=M?GheGdKg=%Z$2Wyfo*Pi_g2I$w7sD3 z_9RLFAtD(m7KL&%-v<#mIW3~7WHK2qxe#4rp)bRQx2#ZbL@Y|3^0Me4q`)Z&W|?Fv z-j#1E&yzo6IZf+}Z{Yz93~%GGosge0qvc=hiChbbWxWP4{<%C$TG%xb;=CF^2C=B-TRAang_ZdbUiIjoDFbq)L31g?htJ&i&exZ|Hu-l$gTK7@* zBV<&a!k7Syp4^q;nCcR7W)76{9&cJEJ%o_HYHii?P8PYe>MD<+K*l%qw^jcPyWV_R zL_YPV1Yu(=c{f) zSzi`_cZhPy&?P5z68nHrS|cJs8@%KTVP=Tp4;vjw#b6@H{hNGzcrj<;qz-6N{5G~F z@IEjs5|FW&`4^tB7E6b!PqHnL zC1j|#3c|Mlc*d6Z*@J-e$}hnShTrG#!Hq~!VmNHfiSOuscFKO8KZN|0Lz9f6I8}E7 zzMHLsv+Pi{6#UokW5O^QVZ82@{EwHWSY?>3=vpGi>y6nzFe%iJ?|_ZT2XAKK^AeGY z{hm^eBC_}mtui4A({Sbvv0zrxz7+RcZKh+bWRlN3c-dVMqg$Jt3f+Q(RYgex7OA}BcQj`*alSJJONxs(e3|= zt6qsW>ZB*L$x)`J@R1WboHAzXPah0;WvL4PBPn7|M95@`M0%1}@N4Qv<3Uv*0^cDe zv-1GB`6`Y-#iz_1sUtOe6bKnj?we1aBe#nP+E@S9rRxY$-%T_SPDYMWTSLO{sNCnm zkKNLj2>4L50Y4I`XZI;y%tVn-iz*{?WYk5Q2yVUumI#=Bu;;zgM_>C%zSoUGwp ziNzj94d2brT7U&uN9XeP+K#6CMfm1SP5MIsxno1_p}Uj>Rj@s&P`Q5yZ&`bEV)w~G z1l^X>57h??t+h=D#B<(ZFLz9ahXofaOx&a%L7et3=6S`bJPu}X;`?aQ8z^~#&1YXt z63bm$HNjS+o&YT&GqstH?7OZ=Rp@=k3SR@?dA{K%>@B~pbZeEz0p57`V#q^>%(|>x zQACxMl-osx5vP%2^;#W-5^sO3sv;`5`?!x&MXQzxy- zb}|Y__RvIN>&vyb=mf}3Fr;^&jEN(Ku-7YB2of*u@lOKab+P5mcUVk(2)TJ&336C<(@C%A zs~)m39GmD?iUmKr4m0?b5Q#Ckg|~Up%KD^>8l6l}yP9`N3Z;x(W8f^zV_C8O#h-YH@&`# zcS$phSh*Z%U$wschC({<^Lw@w5|0C;$@}c3c1UCP)v0yTqGFC=@Fu&Ug1KwwV0iKX z_&xu~*K$8v4|!)7A}edrs-Y8Ntn#qduF=TN_i{nyVS+wd$aC`IvwD$ zhjE?6j5@^3y24BcR%&m0nqQ;W%N-U zPK7*UtAx_wta+6?v_nz51^WUJ(Z{uM5BO-|?-KEpQ6G(bTKzgfYixJuPEM*pw+{enTuV$7g1(Q-ZQuM-aNpT8J4fNiS+E@)H4KX9^}=mF$3T4L!No_g2x$%#&}mW z_)-0X)xc*lfHwbh%#K0r4EBiG4E6>yD{A!5;jU3gTho@JO)G4CU$n1JEtEOVQu;`b( z;^Dz45GcIBToyv!^xJAm3#$rG4XBg!f-_d+e9i( z3yd}4cnAy8NkLyNG2!tlv55wvK+J6Z4*PO1#$3h_}QX~lD_`CPT%mTPf%o7&;OZW@|o_WDix zl;(Ww^e^B_ksInUXMEzmA*OEH>orUG#?FNU(W=r}0yBzI>w#;|>&vWN zwDI6NrM#>InwADm?&;0l5{Fr(srnyXid5kvhn1@8i=w!kdCP?ea?K@Q*M2REf2}9qO(9qf4r`q=BBf=}@=j>1E38kJc0Lj(^O= zX50;54Zj~t@w1c1dJmNqvCS`&a(Z^|<>)>j0+V&AlZ zLzOK9`!+rm#>tkw%V_F}U2~s*kTE2A%2s38Vn4i-+k8*F_-JYpyB92c_GwX^T9r!( zC0G0yarj^#)7YvqxnmsdYiSba@hb4eQOb`fdVhCs1Lk74uGnQ3yw0=TfdfeQqyl-Q`&CK%# zW;MN?gApp*_Hj8hp3$ipe&rOkhR)(+K2*FM_&1 zMud!(+?P6v?iFyFu&%<#ebc~}L@5v^4ATjATcuL9F*tvibcE)pJx&M$ZaKp}+t+L+ zH8i6vQ+H$oo&{=UW=1?^S$&F!^<87iY2w{9Xt1Z1mg0C}Eoz>Syci>dZW-+IpFd*J zbzgrV`E7gDY{eeW;}$9JtvKNb6kGR~rz54lWd4Oa=x(QQTQ&sc+It8e%ot+dV@fQt+qHt=l z%I+cm%ye9!Obl@IF_oKG4d2|iv9X@vY+>)%W2G5db#sFAsCr}P%S$G<<$YB*ZX2Ze zoGO-@TV|xuY9lL4@QHe$Z0rDhF z4&I!l-1ds7?40{NbAb8q%9{>)fB!QEG^y&Wf`oYY-katja{U|?tsPy1(QK*{H%v1{ zX_sUw&AduCQytvP7Sc2OHmhirw+C(==(m{}XBD(8&CN<{6`vM8w9l88TVtgtlw-A% zJG@wNT>}|Tg1x$`Jt^8t1<&B|PX&t#3!GpbMdclS73g0%I$s`mr-Jsln*TZj0*u3d z^}2RO)Z7V9oh5oLUp475E)P7pJATJU?a}pt@;x+ay#}?#2M0BMoVAN49`2e5oWMgq zQlID_>rS2!4Fu#PG|^USuN-=Jv%iKwX-yv+3WVR56PxAS{z zql-`oRuVUA*eX7DLJJN7o9w_3@iAmSB2kQwYWcWoy&wsYfBuNtXK8AZ_@+1zvpbiW zDpzi_X&8+K7oJ=HwZ8JK!0+Gp4dvvoTOS1xUOYR`1a5Id;s1}99Vf_3`uCXnL;U|> z1L{tQq5(^%^JQvN)sIC9jsNi&aLG)t_Yfp}jB%%By%Ek704S%5sCmN-DrEiB>zP(j z+7X!PLrNCn-e{>Lc&Y>P)$e7?t^W@?h+nbnWjDh8%)0gX`r-c_xv_tytK}?0-Vx6z zc3R>;B$8K63?S=^F?jSYtrcp|4#S;SdRvySS>9w&+Z{!O&$91M9f$M|EP8E6u>XTp zGfy5X-b8_`Tch{>4Y_);UHetC10~km?FPx&huS5+xV3cw(a=?;orHHgNF)J}C0b$% z?6g1|{i(Eb^6WotTHBw3yRY^cyFu3fw_BpUMaabuDlIbbJaj!sM}N6v6!^Ew>}x8v_kT8uf200PvokD}(BPc)l8U9mI>+Z~046XDeY#~YWC zm8UT~%!BhW^?|ox*aXm~N{oHpY{P~A#MRWoh^>fy|8N~(_L};0fF2QF{YK!Z6D*Hl znE=p4Vwz@^^v&zgfP@-aha4W%@4#yc9xv~9BVnAY1X0h>8jn<__3%F(*Ic`%J!6i{ zZ6{8=`?jy>WQcYB|5mmaOPSm&;hY+~CgD@Iep8B{y&>ztew6MA3r)XOr1E7I8 z2lT@l1fclR|KJxw{wl!Io+b_7d_G=6UVI>UmpFRkfVRWe@eDl?_+6CA8=3+d$|2Vd zQ*)DKRuHYII0f-%?=!5AIE}DX7EUg1`z&m*K!P)p@ta#8Vb5*%`#o|U^Go;5jSLHY zl?cdk`WVtffoG6-P%a!PJsWJAP;*WBq7o@S3cED1;TA%C6tZ=8=k>p-cfzqogs%)Z zP`3Zejh{nt@MfVmyRPTE)4gkbLIkSX%dxKb+pI9b3u`D`hRrXy1ILPazU^xEvHa;T z;a`we|3EGTJ>Om|jLQlt33t@t;|qcH)|6DpE~VXPD3yT$-SZD&EU?oV&(>GF?112b zSA^dst5#Ee(iouNpRntr>r7%R=yvxM`%7mgG2VK@9_yslBuoy|D1O{ld2si12K2^Y zLeE(9zNV+jc=`)Hab3b0T{FH5a8DkzeXV{#N_m8o0zcD%K+xWHVowmQc=9MRZ({S$cMU?~!RuUaaKENX?`74JdkA0g9HGEX&tyS8GB@-Dz zbR^)XU=v55GVrkT9D0;ISb>+^n@2Ao{G*@IUd8Y3|8{>VE|>_e6Wn_{{r)+A{&Zfk zbNLeQlO!<56ohRsM^GA+$==0$-#-CwK2MYhi=Ch5L`m?K1-AzVZS(APSA@UH1}RW; z`KY9eTeC4)bKQnE4F-Jb^B*)V|)1VwYr|@lil=5H@vYoC-u$ijnsC0RKeXA)&o!`cg z?{rgAT^fCPz3A6PZ)AH7_VVlwS|T70hc+A%pa|d}ImsL9MXjTl0+l7Fg#Sh(!kctA zrBhNi&6@8OHjF+%pMm}S=ZImOzcusWI<~~+t-I=Eybb2Oj|W|2oz>5`!)R8b@2@qK zD;w(>k>d*#zaZmcDJY%tD6*ilglnhYNsnwIeQev7Ke%<8K2CMxnCn7jkgy)7KRALC zV?j;)l0=K&`iq_P_2E}iG5%Sme+X@;-oLeml2pMx61nJvzv;{Pw7MUP-0T>dtuC9A z?ca3QocEE)W$<#-A6(-MzDTdSuITzhDu}&Z7M}c4o$|{>wq)(C;Ly&GoWI6045*GY zI;w1zh++81!=Rwqd8YGs!yylm<{0<7tk@_Xkvqfbi4X1&^G{W5S%Tlu7e7^C=O%eC z*PEwzYH3h-uVdWTCoa|Je3sd^t;;`f&iGbu{XQ~#5KCT@zxCnIsfRBYnOcBtX(Kfr-AS_q*JkvU2;1MVUd-5Af%;9qa!mZ7kywh0zi#svESlwA zwsT5PiQPbVOroh4{la-K_Uj+m%e8X3>G|PHIuKII($c0lr&(E1;6*vhjm9ju1{Tmv z&cfMHD}tJb_TF-LY|6?p)<$qjh`;`$A}Bu1zC0%wTRk$?hluS)+MUXdWbSeBR*n$yKb7q_#sNweKYUS{Xl`gEj(PkybN!cg(@ zyw4>^sCCJ{PwG9jIsdV{G$<<(#wN}Xzg?Qoeo%_GdH z7F*ILw&$&N{MV7L=@>H8M110G)4`b^#^coMwnft^3tM6F?STjGk+YuOrmSNqEJW@& zGu;>aL+bV^O9{AI7RMj5I@k9%D8BpV!SO2TD;ZMDyeP|dv6H>Vf|sgawdlqC$F*kI zQs#d!mHp|d$xUjGM|O`1<9St8)T7yB0%IE31vgv1h3v$2tS$3^80lE1x9%ntBgIEYEo)p!H-@i!b>rxO_+}?&8BsF52Ll zA2<9_%Zf4MB?E=yTwIoRiJ2P&PC$UE^n5pqz-6X$u>x8?Ptnb?*TJ=F2p@mF0&U>N zNI5|iixUuzxfzsXMgS!#_XM<>G7kmiW~0-k7Eo|Se~7WF^cuKk&T|f;^LYeBfq@Pq zv0N0v=c<4!y9Cbf-=8I>6_*u5)-saDcHpY9I%#K^VAx@Su$j zUz0hwri4Hrj0PDs48-yRNKWh2UcM$j;11?UaRM@-)j>>8aG*~S#e7sNF8QZHx)jb> z8*F6``hspzCLpwv<|NU3%P)!9=XQB*Hv5&xw;ykrUn!5B$?h9itM&YFYktU2Z@a_? zz1K#xrR8^{)IfMJwu1wtgC6Rpl$6xlRiQ9kD+(-K5w1&tzy0`%sCw@PGkC<W8eijE9sV;fZmEQO2 zV<`(~LtR{52!sKQC;FBwK;&9^wBWbvmQ6 zvR9N#E-=AgEm8m?!BwCC11*%jzbUtWXfVzFoKoXvfIuU{a|Ff;S1zU3=+2WMDK~QBq zLgY4g5sk!@%Wr+YzdzoW$9bIhd0o!y^*Ybz>wVwK;vz5iDQ*A&z-ww^Xw7<3|6W`i zth@E3KPLcycwlO1U_%71KVuJey7>I!%-+K_5~J8$o$nEh!LRaKt@&LnrN2-eQq z;zRw1OphKB)wCjk4!R>KCEjO>RoYFZW-FHDvG+8&g~|_kfd8u z(=dv`9QW8@93A@e0Mi24821N`1YkZG#bGEXL-N>&rr)5RmM{)MBNW_@GuYgM5$sGZ zEA}ue2WWxnvG~=1&Tg-KB=7_rFC08 z%bFBRwSy)EtFkqgj)9)?eJIgEBkF;+Cvc=!w|(T4pb5HE+BA1?>`C$1JF~&c z_=x*Y#{FY6lcQZp@)P_#xvqAC9*=W~UtQb4eyv#&4Y%q>{O)`?#~6QB4%MFSdQcCA z^b|TmcjJ|)s)()Ey1iT73U9}b&*vb^@9hoTV?XS?pk7sK-vIVweK$J#v+S9od4(m- zyJ^1?vFdWnO@f~+!Xf8=ad;^?YR;*ph*!DNC?Ga)pzE=l={F?*4Z5 z19F55M}i5t>V_yjN3Mq7##_T3AjsMoiF-R!u1erg%fYzl&-P#+4J3?5BXot z9VsyY9cV7#3T}=pacpRf7?G!57>*%Hs@p&f^@%qz_M9HZxjAqNL-oD4n{H!g|i^)_uwjFS8OcV6T?(Pz`h8eE!lI5251<4AGilkKk{@ zqom5X@uv;g-J4S5zMCl*oP9t%yULTIbi0=VLNX7kU{aXu$oj6Oq=#Apmc}D_t5-p3 zSoQ@PmDfk}aeWfN9c$Lh#;F3m^^$GVP|X#9G!P)XU!YC-co>t=k6$B?S~YGHbt0G< zfIWD&$k|NrjQ(&Sn2YCPi_`R8n7L?dT{OfTc3hp@7E0sd=gxdq*HrsbaMO=rSp0`ff=+GvBajm+*Y<7g2CUsHzW z_F_zc?>jEKY44CmG5=+4f#lLfQ>$tbm2>S$bpl!eLtF)Hkp1u*r~5bd4blbh6wU8IkK;n4H}e`ha&Mtc>KI zDjRBKi3*l{<@2&)_RVlDzOY-kyR{^BOs5kh#8x zq}Zb}cG=s#lHp9@?Q6*bZkDh5_bz)dK~HPIGUOAx7V-UG48vl#gn`db_OAIz^9y?V znZ*MFS)Oka+S>RW!At@?ILD z%5|jlPudLE>)-!kk02q2fqU@uaG0}7<+F&&@AOx7%7@Ko+7HN=ZJzZp%(0oPG7pm! zmHr*^aIWe4WRhv`qiU{`H#f=#X?v^NedgRvbeR1OQ^HC@_`vsv0v8T&m zahH%Q!>L%?!5hoD>LDc}{lTTLh_2or(?je(WiRp#^+bAxJ649SB-AOozZa9DDt+K*xOGsoT9LpYUDloDwx`8&Dv=T9!RIi}H{Xgy4n|JYk~x8q(1#HFFR zZbIbNm+q@Dj>&yq`>Y@0{>$%pe?<-bjBONzBpWh`Bk#MWjy~f?V76g z`_`Mq>hu3>Mc)5aZm(C>q-z=9-k*CiM&Y&~!$3PNgowu%P1sgMUk~D`NRPQldOPAJ z;3??W2Br*94lRxL21cbt?UM;~%!iUy|D1~!FUDvk%b8kfHyf);HlNODhy5uVY?GvZ zgeCKwk&XeTgDD>Y96_HeBGQ*+Ix zWv*Mlw#u6n%leK<%$o$pDinj6XCQD&FQq?!)oTgDgPr}JGy$EWjjeq_x8bKi8E2?c zQKFp(tBmEp<#Sp^zx#gnLrSnkkw}H}7vdwnuMtY^f=T!L;21Na&lLw&N#V~C`OTOk z@}>0z>wIJM{lXt&)s0fM6^w@f1kY^04BOS_YF|mdRt| z*F(bVSgBf^Mz3jc>~kcwmW$2hZS&w3aRqYZX}uni!#slQM)1h0%n>CAj-v%Ri(b5Z zbB{ZC&D#;e4~KB*pP0A#P#6s5oYSbZoBFPABbWk?(;KzV0}-tWKhvRe8)0qW+si3% zx51Fycglt;DD23rEss?^>)c5RZru`eo3|HImPu-K#P8x6HNURkN|?2cYS}0IshKc{ z(wAaE`(ghq@)X#|=awK%#^Bf?7b_jF$QmUQ2Sl&+Rq-3J$#`lzpLaUI&kVq;sTS<< zR1uHT?XdbBIIC}8axwn!#+vW@^NUi{{SK5zm@ytwph%8wMq!7P&0pb`b$wk4ke5G^ z*G+B#f?a36xQK!5D!dp6Cp$}_ZQxsk z6(x21TgiJ68p$Uo8ltvHyCN*rXr16n@>$ajXI|Wcue_(omt?yVk3Uq>75>Fe%f{9{ z8)xtYABDm^ykuPCNueu)%L>pg_dslS+~R|7k>;OX3j3h_p=e=cm0TqQl4jU=2SiH+ zjtzO=oJx^*_)iMCzV5CoX|4NO2EYuY$chW~lhCefowIk3IJEGZY6Z=C+4KL{l1!XV z8+(nUgB=LIBiA{xA)0A~Zw;mPkOF1$XsypXdFuWhlt&{RqsYvqe=RG6>st6QF9eX4 zNF^f8^F&)BJ2SMd5X?Yw>5vcK$UUT}nd|57%$SwSk=tI^sJ{V42R0|cG5CwH5sgNK z8FqMU<;U9`XB>=oiXZX%-IaRw85 z;q*rJa5^hCXvwzMl+!cyH`uWkjrKo}A&pknkEGT5mDEndWY=sjwyX8Cj@ZIb~B{=?x)nq*0Tb*2Oj z{+Us~tmwCcq@`fr>JFdO1IAX58BEUd@Q1--h~5cq=r?uEQ$iRlEGEWMEgH1n4@Huc z3|?z)w=UYpw=+h{oey^rH{Me$18(r<1AgS@2vT>DT<6Jwl~oXq4#hw4!>mXpE0ce48M@|G7j0HfSQ#3;yKN$8Tl1q5b`<_(L7)Own$UTZdWny>t+)+j=E7XNGfX8fM5it<81)8>46*?Uzhx~%_wFNDde3Ln!OX!fJ z_PjU?Bz#WAO4S(7!LirJ1jG41kIa5Swj1Gcf{URq0C7%Bdw=sFWI_a5JR8)KdhgqM zYf3@!7tL)7ejPA-%x%!+KC?xM^ho5^jOU5s_BVVhU-vF`*4{+kF3F|yMS0F1r`y=D zJVeXFzR*G@%vJw8ZB2HC6yW2Zgg;?$OCX`5-{gR5(_l|OQE1m<=!ml66$?O|7NHci zo^S=w6?bJ3Op|XH7Ozi%V;`R%`}VSYfu-4dcVN4@CqSfW}VTm;D2{w0-hkgotgHKM@P`vy^Aq7%3QiE63=Ea+sHH!iQBW5EJg(OL-yngu&z1l8OuZ zc|o$BqLDo9&iuS{Y_69DBVS072OY;Q^gVVL!fY;HZv$wB`hIoE%JV#bJVGl$f+`deEkL}$OknqHzT1VDy zgDoy0?Lt2l`XIdXYxyn1O64H6)8Htrb%Fq9^V#_730b>%_pZnEPajXnIa;4jNR)4i zbP<}{eDqE_`|2r(OI&RxZ*@rVnSmjH~;Has5_{Aha5VM(8+~mm0dK(z; zNza!@Yj>HEZ5DNB&E=5|-oGr*!8b=nw-2{c%s4R{8?>dm-&XgNE?gGt2P zM+6CiL!eI`fE5G`mo5=M$ zl8pW!=Z4Hk){C@^LQ&W*Gi_$Sb2$wMzgNaWHm0nzVdHB)ST+c#!#BN0Qo8N1DNQ`E zBTEIOzEk!R>5Z!}J6#ec2i*D_TFXP%3ndpbTL2HbIMV@LEUq>ig#WAGHB-H&^Ay-y zhdi)W=t|avARt4MbY_+}LdZ7$hOwr|jkIHrD9H8AN0A|&3n&&e-(I2>*ati{Ys@%J znyU(E1GElM&LxFwj-pt;8@!tJWzbwgrmQ1h>Gq+>@A*M6<{HGUCHS+fD=BXp93-{| z$D49@NE9y|>e_SQA4$rTvCxY?8I?a_k+paw2k^kY1vzJ_n?*k^L?fi15Swt~y+s`6$KRPDCKydyZ#P8@8ShAn8i~7--Q60pU zQcdr-yeZZ0sxy6VdfuE$)`w^96P|P|db(^SZZZ_aBR|c3oI-8>D{XE9naFjHt<&pA zror!RhSWi(O!!kpn%#`k*`C3)__dBcmi)N8_M3!_Op-Lw(g!ouxN zu|b*oSG2ze?9?8~M2rfM`36lTyOH}zw*6TWPc_y2ZMmJf)Y8J;ehKuB;Zvi|ndD6? z)omXhdiRd(ZquEMk{uH#mcTn&#yx5NrJ;L!ISN^L^V4GH65THsar7Cm`v8R!s=ZQO zWC4UauIW@DKL1hrMm&&yD2}Ro(%3<%pOzZ!w@I}o$ShSc?{+ZCHC&ytXFris?{N-y z-tjX7#r;Qpf4+R~?^plvo`9Qcjo15?Pcc!8w9S)xs-OE}lH?=&Z)U%laNjY=LiRXi z=36<24Hx^p?HChQPvkc&OA1!rydd)Xj;eF{J6lJu+q+UGED0C@7cGv z%nN3GD>Y;qYQIh$cIqmj{%QQ;It#z6Pu8o~d32^&0OPu2l8^}fExeuq@B^BlHJ-wq zGz-(xLWJ-L;EwttEbiP8Vn&RLM?3W3Q)p+~sb{pthP~Jjz%bFnR~LWtjezA@J%sz5 zsw{&1`q#f+?-`&OjgXdJiiauVmCxnBqYalt4+Ha*H8gEbL-bm7Zm*k4kzjd2=}wDr zHrS?aUE8S&BET1>B_F1IW3I<#fJ!1Dpf`R-+lIvA|AV;*pGmR)qd9IV43pXGbd(`4Lw<0k z{Z}%w?0(~25h+X*eEJtDmAYW{1KAkt5%1XGbs!#cH5&jsZqj#l6<;vQ85Ga=!6HO< z^%A8KWMbW}pbxG*6-X$xf>bSk(X|Rz78C|H0$M`U2Q~|FD3L!#=xOZvKKN> zJseFh;-kru%C`kn>K6`^Qg3XaHMgxH9J<+amBNa1nMHh?Zvft(dra(8XGcCp zNA1VnI^PJf`{?h%t|eTq}4?2(d5(9?8xVmW@HIDaRh zfk&bt1F?1fBkGBj{NB(D9aKweG+`w^N+sGOp=-rQbf=DJBB#9Yk!w5d66wd!YJ|eY z>Yc^~ae3Wo-S$FPUxEpQ(cOFEt(|+#LN>1ZF{1ezNP^XDM5lBG57xermVab&Sk*WG zIl0;CBt43ZI_bGtbw=^fX&t>x!bzj#5FO+g;PBE%PpI{U%TCsz4l16>JuAFoZ(Y7` zj@YB}iNlKlf+8-V`5hUBBB}8b<-!`h=*qy0_ucGr&#^;*L34p1Q Lg<-`7xA^}9wKTWe diff --git a/flutter/web/icons/Icon-maskable-512.png b/flutter/web/icons/Icon-maskable-512.png deleted file mode 100644 index 4d94e3123b8c87a2fd8bdc1dafc28a819ae5353b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17282 zcmYMb2{_c>_c(rM45p2lLfJJaOO~?lQw>50Np>YlvXq@LsK^>AOIfmHjglq%RPQKj zvV|~`Jv)(Y{`aNN_xJDVdD82id+yoqIp-b^FX(9=*vqjO0B}I-9LW#>H2e__unh2@ z74NPM0M2S@kxpOqM*Z%go4mnzq+vv9KqJ*js@K@c%qr1Iw8g-VCPE*Pes0zlp@`=tcgxd2dj+}{t4k-Odx#4a^pm+4sZ5E zr^-~nyJx>ztM;>(f}N4^(&M8hdT%=)PHn9GN#|7rho&C8`zgw_>WOlKk1pAzCROsa zsmBh2QQ0Kbb0#^pozn~y$$pWP=cyYp%)B6kI_9p?{|!udfSGZ)}~T&L%c0oK`>odgC- zpkwulHky3YTWa00Y9^1I#{vb^m8C<-R>aQK}sxq&HBniHaw-A2k3F za6UALzCjT@)v3rni6#$xTe9T@oY`LA5>b>;hsSUBlPjlYFyPnmmkMWj0b@1+1?ok< z1)b#r`(Exl2o~1b^!M$z11I8(v;d{PT2`!U4fXn5mFl(^py)bu)G?9Ozj5yY>$JJ^ z5r3Q@ob!1=*{VJ%_9FojQ6+)`W0ud@et!h-_)wr*p?HRlO2L4O^OPbcvO?ZDKwJ%% zu5hHY8C~%}gXv|D{wz^Co4%C*_@6Z^v!Yo9iHr;*cqbm8 z$MvI59V_6na_y6TsDq@ZH|~Jv3H^-Vd1n{+F3y_g4~Vhlul&G?I4gZX^r}f_%X0%< zj{$j3A-@Rq+I~Y31qPRm&1$1juEuCkH4~oCNRf4bLKu{ZEibwQexyOz>f(IVYjZT% zuuTwQpk(d=lQEjjOk|aV;Hz5oQ+7~s9Ha+2KO089KAj9XqMgrj_%nz49IGDl!P|PE z+=(>}+B5t+++Xgc{I=Zd>l?+S=+5E6^SzgRU!8UhFwVKPUH^JsAqlMSBN!SR(ubyO z({{j{nSZiCAa7=m2UpeD_j!97H$s`)GEaHxVD$@>C4{t)-z8uaaZ@ANfzQAJTfse za3Y{m=~KO*lRZYtUEq#;&c18fOpt<|I~gcf{G+Si!_IZw{_c3#^kCakjPo-WXRW27 z?%S!uTqASpr^TPoHYUz}OuQZ0s{G@;*W2ilzQ*s*w`Y{x-9)Qz#eO-fi?Zrr9Y4!y zdp9uWlThbN!PJjwVn5~g^WwEohf~ghyI4H?!Nt1|U!IE&{9X33lY>!}++TRIovJIrxat_7h#DYX|VC(4qt>wv{_BkJIFeq87F-+1{RY*yk>#1k%8u&s5%w6#m+OPND&JU8Ls-di-l~~|q?)T9iSxfp z6AypEH7DR=5B;GoVkpe`Sla-rdQ}4|vyzk5FvrA3-_gh~VJ#VCQl&Gr2T>-wtavjmyOciz5VRn|jq$rFfpn-lV` za>Waab5R{ZeBV>VU9tGSfI{HSg=fdL#ILsxNxL}fxQ<2gHDnR(9uFGpV}#H()j56b z$156=@Y;`0#N%R7F+5_RF6JCvphK}r@`NL&p>nxolfzKRe9?p5Biy~_eyUp4DE?Pm z{rGq3zA)+6^KeHM&Vg?#Ltu?xQ&o*YZ-W3#nW*2&v(BxYeO zJl+BC;~v@q)^`%enwNTdFo(~3#?*O*lN+LdZEUr9^7*o4O& z3I=Obze^L4h|8|icw+K97gcQMD;AG#;{OGj(p3)VTWucQ)Ra zY~q~PgFED>Amt@v{&%mMM|OYVk~#k$s%U|MQjmL^Fu1K=S6P?FnWOn5b3O$MQSu$0 z>U^77-+ldWOtJO+-YyZJ`*NV}Y!vP`*JYlT#RtbCq?$DY3MZ(&+L-fsPq31#sL+9* zOFz8m>)!FV4V@3-AS^k#fb$wzO?Kf-v>#7KcKQs~CjAf=BhO*pDYPtpQ$e7zpM^ejeH( z@XnvVJ+v;_khpYaE?*JNf@rd$Vtw7#I-jHdIG-V=O^O_vOCWHL(hv2;{c3qLH{yjH z-(`k*S6%+)*Tf)O-qk&^B1>dcO`Hf{$x$n6l&F{tO;)JrrIn>^ zl|6-LAn3v!x^)2p|Bb;BrocgUG9>VVm0ZH?z98ON-N+T&aCKE@P;Y|?`>n_7V{R1J zsd@jThA~q>E%X-%I`l-;@2_cvdetP~DRh7{$ULG!@b`SkT#+qbaQ8n=*e99A5KIW&R9(?}?at?hE0@DsPLlN= zBu*OC1pY`KNq%S$Nz?ohWe8zJj4&7f6n3Mk;uIbvp4;sFJ?QbAbFH>++tlr+O%nXG zcwtd`BCBQM`JGNlspAI}A0D+2Z8?8Z$v!M;_WMlP;WD!~(ynT$bDaT!;(b=F=lEMa zYOT_)?$i%+yf=&cBbpU9&MiFU)T!9I@Qe<{N1f-zUikQCbLWpIchdl2Z(i-wEH*s- zk2!J8;mh%HGX48^zVHDI8Q^T@BC|u?2i}XbTIiEdy|=WcSqzAFPWmP;E&26ozcX8d z01lvi9{|UW)3wfakH;e|GoRg^O=W2 z4!|g-IX3p{L5jhiM3}hCd?LB_6Xk$Q_MSJIA z9LS+I;Wsi}*s{<|hlWB1sOl2I?LoN1gT=z%`BGD)e+q&M;9M_g?v36#2G1W*;;!BK zl2v$R4{aKYU?Y;D^7vzvdAKo0z`baIlRPehl|Kor z>VJksM_|F%ZS#H8+qr5x3}E&CuKp>3&d!f{@qn)~H7DZI#I*ynzw557hW}fmenf<9 z`103O!SXlztr@m|=MFU%J!A$4L3(*jLCksGq>f@ogjRsAE6>v<0lsnW^VPnck1g$g zpGJ7|h#?C;3=RXtNWb#Ij^>7Ab_b?bBpBcdowt zQy+N`Vg51GxPAzP&9Bq0sO$CcYq`DIRTLk2@i@SZF4o`3W~Z(HQxe5C5DiYweKYov z+WkV_&;-R>d%)MInWYjQSzBg=?A$=iLQ@(bwp0hYz2^;dyM2c8d7S{D8t0wuA@G0Y zSCqnUN{+(g7X)@cOCe+p#M23``eZdn2AufE{yNX7KH#epb10FqaZM-;-4CF&YPbPB z16gQL85p1ZeP!3=R%yG4c%AfiIqZK?T96QoopeOqC zBm5Bsdpi?e+Fd(v>i^M>V7U7H_*Bk5xBoNetwW#-g+2m#ckgWoW)Ij!u>B8#RO;LA znWO)62*p69U99GcTq#q5y|mN+C#Had3!|-jDkIfUGoRZx?H3k3w(7U`iG^v^&V9vVscQ8=I z6}9nf(PGpPlS`k&Q$!)nSv9j$wffgu7XDcrD=M=7ApqNVuMF*1-GwW{FFe*wfwY0R z)PBo2kNW?Gi){k6vHcjrw!o}@mEv{RCPu`{jZYJ4IcBSI!n3~1Eljn3dZDWF5-;>m zOm-b+^P1L)sHP3px1TsaIlAxQ#@8*TT075bD}`F!iSy3JX^Y1rV8}DF`nw5zIc{|7 zqrQC$;t4oD^ac;-nw;+Z*LpF%=>$ikDhJ0)AJ@iYCe9f=Q*x3Svl!#j9~;-wX0e>r z?lbGT^zB|lFvD!?R9vgW_F7YxYEAlC3DpPpA0+ZquLtr3!Z>15D3sVytb=h6Chz4- zr;R%Kwin(Dczr5csbXR)sJE_^e_)j*`&)UA47XsEuXu5@Y|d);b;Z?b(^t2NWOf9F z&NHsn0LuUFEb@cfPQC+?_X288Wxw}Z>W_bId`G4GjuXAf+ZZv$sn?PT82va*9;QxA39)Za_j zusE^X>@9Zv?@Iwh>*v*;CnWq+yX0EUaix{PF=3T}^z@#Zf^~c1>+I$p%WA2O58v)> z=ocNpAXYp1?&a*;7|uW(l^5$h`0JjySz1Tj__sYn)j5%~4u(cnJDovqbIM2uQEGut z7nupY%kEj!8~R0APDydJG{w&cW=;-R52LMQpl7^dJgsjC}}Z-_|}#eSh;Pz|<@} zyjRqh*cto0V3MTkqnY5MJN09#HHa^LVN92bP=qQ!_ZTo%GjcTPJ1Q%>C}c+i{Jz;GkKjX;+@+ zKBXl^|I?X;f9)4O!jvaaxl1sg1FVx~lr*l}4L<(EjOh|aTdBPNsg|bv zMz}c8I`%6$@3;m<>2Uc}{;cq<-oUhJDOGp)M+uNp-5agpB21vl*0Li~4V$Zk zsoDB+kEbuGIAmVXsAsXXur~KSV@Z{#rdfis?o{%uu*z$R%)VCoGB%4Rhg;`A&y=b~ z1!l6-_VU?K(G*|-)=A7;%qg> zplQEqdAdo)SzORWk4TLt0d!zWxWYJM@!MFG|IwLb<5ynrvA z3AZqP{Bd$XZmv;8*mx%d9^cY;0 zb%}(cBL~#y-e7<@xzHQY=O!OSDeQ2$F!Lu5B^%ZI*l%Tqlk=QR4QxQVN=nhj-_Zas z222+pdPrI|Pv%v*FB@K0$$2X?id<26^XHmUFdguBG#i4-;!8h<`zm?j#c)x})5Vkk zLUMncNquFgUF*pZUJs12x4fVTCn%i`vwZ-M-j8nMPU-3j5qxqOKRUdoKRRzc%STmj z=6F(KXrv>^tW`Llq|QwPu3L)GO(Ij4I4M z=tbQ`6jOb9@VYnJzSJxg>tdc){(58<5v`{Hh^fc@ZO#t?J_{IF-j5xnmX4%wyv@m7 zB4jLTx=qi-Y~7Mt58}58u){4>5AI311IRCbFIsP3_Xi{{SXX4*J~r#VTCf#%gr+(; z>QeLo|21&&D7W}`#cTkIcZQV|C+PTo!rO^ADehZrVFAQ@JcgH6b(&J_>5V9gH^tquS%S)YL6d}`iMvnd~1XY zRCg%tOU0`Ag+L`G_YVNH{+Re&B{gG>!NZ8o;5Gg9fX@?Ne3WBTeg!+NN}Whk5AAZ# zXotPwjdfceI`TOf0#-QUx?XduBXHZD>vAxWbi-J@&M{hHMiC>JlT2tGX8?9mN+9n| zH9L)uz~LufF!Xk7aZQ2;d*t0a11CYv6)zOgn#aXzTN9?`>BC>Mj&Td+ZH3){d4$xn zYaZ5xIM>y>tesQ5}p`X=QB@{)qKUaH}vZnWelu+zRDrTXYFiO#;u7zISN z=f;mbG<)j9V5+gg{=DnI?Z`kJ{(xG*mTd%?k;7<12auU^R934C{I)V-iSX6Y)i8#B zsk4r>GWnv=xAjKcm~{(&q3U{cVlj>mR9$uG*dN%9$Rv6&EM*s$)M)V>9ct9PfPjXg9V_ zlY4T)Va_>U%fD3O+^!Du8fs0YX=Q3tWEhX?BZH4C$4mzq(K}4&fYREtFe?;uao=8) zGm(K>)Yoehn)ilUC8b<)~g zbK;1a9o)HGUWy{im*sz1Z5+1v1UIRwDDXF~l1M5p(;YP&Et4T+9;o%;vo}R60iXEx z{PPTUS~x$vTW)x3Yo00!%bl2Q{;udaZeVKE3a^XQqtv0Z!T=X~r#$EF{K=2jrtlCe zW)FvROus;AfyGUr$KajxDnJ5Lr82U7& z;UXS%`O$VRzK`@*hwDR|*UZ!#58+p9;%25~mdz3Bh`5E&!4#05>e%cHy?OmrA!L)KsnyP(z0#>3vlY=$HD9TY9x30iCkD!(1=7Tc73s&30#Qb*7KS6Ft_S? z^>;7S-U?^@x9EEXOZ*uljBfT@ry-pD_1l?yix|Q5tIeMNX&#z31hc5GO1B|oq&b~W zhGoJFJ{t{lCeUS`cEs|LhfZBEj3h3c&kWu#vYaQayvd%mUpY+_!mf{$dx8u4+**Fq z9*#pW8y4b49S7vol(>)O!4Y9rDiz)P0|fDs{NmW$>Iz6cKPQ*e4lDFU`9M9r_Zp=3 zXB{UTmmq(q(Bq{OfLOaytk2TA5JLWncilKE%&lFySC-&A((=?9kb#?9h$cnCQveWE z;zfKpT&vmraX4A@hYcz(zSb_F00o;C2T&5${Q~WDKxX(J_xPe37R!q&=1RTP_J`#z zX{A{7AR>WW?BxMt3@96l$w|02_`Dbg&G+joLA-&yWvz-Gya(=ulO6vYLlMO$e7@<- zi>`xQSRzrLXr5IkspyybG9PU-&4so0?U+SV=THb+tF{4~~jlR_X(InOPb=fuWVD3F2 zC!EOpQcQ;?D{NYr%CSaU!jF;UlSUS2=HDW@4^8WtJ|+^sW8baW>;+wByj)h&0H9tRrS3$j8>w zeeoUX{-$)W#Nj~8W`~3HOI256#bz4NSS%h@JE!aD^38aaka6uxH@nfZ)5OnMZ>kLk z=+aJOx7d@bF9g!+n zEEj5?`;a>Qp$UwksdBf@4nmSl?sHXyjIg=`RG79w{aPj+1YtLsi#%YFNT%QyIvU-=ok=Tg ze`oplNEqb+>3txhpVsyo@gNG`b)rgWCLye7FjW>RF^HcUXQ>5n-KwW*1ZEx(u-HAQ z>VriYocLFBLWm?GfOT&LiYzql`;EPV)gQ8k!!3-Dn5rug?x^TXBo3jWY>3wa@i_2X z-@BwpVuKNVhDf;|5fL6hTAA(Ef@v$CD@H(D9E6!kc~vt2dVXV=Dm3voyg5JEd!TV9+ck<=`E5m$g{X_tqYKt_+wyle3#f&I{@Sx~PO^x=M8 zN0CGrg@Xvx{6R~=C*nUNjCc$e+G_EfUKu-HW{e9F<^{}_s~Z{~&wM~E={wD*k1_BIw|B!_t z27Q(?e#xYa7jIcDgH%+QNb<6rCm`Qk`x}c`y*#FLLKzYueM7(PAH#v>156-)Fq8p_ z7&f@CGttj2^gVF+D3_LJi0>VxJ{BDS#4^7|ItCyULVlJJMRuEO%~K_0{B2c797_^Y zbKO)F(okHVBQP_45R1j3iv0zOH^Ra$l_wxM2L*cTCoz!TU;CSh7n~-LE9i#gN~{2p zWFA+J1TI*U*S=;qxBuc%GFCG-fNZsR=gao?G;iU<}W=>N|b1@MA8gHX@!2{u$NaA9@B!chadatN80 zY*aI#q&4>)BA^ooa z=}d%l`bI>6q2wW{Ogiun%i7HN9)M@H}pK zxuhE2R>A5->r`;DeyA6@sowKZ@2X^^&3 zjyN}zw*0&hRG38dAaX4*pm>iMT!8C3<~skZ(tB*n9-6h4SAy7#HV!9%9#VdX@NC3< z_<#C*aDP{y%K@++=R1d~`5ja<%}?O);B!G@| zf~j;<@|#_|;&1MUcD4Dz^tyAs!CumFVBE*E=d9#&Z__6W-BRSDv}eTVvC3l{Oc?IIQ^CFY|4`DT5R`9Xa5 zdtUUZOlXoMO30=Zv}u($U;fcotZaM{hK_=M?GheGdKg=%Z$2Wyfo*Pi_g2I$w7sD3 z_9RLFAtD(m7KL&%-v<#mIW3~7WHK2qxe#4rp)bRQx2#ZbL@Y|3^0Me4q`)Z&W|?Fv z-j#1E&yzo6IZf+}Z{Yz93~%GGosge0qvc=hiChbbWxWP4{<%C$TG%xb;=CF^2C=B-TRAang_ZdbUiIjoDFbq)L31g?htJ&i&exZ|Hu-l$gTK7@* zBV<&a!k7Syp4^q;nCcR7W)76{9&cJEJ%o_HYHii?P8PYe>MD<+K*l%qw^jcPyWV_R zL_YPV1Yu(=c{f) zSzi`_cZhPy&?P5z68nHrS|cJs8@%KTVP=Tp4;vjw#b6@H{hNGzcrj<;qz-6N{5G~F z@IEjs5|FW&`4^tB7E6b!PqHnL zC1j|#3c|Mlc*d6Z*@J-e$}hnShTrG#!Hq~!VmNHfiSOuscFKO8KZN|0Lz9f6I8}E7 zzMHLsv+Pi{6#UokW5O^QVZ82@{EwHWSY?>3=vpGi>y6nzFe%iJ?|_ZT2XAKK^AeGY z{hm^eBC_}mtui4A({Sbvv0zrxz7+RcZKh+bWRlN3c-dVMqg$Jt3f+Q(RYgex7OA}BcQj`*alSJJONxs(e3|= zt6qsW>ZB*L$x)`J@R1WboHAzXPah0;WvL4PBPn7|M95@`M0%1}@N4Qv<3Uv*0^cDe zv-1GB`6`Y-#iz_1sUtOe6bKnj?we1aBe#nP+E@S9rRxY$-%T_SPDYMWTSLO{sNCnm zkKNLj2>4L50Y4I`XZI;y%tVn-iz*{?WYk5Q2yVUumI#=Bu;;zgM_>C%zSoUGwp ziNzj94d2brT7U&uN9XeP+K#6CMfm1SP5MIsxno1_p}Uj>Rj@s&P`Q5yZ&`bEV)w~G z1l^X>57h??t+h=D#B<(ZFLz9ahXofaOx&a%L7et3=6S`bJPu}X;`?aQ8z^~#&1YXt z63bm$HNjS+o&YT&GqstH?7OZ=Rp@=k3SR@?dA{K%>@B~pbZeEz0p57`V#q^>%(|>x zQACxMl-osx5vP%2^;#W-5^sO3sv;`5`?!x&MXQzxy- zb}|Y__RvIN>&vyb=mf}3Fr;^&jEN(Ku-7YB2of*u@lOKab+P5mcUVk(2)TJ&336C<(@C%A zs~)m39GmD?iUmKr4m0?b5Q#Ckg|~Up%KD^>8l6l}yP9`N3Z;x(W8f^zV_C8O#h-YH@&`# zcS$phSh*Z%U$wschC({<^Lw@w5|0C;$@}c3c1UCP)v0yTqGFC=@Fu&Ug1KwwV0iKX z_&xu~*K$8v4|!)7A}edrs-Y8Ntn#qduF=TN_i{nyVS+wd$aC`IvwD$ zhjE?6j5@^3y24BcR%&m0nqQ;W%N-U zPK7*UtAx_wta+6?v_nz51^WUJ(Z{uM5BO-|?-KEpQ6G(bTKzgfYixJuPEM*pw+{enTuV$7g1(Q-ZQuM-aNpT8J4fNiS+E@)H4KX9^}=mF$3T4L!No_g2x$%#&}mW z_)-0X)xc*lfHwbh%#K0r4EBiG4E6>yD{A!5;jU3gTho@JO)G4CU$n1JEtEOVQu;`b( z;^Dz45GcIBToyv!^xJAm3#$rG4XBg!f-_d+e9i( z3yd}4cnAy8NkLyNG2!tlv55wvK+J6Z4*PO1#$3h_}QX~lD_`CPT%mTPf%o7&;OZW@|o_WDix zl;(Ww^e^B_ksInUXMEzmA*OEH>orUG#?FNU(W=r}0yBzI>w#;|>&vWN zwDI6NrM#>InwADm?&;0l5{Fr(srnyXid5kvhn1@8i=w!kdCP?ea?K@Q*M2REf2}9qO(9qf4r`q=BBf=}@=j>1E38kJc0Lj(^O= zX50;54Zj~t@w1c1dJmNqvCS`&a(Z^|<>)>j0+V&AlZ zLzOK9`!+rm#>tkw%V_F}U2~s*kTE2A%2s38Vn4i-+k8*F_-JYpyB92c_GwX^T9r!( zC0G0yarj^#)7YvqxnmsdYiSba@hb4eQOb`fdVhCs1Lk74uGnQ3yw0=TfdfeQqyl-Q`&CK%# zW;MN?gApp*_Hj8hp3$ipe&rOkhR)(+K2*FM_&1 zMud!(+?P6v?iFyFu&%<#ebc~}L@5v^4ATjATcuL9F*tvibcE)pJx&M$ZaKp}+t+L+ zH8i6vQ+H$oo&{=UW=1?^S$&F!^<87iY2w{9Xt1Z1mg0C}Eoz>Syci>dZW-+IpFd*J zbzgrV`E7gDY{eeW;}$9JtvKNb6kGR~rz54lWd4Oa=x(QQTQ&sc+It8e%ot+dV@fQt+qHt=l z%I+cm%ye9!Obl@IF_oKG4d2|iv9X@vY+>)%W2G5db#sFAsCr}P%S$G<<$YB*ZX2Ze zoGO-@TV|xuY9lL4@QHe$Z0rDhF z4&I!l-1ds7?40{NbAb8q%9{>)fB!QEG^y&Wf`oYY-katja{U|?tsPy1(QK*{H%v1{ zX_sUw&AduCQytvP7Sc2OHmhirw+C(==(m{}XBD(8&CN<{6`vM8w9l88TVtgtlw-A% zJG@wNT>}|Tg1x$`Jt^8t1<&B|PX&t#3!GpbMdclS73g0%I$s`mr-Jsln*TZj0*u3d z^}2RO)Z7V9oh5oLUp475E)P7pJATJU?a}pt@;x+ay#}?#2M0BMoVAN49`2e5oWMgq zQlID_>rS2!4Fu#PG|^USuN-=Jv%iKwX-yv+3WVR56PxAS{z zql-`oRuVUA*eX7DLJJN7o9w_3@iAmSB2kQwYWcWoy&wsYfBuNtXK8AZ_@+1zvpbiW zDpzi_X&8+K7oJ=HwZ8JK!0+Gp4dvvoTOS1xUOYR`1a5Id;s1}99Vf_3`uCXnL;U|> z1L{tQq5(^%^JQvN)sIC9jsNi&aLG)t_Yfp}jB%%By%Ek704S%5sCmN-DrEiB>zP(j z+7X!PLrNCn-e{>Lc&Y>P)$e7?t^W@?h+nbnWjDh8%)0gX`r-c_xv_tytK}?0-Vx6z zc3R>;B$8K63?S=^F?jSYtrcp|4#S;SdRvySS>9w&+Z{!O&$91M9f$M|EP8E6u>XTp zGfy5X-b8_`Tch{>4Y_);UHetC10~km?FPx&huS5+xV3cw(a=?;orHHgNF)J}C0b$% z?6g1|{i(Eb^6WotTHBw3yRY^cyFu3fw_BpUMaabuDlIbbJaj!sM}N6v6!^Ew>}x8v_kT8uf200PvokD}(BPc)l8U9mI>+Z~046XDeY#~YWC zm8UT~%!BhW^?|ox*aXm~N{oHpY{P~A#MRWoh^>fy|8N~(_L};0fF2QF{YK!Z6D*Hl znE=p4Vwz@^^v&zgfP@-aha4W%@4#yc9xv~9BVnAY1X0h>8jn<__3%F(*Ic`%J!6i{ zZ6{8=`?jy>WQcYB|5mmaOPSm&;hY+~CgD@Iep8B{y&>ztew6MA3r)XOr1E7I8 z2lT@l1fclR|KJxw{wl!Io+b_7d_G=6UVI>UmpFRkfVRWe@eDl?_+6CA8=3+d$|2Vd zQ*)DKRuHYII0f-%?=!5AIE}DX7EUg1`z&m*K!P)p@ta#8Vb5*%`#o|U^Go;5jSLHY zl?cdk`WVtffoG6-P%a!PJsWJAP;*WBq7o@S3cED1;TA%C6tZ=8=k>p-cfzqogs%)Z zP`3Zejh{nt@MfVmyRPTE)4gkbLIkSX%dxKb+pI9b3u`D`hRrXy1ILPazU^xEvHa;T z;a`we|3EGTJ>Om|jLQlt33t@t;|qcH)|6DpE~VXPD3yT$-SZD&EU?oV&(>GF?112b zSA^dst5#Ee(iouNpRntr>r7%R=yvxM`%7mgG2VK@9_yslBuoy|D1O{ld2si12K2^Y zLeE(9zNV+jc=`)Hab3b0T{FH5a8DkzeXV{#N_m8o0zcD%K+xWHVowmQc=9MRZ({S$cMU?~!RuUaaKENX?`74JdkA0g9HGEX&tyS8GB@-Dz zbR^)XU=v55GVrkT9D0;ISb>+^n@2Ao{G*@IUd8Y3|8{>VE|>_e6Wn_{{r)+A{&Zfk zbNLeQlO!<56ohRsM^GA+$==0$-#-CwK2MYhi=Ch5L`m?K1-AzVZS(APSA@UH1}RW; z`KY9eTeC4)bKQnE4F-Jb^B*)V|)1VwYr|@lil=5H@vYoC-u$ijnsC0RKeXA)&o!`cg z?{rgAT^fCPz3A6PZ)AH7_VVlwS|T70hc+A%pa|d}ImsL9MXjTl0+l7Fg#Sh(!kctA zrBhNi&6@8OHjF+%pMm}S=ZImOzcusWI<~~+t-I=Eybb2Oj|W|2oz>5`!)R8b@2@qK zD;w(>k>d*#zaZmcDJY%tD6*ilglnhYNsnwIeQev7Ke%<8K2CMxnCn7jkgy)7KRALC zV?j;)l0=K&`iq_P_2E}iG5%Sme+X@;-oLeml2pMx61nJvzv;{Pw7MUP-0T>dtuC9A z?ca3QocEE)W$<#-A6(-MzDTdSuITzhDu}&Z7M}c4o$|{>wq)(C;Ly&GoWI6045*GY zI;w1zh++81!=Rwqd8YGs!yylm<{0<7tk@_Xkvqfbi4X1&^G{W5S%Tlu7e7^C=O%eC z*PEwzYH3h-uVdWTCoa|Je3sd^t;;`f&iGbu{XQ~#5KCT@zxCnIsfRBYnOcBtX(Kfr-AS_q*JkvU2;1MVUd-5Af%;9qa!mZ7kywh0zi#svESlwA zwsT5PiQPbVOroh4{la-K_Uj+m%e8X3>G|PHIuKII($c0lr&(E1;6*vhjm9ju1{Tmv z&cfMHD}tJb_TF-LY|6?p)<$qjh`;`$A}Bu1zC0%wTRk$?hluS)+MUXdWbSeBR*n$yKb7q_#sNweKYUS{Xl`gEj(PkybN!cg(@ zyw4>^sCCJ{PwG9jIsdV{G$<<(#wN}Xzg?Qoeo%_GdH z7F*ILw&$&N{MV7L=@>H8M110G)4`b^#^coMwnft^3tM6F?STjGk+YuOrmSNqEJW@& zGu;>aL+bV^O9{AI7RMj5I@k9%D8BpV!SO2TD;ZMDyeP|dv6H>Vf|sgawdlqC$F*kI zQs#d!mHp|d$xUjGM|O`1<9St8)T7yB0%IE31vgv1h3v$2tS$3^80lE1x9%ntBgIEYEo)p!H-@i!b>rxO_+}?&8BsF52Ll zA2<9_%Zf4MB?E=yTwIoRiJ2P&PC$UE^n5pqz-6X$u>x8?Ptnb?*TJ=F2p@mF0&U>N zNI5|iixUuzxfzsXMgS!#_XM<>G7kmiW~0-k7Eo|Se~7WF^cuKk&T|f;^LYeBfq@Pq zv0N0v=c<4!y9Cbf-=8I>6_*u5)-saDcHpY9I%#K^VAx@Su$j zUz0hwri4Hrj0PDs48-yRNKWh2UcM$j;11?UaRM@-)j>>8aG*~S#e7sNF8QZHx)jb> z8*F6``hspzCLpwv<|NU3%P)!9=XQB*Hv5&xw;ykrUn!5B$?h9itM&YFYktU2Z@a_? zz1K#xrR8^{)IfMJwu1wtgC6Rpl$6xlRiQ9kD+(-K5w1&tzy0`%sCw@PGkC<W8eijE9sV;fZmEQO2 zV<`(~LtR{52!sKQC;FBwK;&9^wBWbvmQ6 zvR9N#E-=AgEm8m?!BwCC11*%jzbUtWXfVzFoKoXvfIuU{a|Ff;S1zU3=+2WMDK~ { - return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) - || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4)); -} - -export function isDesktop() { - return !isMobile(); -} - -export function msgbox(type, title, text, link) { - if (!type || (type == 'error' && !text)) return; - const text2 = text.toLowerCase(); - var hasRetry = checkIfRetry(type, title, text) ? 'true' : ''; - onGlobalEvent(JSON.stringify({ name: 'msgbox', type, title, text, link: link ?? '', hasRetry })); -} - -function jsonfyForDart(payload) { - var tmp = {}; - for (const [key, value] of Object.entries(payload)) { - if (!key) continue; - if (value instanceof String || typeof value == 'string') { - tmp[key] = value; - } else if (value instanceof Uint8Array) { - tmp[key] = '[' + value.toString() + ']'; - } else { - tmp[key] = JSON.stringify(value); - } - } - return tmp; -} - -export function pushEvent(name, payload) { - payload = jsonfyForDart(payload); - payload.name = name; - onGlobalEvent(JSON.stringify(payload)); -} - -let yuvWorker; -let yuvCanvas; -let gl; -let pixels; -let flipPixels; -let oldSize; -if (YUVCanvas.WebGLFrameSink.isAvailable()) { - var canvas = document.createElement('canvas'); - yuvCanvas = YUVCanvas.attach(canvas, { webGL: true }); - gl = canvas.getContext("webgl"); -} else { - yuvWorker = new Worker("./yuv.js"); -} -let testSpeed = [0, 0]; - -export function draw(display, frame) { - if (yuvWorker) { - // frame's (y/u/v).bytes already detached, can not transferrable any more. - yuvWorker.postMessage({ display, frame }); - } else { - var tm0 = new Date().getTime(); - yuvCanvas.drawFrame(frame); - var width = canvas.width; - var height = canvas.height; - var size = width * height * 4; - if (size != oldSize) { - pixels = new Uint8Array(size); - flipPixels = new Uint8Array(size); - oldSize = size; - } - gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); - const row = width * 4; - const end = (height - 1) * row; - for (let i = 0; i < size; i += row) { - flipPixels.set(pixels.subarray(i, i + row), end - i); - } - onRgba(display, flipPixels); - testSpeed[1] += new Date().getTime() - tm0; - testSpeed[0] += 1; - if (testSpeed[0] > 30) { - console.log('gl: ' + parseInt('' + testSpeed[1] / testSpeed[0])); - testSpeed = [0, 0]; - } - } - /* - var testCanvas = document.getElementById("test-yuv-decoder-canvas"); - if (testCanvas && currentFrame) { - var ctx = testCanvas.getContext("2d"); - testCanvas.width = frame.format.displayWidth; - testCanvas.height = frame.format.displayHeight; - var img = ctx.createImageData(testCanvas.width, testCanvas.height); - img.data.set(currentFrame); - ctx.putImageData(img, 0, 0); - } - */ -} - -export function sendOffCanvas(c) { - let canvas = c.transferControlToOffscreen(); - yuvWorker.postMessage({ canvas }, [canvas]); -} - -export function setConn(conn) { - window.curConn = conn; -} - -export function getConn() { - return window.curConn; -} - -export async function startConn(id) { - setByName('remote_id', id); - await curConn.start(id); -} - -export function close() { - getConn()?.close(); - setConn(undefined); -} - -export function newConn() { - window.curConn?.close(); - const conn = new Connection(); - setConn(conn); - return conn; -} - -let sodium; -export async function verify(signed, pk) { - if (!sodium) { - await _sodium.ready; - sodium = _sodium; - } - if (typeof pk == 'string') { - pk = decodeBase64(pk); - } - return sodium.crypto_sign_open(signed, pk); -} - -export function decodeBase64(pk) { - return sodium.from_base64(pk, sodium.base64_variants.ORIGINAL); -} - -export function genBoxKeyPair() { - const pair = sodium.crypto_box_keypair(); - const sk = pair.privateKey; - const pk = pair.publicKey; - return [sk, pk]; -} - -export function genSecretKey() { - return sodium.crypto_secretbox_keygen(); -} - -export function seal(unsigned, theirPk, ourSk) { - const nonce = Uint8Array.from(Array(24).fill(0)); - return sodium.crypto_box_easy(unsigned, nonce, theirPk, ourSk); -} - -function makeOnce(value) { - var byteArray = Array(24).fill(0); - - for (var index = 0; index < byteArray.length && value > 0; index++) { - var byte = value & 0xff; - byteArray[index] = byte; - value = (value - byte) / 256; - } - - return Uint8Array.from(byteArray); -}; - -export function encrypt(unsigned, nonce, key) { - return sodium.crypto_secretbox_easy(unsigned, makeOnce(nonce), key); -} - -export function decrypt(signed, nonce, key) { - return sodium.crypto_secretbox_open_easy(signed, makeOnce(nonce), key); -} - -window.setByName = (name, value) => { - switch (name) { - case 'remote_id': - localStorage.setItem('remote-id', value); - break; - case 'connect': - newConn(); - startConn(value); - break; - case 'login': - value = JSON.parse(value); - curConn.setRemember(value.remember); - curConn.login({ - os_login: { - username: value.os_username, - password: value.os_password, - }, - password: value.password, - }); - break; - case 'close': - close(); - break; - case 'refresh': - curConn.refresh(); - break; - case 'reconnect': - curConn?.reconnect(); - break; - case 'toggle_option': - curConn.toggleOption(value); - break; - case 'toggle_privacy_mode': - curConn.togglePrivacyMode(value); - break; - case 'image_quality': - curConn.setImageQuality(value); - break; - case 'lock_screen': - curConn.lockScreen(); - break; - case 'ctrl_alt_del': - curConn.ctrlAltDel(); - break; - case 'switch_display': - curConn.switchDisplay(value); - break; - case 'remove': - const peers = getPeers(); - delete peers[value]; - localStorage.setItem('peers', JSON.stringify(peers)); - break; - case 'input_key': - value = JSON.parse(value); - curConn.inputKey(value.name, value.down == 'true', value.press == 'true', value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true'); - break; - case 'input_string': - curConn.inputString(value); - break; - case 'send_mouse': - if (!curConn) return; - let mask = 0; - value = JSON.parse(value); - switch (value.type) { - case 'down': - mask = 1; - break; - case 'up': - mask = 2; - break; - case 'wheel': - mask = 3; - break; - } - switch (value.buttons) { - case 'left': - mask |= 1 << 3; - break; - case 'right': - mask |= 2 << 3; - break; - case 'wheel': - mask |= 4 << 3; - } - curConn.inputMouse(mask, parseInt(value.x || '0'), parseInt(value.y || '0'), value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true'); - break; - case 'send_2fa': - curConn.send2fa(value); - break; - case 'option': - value = JSON.parse(value); - localStorage.setItem(value.name, value.value); - break; - case 'options': - value = JSON.parse(value); - for (const [key, value] of Object.entries(value)) { - localStorage.setItem(key, value); - } - break; - case 'option:local': - case 'option:flutter:local': - case 'option:flutter:peer': - value = JSON.parse(value); - localStorage.setItem(name + ':' + value.name, value.value); - break; - case 'option:user:default': - setUserDefaultOption(value); - break; - case 'option:session': - value = JSON.parse(value); - curConn.setOption(value.name, value.value); - break; - case 'option:peer': - setPeerOption(value); - break; - case 'option:toggle': - return curConn.toggleOption(value); - case 'input_os_password': - curConn.inputOsPassword(value); - break; - case 'session_add_sync': - return sessionAdd(value); - case 'session_start': - sessionStart(value); - break; - case 'session_close': - sessionClose(value); - break; - case 'elevate_with_logon': - curConn.elevateWithLogon(value); - break; - case 'forget': - curConn.setRemember(false); - break; - case 'peer_has_password': - const options = getPeers()[value] || {}; - return (options['password'] ?? '') !== ''; - case 'peer_exists': - return !(!getPeers()[value]); - case 'restart': - curConn.restart(); - break; - case 'fav': - return localStorage.setItem('fav', value); - case 'query_onlines': - queryOnlines(value); - break; - case 'change_prefer_codec': - curConn.changePreferCodec(value); - case 'cursor': - setCustomCursor(value); - break; - default: - break; - } -} - -window.getByName = (name, arg) => { - let v = _getByName(name, arg); - if (typeof v == 'string' || v instanceof String) return v; - if (v == undefined || v == null) return ''; - return JSON.stringify(v); -} - -function _getByName(name, arg) { - switch (name) { - case 'remote_id': - return localStorage.getItem('remote-id'); - case 'remember': - return curConn.getRemember(); - case 'toggle_option': - return curConn.getOption(arg) || false; - case 'option': - return localStorage.getItem(arg); - case 'options': - const keys = [ - 'custom-rendezvous-server', - 'relay-server', - 'api-server', - 'key' - ]; - const obj = {}; - keys.forEach(key => { - const v = localStorage.getItem(key); - if (v) obj[key] = v; - }); - return JSON.stringify(obj); - case 'option:local': - case 'option:flutter:local': - case 'option:flutter:peer': - return localStorage.getItem(name + ':' + arg); - case 'image_quality': - return curConn.getImageQuality(); - case 'translate': - arg = JSON.parse(arg); - return translate(arg.locale, arg.text); - case 'option:user:default': - return getUserDefaultOption(arg); - case 'option:session': - if (curConn) { - return curConn.getOption(arg); - } else { - return getUserDefaultOption(arg); - } - case 'option:peer': - return getPeerOption(arg); - case 'option:toggle': - return curConn.getToggleOption(arg); - case 'get_conn_status': - if (curConn) { - return curConn.getStatus(); - } else { - return JSON.stringify({ status_num: 0 }); - } - case 'test_if_valid_server': - break; - case 'version': - return version; - case 'load_recent_peers': - loadRecentPeers(); - break; - case 'load_fav_peers': - loadFavPeers(); - break; - case 'fav': - return localStorage.getItem('fav') ?? '[]'; - case 'load_recent_peers_sync': - return JSON.stringify({ - peers: JSON.stringify(getRecentPeers()) - }); - case 'api_server': - return getApiServer(); - case 'is_using_public_server': - return !localStorage.getItem('custom-rendezvous-server'); - case 'get_version_number': - return getVersionNumber(arg); - case 'audit_server': - return getAuditServer(arg); - case 'alternative_codecs': - return getAlternativeCodecs(); - case 'screen_info': - return JSON.stringify({ - frame: { - l: window.screenX, - t: window.screenY, - r: window.screenX + window.innerWidth, - b: window.screenY + window.innerHeight, - }, - visibleFrame: { - l: window.screen.availLeft, - t: window.screen.availTop, - r: window.screen.availLeft + window.screen.availWidth, - b: window.screen.availTop + window.screen.availHeight, - }, - scaleFactor: window.devicePixelRatio, - }); - case 'main_display': - return JSON.stringify({ - w: window.screen.availWidth, - h: window.screen.availHeight, - scaleFactor: window.devicePixelRatio, - }); - } - return ''; -} - -let opusWorker = new Worker("./libopus.js"); -let pcmPlayer; - -export function initAudio(channels, sampleRate) { - pcmPlayer = newAudioPlayer(channels, sampleRate); - opusWorker.postMessage({ channels, sampleRate }); -} - -export function playAudio(packet) { - opusWorker.postMessage(packet, [packet.buffer]); -} - -window.init = async () => { - if (yuvWorker) { - yuvWorker.onmessage = (e) => { - onRgba(e.data.display, e.data.frame); - } - } - opusWorker.onmessage = (e) => { - pcmPlayer.feed(e.data); - } - loadVp9(() => { }); - await initZstd(); - console.log('init done'); -} - -export function getPeers() { - return getJsonObj('peers'); -} - -export function getJsonObj(key) { - try { - return JSON.parse(localStorage.getItem(key)) || {}; - } catch (e) { - return {}; - } -} - -function newAudioPlayer(channels, sampleRate) { - return new PCMPlayer({ - channels, - sampleRate, - flushingTime: 2000 - }); -} - -export function copyToClipboard(text) { - if (window.clipboardData && window.clipboardData.setData) { - // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible. - return window.clipboardData.setData("Text", text); - } - else if (document.queryCommandSupported && document.queryCommandSupported("copy")) { - var textarea = document.createElement("textarea"); - textarea.textContent = text; - textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge. - document.body.appendChild(textarea); - textarea.select(); - try { - return document.execCommand("copy"); // Security exception may be thrown by some browsers. - } - catch (ex) { - console.warn("Copy to clipboard failed.", ex); - // return prompt("Copy to clipboard: Ctrl+C, Enter", text); - } - finally { - document.body.removeChild(textarea); - } - } -} - -// Dup to the function in hbb_common, lib.rs -// Maybe we need to move this function to js part. -export function getVersionNumber(v) { - try { - let versions = v.split('-'); - - let n = 0; - - // The first part is the version number. - // 1.1.10 -> 1001100, 1.2.3 -> 1001030, multiple the last number by 10 - // to leave space for patch version. - if (versions.length > 0) { - let last = 0; - for (let x of versions[0].split('.')) { - last = parseInt(x) || 0; - n = n * 1000 + last; - } - n -= last; - n += last * 10; - } - - if (versions.length > 1) { - n += parseInt(versions[1]) || 0; - } - - // Ignore the rest - - return n; - } - catch (e) { - console.error('Failed to parse version number: "' + v + '" ' + e.message); - return 0; - } -} - -// Set the cursor for the flutter-view element -function setCustomCursor(value) { - try { - const obj = JSON.parse(value); - // document querySelector or evaluate can not find the custom element - var body = document.body; - for (var i = 0; i < body.children.length; i++) { - var child = body.children[i]; - if (child.tagName == 'FLUTTER-VIEW') { - child.style.cursor = `url(${obj.url}) ${obj.hotx} ${obj.hoty}, auto`; - } - } - } catch (e) { - console.error('Failed to set custom cursor: ' + e.message); - } -} - -// ========================== options begin ========================== -function setUserDefaultOption(value) { - try { - const ojb = JSON.parse(value); - const userDefaultOptions = JSON.parse(localStorage.getItem('user-default-options')) || {}; - userDefaultOptions[ojb.name] = ojb.value; - localStorage.setItem('user-default-options', JSON.stringify(userDefaultOptions)); - } - catch (e) { - console.error('Failed to set user default options: ' + e.message); - } -} - -export function getUserDefaultOption(value) { - const defaultOptions = { - 'view_style': 'original', - 'scroll_style': 'scrollauto', - 'image_quality': 'balanced', - 'codec-preference': 'auto', - 'custom_image_quality': '50', - 'custom-fps': '30', - }; - try { - const userDefaultOptions = JSON.parse(localStorage.getItem('user-default-options')) || {}; - return userDefaultOptions[value] || defaultOptions[value] || ''; - } - catch (e) { - console.error('Failed to get user default options: ' + e.message); - return defaultOptions[value] || ''; - } -} - -function getPeerOption(value) { - try { - const obj = JSON.parse(value); - const options = getPeers()[obj.id] || {}; - return options[obj.name] ?? getUserDefaultOption(obj.name); - } - catch (e) { - console.error('Failed to get peer option: "' + value + '", ' + e.message); - } -} - -function setPeerOption(param) { - try { - const obj = JSON.parse(param); - const id = obj.id; - const name = obj.name; - const value = obj.value; - const peers = getPeers(); - const options = peers[id] || {}; - - if (value == undefined) { - delete options[name]; - } else { - options[name] = value; - } - options["tm"] = new Date().getTime(); - peers[id] = options; - localStorage.setItem("peers", JSON.stringify(peers)); - } - catch (e) { - console.error('Failed to set peer option: "' + value + '", ' + e.message); - } -} -// ========================= options end =========================== - -// ========================== peers begin ========================== -function getRecentPeers() { - const peers = []; - for (const [id, value] of Object.entries(getPeers())) { - if (!id) continue; - const tm = value['tm']; - const info = value['info']; - const cardInfo = { - id: id, - username: info['username'] || '', - hostname: info['hostname'] || '', - platform: info['platform'] || '', - alias: value.alias || '', - }; - if (!tm || !cardInfo) continue; - peers.push([tm, id, cardInfo]); - } - return peers.sort().reverse().map(x => x[2]); -} - -function loadRecentPeers() { - const peersRecent = getRecentPeers(); - if (peersRecent) { - onRegisteredEvent(JSON.stringify({ name: 'load_recent_peers', peers: JSON.stringify(peersRecent) })); - } -} - -function loadFavPeers() { - try { - const fav = localStorage.getItem('fav') ?? '[]'; - const favs = JSON.parse(fav); - const peersFav = getRecentPeers().filter(x => favs.includes(x.id)); - if (peersFav) { - onRegisteredEvent(JSON.stringify({ name: 'load_fav_peers', peers: JSON.stringify(peersFav) })); - } - } catch (e) { - console.error('Failed to load fav peers: ' + e.message); - } -} - -export function queryOnlines(value) { - // TODO: implement this -} -// ========================== peers end =========================== - -// ========================== session begin ========================== -function sessionAdd(value) { - try { - const data = JSON.parse(value); - window.curConn?.close(); - const conn = new Connection(); - setConn(conn); - return ''; - } catch (e) { - return e.message; - } -} - -function sessionStart(value) { - try { - const conn = getConn(); - if (!conn) { - return; - } - - const data = JSON.parse(value); - if (data['id']) { - startConn(data['id']); - } else { - msgbox('error', 'Error', 'No id found in session data ' + value, ''); - } - } catch (e) { - // TODO: better error handling - msgbox('error', 'Error', e.message, ''); - } -} - -function sessionClose(value) { - close(); -} -// ========================== session end =========================== - -// ========================== settings begin ========================== -function increasePort(host, offset) { - function isIPv6(str) { - const ipv6Pattern = /^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$/; - return ipv6Pattern.test(str); - } - - if (isIPv6(host)) { - if (host.startsWith('[')) { - let tmp = host.split(']:'); - if (tmp.length === 2) { - let port = parseInt(tmp[1]) || 0; - if (port > 0) { - return `${tmp[0]}]:${port + offset}`; - } - } - } - } else if (host.includes(':')) { - let tmp = host.split(':'); - if (tmp.length === 2) { - let port = parseInt(tmp[1]) || 0; - if (port > 0) { - return `${tmp[0]}:${port + offset}`; - } - } - } - return host; -} - -function getAlternativeCodecs() { - return JSON.stringify({ - vp8: true, - av1: false, - h264: false, - h265: false, - }); -} -// ========================== settings end =========================== - -// ========================== server begin ========================== -function getApiServer() { - const api_server = localStorage.getItem('api-server'); - if (api_server) { - return api_server; - } - - const custom_rendezvous_server = localStorage.getItem('custom-rendezvous-server'); - if (custom_rendezvous_server) { - let s = increasePort(custom_rendezvous_server, -2); - if (s == custom_rendezvous_server) { - return `http://${s}:${PORT - 2}`; - } else { - return `http://${s}`; - } - } - return 'https://admin.rustdesk.com'; -} - -function getAuditServer(typ) { - if (!localStorage.getItem("access_token")) { - return ''; - } - const api_server = getApiServer(); - if (!api_server || api_server.includes('rustdesk.com')) { - return ''; - } - return api_server + '/api/audit/' + typ; -} -// ========================== server end ============================ diff --git a/flutter/web/v1/README.md b/flutter/web/v1/README.md new file mode 100644 index 00000000000..b9e2fc5c096 --- /dev/null +++ b/flutter/web/v1/README.md @@ -0,0 +1 @@ +v1 is not compatible with current Flutter source code. \ No newline at end of file diff --git a/flutter/web/index.html b/flutter/web/v1/index.html similarity index 100% rename from flutter/web/index.html rename to flutter/web/v1/index.html diff --git a/flutter/web/js/.gitattributes b/flutter/web/v1/js/.gitattributes similarity index 100% rename from flutter/web/js/.gitattributes rename to flutter/web/v1/js/.gitattributes diff --git a/flutter/web/js/.gitignore b/flutter/web/v1/js/.gitignore similarity index 100% rename from flutter/web/js/.gitignore rename to flutter/web/v1/js/.gitignore diff --git a/flutter/web/js/.yarnrc.yml b/flutter/web/v1/js/.yarnrc.yml similarity index 100% rename from flutter/web/js/.yarnrc.yml rename to flutter/web/v1/js/.yarnrc.yml diff --git a/flutter/web/js/gen_js_from_hbb.py b/flutter/web/v1/js/gen_js_from_hbb.py similarity index 100% rename from flutter/web/js/gen_js_from_hbb.py rename to flutter/web/v1/js/gen_js_from_hbb.py diff --git a/flutter/web/js/index.html b/flutter/web/v1/js/index.html similarity index 100% rename from flutter/web/js/index.html rename to flutter/web/v1/js/index.html diff --git a/flutter/web/js/package.json b/flutter/web/v1/js/package.json similarity index 70% rename from flutter/web/js/package.json rename to flutter/web/v1/js/package.json index 436806e8db6..15e0e75b89d 100644 --- a/flutter/web/js/package.json +++ b/flutter/web/v1/js/package.json @@ -3,19 +3,19 @@ "version": "1.0.0", "scripts": { "dev": "vite", - "build": "python ./gen_js_from_hbb.py > src/gen_js_from_hbb.ts && python ./ts_proto.py && tsc && vite build", + "build": "./gen_js_from_hbb.py > src/gen_js_from_hbb.ts && ./ts_proto.py && tsc && vite build", "preview": "vite preview" }, "devDependencies": { "typescript": "^4.4.4", - "vite": "2.8" + "vite": "^2.7.2" }, "dependencies": { "fast-sha256": "^1.3.0", "libsodium": "^0.7.9", "libsodium-wrappers": "^0.7.9", "pcm-player": "^0.0.11", - "ts-proto": "^1.169.1", + "ts-proto": "^1.101.0", "wasm-feature-detect": "^1.2.11", "zstddec": "^0.0.2" } diff --git a/flutter/web/js/src/codec.js b/flutter/web/v1/js/src/codec.js similarity index 95% rename from flutter/web/js/src/codec.js rename to flutter/web/v1/js/src/codec.js index 6b295adbd24..27c9565ec70 100644 --- a/flutter/web/js/src/codec.js +++ b/flutter/web/v1/js/src/codec.js @@ -25,7 +25,7 @@ export async function loadVp9(callback) { // Multithreading is used only if `options.threading` is true. // This requires browser support for the new `SharedArrayBuffer` and `Atomics` APIs, // currently available in Firefox and Chrome with experimental flags enabled. - // All major browsers disabled SharedArrayBuffer by default on January 5, 2018 + // 所有主流浏览器均默认于2018年1月5日禁用SharedArrayBuffer const isSIMD = await simd(); console.log('isSIMD: ' + isSIMD); window.OGVLoader.loadClass( diff --git a/flutter/web/js/src/common.ts b/flutter/web/v1/js/src/common.ts similarity index 100% rename from flutter/web/js/src/common.ts rename to flutter/web/v1/js/src/common.ts diff --git a/flutter/web/js/src/connection.ts b/flutter/web/v1/js/src/connection.ts similarity index 64% rename from flutter/web/js/src/connection.ts rename to flutter/web/v1/js/src/connection.ts index 297004bd813..b0c479c90cc 100644 --- a/flutter/web/js/src/connection.ts +++ b/flutter/web/v1/js/src/connection.ts @@ -4,10 +4,9 @@ import * as rendezvous from "./rendezvous.js"; import { loadVp9 } from "./codec"; import * as sha256 from "fast-sha256"; import * as globals from "./globals"; -import * as consts from "./consts"; import { decompress, mapKey, sleep } from "./common"; -export const PORT = 21116; +const PORT = 21116; const HOSTS = [ "rs-sg.rustdesk.com", "rs-cn.rustdesk.com", @@ -16,8 +15,8 @@ const HOSTS = [ let HOST = localStorage.getItem("rendezvous-server") || HOSTS[0]; const SCHEMA = "ws://"; -type MsgboxCallback = (type: string, title: string, text: string, link: string) => void; -type DrawCallback = (display: number, data: Uint8Array) => void; +type MsgboxCallback = (type: string, title: string, text: string) => void; +type DrawCallback = (data: Uint8Array) => void; //const cursorCanvas = document.createElement("canvas"); export default class Connection { @@ -67,7 +66,7 @@ export default class Connection { try { this._password = Uint8Array.from(JSON.parse("[" + p + "]")); } catch (e) { - console.error('Failed to get password, ' + e); + console.error(e); } } } @@ -171,7 +170,7 @@ export default class Connection { pk = undefined; } } catch (e) { - console.error('Failed to verify id pk, ', e); + console.error(e); pk = undefined; } if (!pk) @@ -196,7 +195,7 @@ export default class Connection { try { signedId = await globals.verify(signedId.id, Uint8Array.from(pk!)); } catch (e) { - console.error('Failed to verify signed id pk, ', e); + console.error(e); // fall back to non-secure connection in case pk mismatch console.error("pk mismatch, fall back to non-secure"); const public_key = message.PublicKey.fromPartial({}); @@ -243,12 +242,26 @@ export default class Connection { this.login(); } else if (msg?.test_delay) { const test_delay = msg?.test_delay; - console.log('test delay: ', test_delay); + console.log(test_delay); if (!test_delay.from_client) { this._ws?.sendMessage({ test_delay }); } } else if (msg?.login_response) { - this.handleLoginResponse(msg?.login_response); + const r = msg?.login_response; + if (r.error) { + if (r.error == "Wrong Password") { + this._password = undefined; + this.msgbox( + "re-input-password", + r.error, + "Do you want to enter again?" + ); + } else { + this.msgbox("error", "Login Error", r.error); + } + } else if (r.peer_info) { + this.handlePeerInfo(r.peer_info); + } } else if (msg?.video_frame) { this.handleVideoFrame(msg?.video_frame!); } else if (msg?.clipboard) { @@ -261,7 +274,7 @@ export default class Connection { try { globals.copyToClipboard(new TextDecoder().decode(cb.content)); } catch (e) { - console.error('Failed to copy to clipboard, ', e); + console.error(e); } // globals.pushEvent("clipboard", cb); } else if (msg?.cursor_data) { @@ -305,110 +318,13 @@ export default class Connection { } } - handleLoginResponse(response: message.LoginResponse) { - const loginErrorMap: Record = { - [consts.LOGIN_SCREEN_WAYLAND]: { - msgtype: "error", - title: "Login Error", - text: "Login screen using Wayland is not supported", - link: "https://rustdesk.com/docs/en/manual/linux/#login-screen", - try_again: true, - }, - [consts.LOGIN_MSG_DESKTOP_SESSION_NOT_READY]: { - msgtype: "session-login", - title: "", - text: "", - link: "", - try_again: true, - }, - [consts.LOGIN_MSG_DESKTOP_XSESSION_FAILED]: { - msgtype: "session-re-login", - title: "", - text: "", - link: "", - try_again: true, - }, - [consts.LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER]: { - msgtype: "info-nocancel", - title: "another_user_login_title_tip", - text: "another_user_login_text_tip", - link: "", - try_again: false, - }, - [consts.LOGIN_MSG_DESKTOP_XORG_NOT_FOUND]: { - msgtype: "info-nocancel", - title: "xorg_not_found_title_tip", - text: "xorg_not_found_text_tip", - link: "https://rustdesk.com/docs/en/manual/linux/#login-screen", - try_again: true, - }, - [consts.LOGIN_MSG_DESKTOP_NO_DESKTOP]: { - msgtype: "info-nocancel", - title: "no_desktop_title_tip", - text: "no_desktop_text_tip", - link: "https://rustdesk.com/docs/en/manual/linux/#login-screen", - try_again: true, - }, - [consts.LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY]: { - msgtype: "session-login-password", - title: "", - text: "", - link: "", - try_again: true, - }, - [consts.LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG]: { - msgtype: "session-login-re-password", - title: "", - text: "", - link: "", - try_again: true, - }, - [consts.LOGIN_MSG_NO_PASSWORD_ACCESS]: { - msgtype: "wait-remote-accept-nook", - title: "Prompt", - text: "Please wait for the remote side to accept your session request...", - link: "", - try_again: true, - }, - }; - - const err = response.error; - if (err) { - if (err == consts.LOGIN_MSG_PASSWORD_EMPTY) { - this._password = undefined; - this.msgbox("input-password", "Password Required", "", ""); - } - if (err == consts.LOGIN_MSG_PASSWORD_WRONG) { - this._password = undefined; - this.msgbox( - "re-input-password", - err, - "Do you want to enter again?" - ); - } else if (err == consts.LOGIN_MSG_2FA_WRONG || err == consts.REQUIRE_2FA) { - this.msgbox("input-2fa", err, ""); - } else if (err in loginErrorMap) { - const m = loginErrorMap[err]; - this.msgbox(m.msgtype, m.title, m.text, m.link); - } else { - if (err.includes(consts.SCRAP_X11_REQUIRED)) { - this.msgbox("error", "Login Error", err, consts.SCRAP_X11_REF_URL); - } else { - this.msgbox("error", "Login Error", err); - } - } - } else if (response.peer_info) { - this.handlePeerInfo(response.peer_info); - } - } - - msgbox(type_: string, title: string, text: string, link: string = '') { - this._msgbox?.(type_, title, text, link); + msgbox(type_: string, title: string, text: string) { + this._msgbox?.(type_, title, text); } - draw(display: number, frame: any) { - this._draw?.(display, frame); - globals.draw(display, frame); + draw(frame: any) { + this._draw?.(frame); + globals.draw(frame); } close() { @@ -431,55 +347,38 @@ export default class Connection { this._draw = callback; } - login(info?: { - os_login?: message.OSLogin, - password?: Uint8Array - }) { - if (info?.password) { + login(password: string | undefined = undefined) { + if (password) { const salt = this._hash?.salt; - let p = hash([info.password, salt!]); + let p = hash([password, salt!]); this._password = p; const challenge = this._hash?.challenge; p = hash([p, challenge!]); this.msgbox("connecting", "Connecting...", "Logging in..."); - this._sendLoginMessage({ os_login: info.os_login, password: p }); + this._sendLoginMessage(p); } else { let p = this._password; if (p) { const challenge = this._hash?.challenge; p = hash([p, challenge!]); } - this._sendLoginMessage({ os_login: info?.os_login, password: p }); + this._sendLoginMessage(p); } } - changePreferCodec() { - const supported_decoding = message.SupportedDecoding.fromPartial({ - ability_vp9: 1, - ability_h264: 1, - }); - const option = message.OptionMessage.fromPartial({ supported_decoding }); - const misc = message.Misc.fromPartial({ option }); - this._ws?.sendMessage({ misc }); - } - async reconnect() { this.close(); await this.start(this._id); } - _sendLoginMessage(login: { - os_login?: message.OSLogin, - password?: Uint8Array, - }) { + _sendLoginMessage(password: Uint8Array | undefined = undefined) { const login_request = message.LoginRequest.fromPartial({ username: this._id!, my_id: "web", // to-do my_name: "web", // to-do - password: login.password, + password, option: this.getOptionMessage(), video_ack_required: true, - os_login: login.os_login, }); this._ws?.sendMessage({ login_request }); } @@ -536,7 +435,7 @@ export default class Connection { i++; if (i == n) this.sendVideoReceived(); if (ok && dec.frameBuffer && n == i) { - this.draw(vf.display, dec.frameBuffer); + this.draw(dec.frameBuffer); const now = new Date().getTime(); var elapsed = now - tm; this._videoTestSpeed[1] += elapsed; @@ -544,9 +443,9 @@ export default class Connection { if (this._videoTestSpeed[0] >= 30) { console.log( "video decoder: " + - parseInt( - "" + this._videoTestSpeed[1] / this._videoTestSpeed[0] - ) + parseInt( + "" + this._videoTestSpeed[1] / this._videoTestSpeed[0] + ) ); this._videoTestSpeed = [0, 0]; } @@ -557,17 +456,8 @@ export default class Connection { } handlePeerInfo(pi: message.PeerInfo) { - localStorage.setItem('last_remote_id', this._id); this._peerInfo = pi; - if (pi.current_display > pi.displays.length) { - pi.current_display = 0; - } - if (globals.getVersionNumber(pi.version) < globals.getVersionNumber("1.1.10")) { - this.setPermission("restart", false); - } if (pi.displays.length == 0) { - this.setOption("info", pi); - globals.pushEvent("update_privacy_mode", {}); this.msgbox("error", "Remote Error", "No Display"); return; } @@ -577,7 +467,6 @@ export default class Connection { if (p) this.inputOsPassword(p); const username = this.getOption("info")?.username; if (username && !pi.username) pi.username = username; - globals.pushEvent("update_privacy_mode", {}); this.setOption("info", pi); if (this.getRemember()) { if (this._password?.length) { @@ -592,10 +481,6 @@ export default class Connection { } } - setPermission(name: string, value: Boolean) { - globals.pushEvent("permission", { [name]: value }); - } - shouldAutoLogin(): string { const l = this.getOption("lock-after-session-end"); const a = !!this.getOption("auto-login"); @@ -631,7 +516,7 @@ export default class Connection { default: return; } - this.setPermission(name, p.enabled); + globals.pushEvent("permission", { [name]: p.enabled }); } else if (misc.switch_display) { this.loadVideoDecoder(); globals.pushEvent("switch_display", misc.switch_display); @@ -652,27 +537,7 @@ export default class Connection { } getOption(name: string): any { - return this._options[name] ?? globals.getUserDefaultOption(name); - } - - getToggleOption(name: string): Boolean { - // TODO: more default settings - const defaultToggleTrue = [ - 'show-remote-cursor', - 'privacy-mode', - 'enable-file-copy-paste', - 'allow_swap_key', - ]; - return this._options[name] || (defaultToggleTrue.includes(name) ? true : false); - } - - // TODO: - getStatus(): String { - return JSON.stringify({ status_num: 10 }); - } - - // TODO: - checkConnStatus() { + return this._options[name]; } setOption(name: string, value: any) { @@ -727,70 +592,17 @@ export default class Connection { this._ws?.sendMessage({ key_event }); } - restart() { - const misc = message.Misc.fromPartial({}); - misc.restart_remote_device = true; - this._ws?.sendMessage({ misc }); - } - inputString(seq: string) { const key_event = message.KeyEvent.fromPartial({ seq }); this._ws?.sendMessage({ key_event }); } - send2fa(code: string) { - const auth_2fa = message.Auth2FA.fromPartial({ code }); - this._ws?.sendMessage({ auth_2fa }); - } - - _captureDisplays({ add, sub, set }: { - add?: number[], sub?: number[], set?: number[] - }) { - const capture_displays = message.CaptureDisplays.fromPartial({ add, sub, set }); - const misc = message.Misc.fromPartial({ capture_displays }); + switchDisplay(display: number) { + const switch_display = message.SwitchDisplay.fromPartial({ display }); + const misc = message.Misc.fromPartial({ switch_display }); this._ws?.sendMessage({ misc }); } - switchDisplay(v: string) { - try { - const obj = JSON.parse(v); - const value = obj.value; - const isDesktop = obj.isDesktop; - if (value.length == 1) { - const switch_display = message.SwitchDisplay.fromPartial({ display: value[0] }); - const misc = message.Misc.fromPartial({ switch_display }); - this._ws?.sendMessage({ misc }); - - if (!isDesktop) { - this._captureDisplays({ set: value }); - } else { - // If support merging images, check_remove_unused_displays() in ui_session_interface.rs - } - } else { - this._captureDisplays({ set: value }); - } - } - catch (e) { - console.log('Failed to switch display, invalid param "' + v + '"'); - } - } - - elevateWithLogon(value: string) { - try { - const obj = JSON.parse(value); - const logon = message.ElevationRequestWithLogon.fromPartial({ - username: obj.username, - password: obj.password - }); - const elevation_request = message.ElevationRequest.fromPartial({ logon }); - const misc = message.Misc.fromPartial({ elevation_request }); - this._ws?.sendMessage({ misc }); - } - catch (e) { - console.log('Failed to elevate with logon, invalid param "' + value + '"'); - } - } - async inputOsPassword(seq: string) { this.inputMouse(); await sleep(50); @@ -839,52 +651,6 @@ export default class Connection { } toggleOption(name: string) { - - // } else if name == "block-input" { - // option.block_input = BoolOption::Yes.into(); - // } else if name == "unblock-input" { - // option.block_input = BoolOption::No.into(); - // } else if name == "show-quality-monitor" { - // config.show_quality_monitor.v = !config.show_quality_monitor.v; - // } else if name == "allow_swap_key" { - // config.allow_swap_key.v = !config.allow_swap_key.v; - // } else if name == "view-only" { - // config.view_only.v = !config.view_only.v; - // let f = |b: bool| { - // if b { - // BoolOption::Yes.into() - // } else { - // BoolOption::No.into() - // } - // }; - // if config.view_only.v { - // option.disable_keyboard = f(true); - // option.disable_clipboard = f(true); - // option.show_remote_cursor = f(true); - // option.enable_file_transfer = f(false); - // option.lock_after_session_end = f(false); - // } else { - // option.disable_keyboard = f(false); - // option.disable_clipboard = f(self.get_toggle_option("disable-clipboard")); - // option.show_remote_cursor = f(self.get_toggle_option("show-remote-cursor")); - // option.enable_file_transfer = f(self.config.enable_file_transfer.v); - // option.lock_after_session_end = f(self.config.lock_after_session_end.v); - // } - // } else { - // let is_set = self - // .options - // .get(&name) - // .map(|o| !o.is_empty()) - // .unwrap_or(false); - // if is_set { - // self.config.options.remove(&name); - // } else { - // self.config.options.insert(name, "Y".to_owned()); - // } - // self.config.store(&self.id); - // return None; - // } - const v = !this._options[name]; const option = message.OptionMessage.fromPartial({}); const v2 = v @@ -906,43 +672,13 @@ export default class Connection { case "privacy-mode": option.privacy_mode = v2; break; - case "enable-file-copy-paste": - option.enable_file_transfer = v2; - break; case "block-input": option.block_input = message.OptionMessage_BoolOption.Yes; break; case "unblock-input": option.block_input = message.OptionMessage_BoolOption.No; break; - case "show-quality-monitor": - case "allow-swap-key": - break; - case "view-only": - if (v) { - option.disable_keyboard = message.OptionMessage_BoolOption.Yes; - option.disable_clipboard = message.OptionMessage_BoolOption.Yes; - option.show_remote_cursor = message.OptionMessage_BoolOption.Yes; - option.enable_file_transfer = message.OptionMessage_BoolOption.No; - option.lock_after_session_end = message.OptionMessage_BoolOption.No; - } else { - option.disable_keyboard = message.OptionMessage_BoolOption.No; - option.disable_clipboard = this.getToggleOption("disable-clipboard") - ? message.OptionMessage_BoolOption.Yes - : message.OptionMessage_BoolOption.No; - option.show_remote_cursor = this.getToggleOption("show-remote-cursor") - ? message.OptionMessage_BoolOption.Yes - : message.OptionMessage_BoolOption.No; - option.enable_file_transfer = this.getToggleOption("enable-file-copy-paste") - ? message.OptionMessage_BoolOption.Yes - : message.OptionMessage_BoolOption.No; - option.lock_after_session_end = this.getToggleOption("lock-after-session-end") - ? message.OptionMessage_BoolOption.Yes - : message.OptionMessage_BoolOption.No; - } - break; default: - this.setOption(name, this._options[name] ? undefined : "Y"); return; } if (name.indexOf("block-input") < 0) this.setOption(name, v); @@ -950,20 +686,6 @@ export default class Connection { this._ws?.sendMessage({ misc }); } - togglePrivacyMode(value: string) { - try { - const obj = JSON.parse(value); - const toggle_privacy_mode = message.TogglePrivacyMode.fromPartial({ - impl_key: obj.impl_key, - on: obj.on, - }); - const misc = message.Misc.fromPartial({ toggle_privacy_mode }); - this._ws?.sendMessage({ misc }); - } catch (e) { - console.log('Failed to toggle privacy mode, invalid param "' + value + '"') - } - } - getImageQuality() { return this.getOption("image-quality"); } @@ -998,7 +720,7 @@ export default class Connection { loadVp9((decoder: any) => { this._videoDecoder = decoder; console.log("vp9 loaded"); - console.log('The decoder: ', decoder); + console.log(decoder); }); } } diff --git a/flutter/web/v1/js/src/globals.js b/flutter/web/v1/js/src/globals.js new file mode 100644 index 00000000000..953add18d01 --- /dev/null +++ b/flutter/web/v1/js/src/globals.js @@ -0,0 +1,383 @@ +import Connection from "./connection"; +import _sodium from "libsodium-wrappers"; +import { CursorData } from "./message"; +import { loadVp9 } from "./codec"; +import { checkIfRetry, version } from "./gen_js_from_hbb"; +import { initZstd, translate } from "./common"; +import PCMPlayer from "pcm-player"; + +window.curConn = undefined; +window.isMobile = () => { + return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) + || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0, 4)); +} + +export function isDesktop() { + return !isMobile(); +} + +export function msgbox(type, title, text) { + if (!type || (type == 'error' && !text)) return; + const text2 = text.toLowerCase(); + var hasRetry = checkIfRetry(type, title, text) ? 'true' : ''; + onGlobalEvent(JSON.stringify({ name: 'msgbox', type, title, text, hasRetry })); +} + +function jsonfyForDart(payload) { + var tmp = {}; + for (const [key, value] of Object.entries(payload)) { + if (!key) continue; + tmp[key] = value instanceof Uint8Array ? '[' + value.toString() + ']' : JSON.stringify(value); + } + return tmp; +} + +export function pushEvent(name, payload) { + payload = jsonfyForDart(payload); + payload.name = name; + onGlobalEvent(JSON.stringify(payload)); +} + +let yuvWorker; +let yuvCanvas; +let gl; +let pixels; +let flipPixels; +let oldSize; +if (YUVCanvas.WebGLFrameSink.isAvailable()) { + var canvas = document.createElement('canvas'); + yuvCanvas = YUVCanvas.attach(canvas, { webGL: true }); + gl = canvas.getContext("webgl"); +} else { + yuvWorker = new Worker("./yuv.js"); +} +let testSpeed = [0, 0]; + +export function draw(frame) { + if (yuvWorker) { + // frame's (y/u/v).bytes already detached, can not transferrable any more. + yuvWorker.postMessage(frame); + } else { + var tm0 = new Date().getTime(); + yuvCanvas.drawFrame(frame); + var width = canvas.width; + var height = canvas.height; + var size = width * height * 4; + if (size != oldSize) { + pixels = new Uint8Array(size); + flipPixels = new Uint8Array(size); + oldSize = size; + } + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + const row = width * 4; + const end = (height - 1) * row; + for (let i = 0; i < size; i += row) { + flipPixels.set(pixels.subarray(i, i + row), end - i); + } + onRgba(flipPixels); + testSpeed[1] += new Date().getTime() - tm0; + testSpeed[0] += 1; + if (testSpeed[0] > 30) { + console.log('gl: ' + parseInt('' + testSpeed[1] / testSpeed[0])); + testSpeed = [0, 0]; + } + } + /* + var testCanvas = document.getElementById("test-yuv-decoder-canvas"); + if (testCanvas && currentFrame) { + var ctx = testCanvas.getContext("2d"); + testCanvas.width = frame.format.displayWidth; + testCanvas.height = frame.format.displayHeight; + var img = ctx.createImageData(testCanvas.width, testCanvas.height); + img.data.set(currentFrame); + ctx.putImageData(img, 0, 0); + } + */ +} + +export function sendOffCanvas(c) { + let canvas = c.transferControlToOffscreen(); + yuvWorker.postMessage({ canvas }, [canvas]); +} + +export function setConn(conn) { + window.curConn = conn; +} + +export function getConn() { + return window.curConn; +} + +export async function startConn(id) { + setByName('remote_id', id); + await curConn.start(id); +} + +export function close() { + getConn()?.close(); + setConn(undefined); +} + +export function newConn() { + window.curConn?.close(); + const conn = new Connection(); + setConn(conn); + return conn; +} + +let sodium; +export async function verify(signed, pk) { + if (!sodium) { + await _sodium.ready; + sodium = _sodium; + } + if (typeof pk == 'string') { + pk = decodeBase64(pk); + } + return sodium.crypto_sign_open(signed, pk); +} + +export function decodeBase64(pk) { + return sodium.from_base64(pk, sodium.base64_variants.ORIGINAL); +} + +export function genBoxKeyPair() { + const pair = sodium.crypto_box_keypair(); + const sk = pair.privateKey; + const pk = pair.publicKey; + return [sk, pk]; +} + +export function genSecretKey() { + return sodium.crypto_secretbox_keygen(); +} + +export function seal(unsigned, theirPk, ourSk) { + const nonce = Uint8Array.from(Array(24).fill(0)); + return sodium.crypto_box_easy(unsigned, nonce, theirPk, ourSk); +} + +function makeOnce(value) { + var byteArray = Array(24).fill(0); + + for (var index = 0; index < byteArray.length && value > 0; index++) { + var byte = value & 0xff; + byteArray[index] = byte; + value = (value - byte) / 256; + } + + return Uint8Array.from(byteArray); +}; + +export function encrypt(unsigned, nonce, key) { + return sodium.crypto_secretbox_easy(unsigned, makeOnce(nonce), key); +} + +export function decrypt(signed, nonce, key) { + return sodium.crypto_secretbox_open_easy(signed, makeOnce(nonce), key); +} + +window.setByName = (name, value) => { + switch (name) { + case 'remote_id': + localStorage.setItem('remote-id', value); + break; + case 'connect': + newConn(); + startConn(value); + break; + case 'login': + value = JSON.parse(value); + curConn.setRemember(value.remember == 'true'); + curConn.login(value.password); + break; + case 'close': + close(); + break; + case 'refresh': + curConn.refresh(); + break; + case 'reconnect': + curConn.reconnect(); + break; + case 'toggle_option': + curConn.toggleOption(value); + break; + case 'image_quality': + curConn.setImageQuality(value); + break; + case 'lock_screen': + curConn.lockScreen(); + break; + case 'ctrl_alt_del': + curConn.ctrlAltDel(); + break; + case 'switch_display': + curConn.switchDisplay(value); + break; + case 'remove': + const peers = getPeers(); + delete peers[value]; + localStorage.setItem('peers', JSON.stringify(peers)); + break; + case 'input_key': + value = JSON.parse(value); + curConn.inputKey(value.name, value.down == 'true', value.press == 'true', value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true'); + break; + case 'input_string': + curConn.inputString(value); + break; + case 'send_mouse': + let mask = 0; + value = JSON.parse(value); + switch (value.type) { + case 'down': + mask = 1; + break; + case 'up': + mask = 2; + break; + case 'wheel': + mask = 3; + break; + } + switch (value.buttons) { + case 'left': + mask |= 1 << 3; + break; + case 'right': + mask |= 2 << 3; + break; + case 'wheel': + mask |= 4 << 3; + } + curConn.inputMouse(mask, parseInt(value.x || '0'), parseInt(value.y || '0'), value.alt == 'true', value.ctrl == 'true', value.shift == 'true', value.command == 'true'); + break; + case 'option': + value = JSON.parse(value); + localStorage.setItem(value.name, value.value); + break; + case 'peer_option': + value = JSON.parse(value); + curConn.setOption(value.name, value.value); + break; + case 'input_os_password': + curConn.inputOsPassword(value); + break; + default: + break; + } +} + +window.getByName = (name, arg) => { + let v = _getByName(name, arg); + if (typeof v == 'string' || v instanceof String) return v; + if (v == undefined || v == null) return ''; + return JSON.stringify(v); +} + +function getPeersForDart() { + const peers = []; + for (const [id, value] of Object.entries(getPeers())) { + if (!id) continue; + const tm = value['tm']; + const info = value['info']; + if (!tm || !info) continue; + peers.push([tm, id, info]); + } + return peers.sort().reverse().map(x => x.slice(1)); +} + +function _getByName(name, arg) { + switch (name) { + case 'peers': + return getPeersForDart(); + case 'remote_id': + return localStorage.getItem('remote-id'); + case 'remember': + return curConn.getRemember(); + case 'toggle_option': + return curConn.getOption(arg) || false; + case 'option': + return localStorage.getItem(arg); + case 'image_quality': + return curConn.getImageQuality(); + case 'translate': + arg = JSON.parse(arg); + return translate(arg.locale, arg.text); + case 'peer_option': + return curConn.getOption(arg); + case 'test_if_valid_server': + break; + case 'version': + return version; + } + return ''; +} + +let opusWorker = new Worker("./libopus.js"); +let pcmPlayer; + +export function initAudio(channels, sampleRate) { + pcmPlayer = newAudioPlayer(channels, sampleRate); + opusWorker.postMessage({ channels, sampleRate }); +} + +export function playAudio(packet) { + opusWorker.postMessage(packet, [packet.buffer]); +} + +window.init = async () => { + if (yuvWorker) { + yuvWorker.onmessage = (e) => { + onRgba(e.data); + } + } + opusWorker.onmessage = (e) => { + pcmPlayer.feed(e.data); + } + loadVp9(() => { }); + await initZstd(); + console.log('init done'); +} + +export function getPeers() { + try { + return JSON.parse(localStorage.getItem('peers')) || {}; + } catch (e) { + return {}; + } +} + +function newAudioPlayer(channels, sampleRate) { + return new PCMPlayer({ + channels, + sampleRate, + flushingTime: 2000 + }); +} + +export function copyToClipboard(text) { + if (window.clipboardData && window.clipboardData.setData) { + // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible. + return window.clipboardData.setData("Text", text); + + } + else if (document.queryCommandSupported && document.queryCommandSupported("copy")) { + var textarea = document.createElement("textarea"); + textarea.textContent = text; + textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge. + document.body.appendChild(textarea); + textarea.select(); + try { + return document.execCommand("copy"); // Security exception may be thrown by some browsers. + } + catch (ex) { + console.warn("Copy to clipboard failed.", ex); + // return prompt("Copy to clipboard: Ctrl+C, Enter", text); + } + finally { + document.body.removeChild(textarea); + } + } +} \ No newline at end of file diff --git a/flutter/web/js/src/main.ts b/flutter/web/v1/js/src/main.ts similarity index 100% rename from flutter/web/js/src/main.ts rename to flutter/web/v1/js/src/main.ts diff --git a/flutter/web/js/src/style.css b/flutter/web/v1/js/src/style.css similarity index 100% rename from flutter/web/js/src/style.css rename to flutter/web/v1/js/src/style.css diff --git a/flutter/web/js/src/ui.js b/flutter/web/v1/js/src/ui.js similarity index 100% rename from flutter/web/js/src/ui.js rename to flutter/web/v1/js/src/ui.js diff --git a/flutter/web/js/src/vite-env.d.ts b/flutter/web/v1/js/src/vite-env.d.ts similarity index 100% rename from flutter/web/js/src/vite-env.d.ts rename to flutter/web/v1/js/src/vite-env.d.ts diff --git a/flutter/web/js/src/websock.ts b/flutter/web/v1/js/src/websock.ts similarity index 100% rename from flutter/web/js/src/websock.ts rename to flutter/web/v1/js/src/websock.ts diff --git a/flutter/web/js/ts_proto.py b/flutter/web/v1/js/ts_proto.py similarity index 100% rename from flutter/web/js/ts_proto.py rename to flutter/web/v1/js/ts_proto.py diff --git a/flutter/web/js/tsconfig.json b/flutter/web/v1/js/tsconfig.json similarity index 100% rename from flutter/web/js/tsconfig.json rename to flutter/web/v1/js/tsconfig.json diff --git a/flutter/web/js/vite.config.js b/flutter/web/v1/js/vite.config.js similarity index 100% rename from flutter/web/js/vite.config.js rename to flutter/web/v1/js/vite.config.js diff --git a/flutter/web/js/yarn.lock b/flutter/web/v1/js/yarn.lock similarity index 100% rename from flutter/web/js/yarn.lock rename to flutter/web/v1/js/yarn.lock diff --git a/flutter/web/libs/firebase-analytics.js b/flutter/web/v1/libs/firebase-analytics.js similarity index 100% rename from flutter/web/libs/firebase-analytics.js rename to flutter/web/v1/libs/firebase-analytics.js diff --git a/flutter/web/libs/firebase-app.js b/flutter/web/v1/libs/firebase-app.js similarity index 100% rename from flutter/web/libs/firebase-app.js rename to flutter/web/v1/libs/firebase-app.js diff --git a/flutter/web/manifest.json b/flutter/web/v1/manifest.json similarity index 100% rename from flutter/web/manifest.json rename to flutter/web/v1/manifest.json diff --git a/flutter/web/yarn.lock b/flutter/web/v1/yarn.lock similarity index 100% rename from flutter/web/yarn.lock rename to flutter/web/v1/yarn.lock diff --git a/flutter/web/yuv.js b/flutter/web/v1/yuv.js similarity index 100% rename from flutter/web/yuv.js rename to flutter/web/v1/yuv.js diff --git a/flutter/web/yuv.wasm b/flutter/web/v1/yuv.wasm similarity index 100% rename from flutter/web/yuv.wasm rename to flutter/web/v1/yuv.wasm diff --git a/flutter/web/v2/README.md b/flutter/web/v2/README.md new file mode 100644 index 00000000000..7c128776cee --- /dev/null +++ b/flutter/web/v2/README.md @@ -0,0 +1 @@ +Under dev. \ No newline at end of file From 212e8e755954ec48ad39517ea0688711f8c146ef Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 22 Jun 2024 12:45:32 +0800 Subject: [PATCH 109/335] fix one missing file --- flutter/web/{ => v1}/.gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename flutter/web/{ => v1}/.gitignore (100%) diff --git a/flutter/web/.gitignore b/flutter/web/v1/.gitignore similarity index 100% rename from flutter/web/.gitignore rename to flutter/web/v1/.gitignore From 8b5ac390d1be41e875aadf5094ae8c15f584c54a Mon Sep 17 00:00:00 2001 From: solokot Date: Sat, 22 Jun 2024 15:05:56 +0300 Subject: [PATCH 110/335] Update ru.rs (#8442) --- src/lang/ru.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 364946a3953..aabbadf02b8 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -621,6 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Нет"), ("During controlled", "При управлении"), ("During service is on", "При запущенной службе"), - ("Capture screen using DirectX", ""), + ("Capture screen using DirectX", "Захват экрана с помощью DirectX"), ].iter().cloned().collect(); } From 416efe9fd31973588f0d5e0106362c037ec308e2 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Sat, 22 Jun 2024 14:51:33 +0200 Subject: [PATCH 111/335] Update de.rs (#8443) --- src/lang/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index dab7a9cc3f4..90f4c898b1a 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -573,7 +573,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("switch_display_elevated_connections_tip", "Das Umschalten auf einen sekundären Bildschirm wird mit erhöhten Rechten nicht unterstützt, wenn mehrere Verbindungen bestehen. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."), ("input_source_1_tip", "Eingangsquelle 1"), ("input_source_2_tip", "Eingangsquelle 2"), - ("capture_display_elevated_connections_tip", "Das Erfassen mehrerer Bildschirme wird im erweiterten Benutzermodus nicht unterstützt. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."), + ("capture_display_elevated_connections_tip", "Die Aufnahme von mehreren Bildschirmen wird im erweiterten Benutzermodus nicht unterstützt. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."), ("Swap control-command key", "Steuerungs- und Befehlstasten tauschen"), ("swap-left-right-mouse", "Linke und rechte Maustaste tauschen"), ("2FA code", "2FA-Code"), @@ -621,6 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Niemals"), ("During controlled", "Wenn kontrolliert"), ("During service is on", "Wenn der Dienst läuft"), - ("Capture screen using DirectX", ""), + ("Capture screen using DirectX", "Bildschirm mit DirectX aufnehmen"), ].iter().cloned().collect(); } From baeee642ddb4d62647fa33b41b1e6ebd90af5870 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 22 Jun 2024 20:51:07 +0800 Subject: [PATCH 112/335] build 43 --- flutter/pubspec.lock | 6 +++--- flutter/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 9c37d4c399f..57ea92482a3 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -857,10 +857,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.0" mime: dependency: transitive description: @@ -1613,5 +1613,5 @@ packages: source: hosted version: "0.2.1" sdks: - dart: ">=3.3.0-0 <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.16.0" diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index d43f0843fe4..85c6cda115d 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.2.6+42 +version: 1.2.6+43 environment: sdk: '^3.1.0' From a9e0ea852092cb5520e982dda8871397485ab487 Mon Sep 17 00:00:00 2001 From: XLion Date: Sun, 23 Jun 2024 09:09:08 +0800 Subject: [PATCH 113/335] Update tw.rs (#8444) --- src/lang/tw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 7833d574750..a1046ed0096 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -621,6 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "從不"), ("During controlled", "被控期間"), ("During service is on", "服務開啟期間"), - ("Capture screen using DirectX", ""), + ("Capture screen using DirectX", "使用 DirectX 擷取螢幕"), ].iter().cloned().collect(); } From 40cb59336f267609e6e481232fd49f611d95a9cc Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 23 Jun 2024 11:06:47 +0800 Subject: [PATCH 114/335] fix: mobile actions, position (#8446) Signed-off-by: fufesou --- flutter/lib/common.dart | 44 ++++-- flutter/lib/common/widgets/overlay.dart | 131 +++++++++++++++--- flutter/lib/consts.dart | 2 + flutter/lib/desktop/pages/remote_page.dart | 8 +- .../lib/desktop/widgets/remote_toolbar.dart | 4 +- flutter/lib/main.dart | 3 + flutter/lib/mobile/pages/remote_page.dart | 3 +- flutter/lib/models/model.dart | 17 ++- 8 files changed, 164 insertions(+), 48 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 6fe8d59461a..cf5c4d16172 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -718,7 +718,21 @@ class OverlayDialogManager { int _tagCount = 0; OverlayEntry? _mobileActionsOverlayEntry; - RxBool mobileActionsOverlayVisible = false.obs; + RxBool mobileActionsOverlayVisible = true.obs; + + setMobileActionsOverlayVisible(bool v, {store = true}) { + if (store) { + bind.setLocalFlutterOption(k: kOptionShowMobileAction, v: v ? 'Y' : 'N'); + } + // No need to read the value from local storage after setting it. + // It better to toggle the value directly. + mobileActionsOverlayVisible.value = v; + } + + loadMobileActionsOverlayVisible() { + mobileActionsOverlayVisible.value = + bind.getLocalFlutterOption(k: kOptionShowMobileAction) != 'N'; + } void setOverlayState(OverlayKeyState overlayKeyState) { _overlayKeyState = overlayKeyState; @@ -865,14 +879,14 @@ class OverlayDialogManager { ); overlayState.insert(overlay); _mobileActionsOverlayEntry = overlay; - mobileActionsOverlayVisible.value = true; + setMobileActionsOverlayVisible(true); } - void hideMobileActionsOverlay() { + void hideMobileActionsOverlay({store = true}) { if (_mobileActionsOverlayEntry != null) { _mobileActionsOverlayEntry!.remove(); _mobileActionsOverlayEntry = null; - mobileActionsOverlayVisible.value = false; + setMobileActionsOverlayVisible(false, store: store); return; } } @@ -891,21 +905,27 @@ class OverlayDialogManager { } makeMobileActionsOverlayEntry(VoidCallback? onHide, {FFI? ffi}) { - final position = SimpleWrapper(Offset(0, 0)); makeMobileActions(BuildContext context, double s) { final scale = s < 0.85 ? 0.85 : s; final session = ffi ?? gFFI; - // compute overlay position - final screenW = MediaQuery.of(context).size.width; - final screenH = MediaQuery.of(context).size.height; const double overlayW = 200; const double overlayH = 45; - final left = (screenW - overlayW * scale) / 2; - final top = screenH - (overlayH + 80) * scale; - position.value = Offset(left, top); + computeOverlayPosition() { + final screenW = MediaQuery.of(context).size.width; + final screenH = MediaQuery.of(context).size.height; + final left = (screenW - overlayW * scale) / 2; + final top = screenH - (overlayH + 80) * scale; + return Offset(left, top); + } + + if (draggablePositions.mobileActions.isInvalid()) { + draggablePositions.mobileActions.update(computeOverlayPosition()); + } else { + draggablePositions.mobileActions.tryAdjust(overlayW, overlayH, scale); + } return DraggableMobileActions( scale: scale, - position: position, + position: draggablePositions.mobileActions, width: overlayW, height: overlayH, onBackPressed: () => session.inputModel.tap(MouseButtons.right), diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index b16b4d9ada4..9a6c5a4c9fc 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -1,6 +1,8 @@ import 'package:auto_size_text/auto_size_text.dart'; +import 'package:debounce_throttle/debounce_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; @@ -26,9 +28,12 @@ class DraggableChatWindow extends StatelessWidget { @override Widget build(BuildContext context) { + if (draggablePositions.chatWindow.isInvalid()) { + draggablePositions.chatWindow.update(position); + } return isIOS ? IOSDraggable( - position: position, + position: draggablePositions.chatWindow, chatModel: chatModel, width: width, height: height, @@ -45,7 +50,7 @@ class DraggableChatWindow extends StatelessWidget { ) : Draggable( checkKeyboard: true, - position: SimpleWrapper(position), + position: draggablePositions.chatWindow, width: width, height: height, chatModel: chatModel, @@ -176,7 +181,7 @@ class DraggableMobileActions extends StatelessWidget { required this.scale}); final double scale; - final SimpleWrapper position; + final DraggableKeyPosition position; final double width; final double height; final VoidCallback? onBackPressed; @@ -241,6 +246,92 @@ class DraggableMobileActions extends StatelessWidget { } } +class DraggableKeyPosition { + final String key; + Offset _pos; + late Debouncer _debouncerStore; + DraggableKeyPosition(this.key) + : _pos = DraggablePositions.kInvalidDraggablePosition; + + get pos => _pos; + + _loadPosition(String k) { + final value = bind.getLocalFlutterOption(k: k); + if (value.isNotEmpty) { + final parts = value.split(','); + if (parts.length == 2) { + return Offset(double.parse(parts[0]), double.parse(parts[1])); + } + } + return DraggablePositions.kInvalidDraggablePosition; + } + + load() { + _pos = _loadPosition(key); + _debouncerStore = Debouncer(const Duration(milliseconds: 500), + onChanged: (v) => _store(), initialValue: 0); + } + + update(Offset pos) { + _pos = pos; + _triggerStore(); + } + + // Adjust position to keep it in the screen + // Only used for desktop and web desktop + tryAdjust(double w, double h, double scale) { + final size = MediaQuery.of(Get.context!).size; + w = w * scale; + h = h * scale; + double x = _pos.dx; + double y = _pos.dy; + if (x + w > size.width) { + x = size.width - w; + } + final tabBarHeight = isDesktop ? kDesktopRemoteTabBarHeight : 0; + if (y + h > (size.height - tabBarHeight)) { + y = size.height - tabBarHeight - h; + } + if (x < 0) { + x = 0; + } + if (y < 0) { + y = 0; + } + if (x != _pos.dx || y != _pos.dy) { + update(Offset(x, y)); + } + } + + isInvalid() { + return _pos == DraggablePositions.kInvalidDraggablePosition; + } + + _triggerStore() => _debouncerStore.value = _debouncerStore.value + 1; + _store() { + bind.setLocalFlutterOption(k: key, v: '${_pos.dx},${_pos.dy}'); + } +} + +class DraggablePositions { + static const kChatWindow = 'draggablePositionChat'; + static const kMobileActions = 'draggablePositionMobile'; + static const kIOSDraggable = 'draggablePositionIOS'; + + static const kInvalidDraggablePosition = Offset(-999999, -999999); + final chatWindow = DraggableKeyPosition(kChatWindow); + final mobileActions = DraggableKeyPosition(kMobileActions); + final iOSDraggable = DraggableKeyPosition(kIOSDraggable); + + load() { + chatWindow.load(); + mobileActions.load(); + iOSDraggable.load(); + } +} + +DraggablePositions draggablePositions = DraggablePositions(); + class Draggable extends StatefulWidget { Draggable( {Key? key, @@ -255,7 +346,7 @@ class Draggable extends StatefulWidget { final bool checkKeyboard; final bool checkScreenSize; - final SimpleWrapper position; + final DraggableKeyPosition position; final double width; final double height; final ChatModel? chatModel; @@ -277,7 +368,7 @@ class _DraggableState extends State { _chatModel = widget.chatModel; } - get position => widget.position.value; + get position => widget.position.pos; void onPanUpdate(DragUpdateDetails d) { final offset = d.delta; @@ -301,7 +392,7 @@ class _DraggableState extends State { y = position.dy + offset.dy; } setState(() { - widget.position.value = Offset(x, y); + widget.position.update(Offset(x, y)); }); _chatModel?.setChatWindowPosition(position); } @@ -320,7 +411,7 @@ class _DraggableState extends State { // reset if (_lastBottomHeight > 0 && bottomHeight == 0) { setState(() { - widget.position.value = Offset(position.dx, _saveHeight); + widget.position.update(Offset(position.dx, _saveHeight)); }); } @@ -331,7 +422,7 @@ class _DraggableState extends State { if (sumHeight + position.dy > contextHeight) { final y = contextHeight - sumHeight; setState(() { - widget.position.value = Offset(position.dx, y); + widget.position.update(Offset(position.dx, y)); }); } } @@ -362,14 +453,14 @@ class _DraggableState extends State { class IOSDraggable extends StatefulWidget { const IOSDraggable( {Key? key, - this.position = Offset.zero, this.chatModel, + required this.position, required this.width, required this.height, required this.builder}) : super(key: key); - final Offset position; + final DraggableKeyPosition position; final ChatModel? chatModel; final double width; final double height; @@ -380,7 +471,6 @@ class IOSDraggable extends StatefulWidget { } class IOSDraggableState extends State { - late Offset _position; late ChatModel? _chatModel; late double _width; late double _height; @@ -391,25 +481,26 @@ class IOSDraggableState extends State { @override void initState() { super.initState(); - _position = widget.position; _chatModel = widget.chatModel; _width = widget.width; _height = widget.height; } + get position => widget.position; + checkKeyboard() { final bottomHeight = MediaQuery.of(context).viewInsets.bottom; final currentVisible = bottomHeight != 0; // save if (!_keyboardVisible && currentVisible) { - _saveHeight = _position.dy; + _saveHeight = position.value.dy; } // reset if (_lastBottomHeight > 0 && bottomHeight == 0) { setState(() { - _position = Offset(_position.dx, _saveHeight); + position.value = Offset(position.value.dx, _saveHeight); }); } @@ -417,10 +508,10 @@ class IOSDraggableState extends State { if (_keyboardVisible && currentVisible) { final sumHeight = bottomHeight + _height; final contextHeight = MediaQuery.of(context).size.height; - if (sumHeight + _position.dy > contextHeight) { + if (sumHeight + position.value.dy > contextHeight) { final y = contextHeight - sumHeight; setState(() { - _position = Offset(_position.dx, y); + position.value = Offset(position.value.dx, y); }); } } @@ -435,14 +526,14 @@ class IOSDraggableState extends State { return Stack( children: [ Positioned( - left: _position.dx, - top: _position.dy, + left: position.value.dx, + top: position.value.dy, child: GestureDetector( onPanUpdate: (details) { setState(() { - _position += details.delta; + position.value += details.delta; }); - _chatModel?.setChatWindowPosition(_position); + _chatModel?.setChatWindowPosition(position.value); }, child: Material( child: Container( diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 4568a3f02e5..ba93b7fddcd 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -142,6 +142,8 @@ const String kOptionDisableFloatingWindow = "disable-floating-window"; const String kOptionKeepScreenOn = "keep-screen-on"; +const String kOptionShowMobileAction = "showMobileActions"; + const String kUrlActionClose = "close"; const String kTabLabelHomePage = "Home"; diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 47a158682f3..ba5ef3bb1c0 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -134,6 +134,7 @@ class _RemotePageState extends State _ffi.ffiModel.updateEventListener(sessionId, widget.id); if (!isWeb) bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote); _ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); + _ffi.dialogManager.loadMobileActionsOverlayVisible(); // Session option should be set after models.dart/FFI.start _showRemoteCursor.value = bind.sessionGetToggleOptionSync( sessionId: sessionId, arg: 'show-remote-cursor'); @@ -322,13 +323,6 @@ class _RemotePageState extends State if (!_ffi.ffiModel.isPeerAndroid) { return Offstage(); } else { - if (_ffi.connType == ConnType.defaultConn && - _ffi.ffiModel.permissions['keyboard'] != false) { - Timer( - Duration(milliseconds: 10), - () => _ffi.dialogManager - .mobileActionsOverlayVisible.value = true); - } return Obx(() => Offstage( offstage: _ffi.dialogManager .mobileActionsOverlayVisible.isFalse, diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 1c90e25d75d..e75d75c233b 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -579,8 +579,8 @@ class _MobileActionMenu extends StatelessWidget { return Obx(() => _IconMenuButton( assetName: 'assets/actions_mobile.svg', tooltip: 'Mobile Actions', - onPressed: () => - ffi.dialogManager.mobileActionsOverlayVisible.toggle(), + onPressed: () => ffi.dialogManager.setMobileActionsOverlayVisible( + !ffi.dialogManager.mobileActionsOverlayVisible.value), color: ffi.dialogManager.mobileActionsOverlayVisible.isTrue ? _ToolbarTheme.blueColor : _ToolbarTheme.inactiveColor, diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 7e89d5153bc..0386d63cf50 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -6,6 +6,7 @@ import 'package:bot_toast/bot_toast.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/common/widgets/overlay.dart'; import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; import 'package:flutter_hbb/desktop/pages/install_page.dart'; import 'package:flutter_hbb/desktop/pages/server_page.dart'; @@ -156,6 +157,7 @@ void runMobileApp() async { await initEnv(kAppTypeMain); if (isAndroid) androidChannelInit(); if (isAndroid) platformFFI.syncAndroidServiceAppDirConfigPath(); + draggablePositions.load(); await Future.wait([gFFI.abModel.loadCache(), gFFI.groupModel.loadCache()]); gFFI.userModel.refreshCurrentUser(); runApp(App()); @@ -176,6 +178,7 @@ void runMultiWindow( late Widget widget; switch (appType) { case kAppTypeDesktopRemote: + draggablePositions.load(); widget = DesktopRemoteScreen( params: argument, ); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 4d021609a6e..c9a9daf6164 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -82,13 +82,14 @@ class _RemotePageState extends State { .changeCurrentKey(MessageKey(widget.id, ChatModel.clientModeID)); gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted; _blockableOverlayState.applyFfi(gFFI); + gFFI.dialogManager.loadMobileActionsOverlayVisible(); } @override Future dispose() async { // https://github.com/flutter/flutter/issues/64935 super.dispose(); - gFFI.dialogManager.hideMobileActionsOverlay(); + gFFI.dialogManager.hideMobileActionsOverlay(store: false); gFFI.inputModel.listenToMouse(false); gFFI.imageModel.disposeImage(); gFFI.cursorModel.disposeImages(); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 2ea0376096d..e1b024f0d4a 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -917,10 +917,12 @@ class FfiModel with ChangeNotifier { if (parent.target?.connType == ConnType.defaultConn && parent.target != null && parent.target!.ffiModel.permissions['keyboard'] != false) { - Timer( - Duration(milliseconds: delayMSecs), - () => parent.target!.dialogManager - .showMobileActionsOverlay(ffi: parent.target!)); + Timer(Duration(milliseconds: delayMSecs), () { + if (parent.target!.dialogManager.mobileActionsOverlayVisible.isTrue) { + parent.target!.dialogManager + .showMobileActionsOverlay(ffi: parent.target!); + } + }); } } } @@ -1587,10 +1589,13 @@ class CanvasModel with ChangeNotifier { // (focalPoint.dx - _x_1) / s1 + displayOriginX = (focalPoint.dx - _x_2) / s2 + displayOriginX // _x_2 = focalPoint.dx - (focalPoint.dx - _x_1) / s1 * s2 _x = focalPoint.dx - (focalPoint.dx - _x) / s * _scale; - final adjustForKeyboard = parent.target?.cursorModel.adjustForKeyboard() ?? 0.0; + final adjustForKeyboard = + parent.target?.cursorModel.adjustForKeyboard() ?? 0.0; // (focalPoint.dy - _y_1 + adjust) / s1 + displayOriginY = (focalPoint.dy - _y_2 + adjust) / s2 + displayOriginY // _y_2 = focalPoint.dy + adjust - (focalPoint.dy - _y_1 + adjust) / s1 * s2 - _y = focalPoint.dy + adjustForKeyboard - (focalPoint.dy - _y + adjustForKeyboard) / s * _scale; + _y = focalPoint.dy + + adjustForKeyboard - + (focalPoint.dy - _y + adjustForKeyboard) / s * _scale; notifyListeners(); } From 307827be3c664eb29dd9050424bbba3ba4c389c3 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 23 Jun 2024 11:26:15 +0800 Subject: [PATCH 115/335] fix: mobile actions hide and mobile theme (#8447) Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 6 ++---- flutter/lib/mobile/pages/settings_page.dart | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index ba5ef3bb1c0..8427645e2f8 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -328,10 +328,8 @@ class _RemotePageState extends State .mobileActionsOverlayVisible.isFalse, child: Overlay(initialEntries: [ makeMobileActionsOverlayEntry( - () => _ffi - .dialogManager - .mobileActionsOverlayVisible - .value = false, + () => _ffi.dialogManager + .setMobileActionsOverlayVisible(false), ffi: _ffi, ) ]), diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index d1e2ff55abd..909ca0285e5 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -569,8 +569,8 @@ class _SettingsState extends State with WidgetsBindingObserver { SettingsTile( title: Text(translate( Theme.of(context).brightness == Brightness.light - ? 'Dark Theme' - : 'Light Theme')), + ? 'Light Theme' + : 'Dark Theme')), leading: Icon(Theme.of(context).brightness == Brightness.light ? Icons.dark_mode : Icons.light_mode), From 1a69d525af31369561411791721184e4411faea1 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 23 Jun 2024 11:39:44 +0800 Subject: [PATCH 116/335] fix tile type droplist and change to build 44 --- flutter/lib/common/widgets/peer_tab_page.dart | 15 +++++++++++---- flutter/pubspec.yaml | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index a04fec434a4..c89a88de516 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -802,13 +802,20 @@ class _PeerViewDropdownState extends State { child: SizedBox( height: 36, child: getRadio( - Text( - translate(types.indexOf(e) == 0 + Tooltip( + message: translate(types.indexOf(e) == 0 ? 'Big tiles' : types.indexOf(e) == 1 ? 'Small tiles' : 'List'), - style: style), + child: Icon( + e == PeerUiType.grid + ? Icons.grid_view_rounded + : e == PeerUiType.list + ? Icons.view_list_rounded + : Icons.view_agenda_rounded, + size: 18, + )), e, peerCardUiType.value, dense: true, @@ -838,7 +845,7 @@ class _PeerViewDropdownState extends State { child: Icon( peerCardUiType.value == PeerUiType.grid ? Icons.grid_view_rounded - : peerCardUiType.value == PeerUiType.tile + : peerCardUiType.value == PeerUiType.list ? Icons.view_list_rounded : Icons.view_agenda_rounded, size: 18, diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 85c6cda115d..d4d25d0d740 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.2.6+43 +version: 1.2.6+44 environment: sdk: '^3.1.0' From ef82cfa03410a67c64abe40f447aaf0fefc1f655 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sun, 23 Jun 2024 10:05:06 +0000 Subject: [PATCH 117/335] update cs.rs (#8450) --- src/lang/cs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 1ff602be7b2..01cfd925637 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -621,6 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nikdy"), ("During controlled", "Během řízeného"), ("During service is on", "Během služby je v provozu"), - ("Capture screen using DirectX", ""), + ("Capture screen using DirectX", "Snímání obrazovky pomocí DirectX"), ].iter().cloned().collect(); } From 65dd2b8993fd44e8bfeef1f0dd8b7c6ca3ec7348 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sun, 23 Jun 2024 10:05:28 +0000 Subject: [PATCH 118/335] update sk.rs (#8449) --- src/lang/sk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 542373de236..137a9bea3d6 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -621,6 +621,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nikdy"), ("During controlled", "Počas kontrolovaného"), ("During service is on", "Počas služby je v prevádzke"), - ("Capture screen using DirectX", ""), + ("Capture screen using DirectX", "Snímanie obrazovky pomocou DirectX"), ].iter().cloned().collect(); } From 4947cf871871079a37d96dfc711c0654b9be4695 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 23 Jun 2024 22:50:54 +0800 Subject: [PATCH 119/335] fix https://github.com/rustdesk/rustdesk/issues/8452 --- src/ui/index.tis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/index.tis b/src/ui/index.tis index 7c48fffb065..71def3fe7f0 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -382,7 +382,7 @@ class MyIdMenu: Reactor.Component {
Fingerprint: " + handler.get_fingerprint() + " \
" + translate("Privacy Statement") + "
\
" + translate("Website") + "
\ -
Copyright © " + new Date().getFullYear() + " Purslane Ltd.\ +
Copyright © 2024 Purslane Ltd.\
" + handler.get_license() + " \

" + translate("Slogan_tip") + "

\
\ From 65edd55516269f86146514debfaff73c7f8656d4 Mon Sep 17 00:00:00 2001 From: Yevhen Popok Date: Mon, 24 Jun 2024 12:32:00 +0300 Subject: [PATCH 120/335] Update Ukrainian translation (#8453) --- src/lang/ua.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/ua.rs b/src/lang/ua.rs index fee60a93a64..a393d5123e6 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -617,10 +617,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", "Використовувати візуалізацію текстур"), ("Floating window", "Рухоме вікно"), ("floating_window_tip", "Допомагає зберегти фонову службу Rustdesk"), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), - ("Capture screen using DirectX", ""), + ("Keep screen on", "Тримати екран увімкненим"), + ("Never", "Ніколи"), + ("During controlled", "Коли керується"), + ("During service is on", "Коли запущена служба"), + ("Capture screen using DirectX", "Захоплення екрана з використанням DirectX"), ].iter().cloned().collect(); } From 1765c7bbf4b90cdef21547aec0997ba7e32fe26d Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 24 Jun 2024 19:41:15 +0800 Subject: [PATCH 121/335] fix multi display fps control (#8455) * Calculate fps without distinguish displays, use one fps control because the controlled side control fps of all displays with one FPS variable. * Because all displays decode frame in one thread, when there are N displays, the video frames received in one second is `fps * N`, so the calculated decode fps should be divided by N. Because the actual display count is not obvious in rust, when no data frame is received for 5 seconds, the display is considered inactive, and only the active display is used as the dividend. Signed-off-by: 21pages --- src/client.rs | 81 ++++++++++++-------- src/client/io_loop.rs | 146 ++++++++++++++++++++---------------- src/ui_session_interface.rs | 4 +- 3 files changed, 133 insertions(+), 98 deletions(-) diff --git a/src/client.rs b/src/client.rs index 41f290bf07d..40630bb29a1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -43,8 +43,7 @@ use hbb_common::{ rand, rendezvous_proto::*, socket_client, - sodiumoxide::base64, - sodiumoxide::crypto::sign, + sodiumoxide::{base64, crypto::sign}, tcp::FramedStream, timeout, tokio::time::Duration, @@ -2093,8 +2092,6 @@ pub type MediaSender = mpsc::Sender; struct VideoHandlerController { handler: VideoHandler, - count: u128, - duration: std::time::Duration, skip_beginning: u32, } @@ -2111,7 +2108,7 @@ pub fn start_video_audio_threads( MediaSender, MediaSender, Arc>>>, - Arc>>, + Arc>>, Arc>>, ) where @@ -2123,8 +2120,8 @@ where let video_queue_map_cloned = video_queue_map.clone(); let mut video_callback = video_callback; - let fps_map = Arc::new(RwLock::new(HashMap::new())); - let decode_fps_map = fps_map.clone(); + let fps = Arc::new(RwLock::new(None)); + let decode_fps_map = fps.clone(); let chroma = Arc::new(RwLock::new(None)); let chroma_cloned = chroma.clone(); let mut last_chroma = None; @@ -2134,9 +2131,8 @@ where sync_cpu_usage(); get_hwcodec_config(); let mut handler_controller_map = HashMap::new(); - // let mut count = Vec::new(); - // let mut duration = std::time::Duration::ZERO; - // let mut skip_beginning = Vec::new(); + let mut count = 0; + let mut duration = std::time::Duration::ZERO; loop { if let Ok(data) = video_receiver.recv() { match data { @@ -2169,8 +2165,6 @@ where display, VideoHandlerController { handler: VideoHandler::new(format, display), - count: 0, - duration: std::time::Duration::ZERO, skip_beginning: 0, }, ); @@ -2178,6 +2172,8 @@ where if let Some(handler_controller) = handler_controller_map.get_mut(&display) { let mut pixelbuffer = true; let mut tmp_chroma = None; + let format_changed = + handler_controller.handler.decoder.format() != format; match handler_controller.handler.handle_frame( vf, &mut pixelbuffer, @@ -2198,27 +2194,14 @@ where } // fps calculation - // The first frame will be very slow - if handler_controller.skip_beginning < 5 { - handler_controller.skip_beginning += 1; - continue; - } - - handler_controller.duration += start.elapsed(); - handler_controller.count += 1; - if handler_controller.count % 10 == 0 { - fps_map.write().unwrap().insert( - display, - (handler_controller.count * 1000 - / handler_controller.duration.as_millis()) - as usize, - ); - } - // Clear to get real-time fps - if handler_controller.count > 150 { - handler_controller.count = 0; - handler_controller.duration = Duration::ZERO; - } + fps_calculate( + handler_controller, + &fps, + format_changed, + start.elapsed(), + &mut count, + &mut duration, + ); } Err(e) => { // This is a simple workaround. @@ -2334,6 +2317,38 @@ pub fn start_audio_thread() -> MediaSender { audio_sender } +#[inline] +fn fps_calculate( + handler_controller: &mut VideoHandlerController, + fps: &Arc>>, + format_changed: bool, + elapsed: std::time::Duration, + count: &mut usize, + duration: &mut std::time::Duration, +) { + if format_changed { + *count = 0; + *duration = std::time::Duration::ZERO; + handler_controller.skip_beginning = 0; + } + // // The first frame will be very slow + if handler_controller.skip_beginning < 3 { + handler_controller.skip_beginning += 1; + return; + } + *duration += elapsed; + *count += 1; + let ms = duration.as_millis(); + if *count % 10 == 0 && ms > 0 { + *fps.write().unwrap() = Some((*count as usize) * 1000 / (ms as usize)); + } + // Clear to get real-time fps + if *count >= 30 { + *count = 0; + *duration = Duration::ZERO; + } +} + fn get_hwcodec_config() { // for sciter and unilink #[cfg(feature = "hwcodec")] diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 758ba8a13a8..33c39f359a5 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -71,8 +71,8 @@ pub struct Remote { frame_count_map: Arc>>, video_format: CodecFormat, elevation_requested: bool, - fps_control_map: HashMap, - decode_fps_map: Arc>>, + fps_control: FpsControl, + decode_fps: Arc>>, chroma: Arc>>, } @@ -85,7 +85,7 @@ impl Remote { receiver: mpsc::UnboundedReceiver, sender: mpsc::UnboundedSender, frame_count_map: Arc>>, - decode_fps: Arc>>, + decode_fps: Arc>>, chroma: Arc>>, ) -> Self { Self { @@ -110,8 +110,8 @@ impl Remote { stop_voice_call_sender: None, voice_call_request_timestamp: None, elevation_requested: false, - fps_control_map: Default::default(), - decode_fps_map: decode_fps, + fps_control: Default::default(), + decode_fps, chroma, } } @@ -971,69 +971,84 @@ impl Remote { if custom_fps < 5 || custom_fps > 120 { custom_fps = 30; } - let decode_fps_read = self.decode_fps_map.read().unwrap(); - for (display, decode_fps) in decode_fps_read.iter() { - let video_queue_map_read = self.video_queue_map.read().unwrap(); - let Some(video_queue) = video_queue_map_read.get(display) else { - continue; - }; - - if !self.fps_control_map.contains_key(display) { - self.fps_control_map.insert(*display, FpsControl::default()); - } - let Some(ctl) = self.fps_control_map.get_mut(display) else { - return; - }; - - let len = video_queue.len(); - let decode_fps = *decode_fps; - let mut limited_fps = if direct { - decode_fps * 9 / 10 // 30 got 27 - } else { - decode_fps * 4 / 5 // 30 got 24 - }; - if limited_fps > custom_fps { - limited_fps = custom_fps; + let ctl = &mut self.fps_control; + let len = self + .video_queue_map + .read() + .unwrap() + .iter() + .map(|v| v.1.len()) + .max() + .unwrap_or_default(); + let decode_fps = self.decode_fps.read().unwrap().clone(); + let Some(mut decode_fps) = decode_fps else { + return; + }; + if cfg!(feature = "flutter") { + let active_displays = ctl + .last_active_time + .iter() + .filter(|t| t.1.elapsed().as_secs() < 5) + .count(); + if active_displays > 1 { + decode_fps = decode_fps / active_displays; } - let should_decrease = len > 1 && ctl.last_auto_fps.unwrap_or(0) > limited_fps as i32; + } + let mut limited_fps = if direct { + decode_fps * 9 / 10 // 30 got 27 + } else { + decode_fps * 4 / 5 // 30 got 24 + }; + if limited_fps > custom_fps { + limited_fps = custom_fps; + } + let should_decrease = (len > 1 + && ctl.last_auto_fps.clone().unwrap_or(custom_fps as _) > limited_fps) + || len > std::cmp::max(1, limited_fps / 2); - // increase judgement - if len <= 1 { + // increase judgement + if len <= 1 { + if ctl.idle_counter < usize::MAX { ctl.idle_counter += 1; - } else { - ctl.idle_counter = 0; } - let mut should_increase = false; - if let Some(last_auto_fps) = ctl.last_auto_fps { - // ever set - if last_auto_fps + 3 <= limited_fps as i32 && ctl.idle_counter > 3 { - // limited_fps is 5 larger than last set, and idle time is more than 3 seconds - should_increase = true; - } + } else { + ctl.idle_counter = 0; + } + let mut should_increase = false; + if let Some(last_auto_fps) = ctl.last_auto_fps.clone() { + // ever set + if last_auto_fps + 3 <= limited_fps && ctl.idle_counter > 3 { + // limited_fps is 3 larger than last set, and idle time is more than 3 seconds + should_increase = true; } - if ctl.last_auto_fps.is_none() || should_decrease || should_increase { - // limited_fps to ensure decoding is faster than encoding - let mut auto_fps = limited_fps as i32; - if auto_fps < 1 { - auto_fps = 1; - } - // send custom fps - let mut misc = Misc::new(); - misc.set_option(OptionMessage { - custom_fps: auto_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.last_auto_fps = Some(auto_fps); + } + if ctl.last_auto_fps.is_none() || should_decrease || should_increase { + // limited_fps to ensure decoding is faster than encoding + let mut auto_fps = limited_fps; + if should_decrease && limited_fps < len { + auto_fps = limited_fps / 2; + } + if auto_fps < 1 { + auto_fps = 1; } - // send refresh + let mut misc = Misc::new(); + misc.set_option(OptionMessage { + custom_fps: auto_fps as _, + ..Default::default() + }); + let mut msg = Message::new(); + msg.set_misc(misc); + self.sender.send(Data::Message(msg)).ok(); + log::info!("Set fps to {}", auto_fps); + ctl.last_queue_size = len; + ctl.last_auto_fps = Some(auto_fps); + } + // send refresh + for (display, video_queue) in self.video_queue_map.read().unwrap().iter() { let tolerable = std::cmp::min(decode_fps, video_queue.capacity() / 2); - if ctl.refresh_times < 10 // enough - && (len > tolerable - && (ctl.refresh_times == 0 || ctl.last_refresh_instant.elapsed().as_secs() > 10)) + if ctl.refresh_times < 20 // enough + && (len > tolerable + && (ctl.refresh_times == 0 || ctl.last_refresh_instant.elapsed().as_secs() > 10)) { // Refresh causes client set_display, left frames cause flickering. while let Some(_) = video_queue.pop() {} @@ -1086,6 +1101,9 @@ impl Remote { } self.video_sender.send(MediaData::VideoQueue(display)).ok(); } + self.fps_control + .last_active_time + .insert(display, Instant::now()); } Some(message::Union::Hash(hash)) => { self.handler @@ -1840,8 +1858,9 @@ struct FpsControl { last_queue_size: usize, refresh_times: usize, last_refresh_instant: Instant, - last_auto_fps: Option, + last_auto_fps: Option, idle_counter: usize, + last_active_time: HashMap, } impl Default for FpsControl { @@ -1850,8 +1869,9 @@ impl Default for FpsControl { last_queue_size: Default::default(), refresh_times: Default::default(), last_refresh_instant: Instant::now(), - last_auto_fps: None, + last_auto_fps: Default::default(), idle_counter: 0, + last_active_time: Default::default(), } } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 1c56b97d111..b60711a0c5b 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1692,7 +1692,7 @@ pub async fn io_loop(handler: Session, round: u32) { let frame_count_map: Arc>> = Default::default(); let frame_count_map_cl = frame_count_map.clone(); let ui_handler = handler.ui_handler.clone(); - let (video_sender, audio_sender, video_queue_map, decode_fps_map, chroma) = + let (video_sender, audio_sender, video_queue_map, decode_fps, chroma) = start_video_audio_threads( handler.clone(), move |display: usize, @@ -1720,7 +1720,7 @@ pub async fn io_loop(handler: Session, round: u32) { receiver, sender, frame_count_map, - decode_fps_map, + decode_fps, chroma, ); remote.io_loop(&key, &token, round).await; From 00ddd6337250356700130387dc79605e135bc6f8 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Mon, 24 Jun 2024 22:56:13 +0800 Subject: [PATCH 122/335] fix: ios, chat window position, wrong member value (#8464) Signed-off-by: fufesou --- flutter/lib/common/widgets/overlay.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index 9a6c5a4c9fc..145afc3b0a9 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -486,7 +486,7 @@ class IOSDraggableState extends State { _height = widget.height; } - get position => widget.position; + DraggableKeyPosition get position => widget.position; checkKeyboard() { final bottomHeight = MediaQuery.of(context).viewInsets.bottom; @@ -494,13 +494,13 @@ class IOSDraggableState extends State { // save if (!_keyboardVisible && currentVisible) { - _saveHeight = position.value.dy; + _saveHeight = position.pos.dy; } // reset if (_lastBottomHeight > 0 && bottomHeight == 0) { setState(() { - position.value = Offset(position.value.dx, _saveHeight); + position.update(Offset(position.pos.dx, _saveHeight)); }); } @@ -508,10 +508,10 @@ class IOSDraggableState extends State { if (_keyboardVisible && currentVisible) { final sumHeight = bottomHeight + _height; final contextHeight = MediaQuery.of(context).size.height; - if (sumHeight + position.value.dy > contextHeight) { + if (sumHeight + position.pos.dy > contextHeight) { final y = contextHeight - sumHeight; setState(() { - position.value = Offset(position.value.dx, y); + position.update(Offset(position.pos.dx, y)); }); } } @@ -526,14 +526,14 @@ class IOSDraggableState extends State { return Stack( children: [ Positioned( - left: position.value.dx, - top: position.value.dy, + left: position.pos.dx, + top: position.pos.dy, child: GestureDetector( onPanUpdate: (details) { setState(() { - position.value += details.delta; + position.update(position.pos + details.delta); }); - _chatModel?.setChatWindowPosition(position.value); + _chatModel?.setChatWindowPosition(position.pos); }, child: Material( child: Container( From 245f08055f223eeb6b9628e0508ea49282f17e7a Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 25 Jun 2024 00:31:41 +0800 Subject: [PATCH 123/335] fix: mobile, chat menu, hide after tapping (#8465) Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index c9a9daf6164..b47c6b132bb 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -569,9 +569,11 @@ class _RemotePageState extends State { child: Text(translate(label), style: labelStyle), trailingIcon: Transform.scale( scale: (isDesktop || isWebDesktop) ? 0.8 : 1, - child: IconButton( - onPressed: onPressed, - icon: icon, + child: IgnorePointer( + child: IconButton( + onPressed: null, + icon: icon, + ), ), ), onPressed: onPressed, From 93133b9a6cef7a77f602159dfaf5abd401532f60 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 25 Jun 2024 17:25:50 +0800 Subject: [PATCH 124/335] if fps is adjusted below 30, not set fps to 30 when changing image quality (#8468) Signed-off-by: 21pages --- src/client.rs | 1 + src/client/io_loop.rs | 11 +++++------ src/ui_session_interface.rs | 9 ++++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/client.rs b/src/client.rs index 40630bb29a1..806636b7e18 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1203,6 +1203,7 @@ pub struct LoginConfigHandler { pub save_ab_password_to_recent: bool, // true: connected with ab password pub other_server: Option<(String, String, String)>, pub custom_fps: Arc>>, + pub last_auto_fps: Option, pub adapter_luid: Option, pub mark_unsupported: Vec, pub selected_windows_session_id: Option, diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 33c39f359a5..81474f8b87e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1002,8 +1002,9 @@ impl Remote { if limited_fps > custom_fps { limited_fps = custom_fps; } + let last_auto_fps = self.handler.lc.read().unwrap().last_auto_fps.clone(); let should_decrease = (len > 1 - && ctl.last_auto_fps.clone().unwrap_or(custom_fps as _) > limited_fps) + && last_auto_fps.clone().unwrap_or(custom_fps as _) > limited_fps) || len > std::cmp::max(1, limited_fps / 2); // increase judgement @@ -1015,14 +1016,14 @@ impl Remote { ctl.idle_counter = 0; } let mut should_increase = false; - if let Some(last_auto_fps) = ctl.last_auto_fps.clone() { + if let Some(last_auto_fps) = last_auto_fps.clone() { // ever set if last_auto_fps + 3 <= limited_fps && ctl.idle_counter > 3 { // limited_fps is 3 larger than last set, and idle time is more than 3 seconds should_increase = true; } } - if ctl.last_auto_fps.is_none() || should_decrease || should_increase { + if last_auto_fps.is_none() || should_decrease || should_increase { // limited_fps to ensure decoding is faster than encoding let mut auto_fps = limited_fps; if should_decrease && limited_fps < len { @@ -1041,7 +1042,7 @@ impl Remote { self.sender.send(Data::Message(msg)).ok(); log::info!("Set fps to {}", auto_fps); ctl.last_queue_size = len; - ctl.last_auto_fps = Some(auto_fps); + self.handler.lc.write().unwrap().last_auto_fps = Some(auto_fps); } // send refresh for (display, video_queue) in self.video_queue_map.read().unwrap().iter() { @@ -1858,7 +1859,6 @@ struct FpsControl { last_queue_size: usize, refresh_times: usize, last_refresh_instant: Instant, - last_auto_fps: Option, idle_counter: usize, last_active_time: HashMap, } @@ -1869,7 +1869,6 @@ impl Default for FpsControl { last_queue_size: Default::default(), refresh_times: Default::default(), last_refresh_instant: Instant::now(), - last_auto_fps: Default::default(), idle_counter: 0, last_active_time: Default::default(), } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index b60711a0c5b..bb9c6816365 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -410,9 +410,12 @@ impl Session { self.send(Data::Message(msg)); } if value != "custom" { - // non custom quality use 30 fps - let msg = self.lc.write().unwrap().set_custom_fps(30, false); - self.send(Data::Message(msg)); + let last_auto_fps = self.lc.read().unwrap().last_auto_fps; + if last_auto_fps.unwrap_or(usize::MAX) >= 30 { + // non custom quality use 30 fps + let msg = self.lc.write().unwrap().set_custom_fps(30, false); + self.send(Data::Message(msg)); + } } } From c1c2d26ec77f3a2e3c3cae797ae92095efb4051c Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 25 Jun 2024 18:30:32 +0800 Subject: [PATCH 125/335] fix, check video_queue len rather than len when refresh (#8469) Signed-off-by: 21pages --- src/client/io_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 81474f8b87e..987f2b8147e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1048,7 +1048,7 @@ impl Remote { for (display, video_queue) in self.video_queue_map.read().unwrap().iter() { let tolerable = std::cmp::min(decode_fps, video_queue.capacity() / 2); if ctl.refresh_times < 20 // enough - && (len > tolerable + && (video_queue.len() > tolerable && (ctl.refresh_times == 0 || ctl.last_refresh_instant.elapsed().as_secs() > 10)) { // Refresh causes client set_display, left frames cause flickering. From f0dcc919071e1d9160890484ea85b7128adf4b05 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 25 Jun 2024 20:33:38 +0800 Subject: [PATCH 126/335] fix: wrong use of Instant sub, just after booting (#8470) * fix: wrong use of Instant sub, just after booting Signed-off-by: fufesou * fix: ThrottledInterval, first next tick Signed-off-by: fufesou --------- Signed-off-by: fufesou --- src/common.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/common.rs b/src/common.rs index 9be677e182a..199e0d37fff 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1434,7 +1434,7 @@ pub fn using_public_server() -> bool { pub struct ThrottledInterval { interval: Interval, - last_tick: Instant, + next_tick: Instant, min_interval: Duration, } @@ -1443,7 +1443,7 @@ impl ThrottledInterval { let period = i.period(); ThrottledInterval { interval: i, - last_tick: Instant::now() - period * 2, + next_tick: Instant::now(), min_interval: Duration::from_secs_f64(period.as_secs_f64() * 0.9), } } @@ -1456,8 +1456,9 @@ impl ThrottledInterval { pub fn poll_tick(&mut self, cx: &mut std::task::Context<'_>) -> Poll { match self.interval.poll_tick(cx) { Poll::Ready(instant) => { - if self.last_tick.elapsed() >= self.min_interval { - self.last_tick = Instant::now(); + let now = Instant::now(); + if self.next_tick <= now { + self.next_tick = now + self.min_interval; Poll::Ready(instant) } else { // This call is required since tokio 1.27 From 11bdd3cfcd96df7d64af64443267a3dcab9a59b3 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 26 Jun 2024 00:24:57 +0800 Subject: [PATCH 127/335] fix: ios keyboard, composing input (#8471) * fix: ios keyboard, composing input Signed-off-by: fufesou * Incorrect changes Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 91 ++++++++++++++++------- 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index b47c6b132bb..c86df22422e 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -55,6 +55,9 @@ class _RemotePageState extends State { InputModel get inputModel => gFFI.inputModel; SessionID get sessionId => gFFI.sessionId; + final TextEditingController _textController = + TextEditingController(text: initText); + @override void initState() { super.initState(); @@ -145,37 +148,59 @@ class _RemotePageState extends State { setState(() {}); } - // handle mobile virtual keyboard - void handleSoftKeyboardInput(String newValue) { + void _handleIOSSoftKeyboardInput(String newValue) { var oldValue = _value; _value = newValue; - if (isIOS) { - var i = newValue.length - 1; - for (; i >= 0 && newValue[i] != '\1'; --i) {} - var j = oldValue.length - 1; - for (; j >= 0 && oldValue[j] != '\1'; --j) {} - if (i < j) j = i; - newValue = newValue.substring(j + 1); - oldValue = oldValue.substring(j + 1); - var common = 0; - for (; - common < oldValue.length && - common < newValue.length && - newValue[common] == oldValue[common]; - ++common) {} - for (i = 0; i < oldValue.length - common; ++i) { - inputModel.inputKey('VK_BACK'); - } - if (newValue.length > common) { - var s = newValue.substring(common); - if (s.length > 1) { - bind.sessionInputString(sessionId: sessionId, value: s); - } else { - inputChar(s); - } + var i = newValue.length - 1; + for (; i >= 0 && newValue[i] != '\1'; --i) {} + var j = oldValue.length - 1; + for (; j >= 0 && oldValue[j] != '\1'; --j) {} + if (i < j) j = i; + var subNewValue = newValue.substring(j + 1); + var subOldValue = oldValue.substring(j + 1); + + // get common prefix of subNewValue and subOldValue + var common = 0; + for (; + common < subOldValue.length && + common < subNewValue.length && + subNewValue[common] == subOldValue[common]; + ++common) {} + + // get newStr from subNewValue + var newStr = ""; + if (subNewValue.length > common) { + newStr = subNewValue.substring(common); + } + + // Set the value to the old value and early return if is still composing. (1 && 2) + // 1. The composing range is valid + // 2. The new string is shorter than the composing range. + if (_textController.value.isComposingRangeValid) { + final composingLength = _textController.value.composing.end - + _textController.value.composing.start; + if (composingLength > newStr.length) { + _value = oldValue; + return; } - return; } + + // Delete the different part in the old value. + for (i = 0; i < subOldValue.length - common; ++i) { + inputModel.inputKey('VK_BACK'); + } + + // Input the new string. + if (newStr.length > 1) { + bind.sessionInputString(sessionId: sessionId, value: newStr); + } else { + inputChar(newStr); + } + } + + void _handleNonIOSSoftKeyboardInput(String newValue) { + var oldValue = _value; + _value = newValue; if (oldValue.isNotEmpty && newValue.isNotEmpty && oldValue[0] == '\1' && @@ -214,6 +239,15 @@ class _RemotePageState extends State { } } + // handle mobile virtual keyboard + void handleSoftKeyboardInput(String newValue) { + if (isIOS) { + _handleIOSSoftKeyboardInput(newValue); + } else { + _handleNonIOSSoftKeyboardInput(newValue); + } + } + void inputChar(String char) { if (char == '\n') { char = 'VK_RETURN'; @@ -227,6 +261,7 @@ class _RemotePageState extends State { gFFI.invokeMethod("enable_soft_keyboard", true); // destroy first, so that our _value trick can work _value = initText; + _textController.text = _value; setState(() => _showEdit = false); _timer?.cancel(); _timer = Timer(kMobileDelaySoftKeyboard, () { @@ -491,7 +526,7 @@ class _RemotePageState extends State { autofocus: true, focusNode: _mobileFocusNode, maxLines: null, - initialValue: _value, + controller: _textController, // trick way to make backspace work always keyboardType: TextInputType.multiline, onChanged: handleSoftKeyboardInput, From cb5fa85ac2623be24b917f5fdae6477e8cf929a0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 26 Jun 2024 09:59:35 +0800 Subject: [PATCH 128/335] build 45 --- flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index d4d25d0d740..1c9a289beb2 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.2.6+44 +version: 1.2.6+45 environment: sdk: '^3.1.0' From ef56aea74f290bebaadbedea675fb047d128882a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 26 Jun 2024 16:28:31 +0800 Subject: [PATCH 129/335] fix https://github.com/rustdesk/rustdesk/issues/8479 --- src/ui/index.tis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/index.tis b/src/ui/index.tis index 71def3fe7f0..8954ad8d855 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -1208,7 +1208,7 @@ function self.onMouse(evt) { } function check_if_overlay() { - if (handler.get_option('allow-remote-config-modification') == 'Y') { + if (!handler.get_option('allow-remote-config-modification') == 'Y') { var time0 = getTime(); handler.check_mouse_time(); self.timer(120ms, function() { From 49f848a4534324ebe96b9bb444074c20160a3867 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 26 Jun 2024 16:35:29 +0800 Subject: [PATCH 130/335] refactor --- src/ui/index.tis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/index.tis b/src/ui/index.tis index 8954ad8d855..d76cae58316 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -1208,7 +1208,7 @@ function self.onMouse(evt) { } function check_if_overlay() { - if (!handler.get_option('allow-remote-config-modification') == 'Y') { + if (handler.get_option('allow-remote-config-modification') != 'Y') { var time0 = getTime(); handler.check_mouse_time(); self.timer(120ms, function() { From dbbd9179b76094f6cca72cc5d658409159b54938 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 26 Jun 2024 18:42:08 +0800 Subject: [PATCH 131/335] fix: android cursor scale (#8478) * fix: android cursor scale Signed-off-by: fufesou * Min scale restriction for mobile cursor Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index c86df22422e..f86d35a1c28 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -927,7 +927,7 @@ class CursorPaint extends StatelessWidget { final m = Provider.of(context); final c = Provider.of(context); final adjust = gFFI.cursorModel.adjustForKeyboard(); - var s = c.scale; + final s = c.scale; double hotx = m.hotx; double hoty = m.hoty; if (m.image == null) { @@ -936,12 +936,22 @@ class CursorPaint extends StatelessWidget { hoty = preDefaultCursor.image!.height / 2; } } + + final image = m.image ?? preDefaultCursor.image; + final minSize = 24.0; + double mins = + minSize / (image!.width > image.height ? image.width : image.height); + double factor = 1.0; + if (s < mins) { + factor = s / mins; + } + final s2 = s < mins ? mins : s; return CustomPaint( painter: ImagePainter( - image: m.image ?? preDefaultCursor.image, - x: m.x * s - hotx + c.x, - y: m.y * s - hoty + c.y - adjust, - scale: 1), + image: image, + x: (m.x - hotx) * factor + c.x / s2, + y: (m.y - hoty) * factor + (c.y - adjust) / s2, + scale: s2), ); } } From faf363cfd27f58b5755fbd73aa1ce4b882180862 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 26 Jun 2024 18:49:27 +0800 Subject: [PATCH 132/335] add TelegramBot --- src/auth_2fa.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/src/auth_2fa.rs b/src/auth_2fa.rs index 1ea7733422f..f46bc71e68b 100644 --- a/src/auth_2fa.rs +++ b/src/auth_2fa.rs @@ -1,4 +1,5 @@ use hbb_common::{ + anyhow::anyhow, bail, config::Config, get_time, @@ -109,3 +110,75 @@ pub fn get_2fa(raw: Option) -> Option { .map(|x| Some(x)) .unwrap_or_default() } + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct TelegramBot { + #[serde(skip)] + pub token_str: String, + pub token: Vec, + pub chat_id: String, +} + +impl TelegramBot { + fn into_string(&self) -> ResultType { + let token = encrypt_vec_or_original(self.token_str.as_bytes(), "00", 1024); + let bot = TelegramBot { + token, + ..self.clone() + }; + let s = serde_json::to_string(&bot)?; + Ok(s) + } + + fn save(&self) -> ResultType<()> { + let s = self.into_string()?; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + crate::ipc::set_option("telegram_bot", &s); + #[cfg(any(target_os = "android", target_os = "ios"))] + Config::set_option("telegram_bot".to_owned(), s); + Ok(()) + } + + fn get() -> ResultType { + let data = Config::get_option("telegram_bot"); + let mut bot = serde_json::from_str::(&data)?; + let (token, success, _) = decrypt_vec_or_original(&bot.token, "00"); + if success { + bot.token_str = String::from_utf8(token)?; + return Ok(bot); + } + bail!("decrypt_vec_or_original telegram bot token failed") + } +} + +// https://gist.github.com/dideler/85de4d64f66c1966788c1b2304b9caf1 +pub async fn send_2fa_code_to_telegram(code: &str) -> ResultType<()> { + let bot = TelegramBot::get()?; + let url = format!("https://api.telegram.org/bot{}/sendMessage", bot.token_str); + let params = serde_json::json!({"chat_id": bot.chat_id, "text": code}); + crate::post_request(url, params.to_string(), "").await?; + Ok(()) +} + +pub async fn get_chatid_telegram(bot_token: &str) -> ResultType> { + // send a message to the bot first please, otherwise the chat_id will be empty + let url = format!("https://api.telegram.org/bot{}/getUpdates", bot_token); + let resp = crate::post_request(url, "".to_owned(), "") + .await + .map_err(|e| anyhow!(e))?; + let res = serde_json::from_str::(&resp) + .map(|x| { + let chat_id = x["result"][0]["message"]["chat"]["id"].as_str(); + chat_id.map(|x| x.to_owned()) + }) + .map_err(|e| anyhow!(e)); + if let Ok(Some(chat_id)) = res.as_ref() { + let bot = TelegramBot { + token_str: bot_token.to_owned(), + chat_id: chat_id.to_owned(), + ..Default::default() + }; + bot.save()?; + } + res +} From f17e17a6b992edfd02a3ea3d9689df257eeff3ab Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 26 Jun 2024 19:49:52 +0800 Subject: [PATCH 133/335] fix: mobile cursor, check null (#8481) Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index f86d35a1c28..819ba6c96f6 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -938,9 +938,13 @@ class CursorPaint extends StatelessWidget { } final image = m.image ?? preDefaultCursor.image; + if (image == null) { + return Offstage(); + } + final minSize = 24.0; double mins = - minSize / (image!.width > image.height ? image.width : image.height); + minSize / (image.width > image.height ? image.width : image.height); double factor = 1.0; if (s < mins) { factor = s / mins; From ef06b7d5d07b018f5db630203c9e32c0bc36733d Mon Sep 17 00:00:00 2001 From: Stas Solovey Date: Wed, 26 Jun 2024 20:32:05 +0800 Subject: [PATCH 134/335] add Belarusian locale (#8480) * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs * Update ru.rs * Create be.rs (Belarussian locale) * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update be.rs * Update lang.rs * Update lang.rs * Update be.rs --------- Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> --- src/lang.rs | 3 + src/lang/be.rs | 626 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 629 insertions(+) create mode 100644 src/lang/be.rs diff --git a/src/lang.rs b/src/lang.rs index c53ac1c4f88..e8096bb2446 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -2,6 +2,7 @@ use hbb_common::regex::Regex; use std::ops::Deref; mod ar; +mod be; mod bg; mod ca; mod cn; @@ -56,6 +57,7 @@ pub const LANGS: &[(&str, &str)] = &[ ("et", "Eesti keel"), ("hu", "Magyar"), ("bg", "Български"), + ("be", "Беларуская"), ("ru", "Русский"), ("sk", "Slovenčina"), ("id", "Indonesia"), @@ -153,6 +155,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "lv" => lv::T.deref(), "ar" => ar::T.deref(), "bg" => bg::T.deref(), + "be" => be::T.deref(), "he" => he::T.deref(), "hr" => hr::T.deref(), _ => en::T.deref(), diff --git a/src/lang/be.rs b/src/lang/be.rs new file mode 100644 index 00000000000..ab587e91a6c --- /dev/null +++ b/src/lang/be.rs @@ -0,0 +1,626 @@ +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", "Паспрабуйце праз хвіліну"), + ("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", "Немагчыма падключыцца да паседкавага сервера"), + ("Please try later", "Паспрабуйце пазней"), + ("Remote desktop is offline", "Аддаленая прылада не ў сетцы"), + ("Key mismatch", "Неадпаведнасць ключоў"), + ("Timeout", "Час чакання скончыўся"), + ("Failed to connect to relay server", "Немагчыма падключыцца да рэтранслятара"), + ("Failed to connect via rendezvous server", "Немагчыма падключыцца праз паседкавы сервер"), + ("Failed to connect via relay server", "Немагчыма падключыцца праз рэтранслятар"), + ("Failed to make direct connection to remote desktop", "Не ўдалося ўсталяваць прамое падключэнне да аддаленага працоўнага стала"), + ("Set Password", "Усталяваць пароль"), + ("OS Password", "Пароль ўваходу ў аперацыйную сістэму"), + ("install_tip", "У некаторых выпадках RustDesk можа працаваць няправільна на аддаленым вузле з-за UAC. Каб пазбегнуць магчымых праблем з 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", "Адключыць"), + ("Enable 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", "Няправільныя імя ці пароль"), + ("The verification code is incorrect or has expired", "Праверачны код няправільны або скончыўся тэрмін яго дзеяння"), + ("Edit Tag", "Рэдагаваць тэг"), + ("Forget Password", "Забыць пароль"), + ("Favorites", "Абранае"), + ("Add to Favorites", "Дадаць у абранае"), + ("Remove from Favorites", "Выдаліць з абранага"), + ("Empty", "Пуста"), + ("Invalid folder name", "Недапушчальнае імя тэчкі"), + ("Socks5 Proxy", "Socks5-проксі"), + ("Socks5/Http(s) Proxy", "Socks5/Http(s)-проксі"), + ("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-прыладай з дапамогай мышы або націсканняў, неабходна дазволіць RustDesk выкарыстоўваць паслугу \"Асаблівыя магчымасці\"."), + ("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", "Выйсці"), + ("Help", "Дапамога"), + ("Failed", "Не ўдалося"), + ("Succeeded", "Выканана"), + ("Someone turns on privacy mode, exit", "Хтосьці ўключыў рэжым прыватнасці, выхад"), + ("Unsupported", "Не падтрымліваецца"), + ("Peer denied", "Адмоўлена аддаленым вузлом"), + ("Please install plugins", "Усталюйце плагіны"), + ("Peer exit", "Аддалены вузел адключаны"), + ("Failed to turn off", "Немагчыма адключыць"), + ("Turned off", "Адключаны"), + ("Language", "Мова"), + ("Keep RustDesk background service", "Захаваць фонавую службу RustDesk"), + ("Ignore Battery Optimizations", "Ігнараваць аптымізацыю патрэблення батарэі"), + ("android_open_battery_optimizations_tip", "Перайдзіце на наступную старонку налад"), + ("Start on boot", "Запускаць пры загрузцы"), + ("Start the screen sharing service on boot, requires special permissions", "Запускаць службу дэманстрацыі экрана пры загрузцы (патрабуюцца спецыяльныя дазволы)"), + ("Connection not allowed", "Падключэнне не дазволена"), + ("Legacy mode", "Стары рэжым"), + ("Map mode", "Рэжым супастаўлення"), + ("Translate mode", "Рэжым перакладу"), + ("Use permanent password", "Выкарыстоўваць пастаянны пароль"), + ("Use both passwords", "Выкарыстоўваць абодва паролі"), + ("Set permanent password", "Устанавіць пастаянны пароль"), + ("Enable 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 Toolbar", "Паказаць панэль інструментаў"), + ("Hide Toolbar", "Схаваць панэль інструментаў"), + ("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", "Прылада ўводу гуку"), + ("Use IP Whitelisting", "Выкарыстоўваць белы спіс IP"), + ("Network", "Сетка"), + ("Pin Toolbar", "Закрэпіць панэль інструментаў"), + ("Unpin Toolbar", "Адкрэпіць панэль інструментаў"), + ("Recording", "Запіс"), + ("Directory", "Тэчка"), + ("Automatically record incoming sessions", "Аўтаматычна запісваць уваходныя сесіі"), + ("Change", "Змяніць"), + ("Start session recording", "Пачаць запіс сесіі"), + ("Stop session recording", "Спыніць запіс сесіі"), + ("Enable 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", "Закрыта ўручную праз вэб-кансоль"), + ("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", "Дагэтуль патрэбна, каб аддалены карыстальнік націснуў \"OK\" ў акне UAC пры запуску RustDesk."), + ("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Акрамя таго, калі вы хочаце адразу выкарыстоўваць рэлей, можна дадаць да ідэнтыфікатара суфікс \"/r\" або ўключыць \"Заўсёды падключацца праз рэлей\" ў наладах аддаленага вузла."), + ("Reconnect", "Перападключыць"), + ("Codec", "Кодэк"), + ("Resolution", "Разрознасць"), + ("No transfers in progress", "Перадача не ажыццяўляецца"), + ("Set one-time password length", "Усталяваць даўжыню аднаразовага пароля"), + ("RDP Settings", "Налады RDP"), + ("Sort by", "Сартаваць па"), + ("New Connection", "Новае злучэнне"), + ("Restore", "Аднавіць"), + ("Minimize", "Згарнуць"), + ("Maximize", "Разгарнуць"), + ("Your Device", "Ваша прылада"), + ("empty_recent_tip", "Няма апошніх сеансаў!\nЧас запланаваць новы."), + ("empty_favorite_tip", "Яшчэ няма выбраных аддаленых вузлоў?\nДавайце знойдзем, каго можна дадаць у выбранае."), + ("empty_lan_tip", "Не знойдзены аддаленыя вузлы."), + ("empty_address_book_tip", "У адраснай кнізе няма аддаленых вузлоў."), + ("eg: admin", "напрыклад: admin"), + ("Empty Username", "Пустае імя карыстальніка"), + ("Empty Password", "Пусты пароль"), + ("Me", "Я"), + ("identical_file_tip", "Файл ідэнтычны файлу на аддаленым вузле"), + ("show_monitors_tip", "Паказваць маніторы на панэлі інструментаў"), + ("View Mode", "Рэжым прагляду"), + ("login_linux_tip", "Каб ўключыць сеанс працоўнага стала X, неабходна ўвайсці ў аддалены акаўнт Linux."), + ("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"), + ("No need to elevate", "Павышэнне правоў не патрабуецца"), + ("System Sound", "Сістэмны гук"), + ("Default", "Па змаўчанні"), + ("New RDP", "Новы RDP"), + ("Fingerprint", "Адбітак"), + ("Copy Fingerprint", "Капіраваць адбітак"), + ("no fingerprints", "адбіткі адсутнічаюць"), + ("Select a peer", "Выберыце аддалены ўзел"), + ("Select peers", "Выберыце аддаленыя ўзлы"), + ("Plugins", "Плагіны"), + ("Uninstall", "Выдаліць"), + ("Update", "Абнавіць"), + ("Enable", "Уключыць"), + ("Disable", "Адключыць"), + ("Options", "Параметры"), + ("resolution_original_tip", "Арыгінальнае разознасць"), + ("resolution_fit_local_tip", "Супадзенне з лакальнай разрознасцю"), + ("resolution_custom_tip", "Карыстацкая разрознасць"), + ("Collapse toolbar", "Згарнуць панэль інструментаў"), + ("Accept and Elevate", "Прыняць і павысіць"), + ("accept_and_elevate_btn_tooltip", "Дазволіць падлучэнне і павысіць правы UAC."), + ("clipboard_wait_response_timeout_tip", "Час чакання адказу капіравання буфера абмену скончыўся"), + ("Incoming connection", "Уваходнае падлучэнне"), + ("Outgoing connection", "Выходнае падлучэнне"), + ("Exit", "Выхад"), + ("Open", "Адкрыць"), + ("logout_tip", "Вы сапраўды жадаеце выйсці?"), + ("Service", "Служба"), + ("Start", "Запусціць"), + ("Stop", "Спыніць"), + ("exceed_max_devices", "Дасягнута максімальная колькасць кіруемых прылад."), + ("Sync with recent sessions", "Сінхранізацыя з апошнімі сеансамі"), + ("Sort tags", "Сартаваць тэгі"), + ("Open connection in new tab", "Адкрыць падлучэнне ў новай ўкладцы"), + ("Move tab to new window", "Перамясціць ўкладку ў новае акно"), + ("Can not be empty", "Ня можа быць пустым"), + ("Already exists", "Ужо існуе"), + ("Change Password", "Змяніць пароль"), + ("Refresh Password", "Абнавіць пароль"), + ("ID", "ID"), + ("Grid View", "Сетка"), + ("List View", "Спіс"), + ("Select", "Выбар"), + ("Toggle Tags", "Пераключыць тэгі"), + ("pull_ab_failed_tip", "Немагчыма абнавіць адрасную кнігу"), + ("push_ab_failed_tip", "Немагчыма сінхранізаваць адрасную кнігу з серверам"), + ("synced_peer_readded_tip", "Прылады, якія былі на апошніх сеансах, будуць сінхранізаваны з адраснай кнігай."), + ("Change Color", "Змяніць колер"), + ("Primary Color", "Асноўны колер"), + ("HSV Color", "Колер HSV"), + ("Installation Successful!", "Інсталяцыя прайшла паспяхова!"), + ("Installation failed!", "Інсталяцыя не ўдалася!"), + ("Reverse mouse wheel", "Рэверс кола мышы"), + ("{} sessions", "{} сеансаў"), + ("scam_title", "Вы можаце быць АБМАНУТЫ!"), + ("scam_text1", "Калі вы размаўляеце па тэлефоне з кімсці, каго вы НЕ ВЕДАЕЦЕ і каму НЕ ДАВЕРАЕЦЕ, і ён просіць вас выкарыстаць RustDesk і запусціць яго службу, не працягвайце і неадкладна адмяніце размову."), + ("scam_text2", "Магчыма, гэта аферыст, які паспрабуе ўкрасць вашыя грошы або іншую асабістую інфармацыю."), + ("Don't show again", "Не паказваць больш"), + ("I Agree", "Я згодны"), + ("Decline", "Адхіліць"), + ("Timeout in minutes", "Час чакання (у хвілінах)"), + ("auto_disconnect_option_tip", "Аўтаматычна зачыняць уваходныя сеансы пры неактыўнасці карыстальніка"), + ("Connection failed due to inactivity", "Падлучэнне не ўдалося з-за неактыўнасці"), + ("Check for software update on startup", "Праверка абнаўленняў праграмы пры запуску"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "Абнавіце RustDesk Server Pro да версіі {} або новейшай!"), + ("pull_group_failed_tip", "Немагчыма абнавіць групу"), + ("Filter by intersection", "Фільтраваць па перасячэнні"), + ("Remove wallpaper during incoming sessions", "Схаваць фон працоўнага стала падчас ўваходнага сеансу"), + ("Test", "Тэст"), + ("display_is_plugged_out_msg", "Дысплей адключаны, пераключыцеся на першы дысплей."), + ("No displays", "Няма дысплеяў"), + ("elevated_switch_display_msg", "Пераключыцеся на асноўны дысплей, бо ў павышаным рэжыме не падтрымліваецца некалькі дысплеяў."), + ("Open in new window", "Адкрыць у новым акне"), + ("Show displays as individual windows", "Паказваць дысплеі ў асобных акнах"), + ("Use all my displays for the remote session", "Выкарыстоўваць усе мае дысплеі для аддаленага сеансу"), + ("selinux_tip", "На вашай прыладзе ўключаны SELinux, што можа перашкаджаць правільнай працы RustDesk на кіруючым баку."), + ("Change view", "Змяніць выгляд"), + ("Big tiles", "Вялікія пліткі"), + ("Small tiles", "Маленькія пліткі"), + ("List", "Спіс"), + ("Virtual display", "Віртуальны дысплей"), + ("Plug out all", "Адключыць усё"), + ("True color (4:4:4)", "True color (4:4:4)"), + ("Enable blocking user input", "Дазволіць блакаванне ўводу карыстальніка на прыладзе"), + ("id_input_tip", "Можна ўвесці ідэнтыфікатар, просты IP-адрас або дамен з портам (<дамен>:<порт>).\nКаб атрымаць доступ да прылады на іншым серверы, дадайце адрас сервера (@<адрас_сервера>?key=<ключ_значэнне>), напрыклад:\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nКалі неабходна атрымаць доступ да прылады на грамадскім серверы, увядзіце \"@public\", ключ для грамадскага сервера не патрабуецца."), + ("privacy_mode_impl_mag_tip", "Рэжым 1"), + ("privacy_mode_impl_virtual_display_tip", "Рэжым 2"), + ("Enter privacy mode", "Уключыць рэжым канфідэнцыяльнасці"), + ("Exit privacy mode", "Адключыць рэжым канфідэнцыяльнасці"), + ("idd_not_support_under_win10_2004_tip", "Драйвер непрамога адлюстравання не падтрымліваецца. Патрабуецца Windows 10 версіі 2004 ці навейшая."), + ("switch_display_elevated_connections_tip", "Пераключэнне на неасноўны дысплей не падтрымліваецца ў рэжыме павышэння правоў пры наяўнасці некалькіх падлучэнняў. Паўтарыце спробу пасля ўстаноўкі, калі хочаце кіраваць некалькімі дысплеямі."), + ("input_source_1_tip", "Крыніца ўводу 1"), + ("input_source_2_tip", "Крыніца ўводу 2"), + ("capture_display_elevated_connections_tip", "Захоп экрана некалькіх дысплеяў не падтрымліваецца ў рэжыме павышэння правоў. Паўтарыце спробу пасля ўстаноўкі, калі хочаце кіраваць некалькімі дысплеямі."), + ("Swap control-command key", "Памяняць месцамі значэнні кнопак Ctrl і Command"), + ("swap-left-right-mouse", "Памяняць месцамі значэнні левай і правай кнопак мышы"), + ("2FA code", "Код двухфактарнай аўтэнтыфікацыі"), + ("More", "Яшчэ"), + ("enable-2fa-title", "Выкарыстоўваць двухфактарную аўтэнтыфікацыю"), + ("enable-2fa-desc", "Наладзьце праграму аўтэнтыфікацыі. Выкарыстоўвайце, напрыклад, Authy, Microsoft або Google Authenticator на тэлефоне ці кампутары.\n\nСкануйце QR-код з дапамогай праграмы аўтэнтыфікацыі і ўвядзіце код, які пакажа гэта праграма, каб уключыць двухфактарную аўтэнтыфікацыю."), + ("wrong-2fa-code", "Немагчыма пацвердзіць код. Праверце код і налады мясцовага часу."), + ("enter-2fa-title", "Двухфактарная аутэнтыфікацыя"), + ("Email verification code must be 6 characters.", "Код верыфікацыі па электроннай пошце павінен складацца з 6 сімвалаў."), + ("2FA code must be 6 digits.", "Код двухфактарнай аутэнтыфікацыі павінен складацца з 6 лічбаў."), + ("Multiple Windows sessions found", "Знойдзена некалькі сеансаў Windows"), + ("Please select the session you want to connect to", "Выберыце сеанс, да якога вы жадаеце падключыцца"), + ("powered_by_me", "На аснове RustDesk"), + ("outgoing_only_desk_tip", "Гэта спецыялізаваная версія.\nВы можаце падключацца да іншых прылад, але іншыя прылады не могуць падключацца да вашай."), + ("preset_password_warning", "Гэта спецыялізаваная версія з устаноўленым загадзя паролем. Любы, хто ведае гэты пароль, можа атрымаць поўны кантроль над вашай прыладай. Калі гэта для вас нечакана, адразу выдаліце гэта праграмнае забеспячэнне."), + ("Security Alert", "Папярэджанне аб бяспецы"), + ("My address book", "Мая адрасная кніга"), + ("Personal", "Асабісты"), + ("Owner", "Уладальнік"), + ("Set shared password", "Устанавіць агульны пароль"), + ("Exist in", "Існуе ў"), + ("Read-only", "Толькі для чытання"), + ("Read/Write", "Чытанне і запіс"), + ("Full Control", "Поўны кантроль"), + ("share_warning_tip", "Палі вышэй з'яўляюцца агульнымі і бачнымі іншым."), + ("Everyone", "Усе"), + ("ab_web_console_tip", "Больш у вэб-кансолі"), + ("allow-only-conn-window-open-tip", "Дазволіць толькі падключэнне пры адкрытым акне RustDesk"), + ("no_need_privacy_mode_no_physical_displays_tip", "Фізічныя дысплеі адсутнічаюць, няма патрэбы выкарыстоўваць рэжым канфідэнцыяльнасці."), + ("Follow remote cursor", "Сачыць за аддаленага курсарам"), + ("Follow remote window focus", "Сачыць за фокусам аддаленага акна"), + ("default_proxy_tip", "Пратакол і порт па змаўчанні: Socks5 і 1080"), + ("no_audio_input_device_tip", "Прылада ўваходнага аудыё не знойдзена."), + ("Incoming", "Уваходныя"), + ("Outgoing", "Выходныя"), + ("Clear Wayland screen selection", "Адмяніць выбар экрана Wayland"), + ("clear_Wayland_screen_selection_tip", "Пасля адмены можна зноў выбраць экран для дэманстрацыі."), + ("confirm_clear_Wayland_screen_selection_tip", "Адмяніць выбар экрана Wayland?"), + ("android_new_voice_call_tip", "Атрыман новы запыт на галасавы выклік. Калі вы прымеце яго, гук пераключыцца на галасавае злучэнне."), + ("texture_render_tip", "Выкарыстоўваць візуалізацыю тэкстураў для павышэння каб плаўнасці выявы."), + ("Use texture rendering", "Візуалізацыя тэкстураў"), + ("Floating window", "Плавучае акно"), + ("floating_window_tip", "Дапамагае падтрымліваць фонавую службу RustDesk"), + ("Keep screen on", "Трымаць экран уключаным"), + ("Never", "Ніколі"), + ("During controlled", "Пры кіраванні"), + ("During service is on", "Пры запушчанай службе"), + ("Capture screen using DirectX", "Захоп экрана з выкарыстаннем DirectX"), + ].iter().cloned().collect(); +} From 12d3c59172846cc2890a714bc1d0ed9dd118b9e2 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 27 Jun 2024 11:04:55 +0800 Subject: [PATCH 135/335] windows portable loading ui (#8490) Signed-off-by: 21pages --- Cargo.lock | 62 ++++++++- libs/portable/Cargo.toml | 3 + libs/portable/src/main.rs | 7 + libs/portable/src/res/label.png | Bin 0 -> 1234 bytes libs/portable/src/res/spin.gif | Bin 0 -> 59332 bytes libs/portable/src/ui.rs | 232 ++++++++++++++++++++++++++++++++ 6 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 libs/portable/src/res/label.png create mode 100644 libs/portable/src/res/spin.gif create mode 100644 libs/portable/src/ui.rs diff --git a/Cargo.lock b/Cargo.lock index 54bef444475..a3db6573b92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2139,7 +2139,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad46a0e6c9bc688823a742aa969b5c08fdc56c2a436ee00d5c6fbcb5982c55c4" dependencies = [ - "libm", + "libm 0.2.8", ] [[package]] @@ -3493,6 +3493,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "libm" version = "0.2.8" @@ -3813,6 +3819,22 @@ dependencies = [ "tempfile", ] +[[package]] +name = "native-windows-gui" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7003a669f68deb6b7c57d74fff4f8e533c44a3f0b297492440ef4ff5a28454" +dependencies = [ + "bitflags 1.3.2", + "lazy_static", + "newline-converter", + "plotters", + "plotters-backend", + "stretch", + "winapi 0.3.9", + "winapi-build", +] + [[package]] name = "ndk" version = "0.7.0" @@ -3891,6 +3913,15 @@ dependencies = [ "log", ] +[[package]] +name = "newline-converter" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "nix" version = "0.23.2" @@ -4574,6 +4605,24 @@ dependencies = [ "time 0.3.30", ] +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits 0.2.17", + "plotters-backend", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + [[package]] name = "png" version = "0.17.10" @@ -5390,6 +5439,7 @@ dependencies = [ "brotli", "dirs 5.0.1", "md5", + "native-windows-gui", "winapi 0.3.9", "winres", ] @@ -5948,6 +5998,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" +[[package]] +name = "stretch" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0dc6d20ce137f302edf90f9cd3d278866fd7fb139efca6f246161222ad6d87" +dependencies = [ + "lazy_static", + "libm 0.1.4", +] + [[package]] name = "strsim" version = "0.8.0" diff --git a/libs/portable/Cargo.toml b/libs/portable/Cargo.toml index e39762a303a..d0305b6b0b3 100644 --- a/libs/portable/Cargo.toml +++ b/libs/portable/Cargo.toml @@ -14,6 +14,9 @@ dirs = "5.0" md5 = "0.7" winapi = { version = "0.3", features = ["winbase"] } +[target.'cfg(target_os = "windows")'.dependencies] +native-windows-gui = "1.0" + [package.metadata.winres] LegalCopyright = "Copyright © 2024 Purslane Ltd. All rights reserved." ProductName = "RustDesk" diff --git a/libs/portable/src/main.rs b/libs/portable/src/main.rs index 1ffc8aa5dcc..48372d68b8b 100644 --- a/libs/portable/src/main.rs +++ b/libs/portable/src/main.rs @@ -8,6 +8,8 @@ use std::{ use bin_reader::BinaryReader; pub mod bin_reader; +#[cfg(windows)] +mod ui; #[cfg(windows)] const APP_METADATA: &[u8] = include_bytes!("../app_metadata.toml"); @@ -119,6 +121,11 @@ fn main() { let click_setup = args.is_empty() && arg_exe.to_lowercase().ends_with("install.exe"); let quick_support = args.is_empty() && arg_exe.to_lowercase().ends_with("qs.exe"); + #[cfg(windows)] + if args.is_empty() { + ui::setup(); + } + let reader = BinaryReader::default(); if let Some(exe) = setup( reader, diff --git a/libs/portable/src/res/label.png b/libs/portable/src/res/label.png new file mode 100644 index 0000000000000000000000000000000000000000..6876c7935afc33e4f62d0069d7fab30dabd121d9 GIT binary patch literal 1234 zcmeAS@N?(olHy`uVBq!ia0vp^2|%pC!3-pC$@QiIDaPU;cPGZ1Cw1z99L@rd$YKTt zZeb8+WSBKa0;u3nfKP}kQ1bsM7|J1V^=YUzFcznk1o;Is@H=P;Yuf)`nf9^QXIbVf zmb~SgTE&!CAJ{&_n<;Vbp7Sf5qx5UH7;xk)*gI2|@yMO`ikJQO|2k3e;fJii{m+L= zB3P3bC2`sEt2*ZlpxB=<}J_ajpYU2QX!@JWGE>}h&VZVU`e?4B-;Ar*6y zQx+H|$ZSox(iF(e*38<|(^IdxrQxak=lnZbHzXwM3yh5FmL6c5cRy$T$LIs!CqGv9 z)m6A$z{AZV;rPI`Sz<=rsRIhki{^XZ4gdA+?c`74a&E`;_n1pcm^3uyyGrO!&S{i; z%$v6LbyUyuDJ=7iJmN0fET1*WI!I`O*aq~|6l7^K-`9j-Vv=i}qj)66Q- z4=Q?YC(QpWc-vN??^1B%^mXk^Ldti_H$)ydk@)e&y31wj_v-aT*OX68Y`pK+-^=6Y zZfkB}z+z;S7r67(|HI|H7JLd~Kh~yk+VDOCBs5MpC9{(7`< z!Gd_LA4-*;Z)Dh7_#4D52?@ zFOps=8N@G}v5{f5-8=i(pF0k-Zf8BxU@`e@_2K*%oZl~=YC3uTm&B)g9AAZ&&zkcq q!7ENZ+-zePyJM?y*1-we85k;iZYXA&dFuibErX}4pUXO@geCx`f))+{ literal 0 HcmV?d00001 diff --git a/libs/portable/src/res/spin.gif b/libs/portable/src/res/spin.gif new file mode 100644 index 0000000000000000000000000000000000000000..44b2e2e62cf04fd7f19ea414c885a9e7369bc432 GIT binary patch literal 59332 zcmeFaWmMMd*7ki7(se1FiiC6u3Mvf(Qc6h+NOw0V(nv{ncXxO9MR#|1gYO?*YwdOK zz4ra=_3rl_V?9gx#Q5{+JjdYtjbk3ioKr|x@X6D6?VxthDF`$@Jv}osGdnvwH#avw zKfk=Zys@#dv$Jz>aBzBhdUkepetv#&adCNh`Ss_|&l~vL-N1vd$l?OhZ}@m51(@j> zVW2=D(1UYWL>Lenh!*%A_$O%xfe>y$Lx!^hf*$Pg3TwQJA zwfpXT8<8^1-smj$;a4W2Y(+C{B<_ zW~rnL=gHZU{}Yi2j1WeJah%VOkusKfloHocRM+2SVCW)Y;l*g`=Hf2#7?3Wdy7}=` zP`pQCGD*`h^)9E>G_3TBH2X;LY5bEw?E2eqZ{}2;q>7HG=>~92UU6SSDDc;LIXl~< zxMN=@XV@nCQDY=ty@gH6yn%ndM(rBisDP1$A<`V5gWSVTlUdCcoMq1hr@vLuPLZ`$ z_yRn)Rn!aZR$n|Mt4{~5%agTTIyGRiT{fqHj9s=AT4g9YpPIE(tPrueQ`MJPyi>iQ za%Z<@CzOA;_E_9)w{F?5dAEN40(q~YAL-RzNZg>iin?rl_FiOCuT6dfIo< zM)Cyqn@m-Dfi#A#-Kp6JowQ|J2VHbn_syYjiPR3E82g@?_pr}a7xr=-P8IcZL!%b= z^WFQL4dHnh!`j=x>}Vk9*toDyn$7hC3Im?NX4a z*4ex#W$xL6FF|R=ax53w(;?^2;up(kF=+D37`8X(<92B~HvEO6!B185+vqR1iracP zr|(^(Iqd0dT2}G5zQMdu?qd0BFVHtC;2_ZRMz!i#T_d}{s&>nSPW6k+a;{y+H$*78x4`)>m>opW_iXC#EaZMbBb=Ki zee}FuD&GdXdJcv%ooCiG-}UY@Vg&F(19+mxkm4vF;f+l|S;3wgqi+I=_RA*xi9Pox z-3e&c?M=iNdmd!mD2W&zFPlm6_dQ>}O1vA^-b}%|x{u&}bSvbnjrySsaMc=(g4e>;f$JkTH9002$AzZf)OkcnTF=g&8J-lI|J zE-zSY3wS6R`>LYweG>@8DB}`u{;>j@*=NKc*nD*mVaZSB`4H>IV5Xcu8i=)FbJPr` zU8`sC*l?`C+9x&U@YL{*xm#DQ?&K4r%1+xpgPp4*?KOyyMBYuck<~V1#Z6u9&h@29 z*icJat3GkH$ZaIlSRI=7b8swf2LYcHutwx(B4zI-6tO(zWaX5KODQ^o${V109Tz4(OOzZJlqHm$? z-NXQ1J(;9ndWF}?;lv^`DbYwAuT$eMp30;p&z$R~r&Dm~WhARaQfJ=C6^Q7H3qLQqSaFkJmTz%Cczg?|U*ZrYu+Ks2x%q$yyMx&X zSfbf-R0ZdA*x=pIVvtd753#@r{V3(INn>+E@TA29UvSuzHIBh(9p3(QS+#jxRp8ua zVU%?x;e%M4IqFmcGx3EN3Yyhe3*B2(9OPkb=WV&aD6l+XMBA;rk!_v zGG)DceLCmCaC5d4W_)wLmX>#Ou~oKvbGg?hVbA>wsE~dKs_)`&adB~Vb#-fNYj1Dw z=;-LD?E8Z!?dPYz;|&0y!ur1nRAMf3F4~n5eEMt?<3+l#W&&Bn3KPYfLzx2bmSUGI zTVo0KrMZT7^hU!_76-X8^{2bTY}V*yhF3O5k)2^laNA0y-D_RDvSl}BPj}ZX%Wizo zW(GKSs~0NLSXJ$fFS?hc3e!-DcQ3m)SL;^noh^j7X0C@-g%Q`R@zr3wrQ<)q53S05 zywB9Gq=sQdHV@@?$7EIrhgdd2@GeE_ygi{?Ij$Ez)RSn9N9vmKu9V(kf!;^Lp|L)P zSnLtL*pRWw_mgi~XWzrfP$pY5LZ43t(%iM}55ju9uF2ajlROnHua&VJs$`WB1X6QH zT#H5km-Bv2?Xw?ec)fmG<*40>t*a)mx7dZSCVMlC*beWG7NIJsq zY@L)c-hy_kFa2aWR;7cqIw?{@tu`oABegoy(_&Px^b)B8C)Q-s#52<}Z^AwfQ%C1J zRjx$6c)PKjTYa*znO6^|2mv*txky2^lTPXvbQ4Dz6sj;!ZVB|gs7fgw;83J1exuGw zS28JUA5k{0g_&C(9C&HS3tKE&&%2sP5K*~Fy0lZZ*V?tMafIE~U$f1eyj$ygENZ|9 zTFmONho#ioYd|I^G^s^lILR;gDl&!9)JbYpz;~CXnz8K;;R#bazVR!wcQ_i+rtN5f zN`-24T$l&V)aHcpJ#4cmf!!`KQ!L$Fc~fEid}&G+Ee;PpAM}v6Wgn`bOTa$v6-CdM z8x<11(#Wlyhcf4F@t z=-lU)Bjk9GK5r7^;fr!1)Hk8!`NH?aZ22`+>7`a8`I>E4C1_AN8ejMm+cl=j?HFxj zYV=g{hSs4~trZp_n`|e~`q=O2?;08J*1|Jf?KR^VU+uS3{)eD~!N&N7P6)rF6Hu32 zT3T9LTif2=-rwIpIXU@Rj{G~m!avXS$2S1b3H*N%omd~&pM$2m;~x_|udiU$u1I)u z?{N>lKdsT8imTElzgM)OTPqpU9`Sq_#Wv~RSWCg_i5 z7}DObo1Ai3g-$BoEFRwwy|kyK&tt~#R-WZJ=s;x10fRL}h`D)MQd^va3A~fI( zSB(>IM+(-tuXgR2d|wN}E}6vK6LAdu!TW7G*xg4U9P+7xE}a0tuK9%6r@@fhLYBO zAHszde@mwdas@ees+DjSTYyelPLEe24ZJ(h8SME*{a-7l2-(1_{C*O=M+P!gXck)r#X~=i`7yX@lE{IkO8}NXKal zJ9x9U-Vbs_1(uAy4y7wWM7_TQLN=W=)SUh3(_g+6s&7>6pOQtxx8>c{U$w_fVVsLP zJekXTwh}c~dquyLSF;0GjoVN)U~j?)_0=1<361EAp&oqC1h)y5b1RPzPyZF}t$1Q; z1)~d^bzpO0nyJwgUFCP-!3Z+(Q^YwK_c%`b7Ioa+uMX=&U-o6>=Uo-p;}_gSG3^xD z6_pyqL2o(lQ!aX`rJ%1;D5fk^F`u_}}5^yGORZ zzW&`K190@y8vEmi>*v?M;|%~fLjS)6N6TGYsJQvap!(N6@$7xuuO`eP{pmtsJFhS; z^@g*=E-kL{tqjJW>Q?32R~3s-SJ|FYINq}{tZZ@|WOX96H5*}$@EExyybxU0{?uO*_bPPm%;fxMRoZr)B?URe7$LH}2=FU`H>+avk@7W2i6o?y+N>kzvrtVVUOH$j=fLCnm$*+U~90e;s)m;ve&13~| zMB#-Ms^*TjB&;0zHdRJF6;VrAtF(h$Mz7U&`K8f_aEi3qN(Wht)rl=xwB1TaYMf&t zUV5bF-HlXPkKT%)MCFgekAeg2Dw2~p&({}Iqpdd9)8ehD>(imt`0F#$E?#bArY3rC z_}yd`+6876kKpQO=L}qAdX;r@WaXA9jL_ubK@n_eHeE#agSyf30}9%BL$eDJ9&c_H z_3vDa6(=)m(iNe+&!W?q@|)-{8>J@ODfbG|-{D>R=(AnvQ=ynyw$D~cRLyT-xF*zgGtfDjaN^{yHk7s?(VGQ4a-%ene9MztGWSkvZtrzyF1Qa;+i z4*P_w{`LdjW;2x?aafj)3-MRx6`0en0(-580kMPyrHs>ub*{u(BP8JyI3TL6A2uj) zFD9$Ng1ibmRHy!YG@sDau_Wv5J%Z9rd(dC3C5IVZ ztf!~tUTkES?Ober_rCrZM-F08P`^kZL~Qq z29dg4%{5&H2x34}H%GZ6D7W`}+*d$I@W2bjRd+!a_6+w%E-07OVr;b`SMNMtT9;qbw>AZQ&5 z(262V@%l;>2bGIUgvE$1Wemb2?vW^mlXc2hh*MN+@gFJjL*hIsymds(?{2ImX{n^F zB?ni&4T28PLdr;ZBeSudOr}ennH1xsw~_eZbLK|;>zEDdxEs9>UnQ4mA1pQuge>0|>K|xjJdz5`%LecL2w)<@q%$2AiQw6+_(hMd#X}1p7Ux>(3PW(58LOdwCCES#)v3#}unSzq~4fpsgzzt4VQb7I(;SZOe@) z7^4ObyU-D_4QtrvgpCUr_gIcHowis_8VT{AsIW>bos>=LCRLwwVs1Mg@tH1$SkKwZ zNtY|T$dQ!``1Fx83;OfjFfF2MOWQWU4Te+*Mw#d4t-_dNUeqOt=H{<^nZueWKeV^F z5XA9e=d6I4_Pd0x+*LW@Ax@`=YZxD{@Y*pxBKEEZSciXrzi)B|8SOW^M=YJ-hWsx4 zfg2LV(!9qV!}2*@GE&V%UJT~YL|&{|gmE86cO48Z){(V%UY3>ghjv7jYm2;`-niod z{Lo%Yg4=W*5F2qTR^?#g6rtr`K-L-zf<_?3NDWsf4d7OSNzPb_dYcW(6ct9kA*km{%Kv3^>QZSo>%xxs%bxJfnhfHcb zHudYYWN2!c^mJ1E*BRNJ=en6U=|&UOSz(Ql>&Zn{ks&#WsYBYizG#(LdG@uiTXEWW z6D!%4R<`|N@BHv`B6?-@w@b!ezvm8xwGagf!hIsx37QGD4=i6Ziqfc9iH*uFrz-5C zFW+s9N~qqhRLrXxpXW3dyiAyYg}AeF=;>+aA4%a zltYCy<8dR`S5i~Y?Wavsr*Y&ySqoT9zAu|KRm40~a#TGko5w^JHxcyNtYT31*T<|_ z+I{WEAQUzj^>pE@naUHP;}N$4p&0#OdY24s8p+MvCgRKC+=st0yw!CH!4?KY9*x$Ji`q>vdK-(v*UvGPE!$I7H`;j!v1K;9ce|pvNTL%j zj<+al`+A(7HJ=}CQaXEbIbUCvGlc~>Z9g4~bN-5F6X}8sE}!MCVoDEiMHP57?{Xni zKIe8%3t`@!P(3`JH{5M3z=Q0y)`HhVQt1V6l!P@>4MvWk1)hN6Gf4-k+*A$Uh3hkj zEiamva^QV256zcrbia5|!NNpp^&#@ajA0-ptMz4ZS#`jJ($o?T4bw@@pa^3O0R)(l zS;+% z!B%?fDG}bsYIg-YuNq-kf4An07l`t8GA}XIfZ%Bv^?&#J@9$P+!1Pf#ng_=$qBX$JNUoHBjdoSm#Bu&E#fJJFYU98R)N5_nyf zHW5?J7}a*aHKOH-G^UqajddmF=#6bsvRFS1^0TYU%ya7R%Pk@{-^z;9_2X== zBw)4Qt&%`>*lYSz35nY%pYY3pt+;~|W zE`gKrdE3L0G7=)^i_@!V$~Y$woK1`~`nL9c=(C>DfRD&*;Us)zbmdB}XfiqpE?e)P zOt|kqC=YPIf5Lv>Eg-}*z!OO-b-|mmGKf2L8OqiJXuc@wu8&mmT|&XpcMW{;k_#D&c?C&(pyl3BFRs-lRP;{OCeP{)QTFE z8Yv=@p5_LPpPBeJFmgpC4Q(PM^Cqr#NH0q3_&g<5hQ3($dxq~NmLzu&3??T?u#tju z%cqsiKCrMqbS%3Fn}=Zgrc6f7-SZb@edvJvlEV%ARO z;>gNQ6+%nYPW2HIK&TexI)v5fu&AR9bXyv<^i?;gPZf@Mnd&#((S=31RSXp|A zw=tRn90}Q(@7K7qKM+HM*i6zznROJ|;L?}lWIBEr?Bp{en%53%6hRT5x<^_&{ zlfyu6q5==1%29KwTjbS+@?1uj82v3hfNHb zVPj5J%4Z`qy&m$jjwV-YV#RTM*XO4sF1=Ei1dew|({MiS3m4DvZ9QI7Q^62KfP6xI z+di#=M^XO35Yui=QT2iM8iofFs@j6rBljl@hD5ap^4{nLp8Y(Xrf41u z*I$5}4@TAA5jAY3Zi1`T#D*81dae;oE5>NTD{uhH2AWZSeg8()rKRe^1R%%BgU zTAAy&d5MfyaG$6*(i5EQz!~YO-kBLF85l^3&oBhDNlAa)BNuzP|&7W20va<$=*0x0<*KHf4h z#1Q)KUF}yDzJEr~M@W<14^49F6WRWdLE5YhAtk#|o7PA4po@`&%)D$*L6)V0y{a0_<;b^aOCpcuh54p8HZIpjfMjj6A3c^6YMgqkpA(ctLy{AA$0 z{dU-dKJqpOggH&woL@oWx%K3*{WY!B3R2zN^=T}dA#o+bX(q@q3S5Wjvt$%1R9&hlD}6|4 zzl>+u!?3~T9BeNbD`ZzKnCj}cdN3irVH!- ze+9c81dz)#R6WmzN=`wU>8$w&8j?BFCvA)UUl`>n+muh`V16}0{nn=#&K9A%+B`N2$*ZyO1*ghRF4U|u@pD)k7`9Fn znK~lSFR0g(gJps0AR{;)fSs9lhCqxvwO)E0GwVi1x|M<+Pe$p+M&?a!>jiac;fUQv zTIotAbx!38l73!ct_Vhc22vD8K_otbRCXuZZ6r{TCPxyHP;DCufmN}-bW#PFlL1dA zNnN&NhrCl39;BEnu=+7-J9#5h->7P|R#B&VU*!|1W(V3kuNoOot)BPlgkZG}L`jxk zkF>35Qd9n*I=2y-CAz<9cETsJd5T%OpatQnFH>o@DG<*fVaV=p$MDcHD}zdVw%19k zxH;Z+hn4KG8_}z~@con3t%7cpVS}934|j47d!MNqunss;)g1Mr;)yYS2fmxOg zn=G2h&*tHRgX9^aIwzxawa<*F|kWL@9bCg zuQ{H0*z9-;?}+Ziv*0fw*|{Q@XLd1((|dEiStn8OzXmn_9qc6T{wn4We<$XEPT}vp zSU{UF(2E85U_aZ0f4==c&J6&3yZ4t9bEp{qHQ$u~2j6i1cfQ#R?9G?>Ydb-z`H#k& z^U_oxz#ORcmcBGDNG;#hE#K^~A#7lm@#V+&t?#yAgFhlJl-zg4czQ2^s!r0qu${zy<> z0Rf>oXNx+l>bQ|X$Wo~qpJT=3Nj1fI&mu#l)bWzP6q>%()KJy)n*1my?ggkf)NpXP z0b22Flz!{m{%GuB-Bq4VM~~T9q|d9AHZE+e)A3N)3a=Br*?=HofFSj{D2z0~w=i)% zFd*hSGg47(0WlZ;Q2_%Gb9#E2*_j-=SvUFKnY!7?QkvT0a4DI%JaJmaJb znQIR&-l?t9uHK@T=7vHZuA+lBU5GN~z1+zPsGIboiK<_@tCZh>_#k>mA4RpRzX{_Z ze_k`B5J(0@7bg#d^la{jBv?CM2^H8Z|rEHD#gcY*(&CrBR8%U zhZ;DjpY#keE}f>#JYd=JysT^zoDp-{h6Q;J>9blF3!J-Wy1hG#qWbBHfNbCJ6Cj94 z&opC%SS_`TB}{8uO=voEzF^id8MwAuDT>ohoM05V5Wcv=A-F)zQC0N2{04He<(`_) zD~Ekt5@*w~PLFPfLo@>Pn!^DJe~iHq)i{R}h<~@^>1-C^pARC=3I7&E{HH(%FzN80 zy10JYWk1eK{)3jWQQA=<`CAoL&x6A5$>8i-LC#YpOTOB1`i~+k$9~Km!ySoVe4DR%0we5aId!V@z zAk@q&Au0OFv5*VP#c}@|?Ij~fm3h^Ci6WT<JVUkz=!q1`hhz>TDzK?%e(YAJ_3rN z1P`Z(@OOMv#KjBm=wGlZ@UhU#KJ*Qj8cTT3NpG>p%cZA9>K|hIQe8)gIhZU^Gzc#7 z%jfyAc!M|8N6FrDBcbnky{O*aH%8!YPYO8+d>RljeV(Sp168^{69w1ub|%__G?-Y> zW+g-0%0XdOn-6kFZ$93WA`=KAvTf+d!0-b??aK=ha7wtAcSZ`5F3`mlY{r3+o*b&D zn~@&bsgs%gu~H8JnbCz_c0sI&er6()MOIb?ezAUX&5KWZK%nE>KtshZP~G?#v{3!L z_EJSd9G5!9W8#W*CBEvLIi-_qS?Oi73Ya^kPC=sdf~&HkX_d>=xA-dAq>tEZ_35uZ zq_%LWnOX=nE8^*@e=xE zjkm$Fk`=Zhg-q>hK1w45MjL+FV(xyF^@@d$UXez=8x5j#*h=^{+Pt5WeTYSkn~Us- zPxN${bwGlK=vYOFlkTXWZr1m>?XDpZp(o!AdCadaWKp85CJ|gZ@qTu=bW-sRCcCov zpz?7tzrg7GeF5W1I;I&IZOrmH&^@#~K@T}t#uhWWTB${>>&YFZO)LJ2idAZ~ftBc4 zQOdQg$Y*xbog3BG8@QA`cAGSHJNcW1l*M+x47(SGp9`-eR~K&`)89~ev<5x33%J8G3|43~(` zw8lgM)T-h`!jK#-GleF0wI=M1KAT!>drCb!7X4It4f0sLO@7+WbgPx+#wc&Brb2md zoX#&!eg>e{JWa00*bG3eI;vi4`B{Kk7t?}L&NK6Fs&-%qHuyu+zNMN2Vq`0^Ywz~4 zniB?9JBgch~`sqaY zA@7$5_(xW>2LPim)*}LW*hT_^5||}R%|0?eQI)OrY=b7L*_v0$qbi3ofF6{5DG$`uv!!#_GkrLg?8HMA_D$S~#pvzYIK#-BX7(0=X z_e8l%M<}|MX1Dri1c-J(k7l|9 zdT~y-4tcpr7wB!@TnJ$jk z_ySy#P3p8Hv|5ru`sm#T$C4?>0XnIwApwc!X*2e&+htMIip^z1w&uRzG5N30O$B{D zwx2W*5ak}uX^?gYF0*LFDz*`fE3?jk8OS*Llfz;q$6sajVr+60ZCAD_^@&?-mr}x7 z?U0@J*z=~9MPKZ~G#C|bH6u$~93as3Rr5rvZaqD09lUzBO{eNCxd)GucMU!6>|e9A zz{lnIKPC$P4{H5uAJ}bqk^Abm#NzK^_J>~C&)K*??Bo8U9sDQE{)RBCSNaCCVC;Y| zNdU~&Q~g3^fI%7}NN+&Vp?VvsP$pO#nc0Gw0~x?vCvPg*oRQ8<;ldSa8ky1)yR4U$ zaL8HGwb%sI`Hd7+R0XYUoK%IKf|rkqdht1fiU(w64D-s>X=rmMiKBwbs;sbbQy2n8 za#J(>mN!$znLDM@4sKy~rl-YSOM*9-S9fJ}v|E1(X8>-1A#h-9{7U@{+yr||G_-rL zrj3Z?CiSg=?FMA(A2u_!p}9_(@FMtD7qme|q2%*Yf9cNaVtANr+6})XYTiS-SiN7v ziGxzq_jHngm5(2XZWr?Mo-)JWT}rY2L`jC=qdsrhTbS(vFcTrkJRT9I;QS zV->Y9AD~5^DCMV8eQp_NK8SjfJ#D^{Q#NfDWgs>EdJ&abP)%d{v;~ix*eVjknEwgH zfzOXo=*y+jdH4g3TI&|{8ZnEhc!uDUVg0ZfjIm5kAh9SGhRQiE-uI$-tMp-TMQ7Bi zIL1yb39Q539aM=c?p6|pfWFRCLi0m-uUh7##;jL0hyJP({mm2qNG$&Ic-%jNncDSl z1;n3D*pK!`|2*%%yMe!=6V`^sE&oqaYy|J^fo60Fooa4`Ez3%0tg#iE7)z1H0JE6B z;Ph_=#Hf^w!4jDnl^Df2msw!YItoVo&h8@2Go;<$3W&LoithzPKcIlP^{s%Y0=cx_ zoByCn#`Ud$_(e_={aXQXb(Y?^&UWT(LYQCmd#rImj9>AW0-_)gYy4I~Jm6t{@B!<> zEx``muw2ru&-N_AJ#|<}U5!MmJ-`zoP@Ytc&K)6=kNJhH2450W@S-Ls{m`N(k&)Gc z5BuHm`?^9%$UhzX(9SkjtCs-(Ipwl!dBEo-^dw-_$N z-@i?Ih;WPhe~UG~5D86* zcUPc#6%(pQou=#sNU@+yi(4tCbt}bk|3Hd)-%7DWe3^o7zi(2E!=R}9yA zQ-^YLl|FD%Wh@#g$mWHsPd*Zg{Vv6--a?%?N!Qn2u2g~RK#QAs0#Go|7#r>!+VAlr z!dLf0!PCByY=Q%pm$bmWj0Uw}Jp`l}vU#=%FFYW{n$Nwb_IW5=4e~l4QVt!oFf`FJ zcCZk*6jrmH>YMiB9Zs?Eal>El^z*~ev38?|pd9sjlg3y;d_|s@3`i-~NJ3aCCk}g1 zO%0C{6jWo1M%hHk*b;R`Ke>z{1mrLah~uD^M7KysYY)*Jcq-5(sykYDiER#G9lM|LVsQPZ!`*+O*V->5q+mgR zXm|lrbb-}gKd0#ayNB)P_rK*0{3Q*q|Bra(io#v}4_<*PZw;?oUadj=jg!Cg>cfr3 z(sy2c`IT36g&u1+z`za6RnBbVuTE!IWm3(m=5{GzL@r6ftZJtAc(~L2#gMW&bBCvo zl=;Cs>TKbPyiyFAy}64Djk{ya64(bV?BNN+@}V+qBJlV#H2n)OX^2;C-)a6BjlwfESw z16t5WqY7FPOda;CNM<+l+rd?e2i25G4%}5VMb%85M0o_}wJd`M=G~|eC6*+1&L#6s z?mXI~ett@Pi@ui(m8^q&G#9KxFGLMl`Cn2_Q}%)1e||Ko%qiygsbYQsi1}aG{J`^{EE-4Q|5Q40w+GWc7x}U%9Jb@q&K^t@Og4MNiGH9 z@Y^oKDEmEyjx;7dUO{w3yIAt^S20~ry+>xZk(t!Zu})ftcG+I2*lW67wD@PH=-#TW zyx-icf7clXv@ZiIvc7Mg1Xg7I>vEqhytefFks#VX#6iP)V(hcL9a|q@?%-*+kru~K__-9C~ zfP{kjhNReEk>vDELfs-s{F{UVki_s!LeUjE|AHj;??@^RX;U?|O$nRkzo25h|G^Fq zm*`_|$*)K%03?(fmO?raKORRhfF!&0SiT3$tmVyql3-%CRAPlhGQvA)E^gc2AY<$fU_6pWe{Vk=~}> zN_%*%XOIl%Y*yXzX>Gz1q(=m&Wxlo189*$hZzB0*L6o=}mAC2@7hW z`o|etZm3;$TcEQlnA(ojVGElpdqfNCa1l+++6!~8@;ewdFC)8eX0oJuE|4VgduffE zSz2SEl+A?rCZ9z>j_kIi20&-k;hlo6u#X!)N}(3NltX4??nm(*I#yL(%q|*}{Mupx zEgr^tqM~1>JODCYOnKZ(eJ7waUPZr1+FIGMEZKU%dRnY-iWc(xNLldV&fR$nJn{W; zW;$`4{@^}MTc!7$TTjGXHupAP z0lj7Qe=^wpKPUi>V*MI_|5wWj$X@`9vVLpt`e|AH{!#n+```Em{*smzId}MfNvwC@ zh(-8~Sa-e=>-@Wj`nSYtk-H_>mP>jg=RT76 z>3B@`j*skxpoVA2=SN9?bQ5C_{V?sY$@t>wjuy3qYR7N`Q3`)8`cVSJ@`l|AP2p9f z#(Qt9`Z{8gN8_Zl-VNU}LyHH#RChKCzGZwm+TsG6Sa4^#qCeL9HZ_Q#ur&J*V#&aL zBbLa;7tsGktjt?t;cxzXV!c7lqD}ujv5+&03vY>~_|9HWbdvNkq%5=-lU||eAH-_( zsfRiu4){i_`YObs&0VN(#PaLCG_HX{&t^!vCf+h`L}KtwZ$NvfB-IQ9AE4cYHR=PZ z#YMiNXdxQ(k!puI1y+%e#Q7FB=jA9B3NrX~M|KmHMN9QC+RGL;5$uaMxqD zk*BDB2TFpoU4vb2RV`+cac#o|Yv~%aVckJ6DU8ZDD!J0N`zwD|ME#qMjt;-hEBt*Q z@^2d*fwfsbTebeiFY?c${oxG&p!&;gbZqh^MLX{Dd+p@C}7*ihbp~jDS#yi?OIM#Np6@@ER(U1VPwT-Tn^-6SwZv zZMCn~%>x`}L;cO28prsEeNOqSJH=)f^1a#@ctdtu?aREa_I0km`c~~zyRG&G6@IJs zW!_f%8ts0o_SxzEYqgK_Hd_%z1yuXGvWj{Z@N;sDUT@x3`?4}ivz&&v3z#p*)!-Ulgl>LuFu zH3o%Uzhda&L4JN`4Q%2`J2O0t)tpLNv*jS3uF9OOcK~U~QtKSt!O{5y1TLN4{gwjc~wqeF37F;=A z2e|b1A6ycV{e?>xm;jfwBL8bHWdWJL-*ZV{b~G{be{hL-^H&!NU$ed%uDJ?ab2g$6 z23nvLLKE)Q!rTLjhalwWy?RgPsr)ANLz?tv_%})gb>`~6yHNOsC{FpE^|Mf+1q<`+_AF)X)D_ zDX5?CGNo8p5{B(q_?752OFPy3p3!0Dhc!53)V7W|<1}8M<$9HxlC6}VjvkdNF^BmM zc3Jgg-_xD3DCoY|X))ZxC`sD4yf2AG@WMt4oyO=uik&kzuO?W|=zKX`JNJAg+Ir{D zFVFgwOKrrz*6#k$ZTerVrk|S}f512S2RZWJ(`~w(`!Ea7eQ+ZmZ=*a{{~T>q{(rhn z-_dhiF`jf|oTfZ&4|M#tC@#H#Tv>0ee-G?kF77W&B=3If_`Op`V!M5=t)v3A0S|UY z#=yCOcdnNtBHmB;IY-<~O{;_0nP8lc;rJ@CXcLQrrfhQnUqBV*QFc*nqo zne+`kd^Buh0D7p0G~BpUu*rD&NCEUv(FZ5e01MF_+5b}`{fA9ER6GGWafA1A#p)I?3C>UaeiGXx50{W{yfr=4Ngp zMZiE#Jsk3uOzwf+R&J3u*;Zk9rU({Hn_w1=aF1-&mUO&G)^>WWyXJOTgGk7B>5>)l z-SV{&YOLa=Qc=UowHK2+r5$$2c_q!g`q|Lb(*!%REgOo|FpTG-BWZ^elEz|&{0-;K*(G>d%yKP>fAnv(E4bH-L$i;h~M7?q0#CA4WR2VYW(v9`)dh*|b2scr|1 zC|XY(k7>GI>5R%dqn0Ka*!M_I=og`ar$4t6fs1E+Hfq?%eJ`%92R=+b2hVvU(SsL0 zQrAA2_uz1{8MYDZu>#>ry+~VD)`*2n+KA*Hbbco21+P1L8x>AuL=l_wW!5=KZWZG2 z+ix>h`El}=?e;z$jNQ%cA8%yfci2ZH^1s?rXYlXex%9;X3wE1oy2^4`I>x&Lv4r+AJB6~7GU z6C!i5f;|vBpEm{@o=Li4pByJRp?QsAc=bV9C4S5u7P_Taaw0G73)v+1kdCo=fMT!{ zyiWlE<&B8ywdfyp$15NRp(lL8;~B*W$(J|0^6%ftVW$(gD_X6q8l$*W1Q?*$32Q|l z=q0a2LhHVbaWuKWo)gd*(bcvww@XXoU7rWGksy0frn`P(gH^wsTLBDUGAjlsCLIZ^ z;gXMZfkx)PonY~uVwD-cP>kw3#WL%^Qw-L|Qa8K$L{Cqo9FE2yNB&$dJC6c2YpYO( zXp=UNjE-hI|IPNrR!I`Y{ej{sIGT`>G50LPm(!u5+0yf=KHSACp`syK8!slQsZ7bdXE&u@_4)Kz%j(W^{bA){6L0!)8OiMFHOzuo`1-=c~dzYoGSFWzq@ z(!Qmb;w{DASTJ``A;Z>pfoK7gkvE|fb+fn<7WObKMi;db9Xb^8a%~zE^xy7(Y=OQ{ zR5T#^X3JuL9|!fQ`3)DUWv{%%cEGTlP~`EbnmLN@h_rFfagvUHkL0*^5-NDguapQp z?K|Pfo-_7&qsAKI*!3Jd>vcf~o_EG~dNTKk`i0GqC0suMFdnqDC7n0)2i;!CwUEvj z|7l1^f<5Z%F-wjaGv3TRCt!gV4y^qaOpiDxOzEIs#bD%YZ{>RJ*By&JBrox+&3Y2m z{+(wu3=W6A4;d_u5X2;Ej`&oK1A6{s(^NbN2KQGH_P_X2z^r`m0+GKzhc8s=Z}>Vpzr(I-fO$R|tpe4#is6ii z2jB}4=?uda{;AHC3OExUqPP8aP0sx;Tk||0w#pceCqbp4y4Qz<@_295kh5qtdS=!I zUS@i@L?=>NSrQ-r?J*5aCg`UT0mvWRlRRRF!vlg)neigOAb=~9U5nm_YieA-vsL`W zhCPzaf>hIrEmCJbN)nu`76GR zKGi3~OWT2<5$|KH3n0h?11#e0GBdN_7F*vyrjbIQM!1!2b5pv6nr1t#(rIa{q*?@PyLd`=6I?oXLyc9oL|kuB zK1ZsUTiBeqNt?epQyEk>ZiZ!?#Q@>ll%EvY%n_^UbXza|a#OVq3|j(5z@r$G*Ed14 zc7vNSM5Dnin2HAAR$Tp8;5IyaS_}{wOg=_Ck-m~y2W65{K?}{7slqOFQ9_n3B*@i4 zBYPJxIuPa5;GmD2lD4?->827(qY%1San~!7?SKJMp^2j*X(1Hd!Ixa9$4N?@J(44e z=BVItKX)SVgl}dI+wkYg>(XA+sOR7*uTDB}Q!lpl6I9fw2;V=MRfzi_Dl^!}V zbvjPgfZGFAr@?I@qNuNjEs^NWcw?QNfGs6Bz?Kr27{5yB%siv>4Wt@0PTs;kBgyU3 zd!OugGETcW*J{7Mu-j{#B{ttLCh@=8Q)bw`I_wwv6J1j~|IVudX59iK0)KW){ZMc8 z&%=Jdfxnq-J`1%-D@x?|g*+x;DA!vS{f>vn%hgxW5XsMjcmM z@KkWEKSctV;9e=PHNJXdH0uYsx&J(b6BhXAu~-B25>NIacC%X7QM zJ%?5IbjoO7cu${*8}3W)ZAI$-dA>Zr54A=fm~~rH9uN=#_6i7uKR}r9L}5CQ|B}pe zzNqn4$}2JiQ7n}FW3?FW@;g=NGXiLJY~htawOFqe7kr+z6>elL&6Q}_23tss4R-Nt z?Amcezk`c91*Gpy1?1bOpoIZ&~FYnIR5+%c_(}a-d@> z7wI05p=d?Ymy0O{Se2AWwwYU<=#7 z=5M_-QlqeeO^*Pp^5vme3F{jPpKdWW;|bF;wP3$BU~0wGbzo`(+g3BRgFyWO?Idpi zR-q(B7dF!50jxsh16ai}SiRRnv6{`&_-wHnqvsw8p+(<|Dg%ptbg;5{gAn`nQByE6 zoyCBNgx=!;N{O73;Rl@8mP7CE(XEf%vqhzv(2crY8wxMuJ4KvF4acPz>QFF4}Y2DW;+UQ0m&4^hncy;WxgOx|x4(hEpra*6%Kcvg>a!%zM_43WhpC>bx!uJ&91F6YeHZh}_9W!d zc88?HP52eTS#d`;p{=o!8)N_6EA?Zaxuz~<#@MQsL(IV}oLOSqbFV$}&NzlqYsV!d z$>3SO%YC6)=ej$OATFp_I%>`c(B<*g_nz7)tAGV@z^+JdZ62tgXnGRz6Ot~?o7(dqXZ-zQ)2L~B^V@MYS;TJWtOeuU5eX3(gK9uks?hHxG&Bf?^)-}oO|Ye znl%?bW%2FZAO63!_p|r&BwvY-eWbLVp76nLh%?Nl%_<`RJtSwGSZun$4O@O3oT(DF zvhd(3^(j`2Y$sQ@ZoA?joue0^iS)KM$vk%Eo z@yag?juox4*=(Dqq??ON$f_^abbyDgc5VTf1(*fa@@UfnVIyI-#eG+EY|w*(l{O_K z((ZPpzEqH-F%A)`@JW5Rp@2weN<5>@@?? zOZRh%M9Q1E2WB>A7(I>4X3<^h=mwSrU~mX`wSBgh0jYVqUJEeBjRt`~{^jeNNe5@#oK}aonFZMYt;Qvb!GlXE2~YGx$0KBp&)~NLw@d&NXztH^{=Noslk^Fi5(MX9D&G7s&{^$rm40`r8-;GbL)m6}sAnVA; zJZwOeqTgZl;Os%P}WaV_Wg9u>AyrRQ!~6BdNS zT1D$I(e2zRq^ORa$wbLE1+W4uRt9_^(XH;kEs3R?QE+HiXS>hRqavhu@$)s!Ky;0v z=!)LwJGd~k>dmkYhXEhY2&Z9dc!!gTeGTU;B~l82#-InZ6gg*X73Hy#Z3{fDMyem$RwTjd`w2+n9w+n@GBaK|(I_l$Hk6K|}rjB@HXv#pO+Ho@<=Bi2}Nu)BY%t315X)#!iFJ-w%)=D7vAALXe! z4CC7F9~0{H+$E3DJydZ{;o8D4hIM)E&(uIJ@7?TLRodNV?sGX@=jyzCa0E^Tk!jTu zvab1%>-^{V15^FB{PC~q+0Ov36ZP!pTX^#0|3d`;;{DaGy7XDaej}dJuMW9*i2J6`n?~$L5CLhbBMNqM_oPKd6-tZ{kI@6o9 z=U}`3=!m%cuF7vxE-r9e>ifyX#WKCSV-7XEDK~2^M|lMs2adbT_G&sVgbnI68#w2r zee;c83g$d5Ppst^O?DNQHkKRyVXT{cZx$F&3qZ+@-_NI0s9PaC5+(YQGiZQ_N!N@F zKt0PXS+veMByJE~$}Ot4rUM$j(40J~1)$+8-Y}#E8a^RgR153Abz%FSwM`M0v=V6e zIJNBxKhir07Yy+h+vSdGJhv^Ieq-~ceEtq;Z3XDP4PE70iA^_XlLl*tSu_4+wHi6e zhOFN2$7R|K@(S@gydLk1Nof!dLzZ-L~&Ji=jGw% zWwQ$#E#TKW;?@MIiG@(1B48}J{et&vyVL+iyi?A(qzI!pkSpG*3|5p<)xdd3b}ou^ zIP^jYI2<0RGUOF@-@bSMZa>XM;b#L^j!2va%>Zrfv;LB!^st%cw4=$*%Rqd|qssPy zHr$h2!rdb-pQ*b?Prc?SYiAf;kx~mBKufBJP6^BOIdRjJPtwDU)q#n-VLA?rLR~bkw?-Q7f*?E*ot- z9gbhASsUb;2tu72GEkL^BW`@zq_ZM4i)Mw)VcWnDMYu=t@;Wf){xH;D&rI{vO z=wKktvX}MzrR{VO3@n{7+ zd`JjT+d4<~cS0xlBIk_9Fi$s&h4cCFV+XL^?8Yt&6MVHV5kvYM7U<^YxP0g+T4;TH zW@hk?v;rS~T!xLi(_9Ir4fwR+IN?fVbr-PK__8$tBM&@{_c^!cIO$0})65g(&9TPw zL_AsgY=H03u32Q>MKvDBfYIPIsKoU_vxgcYuciaFHJl!Xl9Obei%1%spMDHBV~Kx5 zVL)*{D(BlS_lqdU)(3XxO2M;cU{?cN2yD_RIS11!;8GTn zNeUQd_J=Ozmj|{jAqzt(mkaVKwU<#9oS$s+hy~<@iYVl?1&ircuCAa5&2v^tUK5N9 zgGLNDSIWi>&()R_DrZ!wk{t}pHHT1dbyK;m|;~^7^r1?Iwn_}g8zh=XIsF@*0H|r zM6{%bJ#sEHs|WmaZ{s8PyDDmu+*hePXufxpK=4xSP0T9}8_oAR6dEy?brm*xRCPVK z$XFt(wnAYrJ^a|8U(Y93)A~7;{9RG*AJ5GGNs&7_GyD1Mo&5NlRRG}FU&_?b1Vusr zT{s)$Yf4N>3Rd*ED~=@&r{;4Sk$m>BO`iF#GzL`E*%70NC$_sQb-jmN2iK=lQM4Er zeFf|#%;0D>EN++CM14zUH755)LnaLu?bI@`1zo|R;}Ynj4Q1cDcc7ICaHcj!=}GR zbcIKr2Z2E3*bmU?J3vBmEHwKi0l=}W%*Q0r3&5EfD@Z3LBilOL`1K-iX7)O}crmxw zE0i&}?4$L~Jj}M??flw`OTgC1o*=L_VlD`5jr6Pi!V-?9W3c30vJ+shIOkwKYT!eyOb`X`v9S zC1lW9driibSn!dEZRkloM3wz%7=wHwqLK1mEFj9kM^J8DHo3F}{0z0xEoZg$3tK6G zsU-A5sm{nJa?x{=Z9)Sc>oD0P9JW)kcNt!XArS_mO=KK4yRrp1#Cp}3^RT^I3A||4 z+xI-TdJTmR^af~5lh6-u>MA;XhQwVn7=Gxy0xa!ANS#L>l}4aDN6l2v?Tlz}9Sro- z9SC=idC(b`jWBTM>*MKJ#>&Q{$gXwc6?DJAjiWUqUDX|N0bkW)iSnI0oFlI9%`w{< z4}4;I%~{xj^u{2TSy4LrO&k-+^39e0?TA%210#2>26*w4kQY1JdS5%i#GaeI@+O`& z178!Wwrw>{^v1>@#9q79;ksVE)0b=DdyAjiz4kLwxQ_NW2Vh4ByK~5+!{3kSswKU& z=1Uu%O3bHKM`^g`ce*{5RFVF-4gP;)fe1)$fL-s&_1WL-6+ii$KdJ!0x4%>br3s3h z7U0{s&jisw^6km;559r#X@nchnb-TtPkpz&yJ^{zVP}1qD7&T8@}95q&6T|k+gP+V zzA4Fl=hNr+_x6n`Dod6p1jCv&s_9j}5WS5{b4cEo)g&6E2KZLxv>85Be+U!ZHIms~ z8){~_;61Lex4B4C*u2Z>>G5qwxWClfTgABtuU^dMy>qZ*-~*xNH}(ee@5DJ$2nKWb z6UvpbJg2@w&gEZ=B1HtU-P|1sI&)o%+Y5~#p9~?2nAZy#Mg{X&v%KM-QYO_199Iz= zlhq4Cd@g$zCEW8tFG`K++YnT&DR!C=Bw;o6@+zt8bo4KGT4P_)n;S$2oTh)C9%EP% zgN=Q3r*+21is#f|{65+M;rDEj*&^xGXvkb5_=_td#EVpLKJ~dE2?!J*;_xFldH$^& zNS5-_Bh{Aj(N_bO zkWrxxO9k)tD3?*ufojWzuUW$`6jVr5Ubx+LD40`RTw$1(m1TBs`A(cY?n9=itGj?f zmqx{sQ7i8v;3MB81hXdW69Q=vPirg3Zx4qdtM`Qzx-8kwZRf$kH(+*jP|?|Puu zFCi~gVrHHaA+2U}MB=DsX`=}5dS*g>#a9|pL=Y4V#6z}!}z>0Jw42?9JhLPBMt}$U`UU_G4H5n zyOZ8JUf<>*ublRlOIHv7)C}^0_s=Y^f7R81HIiT55>IrsKYIL5{{OeEz+X*Q`@yt} zZmDa&b*j{C8dRrzCqMeJ?08`AO6ax)2(LPX%N5Qh#0q(ruw43T*^{n2FwH((WF4EX zUgO1r+q4}FGu?DsVXph*jR-jhefh3%jhiG1zvc!Ng@%V9mse016n< zlN|Dn`}HttMf+1iWs{ySHr{y~K>8ro)SBk0%iADIy}{_rseKr{%;;k~$ZkaFT0R9;>{f zrkHp`wmv`O^ML2n-Iw4u26S=OHyg|o7(SEDsULKd&qg`TX3Zsg9FxpbWOFX{Ig|B# ziX3C=G1HDo#tv-y2a>M60Fo$&E%jdkv&U4yVv<)3 z#sxd(R%S+f1TG2|j+ka9F^-vuk>o6>O%TP<%~GG$iJ4Dx&qvf}O19H2u=$R;X%)a7 zkk8SXVa^rAE1i|f)kmBPtMyC9+pASX5gsbKqA4CdokCcT&03Rdo?DzIQJ#&%F#LhC ziT9tj1|gyRZ9C+j6ttiBY9|WX-}0e8`KW(Wfxnu9W`ZK4`F_zlwcx)gw&^qZG2C0p zm_6=zAJP%nhy0+if*Oaqtyn4kmQ?N zmi_Ete*Nns>6bsWJ5z2knb4loSX>xW}exl)UP8xy` zC#YY~y7vO*`NDB2Ijxg%DY5U44WOXCVX7@5cpz0Zg#xpWf8%@LU-^E+F!!Q!oyw3a2+622s3_2inz)ip#uOX(h;tSD z4P)0yc-0bOmc5ltt~-V6kgj#zH#tw)9;pOagPa9bE)^k--AhWRXyD5v3mx)Hl}9uR zogY8hJFM3;hkI=JimtA05MP8GZ1(BH4z>m@kO$i%|3RF}5n#zE4@{9y{!S`zQh}2S UoK)bX0w)zXslZ7E{;w7IFBB{E@c;k- literal 0 HcmV?d00001 diff --git a/libs/portable/src/ui.rs b/libs/portable/src/ui.rs new file mode 100644 index 00000000000..0a015a2486f --- /dev/null +++ b/libs/portable/src/ui.rs @@ -0,0 +1,232 @@ +use native_windows_gui as nwg; +use nwg::NativeUi; +use std::cell::RefCell; + +const GIF_DATA: &[u8] = include_bytes!("./res/spin.gif"); +const LABEL_DATA: &[u8] = include_bytes!("./res/label.png"); +const GIF_SIZE: i32 = 32; +const BG_COLOR: [u8; 3] = [90, 90, 120]; +const BORDER_COLOR: [u8; 3] = [40, 40, 40]; +const GIF_DELAY: u64 = 30; + +#[derive(Default)] +pub struct BasicApp { + window: nwg::Window, + + border_image: nwg::ImageFrame, + bg_image: nwg::ImageFrame, + gif_image: nwg::ImageFrame, + label_image: nwg::ImageFrame, + + border_layout: nwg::GridLayout, + bg_layout: nwg::GridLayout, + inner_layout: nwg::GridLayout, + + timer: nwg::AnimationTimer, + decoder: nwg::ImageDecoder, + gif_index: RefCell, + gif_images: RefCell>, +} + +impl BasicApp { + fn exit(&self) { + self.timer.stop(); + nwg::stop_thread_dispatch(); + } + + fn load_gif(&self) -> Result<(), nwg::NwgError> { + let image_source = self.decoder.from_stream(GIF_DATA)?; + for frame_index in 0..image_source.frame_count() { + let image_data = image_source.frame(frame_index)?; + let image_data = self + .decoder + .resize_image(&image_data, [GIF_SIZE as u32, GIF_SIZE as u32])?; + let bmp = image_data.as_bitmap()?; + self.gif_images.borrow_mut().push(bmp); + } + Ok(()) + } + + fn update_gif(&self) -> Result<(), nwg::NwgError> { + let images = self.gif_images.borrow(); + if images.len() == 0 { + return Err(nwg::NwgError::ImageDecoderError( + -1, + "no gif images".to_string(), + )); + } + let image_index = *self.gif_index.borrow() % images.len(); + self.gif_image.set_bitmap(Some(&images[image_index])); + *self.gif_index.borrow_mut() = (image_index + 1) % images.len(); + Ok(()) + } + + fn start_timer(&self) { + self.timer.start(); + } +} + +mod basic_app_ui { + use super::*; + use native_windows_gui::{self as nwg, Bitmap}; + use nwg::{Event, GridLayoutItem}; + use std::cell::RefCell; + use std::ops::Deref; + use std::rc::Rc; + + pub struct BasicAppUi { + inner: Rc, + default_handler: RefCell>, + } + + impl nwg::NativeUi for BasicApp { + fn build_ui(mut data: BasicApp) -> Result { + data.decoder = nwg::ImageDecoder::new()?; + let col_cnt: i32 = 7; + let row_cnt: i32 = 3; + let border_width: i32 = 1; + let window_size = ( + GIF_SIZE * col_cnt + 2 * border_width, + GIF_SIZE * row_cnt + 2 * border_width, + ); + + // Controls + nwg::Window::builder() + .flags(nwg::WindowFlags::POPUP | nwg::WindowFlags::VISIBLE) + .size(window_size) + .center(true) + .build(&mut data.window)?; + + nwg::ImageFrame::builder() + .parent(&data.window) + .size(window_size) + .background_color(Some(BORDER_COLOR)) + .build(&mut data.border_image)?; + + nwg::ImageFrame::builder() + .parent(&data.border_image) + .size((row_cnt * GIF_SIZE, col_cnt * GIF_SIZE)) + .background_color(Some(BG_COLOR)) + .build(&mut data.bg_image)?; + + nwg::ImageFrame::builder() + .parent(&data.bg_image) + .size((GIF_SIZE, GIF_SIZE)) + .background_color(Some(BG_COLOR)) + .build(&mut data.gif_image)?; + + nwg::ImageFrame::builder() + .parent(&data.bg_image) + .background_color(Some(BG_COLOR)) + .bitmap(Some(&Bitmap::from_bin(LABEL_DATA)?)) + .build(&mut data.label_image)?; + + nwg::AnimationTimer::builder() + .parent(&data.window) + .interval(std::time::Duration::from_millis(GIF_DELAY)) + .build(&mut data.timer)?; + + // Wrap-up + let ui = BasicAppUi { + inner: Rc::new(data), + default_handler: Default::default(), + }; + + // Layouts + nwg::GridLayout::builder() + .parent(&ui.window) + .spacing(0) + .margin([0, 0, 0, 0]) + .max_column(Some(1)) + .max_row(Some(1)) + .child_item(GridLayoutItem::new(&ui.border_image, 0, 0, 1, 1)) + .build(&ui.border_layout)?; + + nwg::GridLayout::builder() + .parent(&ui.border_image) + .spacing(0) + .margin([ + border_width as _, + border_width as _, + border_width as _, + border_width as _, + ]) + .max_column(Some(1)) + .max_row(Some(1)) + .child_item(GridLayoutItem::new(&ui.bg_image, 0, 0, 1, 1)) + .build(&ui.bg_layout)?; + + nwg::GridLayout::builder() + .parent(&ui.bg_image) + .spacing(0) + .margin([0, 0, 0, 0]) + .max_column(Some(col_cnt as _)) + .max_row(Some(row_cnt as _)) + .child_item(GridLayoutItem::new(&ui.gif_image, 2, 1, 1, 1)) + .child_item(GridLayoutItem::new(&ui.label_image, 3, 1, 3, 1)) + .build(&ui.inner_layout)?; + + // Events + let evt_ui = Rc::downgrade(&ui.inner); + let handle_events = move |evt, _evt_data, _handle| { + if let Some(evt_ui) = evt_ui.upgrade().as_mut() { + match evt { + Event::OnWindowClose => { + evt_ui.exit(); + } + Event::OnTimerTick => { + if let Err(e) = evt_ui.update_gif() { + eprintln!("{:?}", e); + } + } + _ => {} + } + } + }; + + ui.default_handler + .borrow_mut() + .push(nwg::full_bind_event_handler( + &ui.window.handle, + handle_events, + )); + + return Ok(ui); + } + } + + impl Drop for BasicAppUi { + /// To make sure that everything is freed without issues, the default handler must be unbound. + fn drop(&mut self) { + let mut handlers = self.default_handler.borrow_mut(); + for handler in handlers.drain(0..) { + nwg::unbind_event_handler(&handler); + } + } + } + + impl Deref for BasicAppUi { + type Target = BasicApp; + + fn deref(&self) -> &BasicApp { + &self.inner + } + } +} + +fn ui() -> Result<(), nwg::NwgError> { + nwg::init()?; + let app = BasicApp::build_ui(Default::default())?; + app.load_gif()?; + app.start_timer(); + nwg::dispatch_thread_events(); + Ok(()) +} + +pub fn setup() { + std::thread::spawn(move || { + if let Err(e) = ui() { + eprintln!("{:?}", e); + } + }); +} From 9c7d4ef1f70c4127c26d1020b591ce71abe2a40b Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 27 Jun 2024 12:11:08 +0800 Subject: [PATCH 136/335] not use nwg default features (#8492) Signed-off-by: 21pages --- Cargo.lock | 49 +--------------------------------------- libs/portable/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a3db6573b92..6deac2f9b54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2139,7 +2139,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad46a0e6c9bc688823a742aa969b5c08fdc56c2a436ee00d5c6fbcb5982c55c4" dependencies = [ - "libm 0.2.8", + "libm", ] [[package]] @@ -3493,12 +3493,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - [[package]] name = "libm" version = "0.2.8" @@ -3827,10 +3821,6 @@ checksum = "4f7003a669f68deb6b7c57d74fff4f8e533c44a3f0b297492440ef4ff5a28454" dependencies = [ "bitflags 1.3.2", "lazy_static", - "newline-converter", - "plotters", - "plotters-backend", - "stretch", "winapi 0.3.9", "winapi-build", ] @@ -3913,15 +3903,6 @@ dependencies = [ "log", ] -[[package]] -name = "newline-converter" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "nix" version = "0.23.2" @@ -4605,24 +4586,6 @@ dependencies = [ "time 0.3.30", ] -[[package]] -name = "plotters" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" -dependencies = [ - "num-traits 0.2.17", - "plotters-backend", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" - [[package]] name = "png" version = "0.17.10" @@ -5998,16 +5961,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" -[[package]] -name = "stretch" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0dc6d20ce137f302edf90f9cd3d278866fd7fb139efca6f246161222ad6d87" -dependencies = [ - "lazy_static", - "libm 0.1.4", -] - [[package]] name = "strsim" version = "0.8.0" diff --git a/libs/portable/Cargo.toml b/libs/portable/Cargo.toml index d0305b6b0b3..82397421b70 100644 --- a/libs/portable/Cargo.toml +++ b/libs/portable/Cargo.toml @@ -15,7 +15,7 @@ md5 = "0.7" winapi = { version = "0.3", features = ["winbase"] } [target.'cfg(target_os = "windows")'.dependencies] -native-windows-gui = "1.0" +native-windows-gui = {version = "1.0", default-features = false, features = ["animation-timer", "image-decoder"]} [package.metadata.winres] LegalCopyright = "Copyright © 2024 Purslane Ltd. All rights reserved." From b04773083080008c6112aa719602fd53d4d113d2 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:05:45 +0800 Subject: [PATCH 137/335] Refact/android input changed notify clients (#8494) * refact: android, input control changed, notify clients Signed-off-by: fufesou * fix: android init input perm Signed-off-by: fufesou --------- Signed-off-by: fufesou --- flutter/lib/models/server_model.dart | 5 +++++ src/flutter_ffi.rs | 7 +++++++ src/ipc.rs | 2 ++ src/server/connection.rs | 8 ++++++++ src/ui_cm_interface.rs | 9 +++++++++ 5 files changed, 31 insertions(+) diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index fc169c96388..b643a7c2098 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -177,6 +177,11 @@ class ServerModel with ChangeNotifier { await timerCallback(); }); } + + // Initial keyboard status is off on mobile + if (isMobile) { + bind.mainSetOption(key: kOptionEnableKeyboard, value: 'N'); + } } /// 1. check android permission diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index d34ecb14d47..b8ae1abeea6 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -786,6 +786,13 @@ pub fn main_show_option(_key: String) -> SyncReturn { } pub fn main_set_option(key: String, value: String) { + #[cfg(target_os = "android")] + if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) { + crate::ui_cm_interface::notify_input_control(config::option2bool( + config::keys::OPTION_ENABLE_KEYBOARD, + &value, + )); + } if key.eq("custom-rendezvous-server") { set_option(key, value.clone()); #[cfg(target_os = "android")] diff --git a/src/ipc.rs b/src/ipc.rs index f1141e041db..c344dc54ee0 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -189,6 +189,8 @@ pub enum Data { MouseMoveTime(i64), Authorize, Close, + #[cfg(target_os = "android")] + InputControl(bool), #[cfg(windows)] SAS, UserSid(Option), diff --git a/src/server/connection.rs b/src/server/connection.rs index c2a754d0685..2c649f0a9e8 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -400,6 +400,9 @@ impl Connection { } #[cfg(target_os = "android")] start_channel(rx_to_cm, tx_from_cm); + #[cfg(target_os = "android")] + conn.send_permission(Permission::Keyboard, conn.keyboard).await; + #[cfg(not(target_os = "android"))] if !conn.keyboard { conn.send_permission(Permission::Keyboard, false).await; } @@ -457,6 +460,11 @@ impl Connection { conn.on_close("connection manager", true).await; break; } + #[cfg(target_os = "android")] + ipc::Data::InputControl(v) => { + conn.keyboard = v; + conn.send_permission(Permission::Keyboard, v).await; + } ipc::Data::CmErr(e) => { if e != "expected" { // cm closed before connection diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 38bfc72b55b..8b4a31f3ddf 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -279,6 +279,15 @@ pub fn close(id: i32) { }; } +#[inline] +#[cfg(target_os = "android")] +pub fn notify_input_control(v: bool) { + for (_, mut client) in CLIENTS.write().unwrap().iter_mut() { + client.keyboard = v; + allow_err!(client.tx.send(Data::InputControl(v))); + } +} + #[inline] pub fn remove(id: i32) { CLIENTS.write().unwrap().remove(&id); From c5d3c7f39036120657f2778197d19b045da2e2c3 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:28:05 +0800 Subject: [PATCH 138/335] Feat/android more actions (#8496) * feat: android volume and power actions Signed-off-by: fufesou * Add translations and refact action menus Signed-off-by: fufesou * Remove divider Signed-off-by: fufesou * fix: recover deleted translations Signed-off-by: fufesou --------- Signed-off-by: fufesou --- .../com/carriez/flutter_hbb/InputService.kt | 63 +++++++++++++-- .../flutter_hbb/KeyboardKeyEventMapper.kt | 6 ++ .../carriez/flutter_hbb/VolumeController.kt | 78 +++++++++++++++++++ flutter/lib/common.dart | 10 +-- flutter/lib/common/widgets/toolbar.dart | 31 ++++++++ flutter/lib/consts.dart | 2 + .../lib/desktop/widgets/remote_toolbar.dart | 35 +++++++++ flutter/lib/models/input_model.dart | 23 ++++++ flutter/lib/models/model.dart | 2 +- libs/hbb_common/protos/message.proto | 4 + src/flutter.rs | 2 +- src/lang/ar.rs | 5 ++ src/lang/be.rs | 5 ++ src/lang/bg.rs | 5 ++ src/lang/ca.rs | 5 ++ src/lang/cn.rs | 5 ++ src/lang/cs.rs | 5 ++ src/lang/da.rs | 5 ++ src/lang/de.rs | 5 ++ src/lang/el.rs | 5 ++ src/lang/eo.rs | 5 ++ src/lang/es.rs | 5 ++ src/lang/et.rs | 5 ++ src/lang/fa.rs | 5 ++ src/lang/fr.rs | 5 ++ src/lang/he.rs | 5 ++ src/lang/hr.rs | 5 ++ src/lang/hu.rs | 5 ++ src/lang/id.rs | 5 ++ src/lang/it.rs | 5 ++ src/lang/ja.rs | 5 ++ src/lang/ko.rs | 5 ++ src/lang/kz.rs | 5 ++ src/lang/lt.rs | 5 ++ src/lang/lv.rs | 5 ++ src/lang/nb.rs | 5 ++ src/lang/nl.rs | 5 ++ src/lang/pl.rs | 5 ++ src/lang/pt_PT.rs | 5 ++ src/lang/ptbr.rs | 5 ++ src/lang/ro.rs | 5 ++ src/lang/ru.rs | 5 ++ src/lang/sk.rs | 5 ++ src/lang/sl.rs | 5 ++ src/lang/sq.rs | 5 ++ src/lang/sr.rs | 5 ++ src/lang/sv.rs | 5 ++ src/lang/template.rs | 5 ++ src/lang/th.rs | 5 ++ src/lang/tr.rs | 5 ++ src/lang/tw.rs | 5 ++ src/lang/ua.rs | 5 ++ src/lang/vn.rs | 5 ++ src/ui_session_interface.rs | 49 +++++++++++- 54 files changed, 500 insertions(+), 15 deletions(-) create mode 100644 flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/VolumeController.kt diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 47c8f302c4a..3fcc72df346 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -18,7 +18,9 @@ import android.widget.EditText import android.view.accessibility.AccessibilityEvent import android.view.ViewGroup.LayoutParams import android.view.accessibility.AccessibilityNodeInfo +import android.view.KeyEvent as KeyEventAndroid import android.graphics.Rect +import android.media.AudioManager import android.accessibilityservice.AccessibilityServiceInfo import android.accessibilityservice.AccessibilityServiceInfo.FLAG_INPUT_METHOD_EDITOR import android.accessibilityservice.AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS @@ -75,6 +77,8 @@ class InputService : AccessibilityService() { private var fakeEditTextForTextStateCalculation: EditText? = null + private val volumeController: VolumeController by lazy { VolumeController(applicationContext.getSystemService(AUDIO_SERVICE) as AudioManager) } + @RequiresApi(Build.VERSION_CODES.N) fun onMouseInput(mask: Int, _x: Int, _y: Int) { val x = max(0, _x) @@ -294,6 +298,18 @@ class InputService : AccessibilityService() { Log.d(logTag, "onKeyEvent $keyEvent textToCommit:$textToCommit") + var ke: KeyEventAndroid? = null + if (Build.VERSION.SDK_INT < 33 || textToCommit == null) { + ke = KeyEventConverter.toAndroidKeyEvent(keyEvent) + } + ke?.let { event -> + if (tryHandleVolumeKeyEvent(event)) { + return + } else if (tryHandlePowerKeyEvent(event)) { + return + } + } + if (Build.VERSION.SDK_INT >= 33) { getInputMethod()?.let { inputMethod -> inputMethod.getCurrentInputConnection()?.let { inputConnection -> @@ -302,7 +318,7 @@ class InputService : AccessibilityService() { inputConnection.commitText(text, 1, null) } } else { - KeyEventConverter.toAndroidKeyEvent(keyEvent).let { event -> + ke?.let { event -> inputConnection.sendKeyEvent(event) } } @@ -311,7 +327,7 @@ class InputService : AccessibilityService() { } else { val handler = Handler(Looper.getMainLooper()) handler.post { - KeyEventConverter.toAndroidKeyEvent(keyEvent)?.let { event -> + ke?.let { event -> val possibleNodes = possibleAccessibiltyNodes() Log.d(logTag, "possibleNodes:$possibleNodes") for (item in possibleNodes) { @@ -325,6 +341,43 @@ class InputService : AccessibilityService() { } } + private fun tryHandleVolumeKeyEvent(event: KeyEventAndroid): Boolean { + when (event.keyCode) { + KeyEventAndroid.KEYCODE_VOLUME_UP -> { + if (event.action == KeyEventAndroid.ACTION_DOWN) { + volumeController.raiseVolume(null, true, AudioManager.STREAM_SYSTEM) + } + return true + } + KeyEventAndroid.KEYCODE_VOLUME_DOWN -> { + if (event.action == KeyEventAndroid.ACTION_DOWN) { + volumeController.lowerVolume(null, true, AudioManager.STREAM_SYSTEM) + } + return true + } + KeyEventAndroid.KEYCODE_VOLUME_MUTE -> { + if (event.action == KeyEventAndroid.ACTION_DOWN) { + volumeController.toggleMute(true, AudioManager.STREAM_SYSTEM) + } + return true + } + else -> { + return false + } + } + } + + private fun tryHandlePowerKeyEvent(event: KeyEventAndroid): Boolean { + if (event.keyCode == KeyEventAndroid.KEYCODE_POWER) { + // Perform power dialog action when action is up + if (event.action == KeyEventAndroid.ACTION_UP) { + performGlobalAction(GLOBAL_ACTION_POWER_DIALOG); + } + return true + } + return false + } + private fun insertAccessibilityNode(list: LinkedList, node: AccessibilityNodeInfo) { if (node == null) { return @@ -422,7 +475,7 @@ class InputService : AccessibilityService() { return linkedList } - private fun trySendKeyEvent(event: android.view.KeyEvent, node: AccessibilityNodeInfo, textToCommit: String?): Boolean { + private fun trySendKeyEvent(event: KeyEventAndroid, node: AccessibilityNodeInfo, textToCommit: String?): Boolean { node.refresh() this.fakeEditTextForTextStateCalculation?.setSelection(0,0) this.fakeEditTextForTextStateCalculation?.setText(null) @@ -487,10 +540,10 @@ class InputService : AccessibilityService() { it.layout(rect.left, rect.top, rect.right, rect.bottom) it.onPreDraw() - if (event.action == android.view.KeyEvent.ACTION_DOWN) { + if (event.action == KeyEventAndroid.ACTION_DOWN) { val succ = it.onKeyDown(event.getKeyCode(), event) Log.d(logTag, "onKeyDown $succ") - } else if (event.action == android.view.KeyEvent.ACTION_UP) { + } else if (event.action == KeyEventAndroid.ACTION_UP) { val success = it.onKeyUp(event.getKeyCode(), event) Log.d(logTag, "keyup $success") } else {} diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/KeyboardKeyEventMapper.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/KeyboardKeyEventMapper.kt index effa3b2aa33..1e63df4054f 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/KeyboardKeyEventMapper.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/KeyboardKeyEventMapper.kt @@ -37,6 +37,8 @@ object KeyEventConverter { action = KeyEvent.ACTION_UP } + // FIXME: The last parameter is the repeat count, not modifiers ? + // https://developer.android.com/reference/android/view/KeyEvent#KeyEvent(long,%20long,%20int,%20int,%20int) return KeyEvent(0, 0, action, chrValue, 0, modifiers) } @@ -112,6 +114,10 @@ object KeyEventConverter { ControlKey.Delete -> KeyEvent.KEYCODE_FORWARD_DEL ControlKey.Clear -> KeyEvent.KEYCODE_CLEAR ControlKey.Pause -> KeyEvent.KEYCODE_BREAK + ControlKey.VolumeMute -> KeyEvent.KEYCODE_VOLUME_MUTE + ControlKey.VolumeUp -> KeyEvent.KEYCODE_VOLUME_UP + ControlKey.VolumeDown -> KeyEvent.KEYCODE_VOLUME_DOWN + ControlKey.Power -> KeyEvent.KEYCODE_POWER else -> 0 // Default to unknown. } } diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/VolumeController.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/VolumeController.kt new file mode 100644 index 00000000000..be30b653e0e --- /dev/null +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/VolumeController.kt @@ -0,0 +1,78 @@ +package com.carriez.flutter_hbb + +// Inspired by https://github.com/yosemiteyss/flutter_volume_controller/blob/main/android/src/main/kotlin/com/yosemiteyss/flutter_volume_controller/VolumeController.kt + +import android.media.AudioManager +import android.os.Build +import android.util.Log + +class VolumeController(private val audioManager: AudioManager) { + private val logTag = "volume controller" + + fun getVolume(streamType: Int): Double { + val current = audioManager.getStreamVolume(streamType) + val max = audioManager.getStreamMaxVolume(streamType) + return current.toDouble() / max + } + + fun setVolume(volume: Double, showSystemUI: Boolean, streamType: Int) { + val max = audioManager.getStreamMaxVolume(streamType) + audioManager.setStreamVolume( + streamType, + (max * volume).toInt(), + if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0 + ) + } + + fun raiseVolume(step: Double?, showSystemUI: Boolean, streamType: Int) { + if (step == null) { + audioManager.adjustStreamVolume( + streamType, + AudioManager.ADJUST_RAISE, + if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0 + ) + } else { + val target = getVolume(streamType) + step + setVolume(target, showSystemUI, streamType) + } + } + + fun lowerVolume(step: Double?, showSystemUI: Boolean, streamType: Int) { + if (step == null) { + audioManager.adjustStreamVolume( + streamType, + AudioManager.ADJUST_LOWER, + if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0 + ) + } else { + val target = getVolume(streamType) - step + setVolume(target, showSystemUI, streamType) + } + } + + fun getMute(streamType: Int): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + audioManager.isStreamMute(streamType) + } else { + audioManager.getStreamVolume(streamType) == 0 + } + } + + private fun setMute(isMuted: Boolean, showSystemUI: Boolean, streamType: Int) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + audioManager.adjustStreamVolume( + streamType, + if (isMuted) AudioManager.ADJUST_MUTE else AudioManager.ADJUST_UNMUTE, + if (showSystemUI) AudioManager.FLAG_SHOW_UI else 0 + ) + } else { + audioManager.setStreamMute(streamType, isMuted) + } + } + + fun toggleMute(showSystemUI: Boolean, streamType: Int) { + val isMuted = getMute(streamType) + setMute(!isMuted, showSystemUI, streamType) + } +} + diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index cf5c4d16172..5313a9e8516 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -928,13 +928,9 @@ makeMobileActionsOverlayEntry(VoidCallback? onHide, {FFI? ffi}) { position: draggablePositions.mobileActions, width: overlayW, height: overlayH, - onBackPressed: () => session.inputModel.tap(MouseButtons.right), - onHomePressed: () => session.inputModel.tap(MouseButtons.wheel), - onRecentPressed: () async { - session.inputModel.sendMouse('down', MouseButtons.wheel); - await Future.delayed(const Duration(milliseconds: 500)); - session.inputModel.sendMouse('up', MouseButtons.wheel); - }, + onBackPressed: session.inputModel.onMobileBack, + onHomePressed: session.inputModel.onMobileHome, + onRecentPressed: session.inputModel.onMobileApps, onHidePressed: onHide, ); } diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index 0c2494c944a..27629d486f1 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -6,6 +6,7 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/common/widgets/dialog.dart'; import 'package:flutter_hbb/consts.dart'; +import 'package:flutter_hbb/models/input_model.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; @@ -76,6 +77,36 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { final sessionId = ffi.sessionId; List v = []; + if (isMobile && + pi.platform == kPeerPlatformAndroid && + perms['keyboard'] != false) { + v.addAll([ + TTextMenu( + child: Text(translate('Back')), + onPressed: () => ffi.inputModel.onMobileBack(), + ), + TTextMenu( + child: Text(translate('Home')), + onPressed: () => ffi.inputModel.onMobileHome(), + ), + TTextMenu( + child: Text(translate('Apps')), + onPressed: () => ffi.inputModel.onMobileApps(), + ), + TTextMenu( + child: Text(translate('Volume up')), + onPressed: () => ffi.inputModel.onMobileVolumeUp(), + ), + TTextMenu( + child: Text(translate('Volume down')), + onPressed: () => ffi.inputModel.onMobileVolumeDown(), + ), + TTextMenu( + child: Text(translate('Power')), + onPressed: () => ffi.inputModel.onMobilePower(), + ), + ]); + } // elevation if (perms['keyboard'] != false && ffi.elevationModel.showRequestMenu) { v.add( diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index ba93b7fddcd..b78d298e5e9 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -155,6 +155,8 @@ const int kWindowMainId = 0; const String kPointerEventKindTouch = "touch"; const String kPointerEventKindMouse = "mouse"; +const String kKeyFlutterKey = "flutter_key"; + const String kKeyShowDisplaysAsIndividualWindows = 'displays_as_individual_windows'; const String kKeyUseAllMyDisplaysForTheRemoteSession = diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index e75d75c233b..1baae26d39b 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_hbb/common/widgets/audio_input.dart'; import 'package:flutter_hbb/common/widgets/toolbar.dart'; import 'package:flutter_hbb/models/chat_model.dart'; +import 'package:flutter_hbb/models/input_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; @@ -1741,6 +1742,7 @@ class _KeyboardMenu extends StatelessWidget { viewMode(), Divider(), ...toolbarToggles(), + ...mobileActions(), ]); } @@ -1877,6 +1879,39 @@ class _KeyboardMenu extends StatelessWidget { ffi: ffi, child: Text(translate('View Mode'))); } + + mobileActions() { + if (pi.platform != kPeerPlatformAndroid) return []; + final enabled = versionCmp(pi.version, '1.2.6') >= 0; + if (!enabled) return []; + return [ + Divider(), + MenuButton( + child: Text(translate('Back')), + onPressed: () => ffi.inputModel.onMobileBack(), + ffi: ffi), + MenuButton( + child: Text(translate('Home')), + onPressed: () => ffi.inputModel.onMobileHome(), + ffi: ffi), + MenuButton( + child: Text(translate('Apps')), + onPressed: () => ffi.inputModel.onMobileApps(), + ffi: ffi), + MenuButton( + child: Text(translate('Volume up')), + onPressed: () => ffi.inputModel.onMobileVolumeUp(), + ffi: ffi), + MenuButton( + child: Text(translate('Volume down')), + onPressed: () => ffi.inputModel.onMobileVolumeDown(), + ffi: ffi), + MenuButton( + child: Text(translate('Power')), + onPressed: () => ffi.inputModel.onMobilePower(), + ffi: ffi), + ]; + } } class _ChatMenu extends StatefulWidget { diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index f3c53f55870..dde815789ae 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -1152,4 +1152,27 @@ class InputModel { platformFFI.stopDesktopWebListener(); } } + + void onMobileBack() => tap(MouseButtons.right); + void onMobileHome() => tap(MouseButtons.wheel); + Future onMobileApps() async { + sendMouse('down', MouseButtons.wheel); + await Future.delayed(const Duration(milliseconds: 500)); + sendMouse('up', MouseButtons.wheel); + } + + // Simulate a key press event. + // `usbHidUsage` is the USB HID usage code of the key. + Future tapHidKey(int usbHidUsage) async { + inputRawKey(kKeyFlutterKey, usbHidUsage, 0, true); + await Future.delayed(Duration(milliseconds: 100)); + inputRawKey(kKeyFlutterKey, usbHidUsage, 0, false); + } + + Future onMobileVolumeUp() async => + await tapHidKey(PhysicalKeyboardKey.audioVolumeUp.usbHidUsage); + Future onMobileVolumeDown() async => + await tapHidKey(PhysicalKeyboardKey.audioVolumeDown.usbHidUsage); + Future onMobilePower() async => + await tapHidKey(PhysicalKeyboardKey.power.usbHidUsage); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index e1b024f0d4a..5b8b24d97ed 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -195,7 +195,7 @@ class FfiModel with ChangeNotifier { if (desktopType == DesktopType.remote) { KeyboardEnabledState.find(id).value = _permissions['keyboard'] != false; } - debugPrint('$_permissions'); + debugPrint('updatePermission: $_permissions'); notifyListeners(); } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 483e12c134b..be8539a1399 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -271,6 +271,10 @@ enum ControlKey { RShift = 73; RControl = 74; RAlt = 75; + VolumeMute = 76; // mainly used on mobile devices as controlled side + VolumeUp = 77; + VolumeDown = 78; + Power = 79; // mainly used on mobile devices as controlled side CtrlAltDel = 100; LockScreen = 101; } diff --git a/src/flutter.rs b/src/flutter.rs index 5f506588139..0cce2ae714c 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -44,7 +44,7 @@ pub(crate) const APP_TYPE_CM: &str = "main"; pub type FlutterSession = Arc>; lazy_static::lazy_static! { - pub(crate) static ref CUR_SESSION_ID: RwLock = Default::default(); + pub(crate) static ref CUR_SESSION_ID: RwLock = Default::default(); // For desktop only static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } diff --git a/src/lang/ar.rs b/src/lang/ar.rs index ab5e6a62915..6aa6cd419e4 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index ab587e91a6c..79993535718 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Пры кіраванні"), ("During service is on", "Пры запушчанай службе"), ("Capture screen using DirectX", "Захоп экрана з выкарыстаннем DirectX"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index b72b2454551..6f0634fd72f 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 1a59ae9c736..d06c80702b4 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 553a4b27a9d..733fba236f6 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "被控期间"), ("During service is on", "服务开启期间"), ("Capture screen using DirectX", "使用 DirectX 捕获屏幕"), + ("Back", "回退"), + ("Apps", "应用"), + ("Volume up", "提升音量"), + ("Volume down", "降低音量"), + ("Power", "电源"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 01cfd925637..76ec131dbe1 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Během řízeného"), ("During service is on", "Během služby je v provozu"), ("Capture screen using DirectX", "Snímání obrazovky pomocí DirectX"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 3e4a673bddc..0930102d15e 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 90f4c898b1a..b04c8574979 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Wenn kontrolliert"), ("During service is on", "Wenn der Dienst läuft"), ("Capture screen using DirectX", "Bildschirm mit DirectX aufnehmen"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 69fa86276c3..49829b2769c 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 1afae087014..36a500b09c8 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 8fc97a9e9e4..b65390f0a92 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Mientras está siendo controlado"), ("During service is on", "Mientras el servicio está activo"), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index f1d04fe2748..e9a8a22a122 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 189fd4e3442..bd45b2f58cd 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 0d8525dbf78..e4fe38f8f31 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 1b415de90ea..00630c4a1d1 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index e919325d40a..335ae8ad666 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 7bb267a8652..060c363e845 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index e77ad450407..9d97e6da031 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 27d8f19a049..eaaf994b07d 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Durante il controllo"), ("During service is on", "Quando il servizio è attivo"), ("Capture screen using DirectX", "Cattura schermo usando DirectX"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index c0bbf703891..9a95f1a05c3 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index c101d151384..e1bb638cd61 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 8d5a214dde0..980d90a4538 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index ab2dde495bf..202bf2f4501 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 1b00e7de2ec..2ee85c013b8 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Lietošanas laikā"), ("During service is on", "Kamēr pakalpojums ir ieslēgts"), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index edebfc7af88..60d79ac6c91 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 85d1d63a599..4c4c70ee98d 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 0f4707ddb94..6a726030bbc 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index e5b53e55933..24311457671 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 072426ff269..afb364837ed 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index acdd4a987be..baa3dd36e6f 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index aabbadf02b8..68627f4938b 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "При управлении"), ("During service is on", "При запущенной службе"), ("Capture screen using DirectX", "Захват экрана с помощью DirectX"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 137a9bea3d6..007123d6096 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Počas kontrolovaného"), ("During service is on", "Počas služby je v prevádzke"), ("Capture screen using DirectX", "Snímanie obrazovky pomocou DirectX"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index dcf5192d9b0..31666c54607 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index d7abb5b4a61..9830a9494c6 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 8c3cea8b69b..0e993c8633c 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 2aceeba2861..f329ab9202d 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 3166f919490..3d84f662453 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 2c4e32aaafb..d1fdd8aabd1 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 47a7a133a99..c6ae97b81f9 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index a1046ed0096..b30e9223aaa 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "被控期間"), ("During service is on", "服務開啟期間"), ("Capture screen using DirectX", "使用 DirectX 擷取螢幕"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index a393d5123e6..3a17b5223f4 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Коли керується"), ("During service is on", "Коли запущена служба"), ("Capture screen using DirectX", "Захоплення екрана з використанням DirectX"), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index acefe2fa561..dbf392dc0d4 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -622,5 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), + ("Back", ""), + ("Apps", ""), + ("Volume up", ""), + ("Volume down", ""), + ("Power", ""), ].iter().cloned().collect(); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index bb9c6816365..38144d5db70 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -804,7 +804,54 @@ impl Session { pub fn handle_flutter_key_event( &self, keyboard_mode: &str, - _name: &str, + name: &str, + platform_code: i32, + position_code: i32, + lock_modes: i32, + down_or_up: bool, + ) { + if name == "flutter_key" { + self._handle_key_flutter_simulation(keyboard_mode, platform_code, down_or_up); + } else { + self._handle_key_non_flutter_simulation( + keyboard_mode, + platform_code, + position_code, + lock_modes, + down_or_up, + ); + } + } + + #[cfg(not(any(target_os = "ios")))] + fn _handle_key_flutter_simulation( + &self, + _keyboard_mode: &str, + platform_code: i32, + down_or_up: bool, + ) { + // https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/keyboard_key.g.dart#L4356 + let ctrl_key = match platform_code { + 0x0007007f => Some(ControlKey::VolumeMute), + 0x00070080 => Some(ControlKey::VolumeUp), + 0x00070081 => Some(ControlKey::VolumeDown), + 0x00070066 => Some(ControlKey::Power), + _ => None, + }; + let Some(ctrl_key) = ctrl_key else { return }; + let mut key_event = KeyEvent { + mode: KeyboardMode::Translate.into(), + down: down_or_up, + ..Default::default() + }; + key_event.set_control_key(ctrl_key); + self.send_key_event(&key_event); + } + + #[cfg(not(any(target_os = "ios")))] + fn _handle_key_non_flutter_simulation( + &self, + keyboard_mode: &str, platform_code: i32, position_code: i32, lock_modes: i32, From aed212d8f87ccc7a67ac36b95a136b79de970fc2 Mon Sep 17 00:00:00 2001 From: solokot Date: Thu, 27 Jun 2024 10:04:53 +0300 Subject: [PATCH 139/335] Update ru.rs (#8497) --- src/lang/ru.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 68627f4938b..c140fa8acea 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -622,10 +622,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "При управлении"), ("During service is on", "При запущенной службе"), ("Capture screen using DirectX", "Захват экрана с помощью DirectX"), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), + ("Back", "Назад"), + ("Apps", "Приложения"), + ("Volume up", "Громкость+"), + ("Volume down", "Громкость-"), + ("Power", "Питание"), ].iter().cloned().collect(); } From e79946b4e4898418976d4a943975589229209aec Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 27 Jun 2024 16:18:41 +0800 Subject: [PATCH 140/335] telegram bot ui settings and code sending --- flutter/lib/common/widgets/dialog.dart | 146 +++++++++++++----- .../lib/desktop/pages/connection_page.dart | 2 +- .../desktop/pages/desktop_setting_page.dart | 41 ++++- src/auth_2fa.rs | 52 ++++--- src/flutter_ffi.rs | 8 + src/lang/en.rs | 2 + src/lang/template.rs | 3 + src/server/connection.rs | 30 +++- src/ui_interface.rs | 14 ++ 9 files changed, 236 insertions(+), 62 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 1b4839dfc8c..9cfa65b1b74 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -4,6 +4,7 @@ import 'dart:convert'; import 'package:bot_toast/bot_toast.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/common/widgets/setting_widgets.dart'; import 'package:flutter_hbb/consts.dart'; @@ -218,50 +219,53 @@ void changeWhiteList({Function()? callback}) async { ), actions: [ dialogButton("Cancel", onPressed: close, isOutline: true), - if (!isOptFixed)dialogButton("Clear", onPressed: () async { - await bind.mainSetOption( - key: kOptionWhitelist, value: defaultOptionWhitelist); - callback?.call(); - close(); - }, isOutline: true), - if (!isOptFixed) dialogButton( - "OK", - onPressed: () async { - setState(() { - msg = ""; - isInProgress = true; - }); - newWhiteListField = controller.text.trim(); - var newWhiteList = ""; - if (newWhiteListField.isEmpty) { - // pass - } else { - final ips = newWhiteListField.trim().split(RegExp(r"[\s,;\n]+")); - // test ip - final ipMatch = RegExp( - r"^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)(\/([1-9]|[1-2][0-9]|3[0-2])){0,1}$"); - final ipv6Match = RegExp( - r"^(((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7})(\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}$"); - for (final ip in ips) { - if (!ipMatch.hasMatch(ip) && !ipv6Match.hasMatch(ip)) { - msg = "${translate("Invalid IP")} $ip"; - setState(() { - isInProgress = false; - }); - return; - } - } - newWhiteList = ips.join(','); - } - if (newWhiteList.trim().isEmpty) { - newWhiteList = defaultOptionWhitelist; - } + if (!isOptFixed) + dialogButton("Clear", onPressed: () async { await bind.mainSetOption( - key: kOptionWhitelist, value: newWhiteList); + key: kOptionWhitelist, value: defaultOptionWhitelist); callback?.call(); close(); - }, - ), + }, isOutline: true), + if (!isOptFixed) + dialogButton( + "OK", + onPressed: () async { + setState(() { + msg = ""; + isInProgress = true; + }); + newWhiteListField = controller.text.trim(); + var newWhiteList = ""; + if (newWhiteListField.isEmpty) { + // pass + } else { + final ips = + newWhiteListField.trim().split(RegExp(r"[\s,;\n]+")); + // test ip + final ipMatch = RegExp( + r"^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)(\/([1-9]|[1-2][0-9]|3[0-2])){0,1}$"); + final ipv6Match = RegExp( + r"^(((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))*((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7})(\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}$"); + for (final ip in ips) { + if (!ipMatch.hasMatch(ip) && !ipv6Match.hasMatch(ip)) { + msg = "${translate("Invalid IP")} $ip"; + setState(() { + isInProgress = false; + }); + return; + } + } + newWhiteList = ips.join(','); + } + if (newWhiteList.trim().isEmpty) { + newWhiteList = defaultOptionWhitelist; + } + await bind.mainSetOption( + key: kOptionWhitelist, value: newWhiteList); + callback?.call(); + close(); + }, + ), ], onCancel: close, ); @@ -1762,6 +1766,66 @@ void renameDialog( }); } +void changeBot({Function()? callback}) async { + if (bind.mainHasValidBotSync()) { + await bind.mainSetOption(key: "bot", value: ""); + callback?.call(); + return; + } + String errorText = ''; + bool loading = false; + final controller = TextEditingController(); + gFFI.dialogManager.show((setState, close, context) { + onVerify() async { + final token = controller.text.trim(); + if (token == "") return; + loading = true; + errorText = ''; + setState(() {}); + final error = await bind.mainVerifyBot(token: token); + if (error == "") { + callback?.call(); + close(); + } else { + errorText = translate(error); + loading = false; + setState(() {}); + } + } + + final codeField = TextField( + autofocus: true, + controller: controller, + decoration: InputDecoration( + hintText: translate('Token'), // 使用hintText设置占位符文本 + ), + ); + + return CustomAlertDialog( + title: Text(translate("Telegram bot")), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SelectableText(translate("enable-bot-desc"), + style: TextStyle(fontSize: 12)) + .marginOnly(bottom: 12), + Row(children: [Expanded(child: codeField)]), + if (errorText != '') + Text(errorText, style: TextStyle(color: Colors.red)) + .marginOnly(top: 12), + ], + ), + actions: [ + dialogButton("Cancel", onPressed: close, isOutline: true), + loading + ? CircularProgressIndicator() + : dialogButton("OK", onPressed: onVerify), + ], + onCancel: close, + ); + }); +} + void change2fa({Function()? callback}) async { if (bind.mainHasValid2FaSync()) { await bind.mainSetOption(key: "2fa", value: ""); diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index d1ecc29f8d3..797cafcbec4 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -340,7 +340,7 @@ class _ConnectionPageState extends State ?.merge(TextStyle(height: 1)), ).marginOnly(right: 4), Tooltip( - waitDuration: Duration(milliseconds: 0), + waitDuration: Duration(milliseconds: 300), message: translate("id_input_tip"), child: Icon( Icons.help_outline_outlined, diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 81717de3b10..d3b6add8ae7 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -679,6 +679,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { // Simple temp wrapper for PR check tmpWrapper() { RxBool has2fa = bind.mainHasValid2FaSync().obs; + RxBool hasBot = bind.mainHasValidBotSync().obs; update() async { has2fa.value = bind.mainHasValid2FaSync(); } @@ -687,7 +688,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { change2fa(callback: update); } - return GestureDetector( + final tfa = GestureDetector( child: InkWell( child: Obx(() => Row( children: [ @@ -708,6 +709,44 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { onChanged(!has2fa.value); }, ).marginOnly(left: _kCheckBoxLeftMargin); + if (!has2fa.value) { + return tfa; + } + updateBot() async { + hasBot.value = bind.mainHasValidBotSync(); + } + + onChangedBot(bool? checked) async { + changeBot(callback: updateBot); + } + + final bot = GestureDetector( + child: Tooltip( + waitDuration: Duration(milliseconds: 300), + message: translate("enable-bot-tip"), + child: InkWell( + child: Obx(() => Row( + children: [ + Checkbox( + value: hasBot.value, + onChanged: enabled ? onChangedBot : null) + .marginOnly(right: 5), + Expanded( + child: Text( + translate('Telegram bot'), + style: TextStyle( + color: disabledTextColor(context, enabled)), + )) + ], + ))), + ), + onTap: () { + onChangedBot(!hasBot.value); + }, + ).marginOnly(left: _kCheckBoxLeftMargin + 30); + return Column( + children: [tfa, bot], + ); } return tmpWrapper(); diff --git a/src/auth_2fa.rs b/src/auth_2fa.rs index f46bc71e68b..161853722bf 100644 --- a/src/auth_2fa.rs +++ b/src/auth_2fa.rs @@ -4,7 +4,7 @@ use hbb_common::{ config::Config, get_time, password_security::{decrypt_vec_or_original, encrypt_vec_or_original}, - ResultType, + tokio, ResultType, }; use serde_derive::{Deserialize, Serialize}; use std::sync::Mutex; @@ -133,46 +133,61 @@ impl TelegramBot { fn save(&self) -> ResultType<()> { let s = self.into_string()?; #[cfg(not(any(target_os = "android", target_os = "ios")))] - crate::ipc::set_option("telegram_bot", &s); + crate::ipc::set_option("bot", &s); #[cfg(any(target_os = "android", target_os = "ios"))] - Config::set_option("telegram_bot".to_owned(), s); + Config::set_option("bot".to_owned(), s); Ok(()) } - fn get() -> ResultType { - let data = Config::get_option("telegram_bot"); + pub fn get() -> ResultType> { + let data = Config::get_option("bot"); + if data.is_empty() { + return Ok(None); + } let mut bot = serde_json::from_str::(&data)?; let (token, success, _) = decrypt_vec_or_original(&bot.token, "00"); if success { bot.token_str = String::from_utf8(token)?; - return Ok(bot); + return Ok(Some(bot)); } bail!("decrypt_vec_or_original telegram bot token failed") } } // https://gist.github.com/dideler/85de4d64f66c1966788c1b2304b9caf1 -pub async fn send_2fa_code_to_telegram(code: &str) -> ResultType<()> { - let bot = TelegramBot::get()?; +pub async fn send_2fa_code_to_telegram(text: &str, bot: TelegramBot) -> ResultType<()> { let url = format!("https://api.telegram.org/bot{}/sendMessage", bot.token_str); - let params = serde_json::json!({"chat_id": bot.chat_id, "text": code}); + let params = serde_json::json!({"chat_id": bot.chat_id, "text": text}); crate::post_request(url, params.to_string(), "").await?; Ok(()) } +#[tokio::main(flavor = "current_thread")] pub async fn get_chatid_telegram(bot_token: &str) -> ResultType> { - // send a message to the bot first please, otherwise the chat_id will be empty let url = format!("https://api.telegram.org/bot{}/getUpdates", bot_token); let resp = crate::post_request(url, "".to_owned(), "") .await .map_err(|e| anyhow!(e))?; - let res = serde_json::from_str::(&resp) - .map(|x| { - let chat_id = x["result"][0]["message"]["chat"]["id"].as_str(); - chat_id.map(|x| x.to_owned()) - }) - .map_err(|e| anyhow!(e)); - if let Ok(Some(chat_id)) = res.as_ref() { + let value = serde_json::from_str::(&resp).map_err(|e| anyhow!(e))?; + + // Check for an error_code in the response + if let Some(error_code) = value.get("error_code").and_then(|code| code.as_i64()) { + // If there's an error_code, try to use the description for the error message + let description = value["description"] + .as_str() + .unwrap_or("Unknown error occurred"); + return Err(anyhow!( + "Telegram API error: {} (error_code: {})", + description, + error_code + )); + } + + let chat_id = value["result"][0]["message"]["chat"]["id"] + .as_str() + .map(|x| x.to_owned()); + + if let Some(chat_id) = chat_id.as_ref() { let bot = TelegramBot { token_str: bot_token.to_owned(), chat_id: chat_id.to_owned(), @@ -180,5 +195,6 @@ pub async fn get_chatid_telegram(bot_token: &str) -> ResultType> }; bot.save()?; } - res + + Ok(chat_id) } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index b8ae1abeea6..5371b225eea 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2178,6 +2178,14 @@ pub fn main_has_valid_2fa_sync() -> SyncReturn { SyncReturn(has_valid_2fa()) } +pub fn main_verify_bot(token: String) -> String { + verify_bot(token) +} + +pub fn main_has_valid_bot_sync() -> SyncReturn { + SyncReturn(has_valid_bot()) +} + pub fn main_get_hard_option(key: String) -> SyncReturn { SyncReturn(get_hard_option(key)) } diff --git a/src/lang/en.rs b/src/lang/en.rs index d8aa388ec95..46a754b6060 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -230,5 +230,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_new_voice_call_tip", "A new voice call request was received. If you accept, the audio will switch to voice communication."), ("texture_render_tip", "Use texture rendering to make the pictures smoother. You could try disabling this option if you encounter rendering issues."), ("floating_window_tip", "It helps to keep RustDesk background service"), + ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), + ("enable-bot-desc", "1, Open a chat with @BotFather.\n2, Send the command \"/newbot\". You will receive a token after completing this step.\n3, Start a chat with your newly created bot. Send a message like \"hello\" to activate it.\n"), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 3d84f662453..d7744cc36c5 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -627,5 +627,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 2c649f0a9e8..2441c775f88 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -401,7 +401,8 @@ impl Connection { #[cfg(target_os = "android")] start_channel(rx_to_cm, tx_from_cm); #[cfg(target_os = "android")] - conn.send_permission(Permission::Keyboard, conn.keyboard).await; + conn.send_permission(Permission::Keyboard, conn.keyboard) + .await; #[cfg(not(target_os = "android"))] if !conn.keyboard { conn.send_permission(Permission::Keyboard, false).await; @@ -1079,6 +1080,33 @@ impl Connection { return; } if self.require_2fa.is_some() && !self.is_recent_session(true) && !self.from_switch { + self.require_2fa.as_ref().map(|totp| { + let bot = crate::auth_2fa::TelegramBot::get(); + let bot = match bot { + Ok(Some(bot)) => bot, + Err(err) => { + log::error!("Failed to get telegram bot: {}", err); + return; + } + _ => return, + }; + let code = totp.generate_current(); + if let Ok(code) = code { + let text = format!( + "2FA code: {}\n\nA new connection has been established to your device with ID {}. The source IP address is {}.", + code, + Config::get_id(), + self.ip, + ); + tokio::spawn(async move { + if let Err(err) = + crate::auth_2fa::send_2fa_code_to_telegram(&text, bot).await + { + log::error!("Failed to send 2fa code to telegram bot: {}", err); + } + }); + } + }); self.send_login_error(crate::client::REQUIRE_2FA).await; return; } diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 6c95d4c4ab3..c87f12b4d92 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -1392,6 +1392,20 @@ pub fn verify2fa(code: String) -> bool { res } +pub fn has_valid_bot() -> bool { + crate::auth_2fa::TelegramBot::get().map_or(false, |bot| bot.is_some()) +} + +pub fn verify_bot(token: String) -> String { + match crate::auth_2fa::get_chatid_telegram(&token) { + Err(err) => err.to_string(), + Ok(None) => { + "To activate the bot, simply send a message like \"hello\" to its chat.".to_owned() + } + _ => "".to_owned(), + } +} + pub fn check_hwcodec() { #[cfg(feature = "hwcodec")] #[cfg(any(target_os = "windows", target_os = "linux"))] From 9e851542eca5b4e5c9b61cc612485f9f39f63991 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 27 Jun 2024 17:36:50 +0800 Subject: [PATCH 141/335] telegram bot works now --- src/auth_2fa.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/auth_2fa.rs b/src/auth_2fa.rs index 161853722bf..6945bf46193 100644 --- a/src/auth_2fa.rs +++ b/src/auth_2fa.rs @@ -162,12 +162,13 @@ pub async fn send_2fa_code_to_telegram(text: &str, bot: TelegramBot) -> ResultTy Ok(()) } -#[tokio::main(flavor = "current_thread")] -pub async fn get_chatid_telegram(bot_token: &str) -> ResultType> { +pub fn get_chatid_telegram(bot_token: &str) -> ResultType> { let url = format!("https://api.telegram.org/bot{}/getUpdates", bot_token); - let resp = crate::post_request(url, "".to_owned(), "") - .await - .map_err(|e| anyhow!(e))?; + // because caller is in tokio runtime, so we must call post_request_sync in new thread. + let handle = std::thread::spawn(move || { + crate::post_request_sync(url, "".to_owned(), "") + }); + let resp = handle.join().map_err(|_| anyhow!("Thread panicked"))??; let value = serde_json::from_str::(&resp).map_err(|e| anyhow!(e))?; // Check for an error_code in the response @@ -183,9 +184,14 @@ pub async fn get_chatid_telegram(bot_token: &str) -> ResultType> )); } - let chat_id = value["result"][0]["message"]["chat"]["id"] - .as_str() - .map(|x| x.to_owned()); + let chat_id = &value["result"][0]["message"]["chat"]["id"]; + let chat_id = if let Some(id) = chat_id.as_i64() { + Some(id.to_string()) + } else if let Some(id) = chat_id.as_str() { + Some(id.to_owned()) + } else { + None + }; if let Some(chat_id) = chat_id.as_ref() { let bot = TelegramBot { From 5bfdf05ff2b6df8dec91807c16013a8f224c0e4f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 27 Jun 2024 18:01:14 +0800 Subject: [PATCH 142/335] /hello rather than hello to activate bot --- src/lang/en.rs | 2 +- src/ui_interface.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/en.rs b/src/lang/en.rs index 46a754b6060..a76d4c7bc3c 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -231,6 +231,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("texture_render_tip", "Use texture rendering to make the pictures smoother. You could try disabling this option if you encounter rendering issues."), ("floating_window_tip", "It helps to keep RustDesk background service"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), - ("enable-bot-desc", "1, Open a chat with @BotFather.\n2, Send the command \"/newbot\". You will receive a token after completing this step.\n3, Start a chat with your newly created bot. Send a message like \"hello\" to activate it.\n"), + ("enable-bot-desc", "1, Open a chat with @BotFather.\n2, Send the command \"/newbot\". You will receive a token after completing this step.\n3, Start a chat with your newly created bot. Send a message beginning with a forward slash ("/") like \"\/hello\" to activate it.\n"), ].iter().cloned().collect(); } diff --git a/src/ui_interface.rs b/src/ui_interface.rs index c87f12b4d92..0d8723dcda8 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -1400,7 +1400,7 @@ pub fn verify_bot(token: String) -> String { match crate::auth_2fa::get_chatid_telegram(&token) { Err(err) => err.to_string(), Ok(None) => { - "To activate the bot, simply send a message like \"hello\" to its chat.".to_owned() + "To activate the bot, simply send a message beginning with a forward slash ("/") like \"hello\" to its chat.".to_owned() } _ => "".to_owned(), } From f8592e0d5b136ee7d41b7e26d97af2defc179d15 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Thu, 27 Jun 2024 12:10:41 +0200 Subject: [PATCH 143/335] Update Italian language (#8502) * Update Italian language * Fix typo --- src/lang/it.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index eaaf994b07d..7e369969923 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -622,10 +622,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Durante il controllo"), ("During service is on", "Quando il servizio è attivo"), ("Capture screen using DirectX", "Cattura schermo usando DirectX"), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), + ("Back", "Indietro"), + ("Apps", "App"), + ("Volume up", "Volume +"), + ("Volume down", "Volume -"), + ("Power", "Alimentazione"), + ("Telegram bot", "Bot Telgram"), + ("enable-bot-tip", "Se abiliti questa funzione, puoi ricevere il codice 2FA dal tuo bot.\nPuò anche funzionare come notifica di connessione."), + ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo invia un messaggio tipo \"/hello\".\n"), ].iter().cloned().collect(); } From a18947eed2c01ec5c26f6e1f431f5035e3f945ec Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 27 Jun 2024 18:23:51 +0800 Subject: [PATCH 144/335] fix typo --- src/lang/en.rs | 2 +- src/ui_interface.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/en.rs b/src/lang/en.rs index a76d4c7bc3c..0f74ba32438 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -231,6 +231,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("texture_render_tip", "Use texture rendering to make the pictures smoother. You could try disabling this option if you encounter rendering issues."), ("floating_window_tip", "It helps to keep RustDesk background service"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), - ("enable-bot-desc", "1, Open a chat with @BotFather.\n2, Send the command \"/newbot\". You will receive a token after completing this step.\n3, Start a chat with your newly created bot. Send a message beginning with a forward slash ("/") like \"\/hello\" to activate it.\n"), + ("enable-bot-desc", "1, Open a chat with @BotFather.\n2, Send the command \"/newbot\". You will receive a token after completing this step.\n3, Start a chat with your newly created bot. Send a message beginning with a forward slash (\"/\") like \"/hello\" to activate it.\n"), ].iter().cloned().collect(); } diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 0d8723dcda8..c05f720a7c6 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -1400,7 +1400,7 @@ pub fn verify_bot(token: String) -> String { match crate::auth_2fa::get_chatid_telegram(&token) { Err(err) => err.to_string(), Ok(None) => { - "To activate the bot, simply send a message beginning with a forward slash ("/") like \"hello\" to its chat.".to_owned() + "To activate the bot, simply send a message beginning with a forward slash (\"/\") like \"/hello\" to its chat.".to_owned() } _ => "".to_owned(), } From cfd27c8d87e273f5be164bba94119b7cde4e62aa Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 27 Jun 2024 20:09:51 +0800 Subject: [PATCH 145/335] wakelock not block system sleep (#8504) Signed-off-by: 21pages --- src/platform/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 65d047cff13..fe66e50dc69 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -104,8 +104,11 @@ pub fn get_wakelock(_display: bool) -> WakeLock { hbb_common::log::info!("new wakelock, require display on: {_display}"); #[cfg(target_os = "android")] return crate::platform::WakeLock::new("server"); + // display: keep screen on + // idle: keep cpu on + // sleep: prevent system from sleeping, even manually #[cfg(not(target_os = "android"))] - return crate::platform::WakeLock::new(_display, true, true); + return crate::platform::WakeLock::new(_display, true, false); } pub(crate) struct InstallingService; // please use new From e1140b1bea94e6b8bd98451470f9e2b34fbc77f7 Mon Sep 17 00:00:00 2001 From: Samuel FORESTIER Date: Thu, 27 Jun 2024 12:14:32 +0000 Subject: [PATCH 146/335] Tidy up configuration files UNIX permissions (#7983) * Add missing libpam package causing build to fail using Docker image ``` wrapper.h:1:10: fatal error: 'security/pam_appl.h' file not found ``` Signed-off-by: Samuel FORESTIER * Tidy up configuration files UNIX permissions Signed-off-by: Samuel FORESTIER --------- Signed-off-by: Samuel FORESTIER --- libs/hbb_common/src/config.rs | 36 ++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index e1649ddd7bf..711d5af37dd 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -487,7 +487,19 @@ pub fn load_path(path: PathBuf, cfg: T) -> crate::ResultType<()> { - Ok(confy::store_path(path, cfg)?) + #[cfg(not(windows))] + { + use std::os::unix::fs::PermissionsExt; + Ok(confy::store_path_perms( + path, + cfg, + fs::Permissions::from_mode(0o600), + )?) + } + #[cfg(windows)] + { + Ok(confy::store_path(path, cfg)?) + } } impl Config { @@ -2471,4 +2483,26 @@ mod tests { assert_eq!(cfg, Ok(cfg_to_compare), "Failed to test wrong_field_str"); } } + + #[test] + fn test_store_load() { + let peerconfig_id = "123456789"; + let cfg: PeerConfig = Default::default(); + cfg.store(&peerconfig_id); + assert_eq!(PeerConfig::load(&peerconfig_id), cfg); + + #[cfg(not(windows))] + { + use std::os::unix::fs::PermissionsExt; + assert_eq!( + // ignore file type information by masking with 0o777 (see https://stackoverflow.com/a/50045872) + fs::metadata(PeerConfig::path(&peerconfig_id)) + .expect("reading metadata failed") + .permissions() + .mode() + & 0o777, + 0o600 + ); + } + } } From ab9e1013b27495a0da0edac15b5724dc7a24b202 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 27 Jun 2024 22:23:25 +0800 Subject: [PATCH 147/335] remove dpiAware from manifest in ci for self-extracted executable (#8508) Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 70312d3e332..8ca3223444c 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -163,6 +163,7 @@ jobs: shell: bash if: env.UPLOAD_ARTIFACT == 'true' run: | + sed -i '/dpiAware/d' res/manifest.xml pushd ./libs/portable pip3 install -r requirements.txt python3 ./generate.py -f ../../rustdesk/ -o . -e ../../rustdesk/rustdesk.exe @@ -297,6 +298,7 @@ jobs: - name: Build self-extracted executable shell: bash run: | + sed -i '/dpiAware/d' res/manifest.xml pushd ./libs/portable pip3 install -r requirements.txt python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe From c1bbdaf9aefc8f27fa69d76777fa8adde244d8b4 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 27 Jun 2024 23:19:37 +0800 Subject: [PATCH 148/335] refact: mobile min cursor size, 12 (#8510) Signed-off-by: fufesou --- flutter/lib/mobile/pages/remote_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 819ba6c96f6..490861f0e0b 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -942,7 +942,7 @@ class CursorPaint extends StatelessWidget { return Offstage(); } - final minSize = 24.0; + final minSize = 12.0; double mins = minSize / (image.width > image.height ? image.width : image.height); double factor = 1.0; From d689bbf38e7a7401405a4d04d2ea9f9520c750bd Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 28 Jun 2024 00:57:16 +0800 Subject: [PATCH 149/335] refact: mobile more actions, divider (#8512) Signed-off-by: fufesou --- flutter/lib/common/widgets/toolbar.dart | 45 ++++-------- flutter/lib/mobile/pages/remote_page.dart | 89 +++++++++++++++-------- 2 files changed, 71 insertions(+), 63 deletions(-) diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index 27629d486f1..2c2da922ab5 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -6,7 +6,6 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/common/widgets/dialog.dart'; import 'package:flutter_hbb/consts.dart'; -import 'package:flutter_hbb/models/input_model.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; @@ -23,6 +22,20 @@ class TTextMenu { required this.onPressed, this.trailingIcon, this.divider = false}); + + Widget getChild() { + if (trailingIcon != null) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + child, + trailingIcon!, + ], + ); + } else { + return child; + } + } } class TRadioMenu { @@ -77,36 +90,6 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { final sessionId = ffi.sessionId; List v = []; - if (isMobile && - pi.platform == kPeerPlatformAndroid && - perms['keyboard'] != false) { - v.addAll([ - TTextMenu( - child: Text(translate('Back')), - onPressed: () => ffi.inputModel.onMobileBack(), - ), - TTextMenu( - child: Text(translate('Home')), - onPressed: () => ffi.inputModel.onMobileHome(), - ), - TTextMenu( - child: Text(translate('Apps')), - onPressed: () => ffi.inputModel.onMobileApps(), - ), - TTextMenu( - child: Text(translate('Volume up')), - onPressed: () => ffi.inputModel.onMobileVolumeUp(), - ), - TTextMenu( - child: Text(translate('Volume down')), - onPressed: () => ffi.inputModel.onMobileVolumeDown(), - ), - TTextMenu( - child: Text(translate('Power')), - onPressed: () => ffi.inputModel.onMobilePower(), - ), - ]); - } // elevation if (perms['keyboard'] != false && ffi.elevationModel.showRequestMenu) { v.add( diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 490861f0e0b..7c4adcf9215 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -553,29 +553,62 @@ class _RemotePageState extends State { color: MyTheme.canvasColor, child: Stack(children: paints)); } + List _getMobileActionMenus() { + if (gFFI.ffiModel.pi.platform != kPeerPlatformAndroid || + !gFFI.ffiModel.keyboard) { + return []; + } + return [ + TTextMenu( + child: Text(translate('Back')), + onPressed: () => gFFI.inputModel.onMobileBack(), + ), + TTextMenu( + child: Text(translate('Home')), + onPressed: () => gFFI.inputModel.onMobileHome(), + ), + TTextMenu( + child: Text(translate('Apps')), + onPressed: () => gFFI.inputModel.onMobileApps(), + ), + TTextMenu( + child: Text(translate('Volume up')), + onPressed: () => gFFI.inputModel.onMobileVolumeUp(), + ), + TTextMenu( + child: Text(translate('Volume down')), + onPressed: () => gFFI.inputModel.onMobileVolumeDown(), + ), + TTextMenu( + child: Text(translate('Power')), + onPressed: () => gFFI.inputModel.onMobilePower(), + ), + ]; + } + void showActions(String id) async { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; + final mobileActionMenus = _getMobileActionMenus(); final menus = toolbarControls(context, id, gFFI); - getChild(TTextMenu menu) { - if (menu.trailingIcon != null) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - menu.child, - menu.trailingIcon!, - ]); - } else { - return menu.child; - } - } - final more = menus - .asMap() - .entries - .map((e) => PopupMenuItem(child: getChild(e.value), value: e.key)) - .toList(); + final List> more = [ + ...mobileActionMenus + .asMap() + .entries + .map((e) => + PopupMenuItem(child: e.value.getChild(), value: e.key)) + .toList(), + if (mobileActionMenus.isNotEmpty) PopupMenuDivider(), + ...menus + .asMap() + .entries + .map((e) => PopupMenuItem( + child: e.value.getChild(), + value: e.key + mobileActionMenus.length)) + .toList(), + ]; () async { var index = await showMenu( context: context, @@ -583,8 +616,12 @@ class _RemotePageState extends State { items: more, elevation: 8, ); - if (index != null && index < menus.length) { - menus[index].onPressed.call(); + if (index != null) { + if (index < mobileActionMenus.length) { + mobileActionMenus[index].onPressed.call(); + } else if (index < mobileActionMenus.length + more.length) { + menus[index - mobileActionMenus.length].onPressed.call(); + } } }(); } @@ -639,23 +676,11 @@ class _RemotePageState extends State { ), onPressVoiceCall), ]; - getChild(TTextMenu menu) { - if (menu.trailingIcon != null) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - menu.child, - menu.trailingIcon!, - ]); - } else { - return menu.child; - } - } final menuItems = menus .asMap() .entries - .map((e) => PopupMenuItem(child: getChild(e.value), value: e.key)) + .map((e) => PopupMenuItem(child: e.value.getChild(), value: e.key)) .toList(); Future.delayed(Duration.zero, () async { final size = MediaQuery.of(context).size; From ed3fb1efa4955133799875e049480868660e3c60 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 28 Jun 2024 09:59:10 +0800 Subject: [PATCH 150/335] bump to 1.2.7 --- .github/workflows/flutter-build.yml | 2 +- .github/workflows/playground.yml | 2 +- Cargo.lock | 4 ++-- Cargo.toml | 2 +- appimage/AppImageBuilder-aarch64.yml | 2 +- appimage/AppImageBuilder-x86_64.yml | 2 +- flutter/pubspec.yaml | 2 +- libs/portable/Cargo.toml | 2 +- res/PKGBUILD | 2 +- res/rpm-flutter-suse.spec | 2 +- res/rpm-flutter.spec | 2 +- res/rpm.spec | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 8ca3223444c..b72196dafd1 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -24,7 +24,7 @@ env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.03.25 VCPKG_COMMIT_ID: "a34c873a9717a888f58dc05268dea15592c2f0ff" - VERSION: "1.2.6" + VERSION: "1.2.7" NDK_VERSION: "r26d" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 99bac2bc022..f0c1e4bd15d 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -18,7 +18,7 @@ env: VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.03.25 VCPKG_COMMIT_ID: "a34c873a9717a888f58dc05268dea15592c2f0ff" - VERSION: "1.2.6" + VERSION: "1.2.7" NDK_VERSION: "r26d" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" diff --git a/Cargo.lock b/Cargo.lock index 6deac2f9b54..503b809d524 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5300,7 +5300,7 @@ dependencies = [ [[package]] name = "rustdesk" -version = "1.2.6" +version = "1.2.7" dependencies = [ "android-wakelock", "android_logger", @@ -5397,7 +5397,7 @@ dependencies = [ [[package]] name = "rustdesk-portable-packer" -version = "1.2.6" +version = "1.2.7" dependencies = [ "brotli", "dirs 5.0.1", diff --git a/Cargo.toml b/Cargo.toml index 939d5c3596e..77554791e83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustdesk" -version = "1.2.6" +version = "1.2.7" authors = ["rustdesk "] edition = "2021" build= "build.rs" diff --git a/appimage/AppImageBuilder-aarch64.yml b/appimage/AppImageBuilder-aarch64.yml index 86364617de0..411d7bb571d 100644 --- a/appimage/AppImageBuilder-aarch64.yml +++ b/appimage/AppImageBuilder-aarch64.yml @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.2.6 + version: 1.2.7 exec: usr/lib/rustdesk/rustdesk exec_args: $@ apt: diff --git a/appimage/AppImageBuilder-x86_64.yml b/appimage/AppImageBuilder-x86_64.yml index cce651b6e14..9c6860dd43e 100644 --- a/appimage/AppImageBuilder-x86_64.yml +++ b/appimage/AppImageBuilder-x86_64.yml @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.2.6 + version: 1.2.7 exec: usr/lib/rustdesk/rustdesk exec_args: $@ apt: diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 1c9a289beb2..dce598bc4ef 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.2.6+45 +version: 1.2.7+46 environment: sdk: '^3.1.0' diff --git a/libs/portable/Cargo.toml b/libs/portable/Cargo.toml index 82397421b70..e39212bf28b 100644 --- a/libs/portable/Cargo.toml +++ b/libs/portable/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustdesk-portable-packer" -version = "1.2.6" +version = "1.2.7" edition = "2021" description = "RustDesk Remote Desktop" diff --git a/res/PKGBUILD b/res/PKGBUILD index 784b0a1b3a1..6f1b4e6801f 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -1,5 +1,5 @@ pkgname=rustdesk -pkgver=1.2.6 +pkgver=1.2.7 pkgrel=0 epoch= pkgdesc="" diff --git a/res/rpm-flutter-suse.spec b/res/rpm-flutter-suse.spec index 0475b0dc718..c4fe69e6778 100644 --- a/res/rpm-flutter-suse.spec +++ b/res/rpm-flutter-suse.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.2.6 +Version: 1.2.7 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm-flutter.spec b/res/rpm-flutter.spec index d7768c378e9..90e45af9cbf 100644 --- a/res/rpm-flutter.spec +++ b/res/rpm-flutter.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.2.6 +Version: 1.2.7 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm.spec b/res/rpm.spec index 60acd18716b..a6d6a956a67 100644 --- a/res/rpm.spec +++ b/res/rpm.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.2.6 +Version: 1.2.7 Release: 0 Summary: RPM package License: GPL-3.0 From 96aff38862394a4a083c4b586ecce81c74f7eb69 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:28:43 +0200 Subject: [PATCH 151/335] Update Italian language (#8520) --- src/lang/it.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 7e369969923..b16246837b7 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -629,6 +629,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Power", "Alimentazione"), ("Telegram bot", "Bot Telgram"), ("enable-bot-tip", "Se abiliti questa funzione, puoi ricevere il codice 2FA dal tuo bot.\nPuò anche funzionare come notifica di connessione."), - ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo invia un messaggio tipo \"/hello\".\n"), + ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), + ("floating_window_tip", "It helps to keep RustDesk background service"), + ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), ].iter().cloned().collect(); } From 3ae163812569106608f6dc8a938558417bae263d Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 28 Jun 2024 22:36:29 +0800 Subject: [PATCH 152/335] fix extracted forground window not foreground (#8521) Signed-off-by: 21pages --- flutter/windows/runner/win32_window.cpp | 23 ++++++++++++ libs/portable/src/main.rs | 48 +++++++++++++++++++------ src/common.rs | 27 +++++++++++++- src/platform/windows.rs | 27 ++++++++++++-- src/ui.rs | 2 ++ 5 files changed, 113 insertions(+), 14 deletions(-) diff --git a/flutter/windows/runner/win32_window.cpp b/flutter/windows/runner/win32_window.cpp index 5c09f650043..2c25f00dd90 100644 --- a/flutter/windows/runner/win32_window.cpp +++ b/flutter/windows/runner/win32_window.cpp @@ -5,6 +5,9 @@ #include "resource.h" +#include // for getenv and _putenv +#include // for strcmp + namespace { constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; @@ -143,6 +146,25 @@ bool Win32Window::CreateAndShow(const std::wstring& title, return OnCreate(); } +static void trySetWindowForeground(HWND window) { + char* value = nullptr; + size_t size = 0; + // Use _dupenv_s to safely get the environment variable + _dupenv_s(&value, &size, "SET_FOREGROUND_WINDOW"); + + if (value != nullptr) { + // Correctly compare the value with "1" + if (strcmp(value, "1") == 0) { + // Clear the environment variable + _putenv("SET_FOREGROUND_WINDOW="); + // Set the window to foreground + SetForegroundWindow(window); + } + // Free the duplicated string + free(value); + } +} + // static LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message, @@ -156,6 +178,7 @@ LRESULT CALLBACK Win32Window::WndProc(HWND const window, auto that = static_cast(window_struct->lpCreateParams); EnableFullDpiSupportIfAvailable(window); that->window_handle_ = window; + trySetWindowForeground(window); } else if (Win32Window* that = GetThisFromHandle(window)) { return that->MessageHandler(window, message, wparam, lparam); } diff --git a/libs/portable/src/main.rs b/libs/portable/src/main.rs index 48372d68b8b..7b68d821c81 100644 --- a/libs/portable/src/main.rs +++ b/libs/portable/src/main.rs @@ -19,6 +19,8 @@ const APP_METADATA_CONFIG: &str = "meta.toml"; const META_LINE_PREFIX_TIMESTAMP: &str = "timestamp = "; const APP_PREFIX: &str = "rustdesk"; const APPNAME_RUNTIME_ENV_KEY: &str = "RUSTDESK_APPNAME"; +#[cfg(windows)] +const SET_FOREGROUND_WINDOW_ENV_KEY: &str = "SET_FOREGROUND_WINDOW"; fn is_timestamp_matches(dir: &PathBuf, ts: &mut u64) -> bool { let Ok(app_metadata) = std::str::from_utf8(APP_METADATA) else { @@ -57,7 +59,13 @@ fn write_meta(dir: &PathBuf, ts: u64) { } } -fn setup(reader: BinaryReader, dir: Option, clear: bool) -> Option { +fn setup( + reader: BinaryReader, + dir: Option, + clear: bool, + _args: &Vec, + _ui: &mut bool, +) -> Option { let dir = if let Some(dir) = dir { dir } else { @@ -72,6 +80,11 @@ fn setup(reader: BinaryReader, dir: Option, clear: bool) -> Option, clear: bool) -> Option) { +fn execute(path: PathBuf, args: Vec, _ui: bool) { println!("executing {}", path.display()); // setup env let exe = std::env::current_exe().unwrap_or_default(); @@ -97,13 +110,28 @@ fn execute(path: PathBuf, args: Vec) { { use std::os::windows::process::CommandExt; cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW); + if _ui { + cmd.env(SET_FOREGROUND_WINDOW_ENV_KEY, "1"); + } } - cmd.env(APPNAME_RUNTIME_ENV_KEY, exe_name) + let _child = cmd + .env(APPNAME_RUNTIME_ENV_KEY, exe_name) .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) .stderr(Stdio::inherit()) - .spawn() - .ok(); + .spawn(); + + #[cfg(windows)] + if _ui { + match _child { + Ok(child) => unsafe { + winapi::um::winuser::AllowSetForegroundWindow(child.id() as u32); + }, + Err(e) => { + eprintln!("{:?}", e); + } + } + } } fn main() { @@ -121,23 +149,21 @@ fn main() { let click_setup = args.is_empty() && arg_exe.to_lowercase().ends_with("install.exe"); let quick_support = args.is_empty() && arg_exe.to_lowercase().ends_with("qs.exe"); - #[cfg(windows)] - if args.is_empty() { - ui::setup(); - } - + let mut ui = false; let reader = BinaryReader::default(); if let Some(exe) = setup( reader, None, click_setup || args.contains(&"--silent-install".to_owned()), + &args, + &mut ui, ) { if click_setup { args = vec!["--install".to_owned()]; } else if quick_support { args = vec!["--quick_support".to_owned()]; } - execute(exe, args); + execute(exe, args, ui); } } diff --git a/src/common.rs b/src/common.rs index 199e0d37fff..b7c07e323b9 100644 --- a/src/common.rs +++ b/src/common.rs @@ -853,7 +853,32 @@ pub fn run_me>(args: Vec) -> std::io::Result>(); + if arg_strs == vec!["--install"] || arg_strs == &["--noinstall"] { + cmd.env(crate::platform::SET_FOREGROUND_WINDOW, "1"); + force_foreground = true; + } + } + let result = cmd.args(&args).spawn(); + match result.as_ref() { + Ok(_child) => + { + #[cfg(windows)] + if force_foreground { + unsafe { winapi::um::winuser::AllowSetForegroundWindow(_child.id() as u32) }; + } + } + Err(err) => log::error!("run_me: {err:?}"), + } + result } #[inline] diff --git a/src/platform/windows.rs b/src/platform/windows.rs index c3cd5567fe2..93b209839f5 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -68,6 +68,7 @@ use winreg::RegKey; pub const FLUTTER_RUNNER_WIN32_WINDOW_CLASS: &'static str = "FLUTTER_RUNNER_WIN32_WINDOW"; // main window, install window pub const EXPLORER_EXE: &'static str = "explorer.exe"; +pub const SET_FOREGROUND_WINDOW: &'static str = "SET_FOREGROUND_WINDOW"; pub fn get_focused_display(displays: Vec) -> Option { unsafe { @@ -461,8 +462,18 @@ const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS; extern "C" { fn get_current_session(rdp: BOOL) -> DWORD; - fn LaunchProcessWin(cmd: *const u16, session_id: DWORD, as_user: BOOL, token_pid: &mut DWORD) -> HANDLE; - fn GetSessionUserTokenWin(lphUserToken: LPHANDLE, dwSessionId: DWORD, as_user: BOOL, token_pid: &mut DWORD) -> BOOL; + fn LaunchProcessWin( + cmd: *const u16, + session_id: DWORD, + as_user: BOOL, + token_pid: &mut DWORD, + ) -> HANDLE; + fn GetSessionUserTokenWin( + lphUserToken: LPHANDLE, + dwSessionId: DWORD, + as_user: BOOL, + token_pid: &mut DWORD, + ) -> BOOL; fn selectInputDesktop() -> BOOL; fn inputDesktopSelected() -> BOOL; fn is_windows_server() -> BOOL; @@ -2517,3 +2528,15 @@ fn nt_terminate_process(process_id: DWORD) -> ResultType<()> { } } } + +pub fn try_set_window_foreground(window: HWND) { + let env_key = SET_FOREGROUND_WINDOW; + if let Ok(value) = std::env::var(env_key) { + if value == "1" { + unsafe { + SetForegroundWindow(window); + } + std::env::remove_var(env_key); + } + } +} diff --git a/src/ui.rs b/src/ui.rs index 813f6d7d1f1..aa36fc578ea 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -87,6 +87,8 @@ pub fn start(args: &mut [String]) { frame.set_title(&crate::get_app_name()); #[cfg(target_os = "macos")] crate::platform::delegate::make_menubar(frame.get_host(), args.is_empty()); + #[cfg(windows)] + crate::platform::try_set_window_foreground(frame.get_hwnd() as _); let page; if args.len() > 1 && args[0] == "--play" { args[0] = "--connect".to_owned(); From c062813c6d5e7d34c7cadcf704fe2eae9330f05c Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 28 Jun 2024 23:15:33 +0800 Subject: [PATCH 153/335] Upgrade arboard (#8522) Signed-off-by: fufesou --- Cargo.lock | 193 ++++++++++++++++++++++++++++++++++++++++---------- Cargo.toml | 2 +- src/common.rs | 69 ++++-------------- 3 files changed, 173 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 503b809d524..49c43885bcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -260,18 +260,18 @@ dependencies = [ [[package]] name = "arboard" -version = "3.3.1" -source = "git+https://github.com/fufesou/arboard?branch=feat/x11_set_conn_timeout#956b5f8693b4fc7fddd7b8cafbe1111a892b34b1" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89" dependencies = [ "clipboard-win", "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", - "image", + "image 0.25.1", "log", - "objc", - "objc-foundation", - "objc_id", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", "parking_lot", - "thiserror", "windows-sys 0.48.0", "wl-clipboard-rs", "x11rb 0.13.0", @@ -603,7 +603,7 @@ version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cexpr", "clang-sys", "lazy_static", @@ -631,9 +631,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde 1.0.190", ] @@ -681,7 +681,7 @@ version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", ] [[package]] @@ -691,7 +691,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" dependencies = [ "block-sys", - "objc2-encode", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", ] [[package]] @@ -797,13 +806,13 @@ version = "0.4.0-beta2" source = "git+https://github.com/clslaid/cacao?branch=feat/set-file-urls#05e1536b0b43aaae308ec72c0eed703e875b7b95" dependencies = [ "bitmask-enum", - "block2", + "block2 0.2.0-alpha.6", "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", "core-graphics 0.23.1 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", "dispatch", "lazy_static", "libc", - "objc2", + "objc2 0.3.0-beta.2", "os_info", "percent-encoding", "url", @@ -815,7 +824,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cairo-sys-rs", "glib 0.18.5", "libc", @@ -996,9 +1005,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "5.1.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec832972fefb8cf9313b45a0d1945e29c9c251f1d4c6eafc5fe2124c02d2e81" +checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" dependencies = [ "error-code", ] @@ -1193,7 +1202,7 @@ name = "core-foundation-sys" version = "0.8.6" source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" dependencies = [ - "objc2-encode", + "objc2-encode 2.0.0-pre.2", ] [[package]] @@ -1232,7 +1241,7 @@ dependencies = [ "core-graphics-types 0.1.2 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", "foreign-types 0.5.0", "libc", - "objc2-encode", + "objc2-encode 2.0.0-pre.2", ] [[package]] @@ -1254,7 +1263,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", "libc", - "objc2-encode", + "objc2-encode 2.0.0-pre.2", ] [[package]] @@ -1937,7 +1946,7 @@ version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "libc", ] @@ -2549,7 +2558,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "futures-channel", "futures-core", "futures-executor", @@ -3151,6 +3160,19 @@ dependencies = [ "tiff", ] +[[package]] +name = "image" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" +dependencies = [ + "bytemuck", + "byteorder", + "num-traits 0.2.17", + "png", + "tiff", +] + [[package]] name = "impersonate_system" version = "0.1.0" @@ -3399,7 +3421,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "serde 1.0.190", "unicode-segmentation", ] @@ -3946,7 +3968,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if 1.0.0", "libc", ] @@ -4136,15 +4158,71 @@ version = "0.2.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + [[package]] name = "objc2" version = "0.3.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a49f420f16c8814efdcd6b4258664de9d9920cbc26b6f95d034a1ca9850ccc2c" dependencies = [ - "block2", - "objc-sys", - "objc2-encode", + "block2 0.2.0-alpha.6", + "objc-sys 0.2.0-beta.2", + "objc2-encode 2.0.0-pre.2", +] + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys 0.3.5", + "objc2-encode 4.0.3", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", ] [[package]] @@ -4153,7 +4231,50 @@ version = "2.0.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" dependencies = [ - "objc-sys", + "objc-sys 0.2.0-beta.2", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation", + "objc2-metal", ] [[package]] @@ -4218,7 +4339,7 @@ version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if 1.0.0", "foreign-types 0.3.2", "libc", @@ -4790,7 +4911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d06cb9646c7a14096231a2474d7f21e5e8c13de090c68d13bde6157cfe7f159" dependencies = [ "html-escape", - "image", + "image 0.24.7", "qrcodegen", ] @@ -5334,7 +5455,7 @@ dependencies = [ "hbb_common", "hex", "hound", - "image", + "image 0.24.7", "impersonate_system", "include_dir", "jni 0.21.1", @@ -5442,7 +5563,7 @@ version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys 0.4.10", @@ -6152,7 +6273,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "image", + "image 0.24.7", "instant", "jni 0.21.1", "lazy_static", @@ -7022,7 +7143,7 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "nix 0.26.4", "wayland-backend", "wayland-scanner", @@ -7034,7 +7155,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7046,7 +7167,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "wayland-backend", "wayland-client", "wayland-protocols", diff --git a/Cargo.toml b/Cargo.toml index 77554791e83..9ad4000c314 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,7 @@ sys-locale = "0.3" enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } ctrlc = "3.2" -arboard = { git = "https://github.com/fufesou/arboard", branch = "feat/x11_set_conn_timeout", features = ["wayland-data-control"] } +arboard = { version = "3.4.0", features = ["wayland-data-control"] } system_shutdown = "4.0" qrcode-generator = "4.1" diff --git a/src/common.rs b/src/common.rs index b7c07e323b9..4092a3b6215 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1524,19 +1524,15 @@ impl ClipboardContext { #[cfg(target_os = "linux")] pub fn new() -> ResultType { - let dur = arboard::Clipboard::get_x11_server_conn_timeout(); - let dur_bak = dur; - let _restore_timeout_on_ret = SimpleCallOnReturn { - b: true, - f: Box::new(move || arboard::Clipboard::set_x11_server_conn_timeout(dur_bak)), - }; - - for i in 1..4 { - arboard::Clipboard::set_x11_server_conn_timeout(dur * i); - match arboard::Clipboard::new() { - Ok(c) => return Ok(ClipboardContext(c)), - Err(arboard::Error::X11ServerConnTimeout) => continue, - Err(err) => return Err(err.into()), + for _ in 0..3 { + let (tx, rx) = std::sync::mpsc::channel(); + std::thread::spawn(move || { + tx.send(arboard::Clipboard::new()).ok(); + }); + match rx.recv_timeout(Duration::from_millis(40)) { + Ok(Ok(c)) => return Ok(ClipboardContext(c)), + Ok(Err(e)) => return Err(e.into()), + Err(_) => continue, } } bail!("Failed to create clipboard context, timeout"); @@ -1548,24 +1544,14 @@ impl ClipboardContext { Ok(self.0.get_text()?) } + // CAUTION: This function must not be called in the main thread!!! It can only be called in the clibpoard thread. + // There's no timeout for this function, so it may block. + // Because of https://github.com/1Password/arboard/blob/151e679ee5c208403b06ba02d28f92c5891f7867/src/platform/linux/x11.rs#L296 + // We cannot use a new thread to get text because the clipboard context is `&mut self`. + // The crate design is somehow not good. #[cfg(target_os = "linux")] pub fn get_text(&mut self) -> ResultType { - let dur = arboard::Clipboard::get_x11_server_conn_timeout(); - let dur_bak = dur; - let _restore_timeout_on_ret = SimpleCallOnReturn { - b: true, - f: Box::new(move || arboard::Clipboard::set_x11_server_conn_timeout(dur_bak)), - }; - - for i in 1..4 { - arboard::Clipboard::set_x11_server_conn_timeout(dur * i); - match self.0.get_text() { - Ok(s) => return Ok(s), - Err(arboard::Error::X11ServerConnTimeout) => continue, - Err(err) => return Err(err.into()), - } - } - bail!("Failed to get text, timeout"); + Ok(self.0.get_text()?) } #[inline] @@ -1881,29 +1867,4 @@ mod tests { Duration::from_nanos(0) ); } - - #[tokio::test] - #[cfg(not(any( - target_os = "android", - target_os = "ios", - all(target_os = "linux", feature = "unix-file-copy-paste") - )))] - async fn test_clipboard_context() { - #[cfg(target_os = "linux")] - let dur = { - let dur = Duration::from_micros(500); - arboard::Clipboard::set_x11_server_conn_timeout(dur); - dur - }; - - let _ctx = ClipboardContext::new(); - #[cfg(target_os = "linux")] - { - assert_eq!( - arboard::Clipboard::get_x11_server_conn_timeout(), - dur, - "Failed to restore x11 server conn timeout" - ); - } - } } From 9f72d05749a88bffa577784099d97374c4276ee3 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 28 Jun 2024 23:32:23 +0800 Subject: [PATCH 154/335] do not run self-host in ci commit flow --- .github/workflows/flutter-build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index b72196dafd1..692cb2a1d0f 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1008,6 +1008,7 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 + if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' with: toolchain: ${{ env.RUST_VERSION }} targets: ${{ matrix.job.target }} @@ -1024,19 +1025,21 @@ jobs: sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml - name: Restore bridge files + if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' uses: actions/download-artifact@master with: name: bridge-artifact path: ./ - name: Setup vcpkg with Github Actions binary cache + if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' uses: lukka/run-vcpkg@v11 with: vcpkgDirectory: /opt/artifacts/vcpkg vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies - if: env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' run: | case ${{ matrix.job.target }} in aarch64-unknown-linux-gnu) @@ -1049,6 +1052,7 @@ jobs: shell: bash - name: Restore bridge files + if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' uses: actions/download-artifact@master with: name: bridge-artifact @@ -1057,7 +1061,7 @@ jobs: - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk id: vcpkg - if: env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' with: arch: ${{ matrix.job.arch }} distro: ${{ matrix.job.distro }} From a632718e80649f2be8a1a7cc1df20a6056b119b9 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 29 Jun 2024 10:46:21 +0800 Subject: [PATCH 155/335] typo --- flutter/lib/common/widgets/dialog.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 9cfa65b1b74..35bbd37168a 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -1797,7 +1797,7 @@ void changeBot({Function()? callback}) async { autofocus: true, controller: controller, decoration: InputDecoration( - hintText: translate('Token'), // 使用hintText设置占位符文本 + hintText: translate('Token'), ), ); From 1fd170b089c9f384459ef1c354e4f63c3f7a9d54 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sat, 29 Jun 2024 11:50:40 +0800 Subject: [PATCH 156/335] fix: mobile more actions, check version 1.2.7 (#8526) Signed-off-by: fufesou --- flutter/lib/desktop/widgets/remote_toolbar.dart | 2 +- flutter/lib/mobile/pages/remote_page.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 1baae26d39b..53df40fedb0 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1882,7 +1882,7 @@ class _KeyboardMenu extends StatelessWidget { mobileActions() { if (pi.platform != kPeerPlatformAndroid) return []; - final enabled = versionCmp(pi.version, '1.2.6') >= 0; + final enabled = versionCmp(pi.version, '1.2.7') >= 0; if (!enabled) return []; return [ Divider(), diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 7c4adcf9215..cd5743d0b2d 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -558,6 +558,8 @@ class _RemotePageState extends State { !gFFI.ffiModel.keyboard) { return []; } + final enabled = versionCmp(gFFI.ffiModel.pi.version, '1.2.7') >= 0; + if (!enabled) return []; return [ TTextMenu( child: Text(translate('Back')), From d67afa49b459127f28a5b6890661a8227973056e Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 29 Jun 2024 14:17:24 +0800 Subject: [PATCH 157/335] portable service only run on main display (#8525) Signed-off-by: 21pages --- flutter/lib/models/model.dart | 14 -------------- src/lang/ar.rs | 6 +++--- src/lang/be.rs | 6 +++--- src/lang/bg.rs | 6 +++--- src/lang/ca.rs | 6 +++--- src/lang/cn.rs | 6 +++--- src/lang/cs.rs | 6 +++--- src/lang/da.rs | 6 +++--- src/lang/de.rs | 6 +++--- src/lang/el.rs | 6 +++--- src/lang/en.rs | 3 --- src/lang/eo.rs | 6 +++--- src/lang/es.rs | 6 +++--- src/lang/et.rs | 6 +++--- src/lang/fa.rs | 6 +++--- src/lang/fr.rs | 6 +++--- src/lang/he.rs | 6 +++--- src/lang/hr.rs | 6 +++--- src/lang/hu.rs | 6 +++--- src/lang/id.rs | 6 +++--- src/lang/it.rs | 9 ++------- src/lang/ja.rs | 6 +++--- src/lang/ko.rs | 6 +++--- src/lang/kz.rs | 6 +++--- src/lang/lt.rs | 6 +++--- src/lang/lv.rs | 6 +++--- src/lang/nb.rs | 6 +++--- src/lang/nl.rs | 6 +++--- src/lang/pl.rs | 6 +++--- src/lang/pt_PT.rs | 6 +++--- src/lang/ptbr.rs | 6 +++--- src/lang/ro.rs | 6 +++--- src/lang/ru.rs | 6 +++--- 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 | 3 --- src/lang/th.rs | 6 +++--- src/lang/tr.rs | 6 +++--- src/lang/tw.rs | 6 +++--- src/lang/ua.rs | 6 +++--- src/lang/vn.rs | 6 +++--- src/server/connection.rs | 35 ---------------------------------- src/server/portable_service.rs | 2 +- 46 files changed, 123 insertions(+), 183 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 5b8b24d97ed..2346efcc6ab 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -438,20 +438,6 @@ class FfiModel with ChangeNotifier { _handlePortableServiceRunning(String peerId, Map evt) { final running = evt['running'] == 'true'; parent.target?.elevationModel.onPortableServiceRunning(running); - if (running) { - if (pi.primaryDisplay != kInvalidDisplayIndex) { - if (pi.currentDisplay != pi.primaryDisplay) { - // Notify to switch display - msgBox(sessionId, 'custom-nook-nocancel-hasclose-info', 'Prompt', - 'elevated_switch_display_msg', '', parent.target!.dialogManager); - bind.sessionSwitchDisplay( - isDesktop: isDesktop, - sessionId: sessionId, - value: Int32List.fromList([pi.primaryDisplay]), - ); - } - } - } } handleAliasChanged(Map evt) { diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 6aa6cd419e4..285df451b43 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index 79993535718..8fa8c535f4c 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Тэст"), ("display_is_plugged_out_msg", "Дысплей адключаны, пераключыцеся на першы дысплей."), ("No displays", "Няма дысплеяў"), - ("elevated_switch_display_msg", "Пераключыцеся на асноўны дысплей, бо ў павышаным рэжыме не падтрымліваецца некалькі дысплеяў."), ("Open in new window", "Адкрыць у новым акне"), ("Show displays as individual windows", "Паказваць дысплеі ў асобных акнах"), ("Use all my displays for the remote session", "Выкарыстоўваць усе мае дысплеі для аддаленага сеансу"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Уключыць рэжым канфідэнцыяльнасці"), ("Exit privacy mode", "Адключыць рэжым канфідэнцыяльнасці"), ("idd_not_support_under_win10_2004_tip", "Драйвер непрамога адлюстравання не падтрымліваецца. Патрабуецца Windows 10 версіі 2004 ці навейшая."), - ("switch_display_elevated_connections_tip", "Пераключэнне на неасноўны дысплей не падтрымліваецца ў рэжыме павышэння правоў пры наяўнасці некалькіх падлучэнняў. Паўтарыце спробу пасля ўстаноўкі, калі хочаце кіраваць некалькімі дысплеямі."), ("input_source_1_tip", "Крыніца ўводу 1"), ("input_source_2_tip", "Крыніца ўводу 2"), - ("capture_display_elevated_connections_tip", "Захоп экрана некалькіх дысплеяў не падтрымліваецца ў рэжыме павышэння правоў. Паўтарыце спробу пасля ўстаноўкі, калі хочаце кіраваць некалькімі дысплеямі."), ("Swap control-command key", "Памяняць месцамі значэнні кнопак Ctrl і Command"), ("swap-left-right-mouse", "Памяняць месцамі значэнні левай і правай кнопак мышы"), ("2FA code", "Код двухфактарнай аўтэнтыфікацыі"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 6f0634fd72f..b2ab8edc89a 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", "Дисплеят е изключен, превключете на първия монитор."), ("No displays", ""), - ("elevated_switch_display_msg", "Превключете към основния монитор, защото множество монитори не се поддържат в потребителски режим с повишени права."), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", "Индиректен драйвер за дисплей не се поддържа. Изисква се Windows 10, версия 2004 или по-нова."), - ("switch_display_elevated_connections_tip", "Превключването към неосновен дисплей не се поддържа в режим на потребител с повишени права, когато има множество връзки. Моля, опитайте отново след инсталация, ако искате да контролирате няколко дисплея."), ("input_source_1_tip", "Входен източник 1"), ("input_source_2_tip", "Входен източник 2"), - ("capture_display_elevated_connections_tip", "Заснемането на множество дисплеи не се поддържа в потребителския режим с повишени права. Моля, опитайте отново след инсталация, ако искате да контролирате няколко дисплея."), ("Swap control-command key", ""), ("swap-left-right-mouse", "Разменете левия и десния бутон на мишката"), ("2FA code", "Код за Двуфакторна удостоверяване"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index d06c80702b4..77289e9a5c4 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 733fba236f6..d53088f9499 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "测试"), ("display_is_plugged_out_msg", "显示器被拔出,切换到第一个显示器。"), ("No displays", "没有显示器。"), - ("elevated_switch_display_msg", "切换到主显示器,因为用户提权后,不支持多显示器画面。"), ("Open in new window", "在新的窗口中打开"), ("Show displays as individual windows", "在单个窗口中打开显示器"), ("Use all my displays for the remote session", "将我的所有显示器用于远程会话"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "进入隐私模式"), ("Exit privacy mode", "退出隐私模式"), ("idd_not_support_under_win10_2004_tip", "不支持 Indirect display driver 。需要 Windows 10 版本 2004 及更高的版本。"), - ("switch_display_elevated_connections_tip", "用户提权后,被控有多个连接,不能切换到非主显示器。若要控制多显示器,请安装后再试。"), ("input_source_1_tip", "输入源 1"), ("input_source_2_tip", "输入源 2"), - ("capture_display_elevated_connections_tip", "用户提权后,不能显示多个显示器。若要控制多显示器,请安装后再试。"), ("Swap control-command key", "交换 Control 键和 Command 键"), ("swap-left-right-mouse", "交换鼠标左右键"), ("2FA code", "双重认证代码"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", "提升音量"), ("Volume down", "降低音量"), ("Power", "电源"), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 76ec131dbe1..c38d8e4aaea 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Test"), ("display_is_plugged_out_msg", "Obrazovka je odpojena, přepněte na první obrazovku."), ("No displays", "Žádné obrazovky"), - ("elevated_switch_display_msg", "Přepnout na primární obrazovku, protože více obrazovek není podporováno ve zvýšeném režimu."), ("Open in new window", "Otevřít v novém okně"), ("Show displays as individual windows", "Zobrazit obrazovky jako jednotlivá okna"), ("Use all my displays for the remote session", "Použít všechny mé obrazovky pro vzdálenou relaci"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Vstup do režimu soukromí"), ("Exit privacy mode", "Ukončit režim soukromí"), ("idd_not_support_under_win10_2004_tip", "Ovladač nepřímého zobrazení není podporován. Je vyžadován systém Windows 10, verze 2004 nebo novější."), - ("switch_display_elevated_connections_tip", "Přepnutí na jinou než primární obrazovku není podporováno ve zvýšeném režimu, pokud existuje více připojení. Pokud chcete ovládat více obrazovek, zkuste to po instalaci znovu."), ("input_source_1_tip", "Vstupní zdroj 1"), ("input_source_2_tip", "Vstupní zdroj 2"), - ("capture_display_elevated_connections_tip", "Snímání více displejů není podporováno v uživatelském režimu se zvýšenými oprávněními. Pokud chcete ovládat více displejů, zkuste to znovu po instalaci."), ("Swap control-command key", "Prohození klávesy control-command"), ("swap-left-right-mouse", "Prohodit levé a pravé tlačítko myši"), ("2FA code", "2FA kód"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 0930102d15e..4bc91708d5b 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index b04c8574979..0fcf7b683d6 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Test"), ("display_is_plugged_out_msg", "Der Bildschirm ist nicht angeschlossen, schalten Sie auf den ersten Bildschirm um."), ("No displays", "Keine Bildschirme"), - ("elevated_switch_display_msg", "Wechseln Sie zum primären Bildschirm, da mehrere Bildschirme im erweiterten Benutzermodus nicht unterstützt werden."), ("Open in new window", "In einem neuen Fenster öffnen"), ("Show displays as individual windows", "Jeden Bildschirm in einem eigenen Fenster anzeigen"), ("Use all my displays for the remote session", "Alle meine Bildschirme für die Fernsitzung verwenden"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Datenschutzmodus aktivieren"), ("Exit privacy mode", "Datenschutzmodus beenden"), ("idd_not_support_under_win10_2004_tip", "Indirekter Grafiktreiber wird nicht unterstützt. Windows 10, Version 2004 oder neuer ist erforderlich."), - ("switch_display_elevated_connections_tip", "Das Umschalten auf einen sekundären Bildschirm wird mit erhöhten Rechten nicht unterstützt, wenn mehrere Verbindungen bestehen. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."), ("input_source_1_tip", "Eingangsquelle 1"), ("input_source_2_tip", "Eingangsquelle 2"), - ("capture_display_elevated_connections_tip", "Die Aufnahme von mehreren Bildschirmen wird im erweiterten Benutzermodus nicht unterstützt. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Bildschirme steuern möchten."), ("Swap control-command key", "Steuerungs- und Befehlstasten tauschen"), ("swap-left-right-mouse", "Linke und rechte Maustaste tauschen"), ("2FA code", "2FA-Code"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 49829b2769c..974d5e7df6f 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Δοκιμή"), ("display_is_plugged_out_msg", "Η οθόνη έχει αποσυνδεθεί, επιστρέψτε στην κύρια οθόνη προβολής"), ("No displays", "Δεν υπάρχουν οθόνες"), - ("elevated_switch_display_msg", "Δεν επιτρέπονται πολλαπλές οθόνες κατά την σύνδεση με αυξημένα δικαιώματα. Συνδεθείτε στην κύρια οθόνη."), ("Open in new window", "Άνοιγμα σε νέο παράθυρο"), ("Show displays as individual windows", "Εμφάνιση οθονών σε ξεχωριστά παράθυρα"), ("Use all my displays for the remote session", "Χρήση όλων των οθονών της απομακρυσμένης σύνδεσης"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Ενεργοποίηση λειτουργίας απορρήτου"), ("Exit privacy mode", "Διακοπή λειτουργίας απορρήτου"), ("idd_not_support_under_win10_2004_tip", "Το πρόγραμμα οδήγησης έμμεσης οθόνης δεν υποστηρίζεται. Απαιτείτε λειτουργικό σύστημα Windows 10 έκδοση 2004 ή νεότερο."), - ("switch_display_elevated_connections_tip", "Αλλαγή σε οθόνη πέρα απο την κύρια δεν υποστηρίζεται σε σύνδεση με αυξημένα δικαιώματα. Αν επιθυμείτε την σύνδεση σε πολλαπλές οθόνες, παρακαλώ εγκαταστήστε την εφαρμογή και δοκιμάστε εκ νέου"), ("input_source_1_tip", "Πηγή εισόδου 1"), ("input_source_2_tip", "Πηγή εισόδου 2"), - ("capture_display_elevated_connections_tip", "Η καταγραφή πολλαπλών οθονών δεν υποστηρίζεται σε σύνδεση με αυξημένα δικαιώματα. Αν επιθυμείτε την καταγραφή πολλαπλών οθονών, παρακαλώ εγκαταστήστε την εφαρμογή και δοκιμάστε εκ νέου"), ("Swap control-command key", "Εναλλαγή κουμπιών control-command"), ("swap-left-right-mouse", "Εναλλαγή αριστερό-δεξί κουμπί του ποντικιού"), ("2FA code", "κωδικός 2FA"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 0f74ba32438..414ced37104 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -198,16 +198,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_group_failed_tip", "Failed to refresh group"), ("doc_fix_wayland", "https://rustdesk.com/docs/en/client/linux/#x11-required"), ("display_is_plugged_out_msg", "The display is plugged out, switch to the first display."), - ("elevated_switch_display_msg", "Switch to the primary display because multiple displays are not supported in elevated user mode."), ("selinux_tip", "SELinux is enabled on your device, which may prevent RustDesk from running properly as controlled side."), ("id_input_tip", "You can input an ID, a direct IP, or a domain with a port (:).\nIf you want to access a device on another server, please append the server address (@?key=), for example,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nIf you want to access a device on a public server, please input \"@public\", the key is not needed for public server.\n\nIf you want to force the use of a relay connection on the first connection, add \"/r\" at the end of the ID, for example, \"9123456234/r\"."), ("privacy_mode_impl_mag_tip", "Mode 1"), ("privacy_mode_impl_virtual_display_tip", "Mode 2"), ("idd_not_support_under_win10_2004_tip", "Indirect display driver is not supported. Windows 10, version 2004 or newer is required."), - ("switch_display_elevated_connections_tip", "Switching to non-primary display is not supported in the elevated user mode when there are multiple connections. Please try again after installation if you want to control multiple displays."), ("input_source_1_tip", "Input source 1"), ("input_source_2_tip", "Input source 2"), - ("capture_display_elevated_connections_tip", "Capturing multiple displays is not supported in the elevated user mode. Please try again after installation if you want to control multiple displays."), ("swap-left-right-mouse", "Swap left-right mouse button"), ("2FA code", "2FA code"), ("enable-2fa-title", "Enable two-factor authentication"), diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 36a500b09c8..c99473558b8 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index b65390f0a92..793f678a093 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Probar"), ("display_is_plugged_out_msg", "La pantalla está desconectada, cambia a la principal."), ("No displays", "No hay pantallas"), - ("elevated_switch_display_msg", "Cambiar a la pantalla principal porque mútliples pantallas no están soportadas en modo elevado."), ("Open in new window", "Abrir en una nueva ventana"), ("Show displays as individual windows", "Mostrar pantallas como ventanas individuales"), ("Use all my displays for the remote session", "Usar todas mis pantallas para la sesión remota"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Entrar al modo privado"), ("Exit privacy mode", "Salir del modo privado"), ("idd_not_support_under_win10_2004_tip", "El controlador de pantalla indirecto no está soportado. Se necesita Windows 10, versión 2004 o superior."), - ("switch_display_elevated_connections_tip", "Cambiar a una pantalla no principal no está soportado en el modo elevado cuando hay múltiples conexiones. Por favor, inténtalo de nuevo tras la instalación si quieres controlar múltiples pantallas."), ("input_source_1_tip", "Fuente de entrada 1"), ("input_source_2_tip", "Fuente de entrada 2"), - ("capture_display_elevated_connections_tip", "La captura de múltiples pantallas en el modo de usaurio con privilegios elevados no está soportada. Por favor, inténtalo de nuevo tras la instalación si quieres controlar múltiples pantallas."), ("Swap control-command key", "Intercambiar teclas control-comando"), ("swap-left-right-mouse", "Intercambiar botones derecho-izquierdo del ratón"), ("2FA code", "Código 2FA"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index e9a8a22a122..b1304bb37bd 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", "See kuvar on välja lülitatud, lülita esmasele kuvarile."), ("No displays", ""), - ("elevated_switch_display_msg", "Lülita ümber esmasele kuvarile, sest kõrgendatud kasutajarežiimis ei toetata mitut kuvarit."), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", "Kaudse kuvari draiver ei ole toetatud. Vajalik on Windows 10, versioon 2004 või uuem."), - ("switch_display_elevated_connections_tip", "Mitme ühenduse korral ei toetata kõrgendatud kasutajarežiimil üleminekut muule kui primaarsele kuvale. Kui soovid juhtida mitut ekraani, palun proovi uuesti pärast paigaldamist."), ("input_source_1_tip", "Sisendallikas 1"), ("input_source_2_tip", "Sisendallikas 2"), - ("capture_display_elevated_connections_tip", "Mitme ekraani jäädvustamine ei ole kõrgendatud kasutajarežiimis toetatud. Kui soovid juhtida mitut ekraani, palun proovi uuesti pärast paigaldamist."), ("Swap control-command key", ""), ("swap-left-right-mouse", "Vaheta vasak ja parem hiirenupp"), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index bd45b2f58cd..e69c4c1a000 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "تست"), ("display_is_plugged_out_msg", "صفحه نمایش قطع شده است، به صفحه نمایش اول بروید."), ("No displays", "بدون نمایشگر"), - ("elevated_switch_display_msg", "به صفحه نمایش اصلی بروید زیرا نمایشگرهای متعدد در حالت کاربر زیاد پشتیبانی نمی شوند."), ("Open in new window", "باز کردن در پنجره جدید"), ("Show displays as individual windows", "نمایش نمایشگرها به عنوان پنجره های جداگانه"), ("Use all my displays for the remote session", "از همه نمایشگرهای من برای جلسه راه دور استفاده کنید"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "ورود به حالت حریم خصوصی"), ("Exit privacy mode", "خروج از حالت حریم خصوصی"), ("idd_not_support_under_win10_2004_tip", "درایور نمایش غیر مستقیم پشتیبانی نمی شود. ویندوز 10، نسخه 2004 یا جدیدتر مورد نیاز است"), - ("switch_display_elevated_connections_tip", "هنگامی که چندین اتصال وجود دارد، تغییر به صفحه نمایش غیر اصلی در حالت کاربر با دسنرسی بالا پشتیبانی نمی شود. اگر می‌خواهید چند نمایشگر را کنترل کنید، لطفاً پس از نصب دوباره امتحان کنید."), ("input_source_1_tip", "منبع ورودی 1"), ("input_source_2_tip", "منبع ورودی 2"), - ("capture_display_elevated_connections_tip", "ضبط چندین نمایشگر در حالت کاربر بالا پشتیبانی نمی شود. اگر می‌خواهید چند نمایشگر را کنترل کنید، لطفاً پس از نصب دوباره امتحان کنید."), ("Swap control-command key", "گرفتن چندین نمایشگر در حالت کاربر زیاد پشتیبانی نمی شود. اگر می‌خواهید چند نمایشگر را کنترل کنید، لطفاً پس از نصب دوباره امتحان کنید."), ("swap-left-right-mouse", "دکمه چپ و راست ماوس را عوض کنید"), ("2FA code", "کد ورود 2 مرحله ای"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index e4fe38f8f31..3db65c93fd3 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", "L'écran est débranché, passez au premier écran."), ("No displays", "Aucun affichage"), - ("elevated_switch_display_msg", "Basculez vers l’affichage principal, car plusieurs affichages ne sont pas pris en charge en mode utilisateur avec privilèges."), ("Open in new window", "Ouvrir dans une nouvelle fenêtre"), ("Show displays as individual windows", "Montrer les affichages sous forme de fenêtres individuelles"), ("Use all my displays for the remote session", "Utiliser tous mes écrans pour la session à distance"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Passer en mode confidentialité"), ("Exit privacy mode", "Quitter le mode confidentialité"), ("idd_not_support_under_win10_2004_tip", "Le pilote d'affichage indirect n'est pas pris en charge. Windows 10, version 2004 ou plus récente est requise."), - ("switch_display_elevated_connections_tip", "Le passage à l'affichage non principal n'est pas pris en charge en mode utilisateur avec privilèges lorsqu'il existe plusieurs connexions. Veuillez réessayer après l'installation si vous souhaitez contrôler plusieurs écrans."), ("input_source_1_tip", "Source entrée 1"), ("input_source_2_tip", "Source entrée 2"), - ("capture_display_elevated_connections_tip", "La capture de plusieurs écrans n'est pas prise en charge en mode utilisateur avec privilièges. Veuillez réessayer après l'installation si vous souhaitez contrôler plusieurs écrans."), ("Swap control-command key", "Échanger la touche de controle-commande"), ("swap-left-right-mouse", "Intervertir le bouton gauche et droit de la souris"), ("2FA code", "code 2FA"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 00630c4a1d1..ca781a6ce13 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", "המסך הופסק, החלף למסך הראשון."), ("No displays", ""), - ("elevated_switch_display_msg", "מעבר למסך הראשי מכיוון שתמיכה במסכים מרובים אינה נתמכת במצב משתמש מוגבה."), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", "נהג התצוגה העקיף אינו נתמך. נדרשת גרסת Windows 10, גרסה 2004 או חדשה יותר."), - ("switch_display_elevated_connections_tip", "מעבר למסך שאינו ראשי אינו נתמך במצב משתמש מוגבה כאשר יש מספר חיבורים. אנא נסה שוב לאחר התקנה אם ברצונך לשלוט במסכים מרובים."), ("input_source_1_tip", "מקור קלט 1"), ("input_source_2_tip", "מקור קלט 2"), - ("capture_display_elevated_connections_tip", "לכידת מסכים מרובים אינה נתמכת במצב משתמש מוגבה. אנא נסה שוב לאחר התקנה אם ברצונך לשלוט במסכים מרובים."), ("Swap control-command key", ""), ("swap-left-right-mouse", "החלף בין כפתור העכבר השמאלי לימני"), ("2FA code", "קוד אימות דו-שלבי"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 335ae8ad666..fc7b4756c04 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Test"), ("display_is_plugged_out_msg", "Zaslon je isključen, prijeđite na prvi zaslon."), ("No displays", "Nema zaslona"), - ("elevated_switch_display_msg", "Prijeđite na primarni zaslon jer više zaslona nije podržano u povišenom načinu rada."), ("Open in new window", "Otvori u novom prozoru"), ("Show displays as individual windows", "Prikaži zaslone kao pojedinačne prozore"), ("Use all my displays for the remote session", "Koristi sve moje zaslone za udaljenu sesiju"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Uđite u način privatnosti"), ("Exit privacy mode", "Izađi iz načina privatnosti"), ("idd_not_support_under_win10_2004_tip", "Neizravni upravljački program za zaslon nije podržan. Potreban je Windows 10 verzija 2004 ili novija."), - ("switch_display_elevated_connections_tip", "Prebacivanje na zaslon koji nije primarni nije podržan u povišenom načinu rada kada postoji više veza. Ako želite kontrolirati više zaslona, pokušajte ponovno nakon instalacije."), ("input_source_1_tip", "Ulazni izvor 1"), ("input_source_2_tip", "Ulazni izvor 2"), - ("capture_display_elevated_connections_tip", "Skeniranje na više zaslona nije podržano u korisničkom načinu rada s povišenim pravima. Ako želite kontrolirati više zaslona, pokušajte ponovno nakon instalacije."), ("Swap control-command key", "Zamjena tipki control-command"), ("swap-left-right-mouse", "Zamjena lijeve i desne tipke miša"), ("2FA code", "2FA kôd"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 060c363e845..c355bba4560 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 9d97e6da031..46ba8a0469a 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Tes"), ("display_is_plugged_out_msg", "Layar terputus, pindah ke layar pertama"), ("No displays", "Tidak ada tampilan"), - ("elevated_switch_display_msg", "Pindah ke tampilan utama, pada mode elevasi, pengggunaan lebih dari satu layar tidak diizinkan"), ("Open in new window", "Buka di jendela baru"), ("Show displays as individual windows", "Tampilkan dengan jendela terpisah"), ("Use all my displays for the remote session", "Gunakan semua layar untuk sesi remote"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Masuk mode privasi"), ("Exit privacy mode", "Keluar mode privasi"), ("idd_not_support_under_win10_2004_tip", "Driver grafis yang Anda gunakan tidak kompatibel dengan versi Windows Anda dan memerlukan Windows 10 versi 2004 atau yang lebih baru"), - ("switch_display_elevated_connections_tip", "Pada mode elevasi, jika terdapat beberapa tampilan yang aktif, maka tidak diizinkan berpindah ke yang bukan tampilan utama, silahkan coba lagi setelah proses instalasi jika kamu ingin melakukan kontrol ke tampilan layar lainnya"), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index b16246837b7..8dc8c60ba03 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Test"), ("display_is_plugged_out_msg", "Lo schermo è scollegato, passo al primo schermo."), ("No displays", "Nessuno schermo"), - ("elevated_switch_display_msg", "Passo allo schermo principale perché in modalità elevata non sono supportati più schermi."), ("Open in new window", "Apri in una nuova finestra"), ("Show displays as individual windows", "Visualizza schermi come finestre individuali"), ("Use all my displays for the remote session", "Usa tutti gli schermi per la sessione remota"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Entra in modalità privacy"), ("Exit privacy mode", "Esci dalla modalità privacy"), ("idd_not_support_under_win10_2004_tip", "Il driver video indiretto non è supportato. È richiesto Windows 10, versione 2004 o successiva."), - ("switch_display_elevated_connections_tip", "Nella modalità elevata quando sono presenti più connessioni non è supportato il passaggio allo schermo non primario. Se vuoi controllare più schermi riprova dopo l'installazione."), ("input_source_1_tip", "Sorgente ingresso (1)"), ("input_source_2_tip", "Sorgente ingresso (2)"), - ("capture_display_elevated_connections_tip", "La cattura di più display non è supportata nella modalità utente con privilegi elevati. Se vuoi controllare più display riprova dopo l'installazione."), ("Swap control-command key", "Scambia tasto controllo-comando"), ("swap-left-right-mouse", "Scambia pulsante sinistro-destro mouse"), ("2FA code", "Codice 2FA"), @@ -616,7 +613,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("texture_render_tip", "Usa il rendering texture per rendere le immagini più fluide. Se riscontri problemi di rendering prova a disabilitare questa opzione."), ("Use texture rendering", "Usa rendering texture"), ("Floating window", "Finestra galleggiante"), - ("floating_window_tip", "Aiuta a mantenere il servizio di RustDesk in background"), + ("floating_window_tip", "It helps to keep RustDesk background service"), ("Keep screen on", "Mantieni schermo acceso"), ("Never", "Mai"), ("During controlled", "Durante il controllo"), @@ -628,9 +625,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume down", "Volume -"), ("Power", "Alimentazione"), ("Telegram bot", "Bot Telgram"), - ("enable-bot-tip", "Se abiliti questa funzione, puoi ricevere il codice 2FA dal tuo bot.\nPuò anche funzionare come notifica di connessione."), - ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), - ("floating_window_tip", "It helps to keep RustDesk background service"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), + ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 9a95f1a05c3..d317fa77ba3 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index e1bb638cd61..0c749c5e2ec 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "테스트"), ("display_is_plugged_out_msg", "디스플레이가 연결되어 있지 않습니다. 첫 번째 디스플레이로 전환하세요."), ("No displays", "디스플레이 없음"), - ("elevated_switch_display_msg", "권한 상승된 사용자 모드에서는 다중 디스플레이가 지원되지 않으므로 기본 디스플레이로 전환하세요."), ("Open in new window", "새 창에서 열기"), ("Show displays as individual windows", "디스플레이를 개별 창으로 표시"), ("Use all my displays for the remote session", "원격 세션에 내 디스플레이를 모두 사용"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "개인정보 보호 모드 사용"), ("Exit privacy mode", "개인정보 보호 모드 종료"), ("idd_not_support_under_win10_2004_tip", "간접 디스플레이 드라이버는 지원되지 않습니다. Windows 10 버전 2004 이상이 필요합니다."), - ("switch_display_elevated_connections_tip", "여러 연결이 있는 경우 권한 상승된 사용자 모드에서는 기본이 아닌 디스플레이로 전환이 지원되지 않습니다. 다중 디스플레이를 제어하려면 설치 후 재시도하세요."), ("input_source_1_tip", "입력소스 1"), ("input_source_2_tip", "입력소스 2"), - ("capture_display_elevated_connections_tip", "권한 상승된 사용자 모드에서는 다중 디스플레이 캡처가 지원되지 않습니다. 다중 디스플레이를 제어하려면 설치 후 재시도하세요."), ("Swap control-command key", "Control 및 Command 키 교체"), ("swap-left-right-mouse", "마우스 왼쪽 버튼과 오른쪽 버튼 바꾸기"), ("2FA code", "2단계 인증 코드"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 980d90a4538..a46791475a6 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 202bf2f4501..0512eb330c5 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 2ee85c013b8..0d540c9c281 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Pārbaudīt"), ("display_is_plugged_out_msg", "Displejs ir atvienots, pārslēdzieties uz pirmo displeju."), ("No displays", "Nav displeju"), - ("elevated_switch_display_msg", "Pārslēdzieties uz primāro displeju, jo paaugstinātajā režīmā netiek atbalstīti vairāki displeji."), ("Open in new window", "Atvērt jaunā logā"), ("Show displays as individual windows", "Rādīt displejus kā atsevišķus logus"), ("Use all my displays for the remote session", "Izmantot visus manus displejus attālajai sesijai"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Ieiet privātuma režīmā"), ("Exit privacy mode", "Iziet no privātuma režīma"), ("idd_not_support_under_win10_2004_tip", "Netiešā displeja draiveris netiek atbalstīts. Nepieciešama operētājsistēma Windows 10, versija 2004 vai jaunāka."), - ("switch_display_elevated_connections_tip", "Pārslēgšanās uz ne primāro displeju netiek atbalstīta paaugstinātā lietotāja režīmā, ja ir vairāki savienojumi. Lūdzu, mēģiniet vēlreiz pēc instalēšanas, ja vēlaties kontrolēt vairākus displejus."), ("input_source_1_tip", "Ievades avots 1"), ("input_source_2_tip", "Ievades avots 2"), - ("capture_display_elevated_connections_tip", "Vairāku displeju uzņemšana netiek atbalstīta paaugstinātā lietotāja režīmā. Lūdzu, mēģiniet vēlreiz pēc instalēšanas, ja vēlaties kontrolēt vairākus displejus."), ("Swap control-command key", "Apmainīt vadības un komandas taustiņu"), ("swap-left-right-mouse", "Apmainīt kreiso un labo peles pogu"), ("2FA code", "2FA kods"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 60d79ac6c91..8a9fc90f464 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 4c4c70ee98d..e0f23d0cf1d 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Test"), ("display_is_plugged_out_msg", "Beeldscherm is uitgeschakeld, schakel over naar het primaire beeldscherm."), ("No displays", "Geen beeldschermen"), - ("elevated_switch_display_msg", "Schakel over naar het primaire beeldscherm, aangezien meerdere beeldschermen niet worden ondersteund in de modus met verhoogde rechten."), ("Open in new window", "Open in een nieuw venster"), ("Show displays as individual windows", "Beeldschermen weergeven als afzonderlijke vensters"), ("Use all my displays for the remote session", "Gebruik al mijn beeldschermen voor de externe sessie"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Privacymodus openen"), ("Exit privacy mode", "Privacymodus afsluiten"), ("idd_not_support_under_win10_2004_tip", "Het indirecte displaystuurprogramma wordt niet ondersteund. Windows 10 versie 2004 of later is vereist."), - ("switch_display_elevated_connections_tip", "Overschakelen naar een niet-hoofdbeeldscherm wordt niet ondersteund in de verhoogde modus wanneer er meerdere verbindingen zijn. Probeer het opnieuw na de installatie als je meerdere schermen wilt beheren."), ("input_source_1_tip", "Invoerbron 1"), ("input_source_2_tip", "Invoerbron 2"), - ("capture_display_elevated_connections_tip", "Scannen van meerdere schermen wordt niet ondersteund in de bevoorrechte gebruikersmodus. Als je meerdere schermen wilt bedienen, probeer het dan opnieuw na de installatie."), ("Swap control-command key", "Wissel controle-commando toets"), ("swap-left-right-mouse", "wissel-links-rechts-muis"), ("2FA code", "2FA-code"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 6a726030bbc..cede4f8b986 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Test"), ("display_is_plugged_out_msg", "Ekran został odłączony, przełącz się na pierwszy ekran."), ("No displays", "Brak ekranów"), - ("elevated_switch_display_msg", "Przełącz się na ekran główny, ponieważ wyświetlanie kilku ekranów nie jest obsługiwane przy podniesionych uprawnieniach."), ("Open in new window", "Otwórz w nowym oknie"), ("Show displays as individual windows", "Pokaż ekrany w osobnych oknach"), ("Use all my displays for the remote session", "Użyj wszystkich moich ekranów do zdalnej sesji"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Wejdź w tryb prywatności"), ("Exit privacy mode", "Wyjdź z trybu prywatności"), ("idd_not_support_under_win10_2004_tip", "Pośredni sterownik ekranu nie jest obsługiwany. Wymagany jest system Windows 10 w wersji 2004 lub nowszej."), - ("switch_display_elevated_connections_tip", "Przełączanie na ekran inny niż główny nie jest obsługiwane przy podniesionych uprawnieniach, gdy istnieje wiele połączeń. Jeśli chcesz sterować wieloma ekranami, należy zainstalować program."), ("input_source_1_tip", "Wejście źródła 1"), ("input_source_2_tip", "Wejście źródła 2"), - ("capture_display_elevated_connections_tip", "Przechwytywanie wielu ekranów nie jest obsługiwane w trybie użytkownika z podwyższonym poziomem uprawnień. Jeśli chcesz sterować wieloma wyświetlaczami, spróbuj ponownie po instalacji."), ("Swap control-command key", "Zamiana przycisków sterujących myszki"), ("swap-left-right-mouse", "Zamień przyciski myszki (LPM - RPM)"), ("2FA code", "Kod 2FA"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 24311457671..0530283763c 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index afb364837ed..8a9e71b3bdb 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Teste"), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index baa3dd36e6f..ddd9f5e4b54 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index c140fa8acea..2f15501611f 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Тест"), ("display_is_plugged_out_msg", "Дисплей отключён, переключитесь на первый дисплей."), ("No displays", "Нет дисплеев"), - ("elevated_switch_display_msg", "Переключитесь на основной дисплей, поскольку в режиме повышенных прав несколько дисплеев не поддерживаются."), ("Open in new window", "Открыть в новом окне"), ("Show displays as individual windows", "Показывать дисплеи в отдельных окнах"), ("Use all my displays for the remote session", "Использовать все мои дисплеи для удалённого сеанса"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Включить режим конфиденциальности"), ("Exit privacy mode", "Отключить режим конфиденциальности"), ("idd_not_support_under_win10_2004_tip", "Драйвер непрямого отображения не поддерживается. Требуется Windows 10 версии 2004 или новее."), - ("switch_display_elevated_connections_tip", "Переключение на неосновной дисплей не поддерживается в режиме повышенных прав при наличии нескольких подключений. Повторите попытку после установки, если хотите управлять несколькими дисплеями."), ("input_source_1_tip", "Источник ввода 1"), ("input_source_2_tip", "Источник ввода 2"), - ("capture_display_elevated_connections_tip", "Захват экрана нескольких дисплеев не поддерживается в режиме повышенных прав. Повторите попытку после установки, если хотите управлять несколькими дисплеями."), ("Swap control-command key", "Поменять местами значения кнопок Ctrl и Command"), ("swap-left-right-mouse", "Поменять местами значения левой и правой кнопок мыши"), ("2FA code", "Код двухфакторной аутентификации"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", "Громкость+"), ("Volume down", "Громкость-"), ("Power", "Питание"), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 007123d6096..2617bae42ed 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Test"), ("display_is_plugged_out_msg", "Obrazovka je odpojená, prepnite na prvú obrazovku."), ("No displays", "Žiadne obrazovky"), - ("elevated_switch_display_msg", "Prepnite na primárnu obrazovku, pretože viacero obrazoviek nie je podporovaných vo zvýšenom režime."), ("Open in new window", "Otvoriť v novom okne"), ("Show displays as individual windows", "Zobraziť obrazovky ako jednotlivé okná"), ("Use all my displays for the remote session", "Použiť všetky moje obrazovky pre vzdialenú reláciu"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Vstup do režimu súkromia"), ("Exit privacy mode", "Ukončiť režim súkromia"), ("idd_not_support_under_win10_2004_tip", "Ovládač nepriameho zobrazenia nie je podporovaný. Vyžaduje sa systém Windows 10, verzia 2004 alebo novšia."), - ("switch_display_elevated_connections_tip", "Prepínanie na inú ako primárnu obrazovku nie je podporované vo zvýšenom režime, ak existuje viacero pripojení. Ak chcete ovládať viacero obrazoviek, skúste to po inštalácii znova."), ("input_source_1_tip", "Vstupný zdroj 1"), ("input_source_2_tip", "Vstupný zdroj 2"), - ("capture_display_elevated_connections_tip", "Snímanie viacerých displejov nie je podporované v režime privilegovaného používateľa. Ak chcete ovládať viac displejov, skúste to po inštalácii znova."), ("Swap control-command key", "Vymeniť kláves ovládania a príkazu"), ("swap-left-right-mouse", "Prehodiť ľavé a pravé tlačidlo myši"), ("2FA code", "2FA kód"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 31666c54607..450d880c40f 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 9830a9494c6..e0f70d430aa 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 0e993c8633c..6b15f651c82 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index f329ab9202d..0e456354f2a 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index d7744cc36c5..b725f6322b4 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), diff --git a/src/lang/th.rs b/src/lang/th.rs index d1fdd8aabd1..1203bc7532a 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index c6ae97b81f9..37a8b1f5261 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index b30e9223aaa..4e7fab39f79 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "測試"), ("display_is_plugged_out_msg", "螢幕已被拔除,切換到第一個螢幕。"), ("No displays", "沒有已連結的螢幕"), - ("elevated_switch_display_msg", "由於權限提升模式不支援多螢幕,所以切換至主螢幕"), ("Open in new window", "在新視窗中開啟"), ("Show displays as individual windows", "在各別的視窗開啟螢幕畫面"), ("Use all my displays for the remote session", "使用所有的螢幕用於遠端連線"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "進入隱私模式"), ("Exit privacy mode", "退出隱私模式"), ("idd_not_support_under_win10_2004_tip", "不支援 Indirect display driver。 需要 Windows 10 版本 2004 或更新的版本。"), - ("switch_display_elevated_connections_tip", "在有多個連線時,切換到非主螢幕在權限提升模式下不支援。如果您想要控制多個螢幕,請在安裝後再試一次。"), ("input_source_1_tip", "輸入源 1"), ("input_source_2_tip", "輸入源 2"), - ("capture_display_elevated_connections_tip", "不支援在權限提升模式下擷取多個螢幕畫面,如果您想要控制多個螢幕,請安裝後再試。"), ("Swap control-command key", "交換 Control 和 Command 按鍵"), ("swap-left-right-mouse", "交換滑鼠左右鍵"), ("2FA code", "二步驟驗證碼"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 3a17b5223f4..5088238419b 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", "Тест"), ("display_is_plugged_out_msg", "Дисплей відключено, перемкніться на перший дисплей"), ("No displays", "Відсутні дисплеї"), - ("elevated_switch_display_msg", "Перемкніться на основний дисплей, оскільки в режимі розширених прав одночасне використання декілька дисплеїв не підтримуються."), ("Open in new window", "Відкрити в новому вікні"), ("Show displays as individual windows", "Показувати дисплеї в окремих вікнах"), ("Use all my displays for the remote session", "Використовувати всі мої дисплеї для віддаленого сеансу"), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", "Увійти в режим конфіденційності"), ("Exit privacy mode", "Вийти з режиму конфіденційності"), ("idd_not_support_under_win10_2004_tip", "Драйвер непрямого відображення не підтримується. Потрібна Windows 10 версії 2004 або новіше."), - ("switch_display_elevated_connections_tip", "В режимі розширених прав, коли є декілька підключень, не підтримується перемикання на неосновний дисплей. Якщо ви хочете керувати декількома дисплеями, будь ласка, спробуйте це після встановлення."), ("input_source_1_tip", "Джерело введення 1"), ("input_source_2_tip", "Джерело введення 2"), - ("capture_display_elevated_connections_tip", "В режимі розширених прав захоплення декількох дисплеїв не підтримується. Якщо ви хочете керувати декількома дисплеями, будь ласка, спробуйте це після встановлення."), ("Swap control-command key", "Поміняти місцями клавіші Control та Command"), ("swap-left-right-mouse", "Поміняти місцями ліву та праву кнопки миші"), ("2FA code", "Код двофакторної автентифікації"), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index dbf392dc0d4..d8a269bc2af 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -551,7 +551,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Test", ""), ("display_is_plugged_out_msg", ""), ("No displays", ""), - ("elevated_switch_display_msg", ""), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -570,10 +569,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter privacy mode", ""), ("Exit privacy mode", ""), ("idd_not_support_under_win10_2004_tip", ""), - ("switch_display_elevated_connections_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("capture_display_elevated_connections_tip", ""), ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), @@ -627,5 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", ""), ("Volume down", ""), ("Power", ""), + ("Telegram bot", ""), + ("enable-bot-tip", ""), + ("enable-bot-desc", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 2441c775f88..2003564a010 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2506,25 +2506,6 @@ impl Connection { } async fn handle_switch_display(&mut self, s: SwitchDisplay) { - #[cfg(windows)] - if portable_client::running() - && *CONN_COUNT.lock().unwrap() > 1 - && s.display != (*display_service::PRIMARY_DISPLAY_IDX as i32) - { - log::info!("Switch to non-primary display is not supported in the elevated mode when there are multiple connections."); - let mut msg_out = Message::new(); - let res = MessageBox { - msgtype: "nook-nocancel-hasclose".to_owned(), - title: "Prompt".to_owned(), - text: "switch_display_elevated_connections_tip".to_owned(), - link: "".to_owned(), - ..Default::default() - }; - msg_out.set_message_box(res); - self.send(msg_out).await; - return; - } - let display_idx = s.display as usize; if self.display_idx != display_idx { if let Some(server) = self.server.upgrade() { @@ -2594,22 +2575,6 @@ impl Connection { } async fn capture_displays(&mut self, add: &[usize], sub: &[usize], set: &[usize]) { - #[cfg(windows)] - if portable_client::running() && (add.len() > 0 || set.len() > 1) { - log::info!("Capturing multiple displays is not supported in the elevated mode."); - let mut msg_out = Message::new(); - let res = MessageBox { - msgtype: "nook-nocancel-hasclose".to_owned(), - title: "Prompt".to_owned(), - text: "capture_display_elevated_connections_tip".to_owned(), - link: "".to_owned(), - ..Default::default() - }; - msg_out.set_message_box(res); - self.send(msg_out).await; - return; - } - if let Some(sever) = self.server.upgrade() { let mut lock = sever.write().unwrap(); for display in add.iter() { diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index e181ac7febd..08b076a7b2a 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -895,7 +895,7 @@ pub mod client { if portable_service_running != RUNNING.lock().unwrap().clone() { log::info!("portable service status mismatch"); } - if portable_service_running { + if portable_service_running && display.is_primary() { log::info!("Create shared memory capturer"); return Ok(Box::new(CapturerPortable::new(current_display))); } else { From f9b0a882133dda197c433dce42d5f9e448c01e58 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 29 Jun 2024 17:14:09 +0800 Subject: [PATCH 158/335] fix switch to primary display but can't see UAC (#8527) How to reproduce: elevate at primary display->switch to another display-> trigger UAC->switch to primary display->can't see UAC Signed-off-by: 21pages --- src/server/portable_service.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 08b076a7b2a..c30f9d87ee4 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -186,9 +186,10 @@ mod utils { let rptr = counter.add(size_of::()); let iw = ptr_to_i32(counter); let ir = ptr_to_i32(counter); - let v = i32_to_vec(iw + 1); + let iw_plus1 = if iw == i32::MAX { 0 } else { iw + 1 }; + let v = i32_to_vec(iw_plus1); std::ptr::copy_nonoverlapping(v.as_ptr(), wptr, size_of::()); - if ir == iw + 1 { + if ir == iw_plus1 { let v = i32_to_vec(iw); std::ptr::copy_nonoverlapping(v.as_ptr(), rptr, size_of::()); } @@ -404,15 +405,15 @@ pub mod server { } }, Some(Err(e)) => { + if crate::platform::windows::desktop_changed() { + crate::platform::try_change_desktop(); + c = None; + std::thread::sleep(spf); + continue; + } if e.kind() != std::io::ErrorKind::WouldBlock { // DXGI_ERROR_INVALID_CALL after each success on Microsoft GPU driver // log::error!("capture frame failed: {:?}", e); - if crate::platform::windows::desktop_changed() { - crate::platform::try_change_desktop(); - c = None; - std::thread::sleep(spf); - continue; - } if c.as_ref().map(|c| c.is_gdi()) == Some(false) { // nog gdi dxgi_failed_times += 1; From 967e63266f992488333ca1b726f60b6a9ffad3a8 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sat, 29 Jun 2024 20:16:22 +0800 Subject: [PATCH 159/335] fix: win, multi-display settings changed (#8531) 1. Windows, multi-display, as the controlled side. 2. Connect 3. Turn on privacy mode, with mode 2. 4. Turn off privacy mode. Then the display settings are continues changed when connect & disconnect. Signed-off-by: fufesou --- src/privacy_mode/win_virtual_display.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/privacy_mode/win_virtual_display.rs b/src/privacy_mode/win_virtual_display.rs index 04a9d776cd9..35e5afe3944 100644 --- a/src/privacy_mode/win_virtual_display.rs +++ b/src/privacy_mode/win_virtual_display.rs @@ -154,6 +154,8 @@ impl PrivacyModeImpl { Self::restore_displays(&self.virtual_displays); allow_err!(Self::commit_change_display(0)); self.restore_plug_out_monitor(); + self.displays.clear(); + self.virtual_displays.clear(); } fn restore_plug_out_monitor(&mut self) { From 1e400d2a64f5baf70d3736106858f4e6d88ecd12 Mon Sep 17 00:00:00 2001 From: Vedant <83997633+vedantmgoyal9@users.noreply.github.com> Date: Sat, 29 Jun 2024 18:47:10 +0530 Subject: [PATCH 160/335] Update winget-releaser to latest (#8532) --- .github/workflows/winget.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml index 098e35f2068..52bb17e6f0b 100644 --- a/.github/workflows/winget.yml +++ b/.github/workflows/winget.yml @@ -6,7 +6,7 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: vedantmgoyal2009/winget-releaser@v2 + - uses: vedantmgoyal9/winget-releaser@main with: identifier: RustDesk.RustDesk version: ${{ github.event.release.tag_name }} From 4b6ba7938f6a8148f1e1fc2984520f6ebd8f4270 Mon Sep 17 00:00:00 2001 From: solokot Date: Sat, 29 Jun 2024 16:41:09 +0300 Subject: [PATCH 161/335] Update ru.rs (#8533) * Update ru.rs * Update ru.rs --- src/lang/ru.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 2f15501611f..3f8adaa4aa9 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -239,7 +239,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty", "Пусто"), ("Invalid folder name", "Недопустимое имя папки"), ("Socks5 Proxy", "SOCKS5-прокси"), - ("Socks5/Http(s) Proxy", "Socks5/Http(s)-прокси"), + ("Socks5/Http(s) Proxy", "SOCKS5/HTTP(S)-прокси"), ("Discovered", "Найдено"), ("install_daemon_tip", "Для запуска при загрузке необходимо установить системную службу"), ("Remote ID", "Удалённый ID"), @@ -315,7 +315,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Использовать постоянный пароль"), ("Use both passwords", "Использовать оба пароля"), ("Set permanent password", "Установить постоянный пароль"), - ("Enable remote restart", "Разшешить удалённую перезагрузку"), + ("Enable remote restart", "Разрешить удалённую перезагрузку"), ("Restart remote device", "Перезапустить удалённое устройство"), ("Are you sure you want to restart", "Вы уверены, что хотите выполнить перезагрузку?"), ("Restarting remote device", "Перезагрузка удалённого устройства"), @@ -445,7 +445,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Voice call", "Голосовой вызов"), ("Text chat", "Текстовый чат"), ("Stop voice call", "Завершить голосовой вызов"), - ("relay_hint_tip", "Прямое подключение может оказаться невозможным. В этом случае можно попытаться подключиться через ретранслятор. \nКроме того, если вы хотите сразу использовать ретранслятор, можно добавить к ID суффикс \"/r\" или включить \"Всегда подключаться через ретранслятор\" в настройках удалённого узла."), + ("relay_hint_tip", "Прямое подключение может оказаться невозможным. В этом случае можно попытаться подключиться через ретранслятор.\nКроме того, если вы хотите сразу использовать ретранслятор, можно добавить к ID суффикс \"/r\" или включить \"Всегда подключаться через ретранслятор\" в настройках удалённого узла."), ("Reconnect", "Переподключить"), ("Codec", "Кодек"), ("Resolution", "Разрешение"), @@ -624,8 +624,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Volume up", "Громкость+"), ("Volume down", "Громкость-"), ("Power", "Питание"), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), + ("Telegram bot", "Telegram-бот"), + ("enable-bot-tip", "Если включено, можно получать код двухфакторной аутентификации от бота. Он также может выполнять функцию уведомления о подключении."), + ("enable-bot-desc", "1) Откройте чат с @BotFather.\n2) Отправьте команду \"/newbot\". После выполнения этого шага вы получите токен.\n3) Начните чат с вашим только что созданным ботом. Отправьте сообщение, начинающееся с прямой косой черты (\"/\"), например, \"/hello\", чтобы его активировать.\n"), ].iter().cloned().collect(); } From 5114a9d36957d912692f4232aa3476f33fdf316a Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 29 Jun 2024 21:50:21 +0800 Subject: [PATCH 162/335] shared memory size use the max resolution of all displays (#8534) The case of setting a monitor inserted later as the primary monitor is not taken into account Signed-off-by: 21pages --- src/server/connection.rs | 1 + src/server/portable_service.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 2003564a010..30261c8be5b 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -3150,6 +3150,7 @@ impl Connection { .map(|t| t.0 = Instant::now()); } + #[cfg(feature = "hwcodec")] fn update_supported_encoding(&mut self) { let Some(last) = &self.last_supported_encoding else { return; diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index c30f9d87ee4..ca86e48e792 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -549,9 +549,13 @@ pub mod client { let mut max_pixel = 0; let align = 64; for d in displays { - let pixel = utils::align(d.width(), align) * utils::align(d.height(), align); - if max_pixel < pixel { - max_pixel = pixel; + let resolutions = crate::platform::resolutions(&d.name()); + for r in resolutions { + let pixel = + utils::align(r.width as _, align) * utils::align(r.height as _, align); + if max_pixel < pixel { + max_pixel = pixel; + } } } let shmem_size = utils::align(ADDR_CAPTURE_FRAME + max_pixel * 4, align); From 2fb35c3596b9ee8dbf017bd9ec84248051cb554d Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 29 Jun 2024 14:16:40 +0000 Subject: [PATCH 163/335] Update sk tr (#8535) * translate normal * translate bot tips --- src/lang/sk.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 2617bae42ed..0c0bfd6891c 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -619,13 +619,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Počas kontrolovaného"), ("During service is on", "Počas služby je v prevádzke"), ("Capture screen using DirectX", "Snímanie obrazovky pomocou DirectX"), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), + ("Back", "Naspäť"), + ("Apps", "Aplikácie"), + ("Volume up", "Zvýšiť hlasitosť"), + ("Volume down", "Znížiť hlasitosť"), + ("Power", "Napájanie"), + ("Telegram bot", "Telegram bot"), + ("enable-bot-tip", "Ak túto funkciu povolíte, kód 2FA môžete dostať od svojho bota. Môže fungovať aj ako upozornenie na pripojenie."), + ("enable-bot-desc", "1, Otvorte chat s @BotFather.\n2, Odošlite príkaz \"/newbot\". Po dokončení tohto kroku dostanete token.\n3, Spustite chat s novo vytvoreným botom. Odošlite správu začínajúcu lomítkom vpred (\"/\"), napríklad \"/hello\", aby ste ho aktivovali.\n"), ].iter().cloned().collect(); } From 750368af7bf9ef202d0b85db0b381a116b305ea6 Mon Sep 17 00:00:00 2001 From: jxdv Date: Sat, 29 Jun 2024 14:26:06 +0000 Subject: [PATCH 164/335] Update cs tr (#8536) * update common tr * update tips --- src/lang/cs.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index c38d8e4aaea..db7a063b976 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -619,13 +619,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Během řízeného"), ("During service is on", "Během služby je v provozu"), ("Capture screen using DirectX", "Snímání obrazovky pomocí DirectX"), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), + ("Back", "Zpět"), + ("Apps", "Aplikace"), + ("Volume up", "Zvýšit hlasitost"), + ("Volume down", "Snížit hlasitost"), + ("Power", "Napájení"), + ("Telegram bot", "Telegram bot"), + ("enable-bot-tip", "Pokud tuto funkci povolíte, můžete od svého bota obdržet kód 2FA. Může také fungovat jako oznámení o připojení."), + ("enable-bot-desc", "1, Otevřete chat s @BotFather.\n2, Pošlete příkaz \"/newbot\". Po dokončení tohoto kroku obdržíte token.\n3, Spusťte chat s nově vytvořeným botem. Pro jeho aktivaci odešlete zprávu začínající lomítkem vpřed (\"/\"), například \"/hello\".\n"), ].iter().cloned().collect(); } From bd334769fab0447a99fedd056a67b0f80c8094a1 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 30 Jun 2024 00:44:10 +0800 Subject: [PATCH 165/335] fix: win, virtual display, privacy mode (#8537) Restore display settings. Only restore by the display registry settings. Do not change display settings twice when turning off privacy mode. Only restore the registry values. Signed-off-by: fufesou --- src/privacy_mode/win_virtual_display.rs | 35 +++---------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/src/privacy_mode/win_virtual_display.rs b/src/privacy_mode/win_virtual_display.rs index 35e5afe3944..fd562c8e7a7 100644 --- a/src/privacy_mode/win_virtual_display.rs +++ b/src/privacy_mode/win_virtual_display.rs @@ -34,7 +34,7 @@ const CONFIG_KEY_REG_RECOVERY: &str = "reg_recovery"; struct Display { dm: DEVMODEW, name: [WCHAR; 32], - primary: bool, + _primary: bool, } pub struct PrivacyModeImpl { @@ -135,7 +135,7 @@ impl PrivacyModeImpl { let display = Display { dm, name: dd.DeviceName, - primary, + _primary: primary, }; let ds = virtual_display_manager::get_cur_device_string(); @@ -149,40 +149,11 @@ impl PrivacyModeImpl { } } - fn restore(&mut self) { - Self::restore_displays(&self.displays); - Self::restore_displays(&self.virtual_displays); - allow_err!(Self::commit_change_display(0)); - self.restore_plug_out_monitor(); - self.displays.clear(); - self.virtual_displays.clear(); - } - fn restore_plug_out_monitor(&mut self) { let _ = virtual_display_manager::plug_out_monitor_indices(&self.virtual_displays_added); self.virtual_displays_added.clear(); } - fn restore_displays(displays: &[Display]) { - for display in displays { - unsafe { - let mut dm = display.dm.clone(); - let flags = if display.primary { - CDS_NORESET | CDS_UPDATEREGISTRY | CDS_SET_PRIMARY - } else { - CDS_NORESET | CDS_UPDATEREGISTRY - }; - ChangeDisplaySettingsExW( - display.name.as_ptr(), - &mut dm, - std::ptr::null_mut(), - flags, - std::ptr::null_mut(), - ); - } - } - } - fn set_primary_display(&mut self) -> ResultType<()> { let display = &self.virtual_displays[0]; @@ -433,7 +404,7 @@ impl PrivacyMode for PrivacyModeImpl { ) -> ResultType<()> { self.check_off_conn_id(conn_id)?; super::win_input::unhook()?; - self.restore(); + self.restore_plug_out_monitor(); restore_reg_connectivity(false); if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID { From 1f4c62e4806fdcdbb003cfbee0f052b0b7d836ef Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 30 Jun 2024 01:13:45 +0800 Subject: [PATCH 166/335] fix: linux, arboard::new(), retry (#8538) Signed-off-by: fufesou --- src/common.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/common.rs b/src/common.rs index 4092a3b6215..f5d6b4dbbf1 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1524,18 +1524,23 @@ impl ClipboardContext { #[cfg(target_os = "linux")] pub fn new() -> ResultType { - for _ in 0..3 { - let (tx, rx) = std::sync::mpsc::channel(); - std::thread::spawn(move || { - tx.send(arboard::Clipboard::new()).ok(); - }); - match rx.recv_timeout(Duration::from_millis(40)) { - Ok(Ok(c)) => return Ok(ClipboardContext(c)), - Ok(Err(e)) => return Err(e.into()), - Err(_) => continue, + let mut i = 1; + loop { + // Try 5 times to create clipboard + // Arboard::new() connect to X server or Wayland compositor, which shoud be ok at most time + // But sometimes, the connection may fail, so we retry here. + match arboard::Clipboard::new() { + Ok(x) => return Ok(ClipboardContext(x)), + Err(e) => { + if i == 5 { + return Err(e.into()); + } else { + std::thread::sleep(std::time::Duration::from_millis(30 * i)); + } + } } + i += 1; } - bail!("Failed to create clipboard context, timeout"); } #[inline] @@ -1544,11 +1549,6 @@ impl ClipboardContext { Ok(self.0.get_text()?) } - // CAUTION: This function must not be called in the main thread!!! It can only be called in the clibpoard thread. - // There's no timeout for this function, so it may block. - // Because of https://github.com/1Password/arboard/blob/151e679ee5c208403b06ba02d28f92c5891f7867/src/platform/linux/x11.rs#L296 - // We cannot use a new thread to get text because the clipboard context is `&mut self`. - // The crate design is somehow not good. #[cfg(target_os = "linux")] pub fn get_text(&mut self) -> ResultType { Ok(self.0.get_text()?) From 1252f45506257e5a4a3819be61473e705bc3b20b Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 30 Jun 2024 10:57:15 +0800 Subject: [PATCH 167/335] fix 100% cpu usage of clipboard disabled on remote menu --- src/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/client.rs b/src/client.rs index 806636b7e18..6f3d88c6415 100644 --- a/src/client.rs +++ b/src/client.rs @@ -736,6 +736,7 @@ impl Client { break; } if !TEXT_CLIPBOARD_STATE.lock().unwrap().is_required { + std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); continue; } From 2116fec20b5a2165df97e329c156baf308668efc Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 30 Jun 2024 12:07:32 +0800 Subject: [PATCH 168/335] wl-clipboard-rs 0.9 and refresh cargo.lock --- Cargo.lock | 2382 ++++++++++++++++++++++++++-------------------------- Cargo.toml | 3 +- 2 files changed, 1211 insertions(+), 1174 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49c43885bcf..f532678fb81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if 1.0.0", "cipher", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", @@ -41,30 +41,30 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", "once_cell", "version_check", - "zerocopy 0.7.32", + "zerocopy 0.7.34", ] [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allo-isolate" -version = "0.1.20" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56b7997817c178b853573e8bdfb6c3afe02810b43f17d766d6703560074b0c3" +checksum = "97b6d794345b06592d0ebeed8e477e41b71e5a0a49df4fc0e4184d5938b99509" dependencies = [ "anyhow", "atomic", @@ -89,20 +89,19 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alsa" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47" +checksum = "37fe60779335388a88c01ac6c3be40304d1e349de3ada3b15f7808bb90fa9dce" dependencies = [ "alsa-sys", - "bitflags 1.3.2", + "bitflags 2.6.0", "libc", - "nix 0.24.3", ] [[package]] @@ -144,7 +143,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" dependencies = [ "android_log-sys", - "env_logger 0.10.0", + "env_logger 0.10.2", "log", "once_cell", ] @@ -169,103 +168,66 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "apple-bindgen" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f109ee76f68b4767848cb5dc93bfcc7c425deca849c4c81fa11cdce525e3d2" -dependencies = [ - "apple-sdk", - "bindgen 0.63.0", - "derive_more", - "regex", - "serde 1.0.190", - "thiserror", - "toml 0.6.0", -] - -[[package]] -name = "apple-sdk" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04f192a700686ee70008ff4e4eb76fe7d11814ab93b7ee9d48c36b9a9f0bd2a" -dependencies = [ - "plist", - "serde 1.0.190", - "serde_json 1.0.107", -] - -[[package]] -name = "apple-sys" -version = "0.2.0" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b3a1c3342678cd72676d0c1644fde496c1f65ea41f51465f54a89cad3bdf34" -dependencies = [ - "apple-bindgen", - "apple-sdk", - "objc", -] +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arboard" version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb4009533e8ff8f1450a5bcbc30f4242a1d34442221f72314bea1f5dc9c7f89" +source = "git+https://github.com/rustdesk-org/arboard#61b448d8261fb313d67a61d03fc130bd738db396" dependencies = [ "clipboard-win", - "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.23.2", "image 0.25.1", "log", "objc2 0.5.2", @@ -274,7 +236,7 @@ dependencies = [ "parking_lot", "windows-sys 0.48.0", "wl-clipboard-rs", - "x11rb 0.13.0", + "x11rb 0.13.1", ] [[package]] @@ -289,20 +251,21 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.9.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 2.5.3", + "event-listener-strategy", "futures-core", + "pin-project-lite", ] [[package]] name = "async-compression" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60" +checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" dependencies = [ "flate2", "futures-core", @@ -313,15 +276,14 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.6.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ - "async-lock", "async-task", "concurrent-queue", - "fastrand 2.0.1", - "futures-lite", + "fastrand 2.1.0", + "futures-lite 2.3.0", "slab", ] @@ -331,10 +293,10 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" dependencies = [ - "async-lock", - "autocfg 1.1.0", + "async-lock 2.8.0", + "autocfg 1.3.0", "blocking", - "futures-lite", + "futures-lite 1.13.0", ] [[package]] @@ -343,20 +305,39 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ - "async-lock", - "autocfg 1.1.0", + "async-lock 2.8.0", + "autocfg 1.3.0", "cfg-if 1.0.0", "concurrent-queue", - "futures-lite", + "futures-lite 1.13.0", "log", "parking", - "polling", + "polling 2.8.0", "rustix 0.37.27", "slab", "socket2 0.4.10", "waker-fn", ] +[[package]] +name = "async-io" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +dependencies = [ + "async-lock 3.4.0", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-io", + "futures-lite 2.3.0", + "parking", + "polling 3.7.2", + "rustix 0.38.34", + "slab", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "async-lock" version = "2.8.0" @@ -366,67 +347,78 @@ dependencies = [ "event-listener 2.5.3", ] +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-process" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "async-signal", "blocking", "cfg-if 1.0.0", - "event-listener 3.0.0", - "futures-lite", - "rustix 0.38.21", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.34", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "async-signal" -version = "0.2.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a5415b7abcdc9cd7d63d6badba5288b2ca017e3fbd4173b8f405449f1a2399" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" dependencies = [ - "async-io", - "async-lock", + "async-io 2.3.3", + "async-lock 3.4.0", "atomic-waker", "cfg-if 1.0.0", "futures-core", "futures-io", - "rustix 0.38.21", + "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "async-task" -version = "4.5.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -449,7 +441,7 @@ dependencies = [ "glib-sys 0.18.1", "gobject-sys 0.18.0", "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -481,20 +473,20 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -513,15 +505,15 @@ checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -544,33 +536,11 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.79", - "quote 1.0.35", - "regex", - "rustc-hash", - "shlex", - "which", -] - -[[package]] -name = "bindgen" -version = "0.63.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" -dependencies = [ - "bitflags 1.3.2", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "regex", "rustc-hash", "shlex", - "syn 1.0.109", "which", ] @@ -588,33 +558,33 @@ dependencies = [ "log", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "regex", "rustc-hash", "shlex", - "syn 2.0.55", + "syn 2.0.68", "which", ] [[package]] name = "bindgen" -version = "0.68.1" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", + "itertools 0.12.1", "lazy_static", "lazycell", - "peeking_take_while", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "regex", "rustc-hash", "shlex", - "syn 2.0.55", + "syn 2.0.68", ] [[package]] @@ -635,17 +605,17 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ - "serde 1.0.190", + "serde 1.0.203", ] [[package]] name = "bitmask-enum" -version = "2.2.2" +version = "2.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fb8528abca6895a5ada33d62aedd538a5c33e77068256483b44a3230270163" +checksum = "afb15541e888071f64592c0b4364fdff21b7cb0a247f984296699351963a8721" dependencies = [ - "quote 1.0.35", - "syn 2.0.55", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -705,25 +675,22 @@ dependencies = [ [[package]] name = "blocking" -version = "1.4.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel", - "async-lock", "async-task", - "fastrand 2.0.1", "futures-io", - "futures-lite", + "futures-lite 2.3.0", "piper", - "tracing", ] [[package]] name = "brotli" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -748,21 +715,15 @@ checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "bytecount" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" [[package]] name = "byteorder" @@ -776,7 +737,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" dependencies = [ - "serde 1.0.190", + "serde 1.0.203", ] [[package]] @@ -807,8 +768,8 @@ source = "git+https://github.com/clslaid/cacao?branch=feat/set-file-urls#05e1536 dependencies = [ "bitmask-enum", "block2 0.2.0-alpha.6", - "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", - "core-graphics 0.23.1 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", + "core-foundation 0.9.3", + "core-graphics 0.23.1", "dispatch", "lazy_static", "libc", @@ -840,17 +801,18 @@ checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys 0.18.1", "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] name = "cc" -version = "1.0.83" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "779e6b7d17797c0b42023d417228c02889300190e700cb074c3438d9c541d332" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -870,9 +832,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.5" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -890,18 +852,24 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", - "num-traits 0.2.17", + "num-traits 0.2.19", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -912,7 +880,7 @@ checksum = "2315f7119b7146d6a883de6acd63ddf96071b5f79d9d98d2adaa84d749f6abf1" dependencies = [ "debug-helper", "num-bigint", - "num-traits 0.2.17", + "num-traits 0.2.19", "once_cell", "regex", ] @@ -929,13 +897,13 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.7.4", + "libloading 0.8.4", ] [[package]] @@ -955,30 +923,30 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim 0.11.1", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clipboard" @@ -995,7 +963,7 @@ dependencies = [ "parking_lot", "percent-encoding", "rand 0.8.5", - "serde 1.0.190", + "serde 1.0.203", "serde_derive", "thiserror", "utf16string", @@ -1039,7 +1007,7 @@ dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", "core-graphics 0.22.3", "foreign-types 0.3.2", "libc", @@ -1055,8 +1023,8 @@ dependencies = [ "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", + "core-graphics 0.23.2", "foreign-types 0.5.0", "libc", "objc", @@ -1070,8 +1038,8 @@ checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ "bitflags 1.3.2", "block", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics-types 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "libc", "objc", ] @@ -1084,15 +1052,15 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "combine" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ "bytes", "memchr", @@ -1100,9 +1068,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -1113,7 +1081,7 @@ version = "0.4.0-2" source = "git+https://github.com/rustdesk-org/confy#83db9ec19a2f97e9718aef69e4fc5611bb382479" dependencies = [ "directories-next", - "serde 1.0.190", + "serde 1.0.203", "thiserror", "toml 0.5.11", ] @@ -1130,9 +1098,9 @@ dependencies = [ [[package]] name = "const_fn" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d" [[package]] name = "const_format" @@ -1149,8 +1117,8 @@ version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "unicode-xid 0.2.4", ] @@ -1166,36 +1134,30 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" dependencies = [ - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.6 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", "libc", ] [[package]] name = "core-foundation" -version = "0.9.3" -source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "core-foundation-sys 0.8.6", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc", ] [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-foundation-sys" @@ -1212,8 +1174,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics-types 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types 0.3.2", "libc", ] @@ -1221,49 +1183,49 @@ dependencies = [ [[package]] name = "core-graphics" version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics-types 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.3", + "core-graphics-types 0.1.2", "foreign-types 0.5.0", "libc", + "objc2-encode 2.0.0-pre.2", ] [[package]] name = "core-graphics" -version = "0.23.1" -source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", - "core-graphics-types 0.1.2 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types 0.5.0", "libc", - "objc2-encode 2.0.0-pre.2", ] [[package]] name = "core-graphics-types" version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.3", "libc", + "objc2-encode 2.0.0-pre.2", ] [[package]] name = "core-graphics-types" -version = "0.1.2" -source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", + "core-foundation 0.9.4", "libc", - "objc2-encode 2.0.0-pre.2", ] [[package]] @@ -1273,114 +1235,102 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" dependencies = [ "bitflags 1.3.2", - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "coreaudio-sys", ] [[package]] name = "coreaudio-sys" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8478e5bdad14dce236b9898ea002eabfa87cbe14f0aa538dbe3b6a4bec4332d" +checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" dependencies = [ - "bindgen 0.68.1", + "bindgen 0.69.4", ] [[package]] name = "cpal" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" dependencies = [ "alsa", - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "coreaudio-rs", "dasp_sample", - "jni 0.19.0", + "jni 0.21.1", "js-sys", "libc", "mach2", - "ndk", + "ndk 0.8.0", "ndk-context", "oboe", - "once_cell", - "parking_lot", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows 0.46.0", + "windows 0.54.0", ] [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ - "cfg-if 1.0.0", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if 1.0.0", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.15" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg 1.1.0", - "cfg-if 1.0.0", "crossbeam-utils", - "memoffset 0.9.0", - "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if 1.0.0", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1400,19 +1350,19 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.1" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ - "nix 0.27.1", - "windows-sys 0.48.0", + "nix 0.28.0", + "windows-sys 0.52.0", ] [[package]] name = "dart-sys" -version = "4.0.2" +version = "4.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8b5680b5c2cc52f50acb2457d9b3a3b58adcca785db13a0e3655626f601de6" +checksum = "57967e4b200d767d091b961d6ab42cc7d0cc14fe9e052e75d0d3cf9eb732d895" dependencies = [ "cc", ] @@ -1424,7 +1374,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if 1.0.0", - "hashbrown 0.14.2", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -1594,9 +1544,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] @@ -1607,32 +1557,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 1.0.109", -] - -[[package]] -name = "derive-new" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" -dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2 1.0.79", - "quote 1.0.35", - "rustc_version", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] @@ -1732,7 +1658,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.1", + "libloading 0.8.4", ] [[package]] @@ -1765,8 +1691,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a09ac8bb8c16a282264c379dffba707b9c998afc7506009137f3c6136888078" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] @@ -1795,15 +1721,21 @@ checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" dependencies = [ "lazy_static", "regex", - "serde 1.0.190", + "serde 1.0.203", "strsim 0.10.0", ] [[package]] name = "downcast-rs" -version = "1.2.0" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "dtoa" @@ -1818,7 +1750,7 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.190", + "serde 1.0.203", "serde_derive", "thiserror", ] @@ -1834,9 +1766,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" @@ -1857,7 +1789,7 @@ dependencies = [ "objc", "pkg-config", "rdev", - "serde 1.0.190", + "serde 1.0.203", "serde_derive", "tfc", "unicode-segmentation", @@ -1875,43 +1807,43 @@ dependencies = [ [[package]] name = "enum-map" -version = "2.7.0" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53337c2dbf26a3c31eccc73a37b10c1614e8d4ae99b6a50d553e8936423c1f16" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" dependencies = [ "enum-map-derive", ] [[package]] name = "enum-map-derive" -version = "0.14.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d0b288e3bb1d861c4403c1774a6f7a798781dfc519b3647df2a3dd4ae95f25" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "enumflags2" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ "enumflags2_derive", - "serde 1.0.190", + "serde 1.0.203", ] [[package]] name = "enumflags2_derive" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -1929,9 +1861,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -1958,19 +1890,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "error-code" -version = "3.0.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" +checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" [[package]] name = "evdev" @@ -1990,20 +1922,41 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "3.0.0" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e56284f00d94c1bc7fd3c77027b4623c88c1f53d8d2394c6199f2921dea325" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + [[package]] name = "exr" -version = "1.71.0" +version = "1.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" dependencies = [ "bit_field", "flume", @@ -2026,15 +1979,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -2045,20 +1998,20 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.9.0", + "memoffset 0.9.1", "rustc_version", ] [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", ] [[package]] @@ -2069,9 +2022,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -2079,9 +2032,9 @@ dependencies = [ [[package]] name = "flexi_logger" -version = "0.27.3" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac35b454b60e1836602173e2eb7ef531173388c0212e02ec7f9fac086159ee5" +checksum = "469e584c031833564840fb0cdbce99bdfe946fd45480a188545e73a76f45461c" dependencies = [ "chrono", "crossbeam-channel", @@ -2132,9 +2085,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "1.82.3" +version = "1.82.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1ab3d175f0a09c1adb55fd98d7b6460b00af72c4e889b9eec2c5aee88273996" +checksum = "a7fe743d921bedf4578b9472346d03a9643a01cd565ca7df7961baebad534ba5" [[package]] name = "fnv" @@ -2176,9 +2129,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2195,9 +2148,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -2244,9 +2197,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -2259,9 +2212,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -2269,15 +2222,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -2286,9 +2239,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -2305,34 +2258,47 @@ dependencies = [ "waker-fn", ] +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand 2.1.0", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -2384,7 +2350,7 @@ dependencies = [ "glib-sys 0.18.1", "gobject-sys 0.18.0", "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -2401,7 +2367,7 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -2415,7 +2381,7 @@ dependencies = [ "gobject-sys 0.18.0", "libc", "pkg-config", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -2427,7 +2393,7 @@ dependencies = [ "gdk-sys", "glib-sys 0.18.1", "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", "x11 2.21.0", ] @@ -2463,9 +2429,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2474,9 +2440,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -2484,9 +2450,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gio" @@ -2516,7 +2482,7 @@ dependencies = [ "glib-sys 0.18.1", "gobject-sys 0.18.0", "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", "winapi 0.3.9", ] @@ -2583,11 +2549,11 @@ checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" dependencies = [ "anyhow", "heck 0.3.3", - "itertools", + "itertools 0.9.0", "proc-macro-crate 0.1.5", "proc-macro-error", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] @@ -2598,11 +2564,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.0", + "proc-macro-crate 2.0.2", "proc-macro-error", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2622,7 +2588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -2650,7 +2616,7 @@ checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys 0.18.1", "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -2670,7 +2636,7 @@ dependencies = [ "gstreamer-sys", "libc", "muldiv", - "num-rational 0.3.2", + "num-rational", "once_cell", "paste", "pretty-hex", @@ -2822,7 +2788,7 @@ dependencies = [ "gobject-sys 0.18.0", "libc", "pango-sys", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -2833,9 +2799,9 @@ checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" dependencies = [ "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -2850,7 +2816,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.0.2", + "indexmap", "slab", "tokio", "tokio-util", @@ -2859,10 +2825,11 @@ dependencies = [ [[package]] name = "half" -version = "2.2.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ + "cfg-if 1.0.0", "crunchy", ] @@ -2872,16 +2839,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", "allocator-api2", ] @@ -2891,14 +2858,14 @@ version = "0.1.0" dependencies = [ "anyhow", "backtrace", - "base64 0.22.0", + "base64 0.22.1", "bytes", "chrono", "confy", "directories-next", "dirs-next", "dlopen", - "env_logger 0.10.0", + "env_logger 0.10.2", "filetime", "flexi_logger", "futures", @@ -2916,9 +2883,9 @@ dependencies = [ "regex", "rustls-pki-types", "rustls-platform-verifier", - "serde 1.0.190", + "serde 1.0.203", "serde_derive", - "serde_json 1.0.107", + "serde_json 1.0.118", "socket2 0.3.19", "sodiumoxide", "sysinfo", @@ -2932,7 +2899,7 @@ dependencies = [ "url", "uuid", "winapi 0.3.9", - "zstd 0.13.0", + "zstd 0.13.1", ] [[package]] @@ -2950,6 +2917,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -2961,9 +2934,15 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hex" @@ -2982,11 +2961,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3012,7 +2991,7 @@ checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", - "itoa 1.0.9", + "itoa 1.0.11", ] [[package]] @@ -3028,9 +3007,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -3047,21 +3026,21 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.4.18" -source = "git+https://github.com/21pages/hwcodec#b84d5bbefa949194d1fc51a5c7e9d7988e315ce6" +source = "git+https://github.com/21pages/hwcodec#4b15d782512f95cb158577853e6cdb67a37502c1" dependencies = [ "bindgen 0.59.2", "cc", "log", - "serde 1.0.190", + "serde 1.0.203", "serde_derive", - "serde_json 1.0.107", + "serde_json 1.0.118", ] [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "f361cde2f109281a220d4307746cdfd5ee3f410da58a70377762396775634b33" dependencies = [ "bytes", "futures-channel", @@ -3072,9 +3051,9 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.9", + "itoa 1.0.11", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -3090,7 +3069,7 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.10", + "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", ] @@ -3110,16 +3089,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core 0.51.1", + "windows-core 0.52.0", ] [[package]] @@ -3133,9 +3112,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -3143,9 +3122,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", @@ -3153,8 +3132,7 @@ dependencies = [ "exr", "gif", "jpeg-decoder", - "num-rational 0.4.1", - "num-traits 0.2.17", + "num-traits 0.2.19", "png", "qoi", "tiff", @@ -3168,7 +3146,7 @@ checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" dependencies = [ "bytemuck", "byteorder", - "num-traits 0.2.17", + "num-traits 0.2.19", "png", "tiff", ] @@ -3183,41 +3161,31 @@ dependencies = [ [[package]] name = "include_dir" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" -dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", -] - -[[package]] -name = "indexmap" -version = "1.9.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ - "autocfg 1.1.0", - "hashbrown 0.12.3", + "proc-macro2 1.0.86", + "quote 1.0.36", ] [[package]] name = "indexmap" -version = "2.0.2" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.5", ] [[package]] @@ -3251,9 +3219,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if 1.0.0", ] @@ -3264,7 +3232,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -3277,13 +3245,13 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.3", - "rustix 0.38.21", - "windows-sys 0.48.0", + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -3292,6 +3260,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.9.0" @@ -3301,6 +3275,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.3.4" @@ -3309,9 +3292,9 @@ checksum = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jni" @@ -3327,20 +3310,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "jni" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - [[package]] name = "jni" version = "0.21.1" @@ -3365,27 +3334,27 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" dependencies = [ "rayon", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -3393,12 +3362,11 @@ dependencies = [ [[package]] name = "keepawake" version = "0.4.3" -source = "git+https://github.com/rustdesk-org/keepawake-rs#ad94454a75cf1ff9e95e217dee9dd6a378bf625e" +source = "git+https://github.com/rustdesk-org/keepawake-rs#64d568586dd16551d02120e19668d2b0fec8e3c9" dependencies = [ "anyhow", - "apple-sys", "cfg-if 1.0.0", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", "shadow-rs", "windows 0.48.0", "winres", @@ -3422,15 +3390,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ "bitflags 2.6.0", - "serde 1.0.190", + "serde 1.0.203", "unicode-segmentation", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -3470,9 +3438,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.150" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libdbus-sys" @@ -3507,12 +3475,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if 1.0.0", - "windows-sys 0.48.0", + "windows-targets 0.52.5", ] [[package]] @@ -3530,8 +3498,8 @@ dependencies = [ "bitflags 1.3.2", "libc", "libpulse-sys", - "num-derive", - "num-traits 0.2.17", + "num-derive 0.3.3", + "num-traits 0.2.19", "winapi 0.3.9", ] @@ -3563,12 +3531,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc19e110fbf42c17260d30f6d3dc545f58491c7830d38ecb9aaca96e26067a9b" dependencies = [ "libc", - "num-derive", - "num-traits 0.2.17", + "num-derive 0.3.3", + "num-traits 0.2.19", "pkg-config", "winapi 0.3.9", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "libsamplerate-sys" version = "0.1.12" @@ -3611,9 +3589,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" dependencies = [ "cc", "libc", @@ -3623,12 +3601,9 @@ dependencies = [ [[package]] name = "line-wrap" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] +checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" [[package]] name = "linux-raw-sys" @@ -3638,41 +3613,41 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", "scopeguard", ] [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "mac_address" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4863ee94f19ed315bf3bc00299338d857d4b5bc856af375cc97d237382ad3856" +checksum = "8836fae9d0d4be2c8b4efcdd79e828a2faa058a90d005abf42f91cac5493a08e" dependencies = [ - "nix 0.23.2", + "nix 0.28.0", "winapi 0.3.9", ] [[package]] name = "mach2" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" dependencies = [ "libc", ] @@ -3720,9 +3695,9 @@ checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -3730,7 +3705,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", ] [[package]] @@ -3739,16 +3714,16 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", ] [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", ] [[package]] @@ -3765,9 +3740,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", @@ -3795,12 +3770,13 @@ dependencies = [ [[package]] name = "muda" -version = "0.11.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e406691fa7749604bbc7964bde28a300572d52621bb84540f6907c0f8fe08737" +checksum = "86b959f97c97044e4c96e32e1db292a7d594449546a3c6b77ae613dc3a5b5145" dependencies = [ "cocoa 0.25.0", "crossbeam-channel", + "dpi", "gtk", "keyboard-types", "libxdo", @@ -3819,11 +3795,10 @@ checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -3855,12 +3830,26 @@ checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" dependencies = [ "bitflags 1.3.2", "jni-sys", - "ndk-sys", - "num_enum", + "ndk-sys 0.4.1+23.1.7779620", + "num_enum 0.5.11", "raw-window-handle 0.5.2", "thiserror", ] +[[package]] +name = "ndk" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" +dependencies = [ + "bitflags 2.6.0", + "jni-sys", + "log", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum 0.7.2", + "thiserror", +] + [[package]] name = "ndk-context" version = "0.1.1" @@ -3876,6 +3865,15 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + [[package]] name = "netlink-packet-core" version = "0.5.0" @@ -3916,9 +3914,9 @@ dependencies = [ [[package]] name = "netlink-sys" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" dependencies = [ "bytes", "libc", @@ -3938,17 +3936,6 @@ dependencies = [ "memoffset 0.6.5", ] -[[package]] -name = "nix" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" -dependencies = [ - "bitflags 1.3.2", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nix" version = "0.26.4" @@ -3959,18 +3946,19 @@ dependencies = [ "cfg-if 1.0.0", "libc", "memoffset 0.7.1", - "pin-utils", ] [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if 1.0.0", + "cfg_aliases", "libc", + "memoffset 0.9.1", ] [[package]] @@ -3983,15 +3971,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom8" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" -dependencies = [ - "memchr", -] - [[package]] name = "ntapi" version = "0.4.1" @@ -4012,65 +3991,69 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg 1.1.0", "num-integer", - "num-traits 0.2.17", + "num-traits 0.2.19", ] [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ - "num-traits 0.2.17", + "num-traits 0.2.19", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "num-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "autocfg 1.1.0", - "num-traits 0.2.17", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] -name = "num-rational" -version = "0.3.2" +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits 0.2.17", + "num-traits 0.2.19", ] [[package]] name = "num-rational" -version = "0.4.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", "num-integer", - "num-traits 0.2.17", + "num-traits 0.2.19", ] [[package]] @@ -4079,16 +4062,16 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.17", + "num-traits 0.2.19", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", ] [[package]] @@ -4097,7 +4080,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.3", + "hermit-abi 0.3.9", "libc", ] @@ -4107,7 +4090,16 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive 0.7.2", ] [[package]] @@ -4117,16 +4109,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate 2.0.2", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", +] + [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -4297,47 +4301,47 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "memchr", ] [[package]] name = "oboe" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0" +checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" dependencies = [ - "jni 0.20.0", - "ndk", + "jni 0.21.1", + "ndk 0.8.0", "ndk-context", - "num-derive", - "num-traits 0.2.17", + "num-derive 0.4.2", + "num-traits 0.2.19", "oboe-sys", ] [[package]] name = "oboe-sys" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2" +checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" dependencies = [ "cc", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ "bitflags 2.6.0", "cfg-if 1.0.0", @@ -4354,9 +4358,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -4367,9 +4371,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -4417,23 +4421,23 @@ dependencies = [ [[package]] name = "os_info" -version = "3.7.0" +version = "3.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", - "serde 1.0.190", - "winapi 0.3.9", + "serde 1.0.203", + "windows-sys 0.52.0", ] [[package]] name = "os_pipe" -version = "1.1.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" +checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4442,9 +4446,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38731fa859ef679f1aec66ca9562165926b442f298467f76f5990f431efe87dc" dependencies = [ - "serde 1.0.190", + "serde 1.0.203", "serde_derive", - "serde_json 1.0.107", + "serde_json 1.0.118", ] [[package]] @@ -4474,8 +4478,8 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94f3b9b97df3c6d4e51a14916639b24e02c7d15d1dba686ce9b1118277cb811" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] @@ -4510,7 +4514,7 @@ dependencies = [ "glib-sys 0.18.1", "gobject-sys 0.18.0", "libc", - "system-deps 6.1.2", + "system-deps 6.2.2", ] [[package]] @@ -4534,9 +4538,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -4544,15 +4548,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.2", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -4568,9 +4572,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" @@ -4592,18 +4596,18 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.0.2", + "indexmap", ] [[package]] @@ -4646,29 +4650,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -4678,40 +4682,40 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.1.0", "futures-io", ] [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plist" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa" +checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" dependencies = [ - "base64 0.21.5", - "indexmap 1.9.3", + "base64 0.21.7", + "indexmap", "line-wrap", - "quick-xml", - "serde 1.0.190", - "time 0.3.30", + "quick-xml 0.31.0", + "serde 1.0.203", + "time 0.3.36", ] [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -4726,7 +4730,7 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", "bitflags 1.3.2", "cfg-if 1.0.0", "concurrent-queue", @@ -4736,6 +4740,21 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "polling" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +dependencies = [ + "cfg-if 1.0.0", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.34", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -4756,19 +4775,19 @@ checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ - "proc-macro2 1.0.79", - "syn 2.0.55", + "proc-macro2 1.0.86", + "syn 2.0.68", ] [[package]] name = "primal-check" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df7f93fd637f083201473dab4fee2db4c429d32e55e3299980ab3957ab916a0" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" dependencies = [ "num-integer", ] @@ -4794,11 +4813,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" dependencies = [ - "toml_edit 0.20.7", + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -4808,8 +4828,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", "version_check", ] @@ -4820,8 +4840,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "version_check", ] @@ -4836,18 +4856,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "protobuf" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58678a64de2fced2bdec6bca052a6716a0efe692d6e3f53d1bda6a1def64cfc0" +checksum = "df67496db1a89596beaced1579212e9b7c53c22dca1d9745de00ead76573d514" dependencies = [ "bytes", "once_cell", @@ -4857,9 +4877,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32777b0b3f6538d9d2e012b3fad85c7e4b9244b5958d04a6415f4333782b7a77" +checksum = "eab09155fad2d39333d3796f67845d43e29b266eea74f7bc93f153f707f126dc" dependencies = [ "anyhow", "once_cell", @@ -4872,12 +4892,12 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cb37955261126624a25b5e6bda40ae34cf3989d52a783087ca6091b29b5642" +checksum = "1a16027030d4ec33e423385f73bb559821827e9ec18c50e7874e4d6de5a4e96f" dependencies = [ "anyhow", - "indexmap 1.9.3", + "indexmap", "log", "protobuf", "protobuf-support", @@ -4888,9 +4908,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ed294a835b0f30810e13616b1cd34943c6d1e84a8f3b0dcfe466d256c3e7e7" +checksum = "70e2d30ab1878b2e72d1e2fc23ff5517799c9929e2cf81a8516f9f4dcf2b9cf3" dependencies = [ "thiserror", ] @@ -4911,7 +4931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d06cb9646c7a14096231a2474d7f21e5e8c13de090c68d13bde6157cfe7f159" dependencies = [ "html-escape", - "image 0.24.7", + "image 0.24.9", "qrcodegen", ] @@ -4943,6 +4963,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "0.6.13" @@ -4954,11 +4983,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.86", ] [[package]] @@ -5111,15 +5140,15 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "raw-window-handle" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -5127,9 +5156,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -5141,8 +5170,8 @@ version = "0.5.0-2" source = "git+https://github.com/fufesou/rdev#b3434caee84c92412b45a2f655a15ac5dad33488" dependencies = [ "cocoa 0.24.1", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.8.4", + "core-foundation 0.9.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.22.3", "dispatch", "enum-map", @@ -5179,47 +5208,38 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -5229,9 +5249,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -5240,9 +5260,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "repng" @@ -5260,7 +5280,7 @@ version = "0.11.23" source = "git+https://github.com/rustdesk-org/reqwest#9cb758c9fb2f4edc62eb790acfd45a6a3da21ed3" dependencies = [ "async-compression", - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -5279,11 +5299,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.10", + "rustls 0.21.12", "rustls-native-certs 0.6.3", - "rustls-pemfile 1.0.3", - "serde 1.0.190", - "serde_json 1.0.107", + "rustls-pemfile 1.0.4", + "serde 1.0.203", + "serde_json 1.0.118", "serde_urlencoded", "sync_wrapper", "system-configuration", @@ -5303,16 +5323,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.5" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if 1.0.0", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5337,23 +5358,23 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.2.0" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" dependencies = [ "libc", "rtoolbox", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] name = "rtoolbox" -version = "0.0.1" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" dependencies = [ "libc", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] @@ -5364,7 +5385,7 @@ checksum = "cd70209c27d5b08f5528bdc779ea3ffb418954e28987f9f9775c6eac41003f9c" dependencies = [ "num-complex", "num-integer", - "num-traits 0.2.17", + "num-traits 0.2.19", "realfft", ] @@ -5400,9 +5421,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -5433,10 +5454,10 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "cidr-utils", - "clap 4.4.7", + "clap 4.5.8", "clipboard", "cocoa 0.24.1", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", "core-graphics 0.22.3", "cpal", "crossbeam-queue", @@ -5455,13 +5476,13 @@ dependencies = [ "hbb_common", "hex", "hound", - "image 0.24.7", + "image 0.24.9", "impersonate_system", "include_dir", "jni 0.21.1", "keepawake", "lazy_static", - "libloading 0.8.1", + "libloading 0.8.4", "libpulse-binding", "libpulse-simple-binding", "mac_address", @@ -5480,16 +5501,16 @@ dependencies = [ "repng", "reqwest", "ringbuf", - "rpassword 7.2.0", + "rpassword 7.3.1", "rubato", "runas", "rust-pulsectl", "samplerate", "sciter-rs", "scrap", - "serde 1.0.190", + "serde 1.0.203", "serde_derive", - "serde_json 1.0.107", + "serde_json 1.0.118", "serde_repr", "sha2", "shared_memory", @@ -5530,13 +5551,13 @@ dependencies = [ [[package]] name = "rustfft" -version = "6.1.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434" +checksum = "43806561bc506d0c5d160643ad742e3161049ac01027b5e6d7524091fd401d86" dependencies = [ "num-complex", "num-integer", - "num-traits 0.2.17", + "num-traits 0.2.19", "primal-check", "strength_reduce", "transpose", @@ -5559,22 +5580,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.10", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.14", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", @@ -5584,15 +5605,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.4" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4d6d8ad9f2492485e13453acbb291dd08f64441b6609c491f1c2cd2c6b4fe1" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.2", + "rustls-webpki 0.102.4", "subtle", "zeroize", ] @@ -5604,7 +5625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.3", + "rustls-pemfile 1.0.4", "schannel", "security-framework", ] @@ -5624,11 +5645,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -5637,34 +5658,34 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-platform-verifier" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5f0d26fa1ce3c790f9590868f0109289a044acb954525f933e2aa3b871c157d" +checksum = "3e3beb939bcd33c269f4bf946cc829fcd336370267c4a927ac0399c84a3151a1" dependencies = [ - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.8.4", + "core-foundation 0.9.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "jni 0.19.0", "log", "once_cell", - "rustls 0.23.4", + "rustls 0.23.10", "rustls-native-certs 0.7.0", "rustls-platform-verifier-android", - "rustls-webpki 0.102.2", + "rustls-webpki 0.102.4", "security-framework", "security-framework-sys", - "webpki-roots 0.26.1", + "webpki-roots 0.26.3", "winapi 0.3.9", ] @@ -5686,9 +5707,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" dependencies = [ "ring", "rustls-pki-types", @@ -5697,21 +5718,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "safemem" -version = "0.3.3" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -5733,11 +5748,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5781,14 +5796,14 @@ dependencies = [ "jni 0.21.1", "lazy_static", "log", - "ndk", + "ndk 0.7.0", "ndk-context", "num_cpus", "pkg-config", "quest", "repng", - "serde 1.0.190", - "serde_json 1.0.107", + "serde 1.0.203", + "serde_json 1.0.118", "target_build_utils", "tracing", "webm", @@ -5807,13 +5822,13 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.8.4", + "bitflags 2.6.0", + "core-foundation 0.9.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "num-bigint", "security-framework-sys", @@ -5821,19 +5836,19 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc", ] [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" @@ -5843,22 +5858,22 @@ checksum = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" [[package]] name = "serde" -version = "1.0.190" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -5875,33 +5890,33 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4" dependencies = [ - "itoa 1.0.9", + "itoa 1.0.11", "ryu", - "serde 1.0.190", + "serde 1.0.203", ] [[package]] name = "serde_repr" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ - "serde 1.0.190", + "serde 1.0.203", ] [[package]] @@ -5911,9 +5926,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.9", + "itoa 1.0.11", "ryu", - "serde 1.0.190", + "serde 1.0.203", ] [[package]] @@ -5947,8 +5962,8 @@ dependencies = [ "const_format", "git2", "is_debug", - "time 0.3.30", - "tzdb", + "time 0.3.36", + "tzdb 0.5.10", ] [[package]] @@ -5978,9 +5993,9 @@ checksum = "6057adedbec913419c92996f395ba69931acbd50b7d56955394cd3f7bedbfa45" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -6009,14 +6024,14 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg 1.1.0", + "autocfg 1.3.0", ] [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -6041,12 +6056,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -6058,7 +6073,7 @@ dependencies = [ "ed25519", "libc", "libsodium-sys", - "serde 1.0.190", + "serde 1.0.203", ] [[package]] @@ -6094,6 +6109,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.18.0" @@ -6113,8 +6134,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] @@ -6125,17 +6146,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "rustversion", "syn 1.0.109", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -6154,19 +6175,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.55" +version = "2.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "unicode-ident", ] @@ -6191,7 +6212,7 @@ version = "0.29.10" source = "git+https://github.com/rustdesk-org/sysinfo#f45dcc6510d48c3a1401c5a33eedccc8899f67b2" dependencies = [ "cfg-if 1.0.0", - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc", "ntapi", "once_cell", @@ -6206,7 +6227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6216,7 +6237,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" dependencies = [ - "core-foundation-sys 0.8.4", + "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc", ] @@ -6237,15 +6258,15 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.1.2" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af52f9402f94aac4948a2518b43359be8d9ce6cd9efc1c4de3b2f7b7e897d6" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck 0.4.1", + "heck 0.5.0", "pkg-config", - "toml 0.8.6", - "version-compare 0.1.1", + "toml 0.8.2", + "version-compare 0.2.0", ] [[package]] @@ -6266,27 +6287,27 @@ dependencies = [ "bitflags 1.3.2", "cc", "cocoa 0.25.0", - "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.4", + "core-graphics 0.23.2", "crossbeam-channel", "dispatch", "gdkwayland-sys", "gdkx11-sys", "gtk", - "image 0.24.7", + "image 0.24.9", "instant", "jni 0.21.1", "lazy_static", "libc", "log", - "ndk", + "ndk 0.7.0", "ndk-context", - "ndk-sys", + "ndk-sys 0.4.1+23.1.7779620", "objc", "once_cell", "parking_lot", "png", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.2", "scopeguard", "tao-macros", "unicode-segmentation", @@ -6303,8 +6324,8 @@ name = "tao-macros" version = "0.1.2" source = "git+https://github.com/rustdesk-org/tao?branch=dev#288c219cb0527e509590c2b2d8e7072aa9feb2d3" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] @@ -6316,9 +6337,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "target_build_utils" @@ -6337,28 +6358,27 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "006851c9ccefa3c38a7646b8cec804bb429def3da10497bfa977179869c3e8e2" dependencies = [ - "quick-xml", + "quick-xml 0.30.0", "windows 0.51.1", ] [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", - "fastrand 2.0.1", - "redox_syscall 0.4.1", - "rustix 0.38.21", - "windows-sys 0.48.0", + "fastrand 2.1.0", + "rustix 0.38.34", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -6395,22 +6415,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -6424,9 +6444,9 @@ dependencies = [ [[package]] name = "tiff" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ "flate2", "jpeg-decoder", @@ -6446,16 +6466,17 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", - "itoa 1.0.9", + "itoa 1.0.11", "libc", + "num-conv", "num_threads", "powerfmt", - "serde 1.0.190", + "serde 1.0.203", "time-core", "time-macros", ] @@ -6468,18 +6489,19 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82" dependencies = [ "tinyvec_macros", ] @@ -6504,7 +6526,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.7", "tokio-macros", "windows-sys 0.48.0", ] @@ -6515,9 +6537,9 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -6536,7 +6558,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.10", + "rustls 0.21.12", "tokio", ] @@ -6546,7 +6568,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.4", + "rustls 0.23.10", "rustls-pki-types", "tokio", ] @@ -6581,20 +6603,19 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown 0.14.2", + "hashbrown 0.14.5", "pin-project-lite", "slab", "tokio", - "tracing", ] [[package]] @@ -6603,19 +6624,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "toml" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217" -dependencies = [ - "serde 1.0.190", - "serde_spanned", - "toml_datetime 0.5.1", - "toml_edit 0.18.1", + "serde 1.0.203", ] [[package]] @@ -6624,53 +6633,31 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "serde 1.0.190", + "serde 1.0.203", "serde_spanned", - "toml_datetime 0.6.5", + "toml_datetime", "toml_edit 0.19.15", ] [[package]] name = "toml" -version = "0.8.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ - "serde 1.0.190", + "serde 1.0.203", "serde_spanned", - "toml_datetime 0.6.5", - "toml_edit 0.20.7", -] - -[[package]] -name = "toml_datetime" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" -dependencies = [ - "serde 1.0.190", + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde 1.0.190", -] - -[[package]] -name = "toml_edit" -version = "0.18.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ - "indexmap 1.9.3", - "nom8", - "serde 1.0.190", - "serde_spanned", - "toml_datetime 0.5.1", + "serde 1.0.203", ] [[package]] @@ -6679,31 +6666,31 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.2", - "serde 1.0.190", + "indexmap", + "serde 1.0.203", "serde_spanned", - "toml_datetime 0.6.5", + "toml_datetime", "winnow", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.0.2", - "serde 1.0.190", + "indexmap", + "serde 1.0.203", "serde_spanned", - "toml_datetime 0.6.5", + "toml_datetime", "winnow", ] [[package]] name = "totp-rs" -version = "5.4.0" +version = "5.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3504f96adf86d28e7eb16fa236a7951ec72c15ee100d1b5318e225944bc8cb" +checksum = "6c4ae9724c5888c0417d2396037ed3b60665925624766416e3e342b6ba5dbd3f" dependencies = [ "base32", "constant_time_eq 0.2.6", @@ -6738,9 +6725,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -6764,16 +6751,17 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.11.3" -source = "git+https://github.com/tauri-apps/tray-icon#b8dbd42c6f94a29f34b0a0daa619486277185512" +version = "0.14.3" +source = "git+https://github.com/tauri-apps/tray-icon#d4078696edba67b0ab42cef67e6a421a0332c96f" dependencies = [ - "cocoa 0.25.0", - "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.23.2", "crossbeam-channel", - "dirs-next", + "dirs 5.0.1", "libappindicator", "muda", - "objc", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation", "once_cell", "png", "thiserror", @@ -6782,13 +6770,13 @@ dependencies = [ [[package]] name = "tree_magic_mini" -version = "3.0.3" +version = "3.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91adfd0607cacf6e4babdb870e9bec4037c1c4b151cfd279ccefc5e0c7feaa6d" +checksum = "469a727cac55b41448315cc10427c069c618ac59bb6a4480283fcd811749bdc2" dependencies = [ - "bytecount", "fnv", - "lazy_static", + "home", + "memchr", "nom", "once_cell", "petgraph", @@ -6817,20 +6805,42 @@ dependencies = [ [[package]] name = "tzdb" -version = "0.5.7" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a18ee5bde3433d683d41859650804a5ad89cad17f153a53f1e6a96e0da2d969" +dependencies = [ + "iana-time-zone", + "tz-rs", + "tzdb 0.6.1", +] + +[[package]] +name = "tzdb" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec758958f2fb5069cd7fae385be95cc8eceb8cdfd270c7d14de6034f0108d99e" +checksum = "1b580f6b365fa89f5767cdb619a55d534d04a4e14c2d7e5b9a31e94598687fb1" dependencies = [ "iana-time-zone", "tz-rs", + "tzdb_data", +] + +[[package]] +name = "tzdb_data" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1889fdffac09d65c1d95c42d5202e9b21ad8c758f426e9fe09088817ea998d6" +dependencies = [ + "tz-rs", ] [[package]] name = "uds_windows" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ + "memoffset 0.9.1", "tempfile", "winapi 0.3.9", ] @@ -6846,9 +6856,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -6858,24 +6868,24 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -6897,14 +6907,14 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde 1.0.190", + "serde 1.0.203", ] [[package]] @@ -6950,15 +6960,15 @@ checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.5.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" dependencies = [ "getrandom", ] @@ -6983,9 +6993,9 @@ checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" [[package]] name = "version-compare" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" @@ -7003,15 +7013,15 @@ dependencies = [ [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -7059,9 +7069,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -7069,24 +7079,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -7096,42 +7106,42 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ - "quote 1.0.35", + "quote 1.0.36", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wayland-backend" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" +checksum = "34e9e6b6d4a2bb4e7e69433e0b35c7923b95d4dc8503a84d25ec917a4bbfdf07" dependencies = [ "cc", "downcast-rs", - "nix 0.26.4", + "rustix 0.38.34", "scoped-tls", "smallvec", "wayland-sys", @@ -7139,21 +7149,21 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.1" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +checksum = "1e63801c85358a431f986cffa74ba9599ff571fc5774ac113ed3b490c19a1133" dependencies = [ "bitflags 2.6.0", - "nix 0.26.4", + "rustix 0.38.34", "wayland-backend", "wayland-scanner", ] [[package]] name = "wayland-protocols" -version = "0.31.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" +checksum = "83d0f1056570486e26a3773ec633885124d79ae03827de05ba6c85f79904026c" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -7163,9 +7173,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "a7dab47671043d9f5397035975fe1cac639e5bca5cc0b3c32d09f01612e34d24" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -7176,20 +7186,20 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.0" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" +checksum = "67da50b9f80159dec0ea4c11c13e24ef9e7574bd6ce24b01860a175010cea565" dependencies = [ - "proc-macro2 1.0.79", - "quick-xml", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quick-xml 0.31.0", + "quote 1.0.36", ] [[package]] name = "wayland-sys" -version = "0.31.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +checksum = "105b1842da6554f91526c14a2a2172897b7f745a805d62af4ce698706be79c12" dependencies = [ "dlib", "log", @@ -7198,9 +7208,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -7230,18 +7240,18 @@ checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" @@ -7252,14 +7262,14 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.21", + "rustix 0.38.34", ] [[package]] name = "whoami" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fec781d48b41f8163426ed18e8fc2864c12937df9ce54c88ede7bd47270893e" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ "redox_syscall 0.4.1", "wasite", @@ -7268,9 +7278,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "win-sys" @@ -7311,18 +7321,18 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi 0.3.9", + "windows-sys 0.52.0", ] [[package]] name = "winapi-wsapoll" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +checksum = "1eafc5f679c576995526e81635d0cf9695841736712b4e892f87abbe6fed3f28" dependencies = [ "winapi 0.3.9", ] @@ -7368,15 +7378,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows" version = "0.48.0" @@ -7405,7 +7406,17 @@ dependencies = [ "windows-core 0.52.0", "windows-implement", "windows-interface", - "windows-targets 0.52.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-targets 0.52.5", ] [[package]] @@ -7423,7 +7434,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", ] [[package]] @@ -7432,9 +7453,9 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] @@ -7443,9 +7464,18 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.5", ] [[package]] @@ -7483,7 +7513,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -7518,26 +7548,27 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows-version" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +checksum = "6998aa457c9ba8ff2fb9f13e9d2a930dabcea28f1d0ab94d687d8b3654844515" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -7554,9 +7585,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -7584,9 +7615,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -7614,9 +7645,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -7644,9 +7681,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -7674,9 +7711,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -7692,9 +7729,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -7722,15 +7759,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.5.17" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -7766,15 +7803,14 @@ dependencies = [ [[package]] name = "wl-clipboard-rs" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57af79e973eadf08627115c73847392e6b766856ab8e3844a59245354b23d2fa" +checksum = "4de22eebb1d1e2bad2d970086e96da0e12cde0b411321e5b0f7b2a1f876aa26f" dependencies = [ - "derive-new", "libc", "log", - "nix 0.26.4", "os_pipe", + "rustix 0.38.34", "tempfile", "thiserror", "tree_magic_mini", @@ -7852,13 +7888,13 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" dependencies = [ "gethostname 0.4.3", - "rustix 0.38.21", - "x11rb-protocol 0.13.0", + "rustix 0.38.34", + "x11rb-protocol 0.13.1", ] [[package]] @@ -7872,31 +7908,31 @@ dependencies = [ [[package]] name = "x11rb-protocol" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] name = "xdg-home" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8" dependencies = [ - "nix 0.26.4", - "winapi 0.3.9", + "libc", + "windows-sys 0.52.0", ] [[package]] name = "zbus" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" dependencies = [ "async-broadcast", "async-executor", "async-fs", - "async-io", - "async-lock", + "async-io 1.13.0", + "async-lock 2.8.0", "async-process", "async-recursion", "async-task", @@ -7914,7 +7950,7 @@ dependencies = [ "once_cell", "ordered-stream", "rand 0.8.5", - "serde 1.0.190", + "serde 1.0.203", "serde_repr", "sha1", "static_assertions", @@ -7929,13 +7965,13 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "regex", "syn 1.0.109", "zvariant_utils", @@ -7943,11 +7979,11 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" dependencies = [ - "serde 1.0.190", + "serde 1.0.203", "static_assertions", "zvariant", ] @@ -7964,11 +8000,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ - "zerocopy-derive 0.7.32", + "zerocopy-derive 0.7.34", ] [[package]] @@ -7977,27 +8013,27 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", - "syn 2.0.55", + "proc-macro2 1.0.86", + "quote 1.0.36", + "syn 2.0.68", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zip" @@ -8015,7 +8051,7 @@ dependencies = [ "hmac", "pbkdf2", "sha1", - "time 0.3.30", + "time 0.3.36", "zstd 0.11.2+zstd.1.5.2", ] @@ -8030,11 +8066,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" dependencies = [ - "zstd-safe 7.0.0", + "zstd-safe 7.1.0", ] [[package]] @@ -8049,18 +8085,18 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "7.0.0" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.11+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "75652c55c0b6f3e6f12eb786fe1bc960396bf05a1eb3bf1f3691c3610ac2e6d4" dependencies = [ "cc", "pkg-config", @@ -8077,27 +8113,27 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" dependencies = [ "byteorder", "enumflags2", "libc", - "serde 1.0.190", + "serde 1.0.203", "static_assertions", "zvariant_derive", ] [[package]] name = "zvariant_derive" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", "zvariant_utils", ] @@ -8108,7 +8144,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.36", "syn 1.0.109", ] diff --git a/Cargo.toml b/Cargo.toml index 9ad4000c314..513057427b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,8 @@ sys-locale = "0.3" enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } ctrlc = "3.2" -arboard = { version = "3.4.0", features = ["wayland-data-control"] } +# arboard = { version = "3.4.0", features = ["wayland-data-control"] } +arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] } system_shutdown = "4.0" qrcode-generator = "4.1" From 25d0ced8ba02f9cdf85d46be9c02654332b55b4f Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 30 Jun 2024 13:54:40 +0800 Subject: [PATCH 169/335] fix ci (#8543) Signed-off-by: 21pages --- libs/hbb_common/Cargo.toml | 2 +- libs/hbb_common/src/platform/windows.rs | 3 +-- src/tray.rs | 23 ++++++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 8ae3ebcd571..7aeb4379776 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -58,7 +58,7 @@ tokio-native-tls ="0.3" protobuf-codegen = { version = "3.4" } [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3", features = ["winuser", "synchapi", "pdh", "memoryapi"] } +winapi = { version = "0.3", features = ["winuser", "synchapi", "pdh", "memoryapi", "sysinfoapi"] } [target.'cfg(target_os = "macos")'.dependencies] osascript = "0.3" diff --git a/libs/hbb_common/src/platform/windows.rs b/libs/hbb_common/src/platform/windows.rs index c36c384d3ab..7481631ace1 100644 --- a/libs/hbb_common/src/platform/windows.rs +++ b/libs/hbb_common/src/platform/windows.rs @@ -1,6 +1,5 @@ use std::{ collections::VecDeque, - os::windows::raw::HANDLE, sync::{Arc, Mutex}, time::Instant, }; @@ -17,7 +16,7 @@ use winapi::{ sysinfoapi::VerSetConditionMask, winbase::{VerifyVersionInfoW, INFINITE, WAIT_OBJECT_0}, winnt::{ - OSVERSIONINFOEXW, VER_BUILDNUMBER, VER_GREATER_EQUAL, VER_MAJORVERSION, + HANDLE, OSVERSIONINFOEXW, VER_BUILDNUMBER, VER_GREATER_EQUAL, VER_MAJORVERSION, VER_MINORVERSION, VER_SERVICEPACKMAJOR, VER_SERVICEPACKMINOR, }, }, diff --git a/src/tray.rs b/src/tray.rs index 8251d2505c9..01f91dbdc13 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -137,14 +137,23 @@ pub fn make_tray() -> hbb_common::ResultType<()> { if let Ok(_event) = tray_channel.try_recv() { #[cfg(target_os = "windows")] - if _event.click_type == tray_icon::ClickType::Left - || _event.click_type == tray_icon::ClickType::Double - { - if last_click.elapsed() < std::time::Duration::from_secs(1) { - return; + match _event { + TrayEvent::Click { + button, + button_state, + .. + } => { + if button == tray_icon::MouseButton::Left + && button_state == tray_icon::MouseButtonState::Up + { + if last_click.elapsed() < std::time::Duration::from_secs(1) { + return; + } + open_func(); + last_click = std::time::Instant::now(); + } } - open_func(); - last_click = std::time::Instant::now(); + _ => {} } } From 1f129e6ef3c7b0c416de3b258a33b117c77dd137 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 30 Jun 2024 17:05:09 +0800 Subject: [PATCH 170/335] change update_clipboard to threaded, since creating a context may take long --- Cargo.lock | 38 ++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 ++ src/client.rs | 4 ++-- src/client/io_loop.rs | 2 +- src/common.rs | 20 +++++++++++--------- 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f532678fb81..5449fad9141 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -967,10 +967,24 @@ dependencies = [ "serde_derive", "thiserror", "utf16string", - "x11-clipboard", + "x11-clipboard 0.8.1", "x11rb 0.12.0", ] +[[package]] +name = "clipboard-master" +version = "4.0.0-beta.6" +source = "git+https://github.com/rustdesk-org/clipboard-master#38c0a5c0e5e0cab48abf1209900e3543487fc474" +dependencies = [ + "objc", + "objc-foundation", + "objc_id", + "windows-win", + "wl-clipboard-rs", + "x11-clipboard 0.9.2", + "x11rb 0.13.1", +] + [[package]] name = "clipboard-win" version = "5.3.1" @@ -5456,6 +5470,7 @@ dependencies = [ "cidr-utils", "clap 4.5.8", "clipboard", + "clipboard-master", "cocoa 0.24.1", "core-foundation 0.9.4", "core-graphics 0.22.3", @@ -5532,7 +5547,7 @@ dependencies = [ "winreg 0.11.0", "winres", "wol-rs", - "x11-clipboard", + "x11-clipboard 0.8.1", "x11rb 0.12.0", "zip", ] @@ -7571,6 +7586,15 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-win" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e23e33622b3b52f948049acbec9bcc34bf6e26d74176b88941f213c75cf2dc" +dependencies = [ + "error-code", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7862,6 +7886,16 @@ dependencies = [ "x11rb 0.12.0", ] +[[package]] +name = "x11-clipboard" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98785a09322d7446e28a13203d2cae1059a0dd3dfb32cb06d0a225f023d8286" +dependencies = [ + "libc", + "x11rb 0.13.1", +] + [[package]] name = "x11-dl" version = "2.21.0" diff --git a/Cargo.toml b/Cargo.toml index 513057427b7..f40424cafc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,8 @@ clipboard = { path = "libs/clipboard" } ctrlc = "3.2" # arboard = { version = "3.4.0", features = ["wayland-data-control"] } arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] } +clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master"} + system_shutdown = "4.0" qrcode-generator = "4.1" diff --git a/src/client.rs b/src/client.rs index 6f3d88c6415..72de9406924 100644 --- a/src/client.rs +++ b/src/client.rs @@ -144,8 +144,8 @@ const PUBLIC_SERVER: &str = "public"; #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn get_old_clipboard_text() -> &'static Arc> { - &OLD_CLIPBOARD_TEXT +pub fn get_old_clipboard_text() -> Arc> { + OLD_CLIPBOARD_TEXT.clone() } #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 987f2b8147e..a3d10fe3dac 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1180,7 +1180,7 @@ impl Remote { Some(message::Union::Clipboard(cb)) => { if !self.handler.lc.read().unwrap().disable_clipboard.v { #[cfg(not(any(target_os = "android", target_os = "ios")))] - update_clipboard(cb, Some(&crate::client::get_old_clipboard_text())); + update_clipboard(cb, Some(crate::client::get_old_clipboard_text())); #[cfg(any(target_os = "android", target_os = "ios"))] { let content = if cb.compress { diff --git a/src/common.rs b/src/common.rs index f5d6b4dbbf1..7568a762d84 100644 --- a/src/common.rs +++ b/src/common.rs @@ -364,7 +364,7 @@ pub fn get_default_sound_input() -> Option { } #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn update_clipboard(clipboard: Clipboard, old: Option<&Arc>>) { +fn update_clipboard_(clipboard: Clipboard, old: Option>>) { let content = if clipboard.compress { decompress(&clipboard.content) } else { @@ -378,7 +378,7 @@ pub fn update_clipboard(clipboard: Clipboard, old: Option<&Arc>>) match ClipboardContext::new() { Ok(mut ctx) => { let side = if old.is_none() { "host" } else { "client" }; - let old = if let Some(old) = old { old } else { &CONTENT }; + let old = if let Some(old) = old { old } else { CONTENT.clone() }; *old.lock().unwrap() = content.clone(); let _lock = ARBOARD_MTX.lock().unwrap(); allow_err!(ctx.set_text(content)); @@ -391,6 +391,13 @@ pub fn update_clipboard(clipboard: Clipboard, old: Option<&Arc>>) } } +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn update_clipboard(clipboard: Clipboard, old: Option>>) { + std::thread::spawn(move || { + update_clipboard_(clipboard, old); + }); +} + #[cfg(feature = "use_rubato")] pub fn resample_channels( data: &[f32], @@ -1519,6 +1526,8 @@ impl ClipboardContext { #[inline] #[cfg(any(target_os = "windows", target_os = "macos"))] pub fn new() -> ResultType { + let x: Option<()> = None; + x.unwrap(); Ok(ClipboardContext(arboard::Clipboard::new()?)) } @@ -1543,13 +1552,6 @@ impl ClipboardContext { } } - #[inline] - #[cfg(any(target_os = "windows", target_os = "macos"))] - pub fn get_text(&mut self) -> ResultType { - Ok(self.0.get_text()?) - } - - #[cfg(target_os = "linux")] pub fn get_text(&mut self) -> ResultType { Ok(self.0.get_text()?) } From 1719e478e334678a2a0ab1458ce4c3fd449dd9c0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 30 Jun 2024 17:07:23 +0800 Subject: [PATCH 171/335] remove debug code --- src/common.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/common.rs b/src/common.rs index 7568a762d84..a566cee9dc1 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1526,8 +1526,6 @@ impl ClipboardContext { #[inline] #[cfg(any(target_os = "windows", target_os = "macos"))] pub fn new() -> ResultType { - let x: Option<()> = None; - x.unwrap(); Ok(ClipboardContext(arboard::Clipboard::new()?)) } From d537e2563dc525483a1c91036d42dfab736847ca Mon Sep 17 00:00:00 2001 From: FastAct <93490087+FastAct@users.noreply.github.com> Date: Sun, 30 Jun 2024 13:04:22 +0200 Subject: [PATCH 172/335] Update nl.rs (#8546) --- src/lang/nl.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lang/nl.rs b/src/lang/nl.rs index e0f23d0cf1d..c85c76aa84a 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -612,20 +612,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_new_voice_call_tip", "Er is een nieuwe spraakoproep ontvangen. Als u het aanvaardt, schakelt de audio over naar spraakcommunicatie."), ("texture_render_tip", "Pas textuurrendering toe om afbeeldingen vloeiender te maken."), ("Use texture rendering", "Textuurrendering gebruiken"), - ("Floating window", ""), - ("floating_window_tip", ""), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), - ("Capture screen using DirectX", ""), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), + ("Floating window", "Zwevend venster"), + ("floating_window_tip", "Helpt RustDesk op de achtergrond actief te houden"), + ("Keep screen on", "Scherm ingeschakeld laten"), + ("Never", "Nooit"), + ("During controlled", "Tijdens gecontroleerde"), + ("During service is on", "Tijdens actieve service"), + ("Capture screen using DirectX", "Scherm opnemen via DirectX"), + ("Back", "Terug"), + ("Apps", "Apps"), + ("Volume up", "Volume verhogen"), + ("Volume down", "Volume verlagen"), + ("Power", "Stroom"), + ("Telegram bot", "Telegram bot"), + ("enable-bot-tip", "Als u deze functie inschakelt, kunt u een 2FA-code ontvangen van uw bot. Het kan ook fungeren als een verbindingsmelding."), + ("enable-bot-desc", "1, Open een chat met @BotFather.\n2, Verzend het commando \"/newbot\". Als deze stap voltooid is, ontvang je een token.\n3, Start een chat met de nieuw aangemaakte bot. Om hem te activeren stuurt u een bericht dat begint met een schuine streep (\"/\"), bijvoorbeeld \"/hello".\n"), ].iter().cloned().collect(); } From 15fa80fb2695ee7da8357bb484d2c283728c5d14 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 30 Jun 2024 19:17:24 +0800 Subject: [PATCH 173/335] fix ci --- src/lang/nl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/nl.rs b/src/lang/nl.rs index c85c76aa84a..88500aeafc3 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -626,6 +626,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Power", "Stroom"), ("Telegram bot", "Telegram bot"), ("enable-bot-tip", "Als u deze functie inschakelt, kunt u een 2FA-code ontvangen van uw bot. Het kan ook fungeren als een verbindingsmelding."), - ("enable-bot-desc", "1, Open een chat met @BotFather.\n2, Verzend het commando \"/newbot\". Als deze stap voltooid is, ontvang je een token.\n3, Start een chat met de nieuw aangemaakte bot. Om hem te activeren stuurt u een bericht dat begint met een schuine streep (\"/\"), bijvoorbeeld \"/hello".\n"), + ("enable-bot-desc", "1, Open een chat met @BotFather.\n2, Verzend het commando \"/newbot\". Als deze stap voltooid is, ontvang je een token.\n3, Start een chat met de nieuw aangemaakte bot. Om hem te activeren stuurt u een bericht dat begint met een schuine streep (\"/\"), bijvoorbeeld \"/hello\".\n"), ].iter().cloned().collect(); } From 763174657b79112f6b40e95ea513f148ebc53d57 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 30 Jun 2024 21:24:18 +0800 Subject: [PATCH 174/335] add type to all Getx put/get/delete/isRegistered (#8550) Signed-off-by: 21pages --- flutter/lib/common.dart | 2 +- flutter/lib/common/shared_state.dart | 104 +++++++++--------- .../desktop/pages/desktop_setting_page.dart | 6 +- .../lib/desktop/pages/desktop_tab_page.dart | 2 +- .../lib/desktop/pages/file_manager_page.dart | 2 +- .../lib/desktop/pages/port_forward_page.dart | 2 +- flutter/lib/desktop/pages/remote_page.dart | 2 +- flutter/lib/desktop/pages/server_page.dart | 2 +- 8 files changed, 62 insertions(+), 60 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 5313a9e8516..5c927521388 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1428,7 +1428,7 @@ Future initGlobalFFI() async { _globalFFI = FFI(null); debugPrint("_globalFFI init end"); // after `put`, can also be globally found by Get.find(); - Get.put(_globalFFI, permanent: true); + Get.put(_globalFFI, permanent: true); } String translate(String name) { diff --git a/flutter/lib/common/shared_state.dart b/flutter/lib/common/shared_state.dart index bd95f1db94e..908c98a70e3 100644 --- a/flutter/lib/common/shared_state.dart +++ b/flutter/lib/common/shared_state.dart @@ -10,16 +10,16 @@ class PrivacyModeState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxString state = ''.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } } static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } else { Get.find(tag: key).value = ''; } @@ -33,9 +33,9 @@ class BlockInputState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxBool state = false.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = false; } @@ -43,8 +43,8 @@ class BlockInputState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -56,9 +56,9 @@ class CurrentDisplayState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxInt state = RxInt(0); - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = 0; } @@ -66,8 +66,8 @@ class CurrentDisplayState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -105,16 +105,16 @@ class ConnectionTypeState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final ConnectionType collectionType = ConnectionType(); - Get.put(collectionType, tag: key); + Get.put(collectionType, tag: key); } } static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -127,9 +127,9 @@ class FingerprintState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxString state = ''.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = ''; } @@ -137,8 +137,8 @@ class FingerprintState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -150,9 +150,9 @@ class ShowRemoteCursorState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxBool state = false.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = false; } @@ -160,8 +160,8 @@ class ShowRemoteCursorState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -173,9 +173,9 @@ class ShowRemoteCursorLockState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxBool state = false.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = false; } @@ -183,8 +183,8 @@ class ShowRemoteCursorLockState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -196,10 +196,10 @@ class KeyboardEnabledState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { // Server side, default true final RxBool state = true.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = true; } @@ -207,8 +207,8 @@ class KeyboardEnabledState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -220,9 +220,9 @@ class RemoteCursorMovedState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxBool state = false.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = false; } @@ -230,8 +230,8 @@ class RemoteCursorMovedState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -243,9 +243,9 @@ class RemoteCountState { static void init() { final key = tag(); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxInt state = 1.obs; - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = 1; } @@ -253,8 +253,8 @@ class RemoteCountState { static void delete() { final key = tag(); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -266,9 +266,9 @@ class PeerBoolOption { static void init(String id, String opt, bool Function() init_getter) { final key = tag(id, opt); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxBool value = RxBool(init_getter()); - Get.put(value, tag: key); + Get.put(value, tag: key); } else { Get.find(tag: key).value = init_getter(); } @@ -276,8 +276,8 @@ class PeerBoolOption { static void delete(String id, String opt) { final key = tag(id, opt); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -290,9 +290,9 @@ class PeerStringOption { static void init(String id, String opt, String Function() init_getter) { final key = tag(id, opt); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxString value = RxString(init_getter()); - Get.put(value, tag: key); + Get.put(value, tag: key); } else { Get.find(tag: key).value = init_getter(); } @@ -300,8 +300,8 @@ class PeerStringOption { static void delete(String id, String opt) { final key = tag(id, opt); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } @@ -314,9 +314,9 @@ class UnreadChatCountState { static void init(String id) { final key = tag(id); - if (!Get.isRegistered(tag: key)) { + if (!Get.isRegistered(tag: key)) { final RxInt state = RxInt(0); - Get.put(state, tag: key); + Get.put(state, tag: key); } else { Get.find(tag: key).value = 0; } @@ -324,8 +324,8 @@ class UnreadChatCountState { static void delete(String id) { final key = tag(id); - if (Get.isRegistered(tag: key)) { - Get.delete(tag: key); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); } } diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index d3b6add8ae7..0035ef4931d 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -84,8 +84,10 @@ class DesktopSettingPage extends StatefulWidget { } if (Get.isRegistered(tag: _kSettingPageControllerTag)) { DesktopTabPage.onAddSetting(initialPage: page); - PageController controller = Get.find(tag: _kSettingPageControllerTag); - Rx selected = Get.find(tag: _kSettingPageTabKeyTag); + PageController controller = + Get.find(tag: _kSettingPageControllerTag); + Rx selected = + Get.find>(tag: _kSettingPageTabKeyTag); selected.value = page; controller.jumpToPage(index); } else { diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index e611aca44f9..0f7e77c810b 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -20,7 +20,7 @@ class DesktopTabPage extends StatefulWidget { static void onAddSetting( {SettingsTabKey initialPage = SettingsTabKey.general}) { try { - DesktopTabController tabController = Get.find(); + DesktopTabController tabController = Get.find(); tabController.add(TabInfo( key: kTabLabelSettingPage, label: kTabLabelSettingPage, diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index c429fa24560..29ab0f80693 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -92,7 +92,7 @@ class _FileManagerPageState extends State _ffi.dialogManager .showLoading(translate('Connecting...'), onCancel: closeConnection); }); - Get.put(_ffi, tag: 'ft_${widget.id}'); + Get.put(_ffi, tag: 'ft_${widget.id}'); if (!isLinux) { WakelockPlus.enable(); } diff --git a/flutter/lib/desktop/pages/port_forward_page.dart b/flutter/lib/desktop/pages/port_forward_page.dart index b9a3248facf..99f8df3409e 100644 --- a/flutter/lib/desktop/pages/port_forward_page.dart +++ b/flutter/lib/desktop/pages/port_forward_page.dart @@ -63,7 +63,7 @@ class _PortForwardPageState extends State isSharedPassword: widget.isSharedPassword, forceRelay: widget.forceRelay, isRdp: widget.isRDP); - Get.put(_ffi, tag: 'pf_${widget.id}'); + Get.put(_ffi, tag: 'pf_${widget.id}'); debugPrint("Port forward page init success with id ${widget.id}"); widget.tabController.onSelected?.call(widget.id); } diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 8427645e2f8..4d61c373892 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -107,7 +107,7 @@ class _RemotePageState extends State super.initState(); _initStates(widget.id); _ffi = FFI(widget.sessionId); - Get.put(_ffi, tag: widget.id); + Get.put(_ffi, tag: widget.id); _ffi.imageModel.addCallbackOnFirstImage((String peerId) { showKBLayoutTypeChooserIfNeeded( _ffi.ffiModel.pi.platform, _ffi.dialogManager); diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 2ccad8c329d..4af54b9af29 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -36,7 +36,7 @@ class _DesktopServerPageState extends State void initState() { gFFI.ffiModel.updateEventListener(gFFI.sessionId, ""); windowManager.addListener(this); - Get.put(tabController); + Get.put(tabController); tabController.onRemoved = (_, id) { onRemoveId(id); }; From 01672bc6970384f428cf1a6607febd80bddbbe63 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 30 Jun 2024 21:57:32 +0800 Subject: [PATCH 175/335] clipboard image, not tested, todo: set_html --- Cargo.toml | 4 +- libs/hbb_common/protos/message.proto | 2 + src/client.rs | 16 +- src/client/io_loop.rs | 2 +- src/common.rs | 318 +++++++++++++++++++++------ src/server/clipboard_service.rs | 8 +- 6 files changed, 262 insertions(+), 88 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f40424cafc4..0c54897e1c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,8 +90,8 @@ enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } ctrlc = "3.2" # arboard = { version = "3.4.0", features = ["wayland-data-control"] } -arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] } -clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master"} +arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control", "image-data"] } +clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" } system_shutdown = "4.0" qrcode-generator = "4.1" diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index be8539a1399..f346a72288c 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -318,6 +318,8 @@ message Hash { message Clipboard { bool compress = 1; bytes content = 2; + int32 width = 3; + int32 height = 4; } enum FileType { diff --git a/src/client.rs b/src/client.rs index 72de9406924..3a59b4b4bdb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -136,7 +136,7 @@ lazy_static::lazy_static! { #[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { static ref ENIGO: Arc> = Arc::new(Mutex::new(enigo::Enigo::new())); - static ref OLD_CLIPBOARD_TEXT: Arc> = Default::default(); + static ref OLD_CLIPBOARD_DATA: Arc> = Default::default(); static ref TEXT_CLIPBOARD_STATE: Arc> = Arc::new(Mutex::new(TextClipboardState::new())); } @@ -144,8 +144,8 @@ const PUBLIC_SERVER: &str = "public"; #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn get_old_clipboard_text() -> Arc> { - OLD_CLIPBOARD_TEXT.clone() +pub fn get_old_clipboard_text() -> Arc> { + OLD_CLIPBOARD_DATA.clone() } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -740,7 +740,7 @@ impl Client { continue; } - if let Some(msg) = check_clipboard(&mut ctx, Some(&OLD_CLIPBOARD_TEXT)) { + if let Some(msg) = check_clipboard(&mut ctx, Some(OLD_CLIPBOARD_DATA.clone())) { #[cfg(feature = "flutter")] crate::flutter::send_text_clipboard_msg(msg); #[cfg(not(feature = "flutter"))] @@ -766,12 +766,12 @@ impl Client { #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn get_current_text_clipboard_msg() -> Option { - let txt = &*OLD_CLIPBOARD_TEXT.lock().unwrap(); - if txt.is_empty() { + fn get_current_clipboard_msg() -> Option { + let data = &*OLD_CLIPBOARD_DATA.lock().unwrap(); + if data.is_empty() { None } else { - Some(crate::create_clipboard_msg(txt.clone())) + Some(data.create_msg()) } } } diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index a3d10fe3dac..98dcaaad7c9 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1139,7 +1139,7 @@ impl Remote { } #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Some(msg_out) = Client::get_current_text_clipboard_msg() { + if let Some(msg_out) = Client::get_current_clipboard_msg() { let sender = self.sender.clone(); let permission_config = self.handler.get_permission_config(); tokio::spawn(async move { diff --git a/src/common.rs b/src/common.rs index a566cee9dc1..eb2a72ee98b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,10 +2,15 @@ use std::{ borrow::Cow, collections::HashMap, future::Future, - sync::{Arc, Mutex, RwLock}, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, Mutex, RwLock, + }, task::Poll, }; +use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; +use scrap::libc::RUSAGE_SELF; use serde_json::Value; #[derive(Debug, Eq, PartialEq)] @@ -183,7 +188,7 @@ pub mod input { } lazy_static::lazy_static! { - pub static ref CONTENT: Arc> = Default::default(); + pub static ref CONTENT: Arc> = Default::default(); pub static ref SOFTWARE_UPDATE_URL: Arc> = Default::default(); } @@ -273,42 +278,33 @@ pub fn valid_for_numlock(evt: &KeyEvent) -> bool { } } -pub fn create_clipboard_msg(content: String) -> Message { - let bytes = content.into_bytes(); - let compressed = compress_func(&bytes); - let compress = compressed.len() < bytes.len(); - let content = if compress { compressed } else { bytes }; - let mut msg = Message::new(); - msg.set_clipboard(Clipboard { - compress, - content: content.into(), - ..Default::default() - }); - msg -} - #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn check_clipboard( ctx: &mut Option, - old: Option<&Arc>>, + old: Option>>, ) -> Option { if ctx.is_none() { - *ctx = ClipboardContext::new().ok(); + *ctx = ClipboardContext::new(true).ok(); } let ctx2 = ctx.as_mut()?; let side = if old.is_none() { "host" } else { "client" }; - let old = if let Some(old) = old { old } else { &CONTENT }; + let old = if let Some(old) = old { + old + } else { + CONTENT.clone() + }; let content = { let _lock = ARBOARD_MTX.lock().unwrap(); - ctx2.get_text() + ctx2.get() }; if let Ok(content) = content { - if content.len() < 2_000_000 && !content.is_empty() { + if !content.is_empty() { let changed = content != *old.lock().unwrap(); if changed { log::info!("{} update found on {}", CLIPBOARD_NAME, side); - *old.lock().unwrap() = content.clone(); - return Some(create_clipboard_msg(content)); + let msg = content.create_msg(); + *old.lock().unwrap() = content; + return Some(msg); } } } @@ -364,35 +360,32 @@ pub fn get_default_sound_input() -> Option { } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn update_clipboard_(clipboard: Clipboard, old: Option>>) { - let content = if clipboard.compress { - decompress(&clipboard.content) - } else { - clipboard.content.into() - }; - if let Ok(content) = String::from_utf8(content) { - if content.is_empty() { - // ctx.set_text may crash if content is empty - return; +fn update_clipboard_(clipboard: Clipboard, old: Option>>) { + let content = ClipboardData::from_msg(clipboard); + if content.is_empty() { + return; + } + match ClipboardContext::new(false) { + Ok(mut ctx) => { + let side = if old.is_none() { "host" } else { "client" }; + let old = if let Some(old) = old { + old + } else { + CONTENT.clone() + }; + *old.lock().unwrap() = content.clone(); + let _lock = ARBOARD_MTX.lock().unwrap(); + allow_err!(ctx.set(content)); + log::debug!("{} updated on {}", CLIPBOARD_NAME, side); } - match ClipboardContext::new() { - Ok(mut ctx) => { - let side = if old.is_none() { "host" } else { "client" }; - let old = if let Some(old) = old { old } else { CONTENT.clone() }; - *old.lock().unwrap() = content.clone(); - let _lock = ARBOARD_MTX.lock().unwrap(); - allow_err!(ctx.set_text(content)); - log::debug!("{} updated on {}", CLIPBOARD_NAME, side); - } - Err(err) => { - log::error!("Failed to create clipboard context: {}", err); - } + Err(err) => { + log::error!("Failed to create clipboard context: {}", err); } } } #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn update_clipboard(clipboard: Clipboard, old: Option>>) { +pub fn update_clipboard(clipboard: Clipboard, old: Option>>) { std::thread::spawn(move || { update_clipboard_(clipboard, old); }); @@ -1510,57 +1503,236 @@ pub fn rustdesk_interval(i: Interval) -> ThrottledInterval { ThrottledInterval::new(i) } +#[derive(Clone)] +pub enum ClipboardData { + Text(String), + Image(arboard::ImageData<'static>, u64), + Empty, +} + +impl Default for ClipboardData { + fn default() -> Self { + ClipboardData::Empty + } +} + +impl ClipboardData { + fn image(image: arboard::ImageData<'static>) -> ClipboardData { + let hash = 0; + /* + use std::hash::{DefaultHasher, Hash, Hasher}; + let mut hasher = DefaultHasher::new(); + image.bytes.hash(&mut hasher); + let hash = hasher.finish(); + */ + ClipboardData::Image(image, hash) + } + + pub fn is_empty(&self) -> bool { + match self { + ClipboardData::Empty => true, + ClipboardData::Text(s) => s.is_empty(), + ClipboardData::Image(a, _) => a.bytes.is_empty(), + _ => false, + } + } + + fn from_msg(clipboard: Clipboard) -> Self { + let data = if clipboard.compress { + decompress(&clipboard.content) + } else { + clipboard.content.into() + }; + if clipboard.width > 0 && clipboard.height > 0 { + ClipboardData::Image( + arboard::ImageData { + bytes: data.into(), + width: clipboard.width as _, + height: clipboard.height as _, + }, + 0, + ) + } else { + if let Ok(content) = String::from_utf8(data) { + ClipboardData::Text(content) + } else { + ClipboardData::Empty + } + } + } + + pub fn create_msg(&self) -> Message { + let mut msg = Message::new(); + + match self { + ClipboardData::Text(s) => { + let compressed = compress_func(s.as_bytes()); + let compress = compressed.len() < s.as_bytes().len(); + let content = if compress { + compressed + } else { + s.clone().into_bytes() + }; + msg.set_clipboard(Clipboard { + compress, + content: content.into(), + ..Default::default() + }); + } + ClipboardData::Image(a, _) => { + let compressed = compress_func(&a.bytes); + let compress = compressed.len() < a.bytes.len(); + let content = if compress { + compressed + } else { + a.bytes.to_vec() + }; + msg.set_clipboard(Clipboard { + compress, + content: content.into(), + width: a.width as _, + height: a.height as _, + ..Default::default() + }); + } + _ => {} + } + msg + } +} + +impl PartialEq for ClipboardData { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, + (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => { + a.width == b.width && a.height == b.height && a.bytes == b.bytes + } + (ClipboardData::Empty, ClipboardData::Empty) => true, + _ => false, + } + } +} + #[cfg(not(any( target_os = "android", target_os = "ios", all(target_os = "linux", feature = "unix-file-copy-paste") )))] -pub struct ClipboardContext(arboard::Clipboard); +pub struct ClipboardContext(arboard::Clipboard, (Arc, u64), Option); #[cfg(not(any( target_os = "android", target_os = "ios", all(target_os = "linux", feature = "unix-file-copy-paste") )))] +#[allow(unreachable_code)] impl ClipboardContext { - #[inline] - #[cfg(any(target_os = "windows", target_os = "macos"))] - pub fn new() -> ResultType { - Ok(ClipboardContext(arboard::Clipboard::new()?)) - } - - #[cfg(target_os = "linux")] - pub fn new() -> ResultType { - let mut i = 1; - loop { - // Try 5 times to create clipboard - // Arboard::new() connect to X server or Wayland compositor, which shoud be ok at most time - // But sometimes, the connection may fail, so we retry here. - match arboard::Clipboard::new() { - Ok(x) => return Ok(ClipboardContext(x)), - Err(e) => { - if i == 5 { - return Err(e.into()); - } else { - std::thread::sleep(std::time::Duration::from_millis(30 * i)); + pub fn new(listen: bool) -> ResultType { + let board; + #[cfg(not(target_os = "linux"))] + { + board = arboard::Clipboard::new()?; + } + #[cfg(target_os = "linux")] + { + let mut i = 1; + loop { + // Try 5 times to create clipboard + // Arboard::new() connect to X server or Wayland compositor, which shoud be ok at most time + // But sometimes, the connection may fail, so we retry here. + match arboard::Clipboard::new() { + Ok(x) => { + board = x; + break; + } + Err(e) => { + if i == 5 { + return Err(e.into()); + } else { + std::thread::sleep(std::time::Duration::from_millis(30 * i)); + } } } + i += 1; } - i += 1; } - } - pub fn get_text(&mut self) -> ResultType { - Ok(self.0.get_text()?) + let change_count: Arc = Default::default(); + let mut shutdown = None; + if listen { + struct Handler(Arc); + impl ClipboardHandler for Handler { + fn on_clipboard_change(&mut self) -> CallbackResult { + self.0.fetch_add(1, Ordering::SeqCst); + CallbackResult::Next + } + + fn on_clipboard_error(&mut self, error: std::io::Error) -> CallbackResult { + log::trace!("Error of clipboard listener: {}", error); + CallbackResult::Next + } + } + match Master::new(Handler(change_count.clone())) { + Ok(master) => { + let mut master = master; + shutdown = Some(master.shutdown_channel()); + std::thread::spawn(move || { + log::debug!("Clipboard listener started"); + if let Err(err) = master.run() { + log::error!("Failed to run clipboard listener: {}", err); + } else { + log::debug!("Clipboard listener stopped"); + } + }); + } + Err(err) => { + log::error!("Failed to create clipboard listener: {}", err); + } + } + } + Ok(ClipboardContext(board, (change_count, 0), shutdown)) } #[inline] - pub fn set_text<'a, T: Into>>(&mut self, text: T) -> ResultType<()> { - self.0.set_text(text)?; + pub fn change_count(&self) -> u64 { + debug_assert!(self.2.is_some()); + self.1 .0.load(Ordering::SeqCst) + } + + pub fn get(&mut self) -> ResultType { + let cn = self.change_count(); + // only for image for the time being, + // because I do not want to change behavior of text clipboard for the time being + if cn != self.1 .1 { + self.1 .1 = cn; + if let Ok(image) = self.0.get_image() { + if image.width > 0 && image.height > 0 { + return Ok(ClipboardData::image(image)); + } + } + } + Ok(ClipboardData::Text(self.0.get_text()?)) + } + + fn set(&mut self, data: ClipboardData) -> ResultType<()> { + match data { + ClipboardData::Text(s) => self.0.set_text(s)?, + ClipboardData::Image(a, _) => self.0.set_image(a)?, + _ => {} + } Ok(()) } } +impl Drop for ClipboardContext { + fn drop(&mut self) { + if let Some(shutdown) = self.2.take() { + let _ = shutdown.signal(); + } + } +} + pub fn load_custom_client() { #[cfg(debug_assertions)] if let Ok(data) = std::fs::read_to_string("./custom.txt") { diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index 34e1635eb85..ab4952af983 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -10,7 +10,7 @@ struct State { impl Default for State { fn default() -> Self { - let ctx = match ClipboardContext::new() { + let ctx = match ClipboardContext::new(true) { Ok(ctx) => Some(ctx), Err(err) => { log::error!("Failed to start {}: {}", NAME, err); @@ -38,9 +38,9 @@ fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> { sp.send(msg); } sp.snapshot(|sps| { - let txt = crate::CONTENT.lock().unwrap().clone(); - if !txt.is_empty() { - let msg_out = crate::create_clipboard_msg(txt); + let data = crate::CONTENT.lock().unwrap().clone(); + if !data.is_empty() { + let msg_out = data.create_msg(); sps.send_shared(Arc::new(msg_out)); } Ok(()) From 84b5cd70ed9a2748893564b15cf3071ecf9b323e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 30 Jun 2024 23:27:33 +0800 Subject: [PATCH 176/335] initialize change_count to 1 so that first data always got --- src/common.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/common.rs b/src/common.rs index eb2a72ee98b..d4dde6ffbab 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, collections::HashMap, future::Future, sync::{ @@ -10,7 +9,6 @@ use std::{ }; use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; -use scrap::libc::RUSAGE_SELF; use serde_json::Value; #[derive(Debug, Eq, PartialEq)] @@ -293,10 +291,7 @@ pub fn check_clipboard( } else { CONTENT.clone() }; - let content = { - let _lock = ARBOARD_MTX.lock().unwrap(); - ctx2.get() - }; + let content = ctx2.get(); if let Ok(content) = content { if !content.is_empty() { let changed = content != *old.lock().unwrap(); @@ -373,9 +368,8 @@ fn update_clipboard_(clipboard: Clipboard, old: Option> } else { CONTENT.clone() }; - *old.lock().unwrap() = content.clone(); - let _lock = ARBOARD_MTX.lock().unwrap(); - allow_err!(ctx.set(content)); + allow_err!(ctx.set(&content)); + *old.lock().unwrap() = content; log::debug!("{} updated on {}", CLIPBOARD_NAME, side); } Err(err) => { @@ -1658,7 +1652,8 @@ impl ClipboardContext { } } - let change_count: Arc = Default::default(); + // starting from 1 so that we can always get initial clipboard data no matter if change + let change_count: Arc = Arc::new(AtomicU64::new(1)); let mut shutdown = None; if listen { struct Handler(Arc); @@ -1702,6 +1697,7 @@ impl ClipboardContext { pub fn get(&mut self) -> ResultType { let cn = self.change_count(); + let _lock = ARBOARD_MTX.lock().unwrap(); // only for image for the time being, // because I do not want to change behavior of text clipboard for the time being if cn != self.1 .1 { @@ -1715,10 +1711,11 @@ impl ClipboardContext { Ok(ClipboardData::Text(self.0.get_text()?)) } - fn set(&mut self, data: ClipboardData) -> ResultType<()> { + fn set(&mut self, data: &ClipboardData) -> ResultType<()> { + let _lock = ARBOARD_MTX.lock().unwrap(); match data { ClipboardData::Text(s) => self.0.set_text(s)?, - ClipboardData::Image(a, _) => self.0.set_image(a)?, + ClipboardData::Image(a, _) => self.0.set_image(a.clone())?, _ => {} } Ok(()) From cd73368cb9ff454151e4b3d86ee8611085e32e71 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 1 Jul 2024 00:24:23 +0800 Subject: [PATCH 177/335] make clipboard dropped after no sub --- src/server/clipboard_service.rs | 18 +++++++++--------- src/server/service.rs | 8 +++++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index ab4952af983..3a782d91955 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -4,12 +4,18 @@ pub use crate::common::{ CONTENT, }; +#[derive(Default)] struct State { ctx: Option, } -impl Default for State { - fn default() -> Self { +impl super::service::Reset for State { + fn reset(&mut self) { + *CONTENT.lock().unwrap() = Default::default(); + self.ctx = None; + } + + fn init(&mut self) { let ctx = match ClipboardContext::new(true) { Ok(ctx) => Some(ctx), Err(err) => { @@ -17,13 +23,7 @@ impl Default for State { None } }; - Self { ctx } - } -} - -impl super::service::Reset for State { - fn reset(&mut self) { - *CONTENT.lock().unwrap() = Default::default(); + self.ctx = ctx; } } diff --git a/src/server/service.rs b/src/server/service.rs index 63c5a89d910..dac975478cb 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -35,6 +35,7 @@ pub struct ServiceInner> { pub trait Reset { fn reset(&mut self); + fn init(&mut self) {} } pub struct ServiceTmpl>(Arc>>); @@ -266,15 +267,16 @@ impl> ServiceTmpl { while sp.active() { let now = time::Instant::now(); if sp.has_subscribes() { + if !may_reset { + may_reset = true; + state.init(); + } if let Err(err) = callback(sp.clone(), &mut state) { log::error!("Error of {} service: {}", sp.name(), err); thread::sleep(time::Duration::from_millis(MAX_ERROR_TIMEOUT)); #[cfg(windows)] crate::platform::windows::try_change_desktop(); } - if !may_reset { - may_reset = true; - } } else if may_reset { state.reset(); may_reset = false; From 3f2dfa521c655dbf98b1e2622b864402f258474a Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 1 Jul 2024 01:43:16 +0800 Subject: [PATCH 178/335] fix ci --- Cargo.lock | 2 +- src/common.rs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5449fad9141..e1f8b39b523 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -974,7 +974,7 @@ dependencies = [ [[package]] name = "clipboard-master" version = "4.0.0-beta.6" -source = "git+https://github.com/rustdesk-org/clipboard-master#38c0a5c0e5e0cab48abf1209900e3543487fc474" +source = "git+https://github.com/rustdesk-org/clipboard-master#5268c7b3d7728699566ad863da0911f249706f8c" dependencies = [ "objc", "objc-foundation", diff --git a/src/common.rs b/src/common.rs index d4dde6ffbab..c3048f31472 100644 --- a/src/common.rs +++ b/src/common.rs @@ -8,6 +8,7 @@ use std::{ task::Poll, }; +#[cfg(not(any(target_os = "android", target_os = "ios")))] use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; use serde_json::Value; @@ -186,6 +187,7 @@ pub mod input { } lazy_static::lazy_static! { + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub static ref CONTENT: Arc> = Default::default(); pub static ref SOFTWARE_UPDATE_URL: Arc> = Default::default(); } @@ -1497,6 +1499,7 @@ pub fn rustdesk_interval(i: Interval) -> ThrottledInterval { ThrottledInterval::new(i) } +#[cfg(not(any(target_os = "android", target_os = "ios")))] #[derive(Clone)] pub enum ClipboardData { Text(String), @@ -1504,12 +1507,14 @@ pub enum ClipboardData { Empty, } +#[cfg(not(any(target_os = "android", target_os = "ios")))] impl Default for ClipboardData { fn default() -> Self { ClipboardData::Empty } } +#[cfg(not(any(target_os = "android", target_os = "ios")))] impl ClipboardData { fn image(image: arboard::ImageData<'static>) -> ClipboardData { let hash = 0; @@ -1595,6 +1600,7 @@ impl ClipboardData { } } +#[cfg(not(any(target_os = "android", target_os = "ios")))] impl PartialEq for ClipboardData { fn eq(&self, other: &Self) -> bool { match (self, other) { From 14343e89d4ed0efaa1e4a0e0834ef933d0ff8e22 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 1 Jul 2024 01:52:39 +0800 Subject: [PATCH 179/335] fix ci --- src/common.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/common.rs b/src/common.rs index c3048f31472..6e90a73c1da 100644 --- a/src/common.rs +++ b/src/common.rs @@ -186,13 +186,13 @@ pub mod input { pub const MOUSE_BUTTON_FORWARD: i32 = 0x10; } +#[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { - #[cfg(not(any(target_os = "android", target_os = "ios")))] pub static ref CONTENT: Arc> = Default::default(); - pub static ref SOFTWARE_UPDATE_URL: Arc> = Default::default(); } lazy_static::lazy_static! { + pub static ref SOFTWARE_UPDATE_URL: Arc> = Default::default(); pub static ref DEVICE_ID: Arc> = Default::default(); pub static ref DEVICE_NAME: Arc> = Default::default(); } @@ -1728,6 +1728,7 @@ impl ClipboardContext { } } +#[cfg(not(any(target_os = "android", target_os = "ios")))] impl Drop for ClipboardContext { fn drop(&mut self) { if let Some(shutdown) = self.2.take() { From e71d86c124e68a9fc04d48a170d51b29a65fbb04 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 1 Jul 2024 02:14:58 +0800 Subject: [PATCH 180/335] move clipboard in common.rs to clipboard.rs --- src/client.rs | 8 +- src/client/io_loop.rs | 6 +- src/clipboard.rs | 409 +++++++++++++++++++++++++++++ src/common.rs | 448 +------------------------------- src/lib.rs | 2 + src/server/clipboard_service.rs | 4 +- src/server/connection.rs | 2 +- 7 files changed, 432 insertions(+), 447 deletions(-) create mode 100644 src/clipboard.rs diff --git a/src/client.rs b/src/client.rs index 3a59b4b4bdb..c3fe10688cf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -64,11 +64,11 @@ use crate::{ ui_session_interface::{InvokeUiSession, Session}, }; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::clipboard::{check_clipboard, CLIPBOARD_INTERVAL}; #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::ui_session_interface::SessionPermissionConfig; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::{check_clipboard, CLIPBOARD_INTERVAL}; pub use super::lang::*; @@ -136,7 +136,7 @@ lazy_static::lazy_static! { #[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { static ref ENIGO: Arc> = Arc::new(Mutex::new(enigo::Enigo::new())); - static ref OLD_CLIPBOARD_DATA: Arc> = Default::default(); + static ref OLD_CLIPBOARD_DATA: Arc> = Default::default(); static ref TEXT_CLIPBOARD_STATE: Arc> = Arc::new(Mutex::new(TextClipboardState::new())); } @@ -144,7 +144,7 @@ const PUBLIC_SERVER: &str = "public"; #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn get_old_clipboard_text() -> Arc> { +pub fn get_old_clipboard_text() -> Arc> { OLD_CLIPBOARD_DATA.clone() } diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 98dcaaad7c9..19074bbd1a0 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -41,7 +41,7 @@ use crate::client::{ 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}; +use crate::clipboard::{update_clipboard, CLIPBOARD_INTERVAL}; use crate::common::{get_default_sound_input, set_sound_input}; use crate::ui_session_interface::{InvokeUiSession, Session}; #[cfg(not(any(target_os = "ios")))] @@ -1135,7 +1135,7 @@ impl Remote { // To make sure current text clipboard data is updated. #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(mut rx) = rx { - timeout(common::CLIPBOARD_INTERVAL, rx.recv()).await.ok(); + timeout(CLIPBOARD_INTERVAL, rx.recv()).await.ok(); } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -1144,7 +1144,7 @@ impl Remote { let permission_config = self.handler.get_permission_config(); tokio::spawn(async move { // due to clipboard service interval time - sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; + sleep(CLIPBOARD_INTERVAL as f32 / 1_000.).await; if permission_config.is_text_clipboard_required() { sender.send(Data::Message(msg_out)).ok(); } diff --git a/src/clipboard.rs b/src/clipboard.rs new file mode 100644 index 00000000000..f86dc355c12 --- /dev/null +++ b/src/clipboard.rs @@ -0,0 +1,409 @@ +use std::sync::{ + atomic::{AtomicU64, Ordering}, + Arc, Mutex, +}; + +use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; +use hbb_common::{ + ResultType, + allow_err, + compress::{compress as compress_func, decompress}, + log, + message_proto::*, +}; + +pub const CLIPBOARD_NAME: &'static str = "clipboard"; +pub const CLIPBOARD_INTERVAL: u64 = 333; + +lazy_static::lazy_static! { + pub static ref CONTENT: Arc> = Default::default(); + static ref ARBOARD_MTX: Arc> = Arc::new(Mutex::new(())); +} + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +static X11_CLIPBOARD: once_cell::sync::OnceCell = + once_cell::sync::OnceCell::new(); + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +fn get_clipboard() -> Result<&'static x11_clipboard::Clipboard, String> { + X11_CLIPBOARD + .get_or_try_init(|| x11_clipboard::Clipboard::new()) + .map_err(|e| e.to_string()) +} + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +pub struct ClipboardContext { + string_setter: x11rb::protocol::xproto::Atom, + string_getter: x11rb::protocol::xproto::Atom, + text_uri_list: x11rb::protocol::xproto::Atom, + + clip: x11rb::protocol::xproto::Atom, + prop: x11rb::protocol::xproto::Atom, +} + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +fn parse_plain_uri_list(v: Vec) -> Result { + let text = String::from_utf8(v).map_err(|_| "ConversionFailure".to_owned())?; + let mut list = String::new(); + for line in text.lines() { + if !line.starts_with("file://") { + continue; + } + let decoded = percent_encoding::percent_decode_str(line) + .decode_utf8() + .map_err(|_| "ConversionFailure".to_owned())?; + list = list + "\n" + decoded.trim_start_matches("file://"); + } + list = list.trim().to_owned(); + Ok(list) +} + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +impl ClipboardContext { + pub fn new() -> Result { + let clipboard = get_clipboard()?; + let string_getter = clipboard + .getter + .get_atom("UTF8_STRING") + .map_err(|e| e.to_string())?; + let string_setter = clipboard + .setter + .get_atom("UTF8_STRING") + .map_err(|e| e.to_string())?; + let text_uri_list = clipboard + .getter + .get_atom("text/uri-list") + .map_err(|e| e.to_string())?; + let prop = clipboard.getter.atoms.property; + let clip = clipboard.getter.atoms.clipboard; + Ok(Self { + text_uri_list, + string_setter, + string_getter, + clip, + prop, + }) + } + + pub fn get_text(&mut self) -> Result { + let clip = self.clip; + let prop = self.prop; + + const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(120); + + let text_content = get_clipboard()? + .load(clip, self.string_getter, prop, TIMEOUT) + .map_err(|e| e.to_string())?; + + let file_urls = get_clipboard()?.load(clip, self.text_uri_list, prop, TIMEOUT); + + if file_urls.is_err() || file_urls.as_ref().unwrap().is_empty() { + log::trace!("clipboard get text, no file urls"); + return String::from_utf8(text_content).map_err(|e| e.to_string()); + } + + let file_urls = parse_plain_uri_list(file_urls.unwrap())?; + + let text_content = String::from_utf8(text_content).map_err(|e| e.to_string())?; + + if text_content.trim() == file_urls.trim() { + log::trace!("clipboard got text but polluted"); + return Err(String::from("polluted text")); + } + + Ok(text_content) + } + + pub fn set_text(&mut self, content: String) -> Result<(), String> { + let clip = self.clip; + + let value = content.clone().into_bytes(); + get_clipboard()? + .store(clip, self.string_setter, value) + .map_err(|e| e.to_string())?; + Ok(()) + } +} + +pub fn check_clipboard( + ctx: &mut Option, + old: Option>>, +) -> Option { + if ctx.is_none() { + *ctx = ClipboardContext::new(true).ok(); + } + let ctx2 = ctx.as_mut()?; + let side = if old.is_none() { "host" } else { "client" }; + let old = if let Some(old) = old { + old + } else { + CONTENT.clone() + }; + let content = ctx2.get(); + if let Ok(content) = content { + if !content.is_empty() { + let changed = content != *old.lock().unwrap(); + if changed { + log::info!("{} update found on {}", CLIPBOARD_NAME, side); + let msg = content.create_msg(); + *old.lock().unwrap() = content; + return Some(msg); + } + } + } + None +} + +fn update_clipboard_(clipboard: Clipboard, old: Option>>) { + let content = ClipboardData::from_msg(clipboard); + if content.is_empty() { + return; + } + match ClipboardContext::new(false) { + Ok(mut ctx) => { + let side = if old.is_none() { "host" } else { "client" }; + let old = if let Some(old) = old { + old + } else { + CONTENT.clone() + }; + allow_err!(ctx.set(&content)); + *old.lock().unwrap() = content; + log::debug!("{} updated on {}", CLIPBOARD_NAME, side); + } + Err(err) => { + log::error!("Failed to create clipboard context: {}", err); + } + } +} + +pub fn update_clipboard(clipboard: Clipboard, old: Option>>) { + std::thread::spawn(move || { + update_clipboard_(clipboard, old); + }); +} + +#[derive(Clone)] +pub enum ClipboardData { + Text(String), + Image(arboard::ImageData<'static>, u64), + Empty, +} + +impl Default for ClipboardData { + fn default() -> Self { + ClipboardData::Empty + } +} + +impl ClipboardData { + fn image(image: arboard::ImageData<'static>) -> ClipboardData { + let hash = 0; + /* + use std::hash::{DefaultHasher, Hash, Hasher}; + let mut hasher = DefaultHasher::new(); + image.bytes.hash(&mut hasher); + let hash = hasher.finish(); + */ + ClipboardData::Image(image, hash) + } + + pub fn is_empty(&self) -> bool { + match self { + ClipboardData::Empty => true, + ClipboardData::Text(s) => s.is_empty(), + ClipboardData::Image(a, _) => a.bytes.is_empty(), + _ => false, + } + } + + fn from_msg(clipboard: Clipboard) -> Self { + let data = if clipboard.compress { + decompress(&clipboard.content) + } else { + clipboard.content.into() + }; + if clipboard.width > 0 && clipboard.height > 0 { + ClipboardData::Image( + arboard::ImageData { + bytes: data.into(), + width: clipboard.width as _, + height: clipboard.height as _, + }, + 0, + ) + } else { + if let Ok(content) = String::from_utf8(data) { + ClipboardData::Text(content) + } else { + ClipboardData::Empty + } + } + } + + pub fn create_msg(&self) -> Message { + let mut msg = Message::new(); + + match self { + ClipboardData::Text(s) => { + let compressed = compress_func(s.as_bytes()); + let compress = compressed.len() < s.as_bytes().len(); + let content = if compress { + compressed + } else { + s.clone().into_bytes() + }; + msg.set_clipboard(Clipboard { + compress, + content: content.into(), + ..Default::default() + }); + } + ClipboardData::Image(a, _) => { + let compressed = compress_func(&a.bytes); + let compress = compressed.len() < a.bytes.len(); + let content = if compress { + compressed + } else { + a.bytes.to_vec() + }; + msg.set_clipboard(Clipboard { + compress, + content: content.into(), + width: a.width as _, + height: a.height as _, + ..Default::default() + }); + } + _ => {} + } + msg + } +} + +impl PartialEq for ClipboardData { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, + (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => { + a.width == b.width && a.height == b.height && a.bytes == b.bytes + } + (ClipboardData::Empty, ClipboardData::Empty) => true, + _ => false, + } + } +} + +#[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] +pub struct ClipboardContext(arboard::Clipboard, (Arc, u64), Option); + +#[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] +#[allow(unreachable_code)] +impl ClipboardContext { + pub fn new(listen: bool) -> ResultType { + let board; + #[cfg(not(target_os = "linux"))] + { + board = arboard::Clipboard::new()?; + } + #[cfg(target_os = "linux")] + { + let mut i = 1; + loop { + // Try 5 times to create clipboard + // Arboard::new() connect to X server or Wayland compositor, which shoud be ok at most time + // But sometimes, the connection may fail, so we retry here. + match arboard::Clipboard::new() { + Ok(x) => { + board = x; + break; + } + Err(e) => { + if i == 5 { + return Err(e.into()); + } else { + std::thread::sleep(std::time::Duration::from_millis(30 * i)); + } + } + } + i += 1; + } + } + + // starting from 1 so that we can always get initial clipboard data no matter if change + let change_count: Arc = Arc::new(AtomicU64::new(1)); + let mut shutdown = None; + if listen { + struct Handler(Arc); + impl ClipboardHandler for Handler { + fn on_clipboard_change(&mut self) -> CallbackResult { + self.0.fetch_add(1, Ordering::SeqCst); + CallbackResult::Next + } + + fn on_clipboard_error(&mut self, error: std::io::Error) -> CallbackResult { + log::trace!("Error of clipboard listener: {}", error); + CallbackResult::Next + } + } + match Master::new(Handler(change_count.clone())) { + Ok(master) => { + let mut master = master; + shutdown = Some(master.shutdown_channel()); + std::thread::spawn(move || { + log::debug!("Clipboard listener started"); + if let Err(err) = master.run() { + log::error!("Failed to run clipboard listener: {}", err); + } else { + log::debug!("Clipboard listener stopped"); + } + }); + } + Err(err) => { + log::error!("Failed to create clipboard listener: {}", err); + } + } + } + Ok(ClipboardContext(board, (change_count, 0), shutdown)) + } + + #[inline] + pub fn change_count(&self) -> u64 { + debug_assert!(self.2.is_some()); + self.1 .0.load(Ordering::SeqCst) + } + + pub fn get(&mut self) -> ResultType { + let cn = self.change_count(); + let _lock = ARBOARD_MTX.lock().unwrap(); + // only for image for the time being, + // because I do not want to change behavior of text clipboard for the time being + if cn != self.1 .1 { + self.1 .1 = cn; + if let Ok(image) = self.0.get_image() { + if image.width > 0 && image.height > 0 { + return Ok(ClipboardData::image(image)); + } + } + } + Ok(ClipboardData::Text(self.0.get_text()?)) + } + + fn set(&mut self, data: &ClipboardData) -> ResultType<()> { + let _lock = ARBOARD_MTX.lock().unwrap(); + match data { + ClipboardData::Text(s) => self.0.set_text(s)?, + ClipboardData::Image(a, _) => self.0.set_image(a.clone())?, + _ => {} + } + Ok(()) + } +} + +impl Drop for ClipboardContext { + fn drop(&mut self) { + if let Some(shutdown) = self.2.take() { + let _ = shutdown.signal(); + } + } +} diff --git a/src/common.rs b/src/common.rs index 6e90a73c1da..fcc65834b09 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,139 +1,19 @@ use std::{ collections::HashMap, future::Future, - sync::{ - atomic::{AtomicU64, Ordering}, - Arc, Mutex, RwLock, - }, + sync::{Arc, Mutex, RwLock}, task::Poll, }; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; use serde_json::Value; -#[derive(Debug, Eq, PartialEq)] -pub enum GrabState { - Ready, - Run, - Wait, - Exit, -} - -#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] -static X11_CLIPBOARD: once_cell::sync::OnceCell = - once_cell::sync::OnceCell::new(); - -#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] -fn get_clipboard() -> Result<&'static x11_clipboard::Clipboard, String> { - X11_CLIPBOARD - .get_or_try_init(|| x11_clipboard::Clipboard::new()) - .map_err(|e| e.to_string()) -} - -#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] -pub struct ClipboardContext { - string_setter: x11rb::protocol::xproto::Atom, - string_getter: x11rb::protocol::xproto::Atom, - text_uri_list: x11rb::protocol::xproto::Atom, - - clip: x11rb::protocol::xproto::Atom, - prop: x11rb::protocol::xproto::Atom, -} - -#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] -fn parse_plain_uri_list(v: Vec) -> Result { - let text = String::from_utf8(v).map_err(|_| "ConversionFailure".to_owned())?; - let mut list = String::new(); - for line in text.lines() { - if !line.starts_with("file://") { - continue; - } - let decoded = percent_encoding::percent_decode_str(line) - .decode_utf8() - .map_err(|_| "ConversionFailure".to_owned())?; - list = list + "\n" + decoded.trim_start_matches("file://"); - } - list = list.trim().to_owned(); - Ok(list) -} - -#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] -impl ClipboardContext { - pub fn new() -> Result { - let clipboard = get_clipboard()?; - let string_getter = clipboard - .getter - .get_atom("UTF8_STRING") - .map_err(|e| e.to_string())?; - let string_setter = clipboard - .setter - .get_atom("UTF8_STRING") - .map_err(|e| e.to_string())?; - let text_uri_list = clipboard - .getter - .get_atom("text/uri-list") - .map_err(|e| e.to_string())?; - let prop = clipboard.getter.atoms.property; - let clip = clipboard.getter.atoms.clipboard; - Ok(Self { - text_uri_list, - string_setter, - string_getter, - clip, - prop, - }) - } - - pub fn get_text(&mut self) -> Result { - let clip = self.clip; - let prop = self.prop; - - const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(120); - - let text_content = get_clipboard()? - .load(clip, self.string_getter, prop, TIMEOUT) - .map_err(|e| e.to_string())?; - - let file_urls = get_clipboard()?.load(clip, self.text_uri_list, prop, TIMEOUT); - - if file_urls.is_err() || file_urls.as_ref().unwrap().is_empty() { - log::trace!("clipboard get text, no file urls"); - return String::from_utf8(text_content).map_err(|e| e.to_string()); - } - - let file_urls = parse_plain_uri_list(file_urls.unwrap())?; - - let text_content = String::from_utf8(text_content).map_err(|e| e.to_string())?; - - if text_content.trim() == file_urls.trim() { - log::trace!("clipboard got text but polluted"); - return Err(String::from("polluted text")); - } - - Ok(text_content) - } - - pub fn set_text(&mut self, content: String) -> Result<(), String> { - let clip = self.clip; - - let value = content.clone().into_bytes(); - get_clipboard()? - .store(clip, self.string_setter, value) - .map_err(|e| e.to_string())?; - Ok(()) - } -} - -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use hbb_common::compress::decompress; use hbb_common::{ allow_err, anyhow::{anyhow, Context}, bail, base64, bytes::Bytes, - compress::compress as compress_func, - config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT}, + config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT, RENDEZVOUS_PORT}, + futures::future::join_all, futures_util::future::poll_fn, get_version_number, log, message_proto::*, @@ -149,18 +29,21 @@ use hbb_common::{ }, ResultType, }; -// #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] -use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all}; use crate::{ hbbs_http::create_http_client_async, ui_interface::{get_option, set_option}, }; -pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future; +#[derive(Debug, Eq, PartialEq)] +pub enum GrabState { + Ready, + Run, + Wait, + Exit, +} -pub const CLIPBOARD_NAME: &'static str = "clipboard"; -pub const CLIPBOARD_INTERVAL: u64 = 333; +pub type NotifyMessageBox = fn(String, String, String, String) -> dyn Future; // the executable name of the portable version pub const PORTABLE_APPNAME_RUNTIME_ENV_KEY: &str = "RUSTDESK_APPNAME"; @@ -186,11 +69,6 @@ pub mod input { pub const MOUSE_BUTTON_FORWARD: i32 = 0x10; } -#[cfg(not(any(target_os = "android", target_os = "ios")))] -lazy_static::lazy_static! { - pub static ref CONTENT: Arc> = Default::default(); -} - lazy_static::lazy_static! { pub static ref SOFTWARE_UPDATE_URL: Arc> = Default::default(); pub static ref DEVICE_ID: Arc> = Default::default(); @@ -205,11 +83,6 @@ lazy_static::lazy_static! { static ref IS_MAIN: bool = std::env::args().nth(1).map_or(true, |arg| !arg.starts_with("--")); } -#[cfg(not(any(target_os = "android", target_os = "ios")))] -lazy_static::lazy_static! { - static ref ARBOARD_MTX: Arc> = Arc::new(Mutex::new(())); -} - pub struct SimpleCallOnReturn { pub b: bool, pub f: Box, @@ -278,36 +151,6 @@ pub fn valid_for_numlock(evt: &KeyEvent) -> bool { } } -#[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn check_clipboard( - ctx: &mut Option, - old: Option>>, -) -> Option { - if ctx.is_none() { - *ctx = ClipboardContext::new(true).ok(); - } - let ctx2 = ctx.as_mut()?; - let side = if old.is_none() { "host" } else { "client" }; - let old = if let Some(old) = old { - old - } else { - CONTENT.clone() - }; - let content = ctx2.get(); - if let Ok(content) = content { - if !content.is_empty() { - let changed = content != *old.lock().unwrap(); - if changed { - log::info!("{} update found on {}", CLIPBOARD_NAME, side); - let msg = content.create_msg(); - *old.lock().unwrap() = content; - return Some(msg); - } - } - } - None -} - /// Set sound input device. pub fn set_sound_input(device: String) { let prior_device = get_option("audio-input".to_owned()); @@ -356,37 +199,6 @@ pub fn get_default_sound_input() -> Option { None } -#[cfg(not(any(target_os = "android", target_os = "ios")))] -fn update_clipboard_(clipboard: Clipboard, old: Option>>) { - let content = ClipboardData::from_msg(clipboard); - if content.is_empty() { - return; - } - match ClipboardContext::new(false) { - Ok(mut ctx) => { - let side = if old.is_none() { "host" } else { "client" }; - let old = if let Some(old) = old { - old - } else { - CONTENT.clone() - }; - allow_err!(ctx.set(&content)); - *old.lock().unwrap() = content; - log::debug!("{} updated on {}", CLIPBOARD_NAME, side); - } - Err(err) => { - log::error!("Failed to create clipboard context: {}", err); - } - } -} - -#[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn update_clipboard(clipboard: Clipboard, old: Option>>) { - std::thread::spawn(move || { - update_clipboard_(clipboard, old); - }); -} - #[cfg(feature = "use_rubato")] pub fn resample_channels( data: &[f32], @@ -1499,244 +1311,6 @@ pub fn rustdesk_interval(i: Interval) -> ThrottledInterval { ThrottledInterval::new(i) } -#[cfg(not(any(target_os = "android", target_os = "ios")))] -#[derive(Clone)] -pub enum ClipboardData { - Text(String), - Image(arboard::ImageData<'static>, u64), - Empty, -} - -#[cfg(not(any(target_os = "android", target_os = "ios")))] -impl Default for ClipboardData { - fn default() -> Self { - ClipboardData::Empty - } -} - -#[cfg(not(any(target_os = "android", target_os = "ios")))] -impl ClipboardData { - fn image(image: arboard::ImageData<'static>) -> ClipboardData { - let hash = 0; - /* - use std::hash::{DefaultHasher, Hash, Hasher}; - let mut hasher = DefaultHasher::new(); - image.bytes.hash(&mut hasher); - let hash = hasher.finish(); - */ - ClipboardData::Image(image, hash) - } - - pub fn is_empty(&self) -> bool { - match self { - ClipboardData::Empty => true, - ClipboardData::Text(s) => s.is_empty(), - ClipboardData::Image(a, _) => a.bytes.is_empty(), - _ => false, - } - } - - fn from_msg(clipboard: Clipboard) -> Self { - let data = if clipboard.compress { - decompress(&clipboard.content) - } else { - clipboard.content.into() - }; - if clipboard.width > 0 && clipboard.height > 0 { - ClipboardData::Image( - arboard::ImageData { - bytes: data.into(), - width: clipboard.width as _, - height: clipboard.height as _, - }, - 0, - ) - } else { - if let Ok(content) = String::from_utf8(data) { - ClipboardData::Text(content) - } else { - ClipboardData::Empty - } - } - } - - pub fn create_msg(&self) -> Message { - let mut msg = Message::new(); - - match self { - ClipboardData::Text(s) => { - let compressed = compress_func(s.as_bytes()); - let compress = compressed.len() < s.as_bytes().len(); - let content = if compress { - compressed - } else { - s.clone().into_bytes() - }; - msg.set_clipboard(Clipboard { - compress, - content: content.into(), - ..Default::default() - }); - } - ClipboardData::Image(a, _) => { - let compressed = compress_func(&a.bytes); - let compress = compressed.len() < a.bytes.len(); - let content = if compress { - compressed - } else { - a.bytes.to_vec() - }; - msg.set_clipboard(Clipboard { - compress, - content: content.into(), - width: a.width as _, - height: a.height as _, - ..Default::default() - }); - } - _ => {} - } - msg - } -} - -#[cfg(not(any(target_os = "android", target_os = "ios")))] -impl PartialEq for ClipboardData { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, - (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => { - a.width == b.width && a.height == b.height && a.bytes == b.bytes - } - (ClipboardData::Empty, ClipboardData::Empty) => true, - _ => false, - } - } -} - -#[cfg(not(any( - target_os = "android", - target_os = "ios", - all(target_os = "linux", feature = "unix-file-copy-paste") -)))] -pub struct ClipboardContext(arboard::Clipboard, (Arc, u64), Option); - -#[cfg(not(any( - target_os = "android", - target_os = "ios", - all(target_os = "linux", feature = "unix-file-copy-paste") -)))] -#[allow(unreachable_code)] -impl ClipboardContext { - pub fn new(listen: bool) -> ResultType { - let board; - #[cfg(not(target_os = "linux"))] - { - board = arboard::Clipboard::new()?; - } - #[cfg(target_os = "linux")] - { - let mut i = 1; - loop { - // Try 5 times to create clipboard - // Arboard::new() connect to X server or Wayland compositor, which shoud be ok at most time - // But sometimes, the connection may fail, so we retry here. - match arboard::Clipboard::new() { - Ok(x) => { - board = x; - break; - } - Err(e) => { - if i == 5 { - return Err(e.into()); - } else { - std::thread::sleep(std::time::Duration::from_millis(30 * i)); - } - } - } - i += 1; - } - } - - // starting from 1 so that we can always get initial clipboard data no matter if change - let change_count: Arc = Arc::new(AtomicU64::new(1)); - let mut shutdown = None; - if listen { - struct Handler(Arc); - impl ClipboardHandler for Handler { - fn on_clipboard_change(&mut self) -> CallbackResult { - self.0.fetch_add(1, Ordering::SeqCst); - CallbackResult::Next - } - - fn on_clipboard_error(&mut self, error: std::io::Error) -> CallbackResult { - log::trace!("Error of clipboard listener: {}", error); - CallbackResult::Next - } - } - match Master::new(Handler(change_count.clone())) { - Ok(master) => { - let mut master = master; - shutdown = Some(master.shutdown_channel()); - std::thread::spawn(move || { - log::debug!("Clipboard listener started"); - if let Err(err) = master.run() { - log::error!("Failed to run clipboard listener: {}", err); - } else { - log::debug!("Clipboard listener stopped"); - } - }); - } - Err(err) => { - log::error!("Failed to create clipboard listener: {}", err); - } - } - } - Ok(ClipboardContext(board, (change_count, 0), shutdown)) - } - - #[inline] - pub fn change_count(&self) -> u64 { - debug_assert!(self.2.is_some()); - self.1 .0.load(Ordering::SeqCst) - } - - pub fn get(&mut self) -> ResultType { - let cn = self.change_count(); - let _lock = ARBOARD_MTX.lock().unwrap(); - // only for image for the time being, - // because I do not want to change behavior of text clipboard for the time being - if cn != self.1 .1 { - self.1 .1 = cn; - if let Ok(image) = self.0.get_image() { - if image.width > 0 && image.height > 0 { - return Ok(ClipboardData::image(image)); - } - } - } - Ok(ClipboardData::Text(self.0.get_text()?)) - } - - fn set(&mut self, data: &ClipboardData) -> ResultType<()> { - let _lock = ARBOARD_MTX.lock().unwrap(); - match data { - ClipboardData::Text(s) => self.0.set_text(s)?, - ClipboardData::Image(a, _) => self.0.set_image(a.clone())?, - _ => {} - } - Ok(()) - } -} - -#[cfg(not(any(target_os = "android", target_os = "ios")))] -impl Drop for ClipboardContext { - fn drop(&mut self) { - if let Some(shutdown) = self.2.take() { - let _ = shutdown.signal(); - } - } -} - pub fn load_custom_client() { #[cfg(debug_assertions)] if let Ok(data) = std::fs::read_to_string("./custom.txt") { diff --git a/src/lib.rs b/src/lib.rs index 1794dff9dae..f8d917a518e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,8 @@ mod custom_server; mod lang; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +mod clipboard; #[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index 3a782d91955..eeeea4999c6 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -1,5 +1,5 @@ use super::*; -pub use crate::common::{ +pub use crate::clipboard::{ check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME, CONTENT, }; @@ -38,7 +38,7 @@ fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> { sp.send(msg); } sp.snapshot(|sps| { - let data = crate::CONTENT.lock().unwrap().clone(); + let data = CONTENT.lock().unwrap().clone(); if !data.is_empty() { let msg_out = data.create_msg(); sps.send_shared(Arc::new(msg_out)); diff --git a/src/server/connection.rs b/src/server/connection.rs index 30261c8be5b..b45da70523b 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2,7 +2,7 @@ use super::{input_service::*, *}; #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::common::update_clipboard; +use crate::clipboard::update_clipboard; #[cfg(target_os = "android")] use crate::keyboard::client::map_key_to_control_key; #[cfg(target_os = "linux")] From 285e974d1a52c891d5fcc28e963d724e085558bc Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 1 Jul 2024 11:01:35 +0800 Subject: [PATCH 181/335] ci for linux x86_64 sciter deb and flatpak (#8545) Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 56 +++++++++++++++++++++-------- build.py | 6 ++-- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 692cb2a1d0f..5d508c19115 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1262,21 +1262,31 @@ jobs: files: | res/rustdesk-${{ env.VERSION }}*.zst - build-rustdesk-sciter-arm: + build-rustdesk-linux-sciter: if: ${{ inputs.upload-artifact }} needs: build-rustdesk-linux # not for dep, just make it run later for parallelism - runs-on: [self-hosted, Linux, ARM64] - name: build-rustdesk-sciter-arm ${{ matrix.job.target }} + runs-on: ${{ matrix.job.on }} + name: build-rustdesk-linux-sciter ${{ matrix.job.target }} strategy: fail-fast: false matrix: # use a high level qemu-user-static job: + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + on: ubuntu-20.04, + distro: ubuntu18.04, + deb_arch: amd64, + sciter_arch: x64, + } - { arch: armv7, target: armv7-unknown-linux-gnueabihf, - deb-arch: armhf, - use-cross: true, + on: [self-hosted, Linux, ARM64], + distro: ubuntu18.04-rustdesk, + deb_arch: armhf, + sciter_arch: arm32, } steps: - name: Export GitHub Actions cache environment variables @@ -1340,7 +1350,7 @@ jobs: id: vcpkg with: arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk + distro: ${{ matrix.job.distro }} githubToken: ${{ github.token }} setup: | ls -l "${PWD}" @@ -1406,13 +1416,13 @@ jobs: pushd /workspace python3 ./res/inline-sciter.py export VCPKG_ROOT=/opt/artifacts/vcpkg - export ARCH=armhf export CARGO_INCREMENTAL=0 cargo build --features inline --release --bins --jobs 1 # 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 + wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/${{ matrix.job.sciter_arch }}/libsciter-gtk.so + export DEB_ARCH=${{ matrix.job.deb_arch }} ./build.py --package ./Release - name: Rename rustdesk @@ -1432,6 +1442,13 @@ jobs: files: | rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb + - name: Upload deb + uses: actions/upload-artifact@master + if: env.UPLOAD_ARTIFACT == 'true' + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb + build-appimage: name: Build appimage ${{ matrix.job.target }} needs: [build-rustdesk-linux] @@ -1489,8 +1506,10 @@ jobs: ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage build-flatpak: - name: Build flatpak ${{ matrix.job.target }} - needs: [build-rustdesk-linux] + name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }} + needs: + - build-rustdesk-linux + - build-rustdesk-linux-sciter runs-on: ${{ matrix.job.on }} if: ${{ inputs.upload-artifact }} strategy: @@ -1502,6 +1521,14 @@ jobs: distro: ubuntu18.04, on: ubuntu-20.04, arch: x86_64, + suffix: "", + } + - { + target: x86_64-unknown-linux-gnu, + distro: ubuntu18.04, + on: ubuntu-20.04, + arch: x86_64, + suffix: "-sciter", } - { target: aarch64-unknown-linux-gnu, @@ -1509,6 +1536,7 @@ jobs: distro: ubuntu22.04, on: [self-hosted, Linux, ARM64], arch: aarch64, + suffix: "", } steps: - name: Checkout source code @@ -1517,12 +1545,12 @@ jobs: - name: Download Binary uses: actions/download-artifact@master with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.deb path: . - name: Rename Binary run: | - mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb + mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.deb flatpak/rustdesk.deb - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk flatpak package for ${{ matrix.job.arch }} @@ -1568,7 +1596,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.arch }}.flatpak com.rustdesk.RustDesk + flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.flatpak com.rustdesk.RustDesk - name: Publish flatpak package uses: softprops/action-gh-release@v1 @@ -1576,7 +1604,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak + flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.flatpak build-rustdesk-web: if: False diff --git a/build.py b/build.py index 18dc14ae196..38a8687be1b 100755 --- a/build.py +++ b/build.py @@ -25,8 +25,8 @@ skip_cargo = False -def get_arch() -> str: - custom_arch = os.environ.get("ARCH") +def get_deb_arch() -> str: + custom_arch = os.environ.get("DEB_ARCH") if custom_arch is None: return "amd64" return custom_arch @@ -294,7 +294,7 @@ def generate_control_file(version): Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g, libappindicator3-1, gstreamer1.0-pipewire Description: A remote control software. -""" % (version, get_arch()) +""" % (version, get_deb_arch()) file = open(control_file_path, "w") file.write(content) file.close() From 0ab500c27c7dba7865cb2426af639c14c3c2afcc Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 1 Jul 2024 16:49:56 +0800 Subject: [PATCH 182/335] bring fdroid.yml back --- .github/workflows/fdroid.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/fdroid.yml diff --git a/.github/workflows/fdroid.yml b/.github/workflows/fdroid.yml new file mode 100644 index 00000000000..49fb8c53e15 --- /dev/null +++ b/.github/workflows/fdroid.yml @@ -0,0 +1,33 @@ +name: Fdroid + +on: + workflow_dispatch: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+-[0-9]+' + +jobs: + # https://gitlab.com/fdroid/fdroiddata/-/blob/master/metadata/com.carriez.flutter_hbb.yml + # https://gitlab.com/basilgello/rustdesk-fdroid-versioner + update-fdroid-version-file: + name: Publish RustDesk version file for F-Droid updater + runs-on: ubuntu-latest + steps: + - name: Generate RustDesk version file + run: | + UPSTREAM_VERNAME="$GITHUB_REF_NAME" + UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr -d '.')" + echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt + echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt + shell: bash + + - name: Publish RustDesk version file + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: "fdroid-version" + files: | + ./rustdesk-version.txt From 62a834973939304742fd0429fd73f7f4d83abf8d Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 2 Jul 2024 00:18:38 +0800 Subject: [PATCH 183/335] fix: clipboard data, decompress, buf too small (#8556) * fix: clipboard data, decompress, buf too small Signed-off-by: fufesou * fix: compress image Signed-off-by: fufesou * decompress image, use default level Signed-off-by: fufesou * chore Signed-off-by: fufesou * decompress, zstd::decode_all Signed-off-by: fufesou --------- Signed-off-by: fufesou --- libs/hbb_common/src/compress.rs | 27 ++------------------------- src/clipboard.rs | 5 +++-- 2 files changed, 5 insertions(+), 27 deletions(-) diff --git a/libs/hbb_common/src/compress.rs b/libs/hbb_common/src/compress.rs index c52dd93a1d9..761d916e4f8 100644 --- a/libs/hbb_common/src/compress.rs +++ b/libs/hbb_common/src/compress.rs @@ -1,5 +1,5 @@ use std::{cell::RefCell, io}; -use zstd::bulk::{Compressor, Decompressor}; +use zstd::bulk::Compressor; // The library supports regular compression levels from 1 up to ZSTD_maxCLevel(), // which is currently 22. Levels >= 20 @@ -7,7 +7,6 @@ use zstd::bulk::{Compressor, Decompressor}; // value 0 means default, which is controlled by ZSTD_CLEVEL_DEFAULT thread_local! { static COMPRESSOR: RefCell>> = RefCell::new(Compressor::new(crate::config::COMPRESS_LEVEL)); - static DECOMPRESSOR: RefCell>> = RefCell::new(Decompressor::new()); } pub fn compress(data: &[u8]) -> Vec { @@ -31,27 +30,5 @@ pub fn compress(data: &[u8]) -> Vec { } pub fn decompress(data: &[u8]) -> Vec { - let mut out = Vec::new(); - DECOMPRESSOR.with(|d| { - if let Ok(mut d) = d.try_borrow_mut() { - match &mut *d { - Ok(d) => { - const MAX: usize = 1024 * 1024 * 64; - const MIN: usize = 1024 * 1024; - let mut n = 30 * data.len(); - n = n.clamp(MIN, MAX); - match d.decompress(data, n) { - Ok(res) => out = res, - Err(err) => { - crate::log::debug!("Failed to decompress: {}", err); - } - } - } - Err(err) => { - crate::log::debug!("Failed to get decompressor: {}", err); - } - } - } - }); - out + zstd::decode_all(data).unwrap_or_default() } diff --git a/src/clipboard.rs b/src/clipboard.rs index f86dc355c12..cdf7fa67d44 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -5,11 +5,11 @@ use std::sync::{ use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; use hbb_common::{ - ResultType, allow_err, compress::{compress as compress_func, decompress}, log, message_proto::*, + ResultType, }; pub const CLIPBOARD_NAME: &'static str = "clipboard"; @@ -218,12 +218,13 @@ impl ClipboardData { } fn from_msg(clipboard: Clipboard) -> Self { + let is_image = clipboard.width > 0 && clipboard.height > 0; let data = if clipboard.compress { decompress(&clipboard.content) } else { clipboard.content.into() }; - if clipboard.width > 0 && clipboard.height > 0 { + if is_image { ClipboardData::Image( arboard::ImageData { bytes: data.into(), From 625b610cfd6e081330f3108b6e8f5272c00b3ce4 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Mon, 1 Jul 2024 18:18:54 +0200 Subject: [PATCH 184/335] Update de.rs (#8559) --- src/lang/de.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 0fcf7b683d6..d41591b610a 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -619,13 +619,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "Wenn kontrolliert"), ("During service is on", "Wenn der Dienst läuft"), ("Capture screen using DirectX", "Bildschirm mit DirectX aufnehmen"), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), + ("Back", "Zurück"), + ("Apps", "Apps"), + ("Volume up", "Lauter"), + ("Volume down", "Leiser"), + ("Power", "Power"), + ("Telegram bot", "Telegram-Bot"), + ("enable-bot-tip", "Wenn Sie diese Funktion aktivieren, können Sie den 2FA-Code von Ihrem Bot erhalten. Er kann auch als Verbindungsbenachrichtigung dienen."), + ("enable-bot-desc", "1. Öffnen Sie einen Chat mit @BotFather.\n2. Senden Sie den Befehl \"/newbot\". Sie erhalten ein Token, nachdem Sie diesen Schritt abgeschlossen haben.\n3. Starten Sie einen Chat mit Ihrem neu erstellten Bot. Senden Sie eine Nachricht, die mit einem Schrägstrich (\"/\") beginnt, z. B. \"/hello\", um ihn zu aktivieren.\n"), ].iter().cloned().collect(); } From a0dc38f749b24b06eed9d6d98538eeb68bf24f69 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 2 Jul 2024 00:19:18 +0800 Subject: [PATCH 185/335] try fix linux arm64 build (#8560) Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 5d508c19115..223b2c08f73 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -968,12 +968,14 @@ jobs: target: x86_64-unknown-linux-gnu, distro: ubuntu18.04, on: ubuntu-20.04, + deb_arch: amd64, } - { arch: aarch64, target: aarch64-unknown-linux-gnu, distro: ubuntu18.04, on: [self-hosted, Linux, ARM64], + deb_arch: arm64, } steps: - name: Export GitHub Actions cache environment variables @@ -1152,7 +1154,6 @@ jobs: aarch64) export PATH=/opt/flutter-elinux/bin:$PATH sed -i "s/flutter build linux --release/flutter-elinux build linux --verbose/g" ./build.py - export ARCH=arm64 sed -i "s/x64\/release/arm64\/release/g" ./build.py ;; x86_64) @@ -1184,6 +1185,7 @@ jobs: # build flutter pushd /workspace export CARGO_INCREMENTAL=0 + export DEB_ARCH=${{ matrix.job.deb_arch }} python3 ./build.py --flutter --skip-cargo for name in rustdesk*??.deb; do mv "$name" "${name%%.deb}-${{ matrix.job.arch }}.deb" From 51db8e706d9f0ac69b147cf147ad246666f92d72 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 2 Jul 2024 00:46:55 +0800 Subject: [PATCH 186/335] fix: win, clipboard image (#8561) The window must belong to the current thread for clipboard-master. https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage#:~:text=The%20window%20must%20belong%20to%20the%20current%20thread. Signed-off-by: fufesou --- src/clipboard.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/clipboard.rs b/src/clipboard.rs index cdf7fa67d44..973f948f654 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -213,7 +213,6 @@ impl ClipboardData { ClipboardData::Empty => true, ClipboardData::Text(s) => s.is_empty(), ClipboardData::Image(a, _) => a.bytes.is_empty(), - _ => false, } } @@ -347,22 +346,25 @@ impl ClipboardContext { CallbackResult::Next } } - match Master::new(Handler(change_count.clone())) { - Ok(master) => { - let mut master = master; - shutdown = Some(master.shutdown_channel()); - std::thread::spawn(move || { - log::debug!("Clipboard listener started"); - if let Err(err) = master.run() { - log::error!("Failed to run clipboard listener: {}", err); - } else { - log::debug!("Clipboard listener stopped"); - } - }); + let change_count_cloned = change_count.clone(); + let (tx, rx) = std::sync::mpsc::channel(); + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage#:~:text=The%20window%20must%20belong%20to%20the%20current%20thread. + std::thread::spawn(move || match Master::new(Handler(change_count_cloned)) { + Ok(mut master) => { + tx.send(master.shutdown_channel()).ok(); + log::debug!("Clipboard listener started"); + if let Err(err) = master.run() { + log::error!("Failed to run clipboard listener: {}", err); + } else { + log::debug!("Clipboard listener stopped"); + } } Err(err) => { log::error!("Failed to create clipboard listener: {}", err); } + }); + if let Ok(st) = rx.recv() { + shutdown = Some(st); } } Ok(ClipboardContext(board, (change_count, 0), shutdown)) From 8602b036bd6e63e9256515d51fa77bdd062b36b3 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 2 Jul 2024 14:32:22 +0800 Subject: [PATCH 187/335] remove special treatment when only use permanent passwrod but no password set (#8566) 1. Remove special treatment when only use permanent passwrod but no password set, it has no need and `Connection not allowd` prompt make user confusing. 2. When only use permanent password is chosen and the permanent password is empty, pop up the set-password dialog, if still not set in the dialog, back to the old choice 3. Add cancel confirm for 2fa and telegram bot Signed-off-by: 21pages --- flutter/lib/common/widgets/dialog.dart | 28 +++++++++++++++ .../lib/desktop/pages/desktop_home_page.dart | 5 ++- .../desktop/pages/desktop_setting_page.dart | 36 ++++++++++++++++--- flutter/lib/mobile/pages/server_page.dart | 18 +++++++--- libs/scrap/src/common/codec.rs | 6 +++- src/lang/ar.rs | 2 ++ src/lang/be.rs | 2 ++ src/lang/bg.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 | 2 ++ src/lang/eo.rs | 2 ++ src/lang/es.rs | 2 ++ src/lang/et.rs | 2 ++ src/lang/fa.rs | 2 ++ src/lang/fr.rs | 2 ++ src/lang/he.rs | 2 ++ src/lang/hr.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/lt.rs | 2 ++ src/lang/lv.rs | 2 ++ src/lang/nb.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 ++ src/server/connection.rs | 9 ++--- 49 files changed, 170 insertions(+), 18 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 35bbd37168a..f140a68b077 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -2188,3 +2188,31 @@ void setSharedAbPasswordDialog(String abName, Peer peer) { ); }); } + +void CommonConfirmDialog(OverlayDialogManager dialogManager, String content, + VoidCallback onConfirm) { + dialogManager.show((setState, close, context) { + submit() { + close(); + onConfirm.call(); + } + + return CustomAlertDialog( + content: Row( + children: [ + Expanded( + child: Text(content, + style: const TextStyle(fontSize: 15), + textAlign: TextAlign.start), + ), + ], + ).marginOnly(bottom: 12), + actions: [ + dialogButton(translate("Cancel"), onPressed: close, isOutline: true), + dialogButton(translate("OK"), onPressed: submit), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 54fae3e99f8..1f53172a530 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -838,7 +838,7 @@ class _DesktopHomePageState extends State } } -void setPasswordDialog() async { +void setPasswordDialog({VoidCallback? notEmptyCallback}) async { final pw = await bind.mainGetPermanentPassword(); final p0 = TextEditingController(text: pw); final p1 = TextEditingController(text: pw); @@ -878,6 +878,9 @@ void setPasswordDialog() async { return; } bind.mainSetPermanentPassword(password: pass); + if (pass.isNotEmpty) { + notEmptyCallback?.call(); + } close(); } diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 0035ef4931d..7c955e6fb4d 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -684,10 +684,18 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { RxBool hasBot = bind.mainHasValidBotSync().obs; update() async { has2fa.value = bind.mainHasValid2FaSync(); + setState(() {}); } onChanged(bool? checked) async { - change2fa(callback: update); + if (checked == false) { + CommonConfirmDialog( + gFFI.dialogManager, translate('cancel-2fa-confirm-tip'), () { + change2fa(callback: update); + }); + } else { + change2fa(callback: update); + } } final tfa = GestureDetector( @@ -716,10 +724,18 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { } updateBot() async { hasBot.value = bind.mainHasValidBotSync(); + setState(() {}); } onChangedBot(bool? checked) async { - changeBot(callback: updateBot); + if (checked == false) { + CommonConfirmDialog( + gFFI.dialogManager, translate('cancel-bot-confirm-tip'), () { + changeBot(callback: updateBot); + }); + } else { + changeBot(callback: updateBot); + } } final bot = GestureDetector( @@ -873,12 +889,22 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { label: value, onChanged: locked ? null - : ((value) { - () async { + : ((value) async { + callback() async { await model.setVerificationMethod( passwordKeys[passwordValues.indexOf(value)]); await model.updatePasswordModel(); - }(); + } + + if (value == + passwordValues[passwordKeys + .indexOf(kUsePermanentPassword)] && + (await bind.mainGetPermanentPassword()) + .isEmpty) { + setPasswordDialog(notEmptyCallback: callback); + } else { + await callback(); + } }), )) .toList(); diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 02c416b645e..3609564ec17 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart'; import 'package:flutter_hbb/mobile/widgets/dialog.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:get/get.dart'; @@ -101,18 +102,27 @@ class ServerPage extends StatefulWidget implements PageShape { ), ]; }, - onSelected: (value) { + onSelected: (value) async { if (value == "changeID") { changeIdDialog(); } else if (value == "setPermanentPassword") { - setPermanentPasswordDialog(gFFI.dialogManager); + setPasswordDialog(); } else if (value == "setTemporaryPasswordLength") { setTemporaryPasswordLengthDialog(gFFI.dialogManager); } else if (value == kUsePermanentPassword || value == kUseTemporaryPassword || value == kUseBothPasswords) { - bind.mainSetOption(key: kOptionVerificationMethod, value: value); - gFFI.serverModel.updatePasswordModel(); + callback() { + bind.mainSetOption(key: kOptionVerificationMethod, value: value); + gFFI.serverModel.updatePasswordModel(); + } + + if (value == kUsePermanentPassword && + (await bind.mainGetPermanentPassword()).isEmpty) { + setPasswordDialog(notEmptyCallback: callback); + } else { + callback(); + } } else if (value.startsWith("AcceptSessionsVia")) { value = value.substring("AcceptSessionsVia".length); if (value == "Password") { diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 9fbcae2cdec..4ef69181ed9 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -21,7 +21,7 @@ use crate::{ use hbb_common::{ anyhow::anyhow, bail, - config::{keys::OPTION_ENABLE_HWCODEC, option2bool, Config, PeerConfig}, + config::{option2bool, Config, PeerConfig}, lazy_static, log, message_proto::{ supported_decoding::PreferCodec, video_frame, Chroma, CodecAbility, EncodedVideoFrames, @@ -836,6 +836,8 @@ impl Decoder { #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] pub fn enable_hwcodec_option() -> bool { + use hbb_common::config::keys::OPTION_ENABLE_HWCODEC; + if cfg!(windows) || cfg!(target_os = "linux") || cfg!(target_os = "android") { return option2bool( OPTION_ENABLE_HWCODEC, @@ -846,6 +848,8 @@ pub fn enable_hwcodec_option() -> bool { } #[cfg(feature = "vram")] pub fn enable_vram_option(encode: bool) -> bool { + use hbb_common::config::keys::OPTION_ENABLE_HWCODEC; + if cfg!(windows) { let enable = option2bool( OPTION_ENABLE_HWCODEC, diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 285df451b43..808535f3edb 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index 8fa8c535f4c..c76c2033245 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index b2ab8edc89a..8283a7dbd58 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 77289e9a5c4..adf0975a24f 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index d53088f9499..05ccf7fd1b6 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", "确定要取消双重认证吗?"), + ("cancel-bot-confirm-tip", "确定要取消 Telegram 机器人吗?"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index db7a063b976..be42f514fca 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram bot"), ("enable-bot-tip", "Pokud tuto funkci povolíte, můžete od svého bota obdržet kód 2FA. Může také fungovat jako oznámení o připojení."), ("enable-bot-desc", "1, Otevřete chat s @BotFather.\n2, Pošlete příkaz \"/newbot\". Po dokončení tohoto kroku obdržíte token.\n3, Spusťte chat s nově vytvořeným botem. Pro jeho aktivaci odešlete zprávu začínající lomítkem vpřed (\"/\"), například \"/hello\".\n"), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 4bc91708d5b..a68258b2f5b 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index d41591b610a..3ed9a4039a2 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram-Bot"), ("enable-bot-tip", "Wenn Sie diese Funktion aktivieren, können Sie den 2FA-Code von Ihrem Bot erhalten. Er kann auch als Verbindungsbenachrichtigung dienen."), ("enable-bot-desc", "1. Öffnen Sie einen Chat mit @BotFather.\n2. Senden Sie den Befehl \"/newbot\". Sie erhalten ein Token, nachdem Sie diesen Schritt abgeschlossen haben.\n3. Starten Sie einen Chat mit Ihrem neu erstellten Bot. Senden Sie eine Nachricht, die mit einem Schrägstrich (\"/\") beginnt, z. B. \"/hello\", um ihn zu aktivieren.\n"), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 974d5e7df6f..eab1c7c1bc9 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 414ced37104..02055724551 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -229,5 +229,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("floating_window_tip", "It helps to keep RustDesk background service"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), ("enable-bot-desc", "1, Open a chat with @BotFather.\n2, Send the command \"/newbot\". You will receive a token after completing this step.\n3, Start a chat with your newly created bot. Send a message beginning with a forward slash (\"/\") like \"/hello\" to activate it.\n"), + ("cancel-2fa-confirm-tip", "Are you sure you want to cancel 2FA?"), + ("cancel-bot-confirm-tip", "Are you sure you want to cancel Telegram bot?"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index c99473558b8..733bc80ba75 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 793f678a093..ca2d991070e 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index b1304bb37bd..43afea356e5 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index e69c4c1a000..df6fb70668e 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 3db65c93fd3..8ad0ca28ac0 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index ca781a6ce13..91a1753bd64 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index fc7b4756c04..2cf6edb94ea 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index c355bba4560..1009f24904c 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 46ba8a0469a..f051231b921 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 8dc8c60ba03..7f3e9804ad6 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Bot Telgram"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index d317fa77ba3..33619dce1cf 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 0c749c5e2ec..56167b968c8 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index a46791475a6..f12bbf56a8a 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 0512eb330c5..2d902966c2d 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 0d540c9c281..65384bc932b 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 8a9fc90f464..4efdd3416dc 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 88500aeafc3..fce7668a025 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram bot"), ("enable-bot-tip", "Als u deze functie inschakelt, kunt u een 2FA-code ontvangen van uw bot. Het kan ook fungeren als een verbindingsmelding."), ("enable-bot-desc", "1, Open een chat met @BotFather.\n2, Verzend het commando \"/newbot\". Als deze stap voltooid is, ontvang je een token.\n3, Start een chat met de nieuw aangemaakte bot. Om hem te activeren stuurt u een bericht dat begint met een schuine streep (\"/\"), bijvoorbeeld \"/hello\".\n"), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index cede4f8b986..71ec07e739b 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 0530283763c..3266ad2f694 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 8a9e71b3bdb..313296bed76 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index ddd9f5e4b54..371c0ed63c3 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 3f8adaa4aa9..d05145cd2a5 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram-бот"), ("enable-bot-tip", "Если включено, можно получать код двухфакторной аутентификации от бота. Он также может выполнять функцию уведомления о подключении."), ("enable-bot-desc", "1) Откройте чат с @BotFather.\n2) Отправьте команду \"/newbot\". После выполнения этого шага вы получите токен.\n3) Начните чат с вашим только что созданным ботом. Отправьте сообщение, начинающееся с прямой косой черты (\"/\"), например, \"/hello\", чтобы его активировать.\n"), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 0c0bfd6891c..c7da5843331 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram bot"), ("enable-bot-tip", "Ak túto funkciu povolíte, kód 2FA môžete dostať od svojho bota. Môže fungovať aj ako upozornenie na pripojenie."), ("enable-bot-desc", "1, Otvorte chat s @BotFather.\n2, Odošlite príkaz \"/newbot\". Po dokončení tohto kroku dostanete token.\n3, Spustite chat s novo vytvoreným botom. Odošlite správu začínajúcu lomítkom vpred (\"/\"), napríklad \"/hello\", aby ste ho aktivovali.\n"), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 450d880c40f..6eda0b94483 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index e0f70d430aa..3767b20dde7 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 6b15f651c82..aee79c3651d 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 0e456354f2a..6bbee4b1963 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index b725f6322b4..787cadc0163 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 1203bc7532a..05bab55ee1f 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 37a8b1f5261..e7aab8d1af5 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 4e7fab39f79..d2bb51b7f26 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 5088238419b..b08afc4b9b3 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index d8a269bc2af..daa0171bb82 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -627,5 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index b45da70523b..5fac698a118 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1,8 +1,8 @@ use super::{input_service::*, *}; -#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] -use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::clipboard::update_clipboard; +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] +use crate::clipboard_file::*; #[cfg(target_os = "android")] use crate::keyboard::client::map_key_to_control_key; #[cfg(target_os = "linux")] @@ -1745,11 +1745,6 @@ impl Connection { .await; } return true; - } else if password::approve_mode() == ApproveMode::Password - && !password::has_valid_password() - { - self.send_login_error("Connection not allowed").await; - return false; } else if self.is_recent_session(false) { if err_msg.is_empty() { #[cfg(target_os = "linux")] From 2f40b9dc042d3da17d745c09e5a5c3f320be6a1e Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:37:45 +0800 Subject: [PATCH 188/335] fix: build ios (#8570) ld: Undefined symbols: _kSecMatchSubjectWholeString, referenced from: security_framework::item::ItemSearchOptions::search::he568de2b0004b0c0 in liblibrustdesk.a[872](security_framework-88ef6afe340eb2ab.security_framework.e4562ffe63567184-cgu.0.rcgu.o) TODO: There's also a warning after commit "2116fec20b5a2165df97e329c156baf308668efc". No idea how to fix it. But it does not stop building. ld: warning: Could not find or use auto-linked framework 'CoreAudioTypes': framework 'CoreAudioTypes' not found Signed-off-by: fufesou --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1f8b39b523..4b25f5cb5f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5837,11 +5837,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ - "bitflags 2.6.0", + "bitflags 1.3.2", "core-foundation 0.9.4", "core-foundation-sys 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc", From 40019b80f60ba3304864f964c9880190c97517f8 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Wed, 3 Jul 2024 05:55:47 +0200 Subject: [PATCH 189/335] Update Italian language (#8576) --- src/lang/it.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 7f3e9804ad6..626c6f7884f 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -627,7 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Bot Telgram"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("cancel-2fa-confirm-tip", "Sei sicuro di voler annullare 2FA?"), + ("cancel-bot-confirm-tip", "Sei sicuro di voler annulare Telegram?"), ].iter().cloned().collect(); } From 182e8c4ac06b435edba0efa473f0d989fdee3418 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Wed, 3 Jul 2024 05:55:59 +0200 Subject: [PATCH 190/335] Update de.rs (#8577) --- src/lang/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 3ed9a4039a2..ff412e4c208 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -627,7 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram-Bot"), ("enable-bot-tip", "Wenn Sie diese Funktion aktivieren, können Sie den 2FA-Code von Ihrem Bot erhalten. Er kann auch als Verbindungsbenachrichtigung dienen."), ("enable-bot-desc", "1. Öffnen Sie einen Chat mit @BotFather.\n2. Senden Sie den Befehl \"/newbot\". Sie erhalten ein Token, nachdem Sie diesen Schritt abgeschlossen haben.\n3. Starten Sie einen Chat mit Ihrem neu erstellten Bot. Senden Sie eine Nachricht, die mit einem Schrägstrich (\"/\") beginnt, z. B. \"/hello\", um ihn zu aktivieren.\n"), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("cancel-2fa-confirm-tip", "Sind Sie sicher, dass Sie 2FA abbrechen möchten?"), + ("cancel-bot-confirm-tip", "Sind Sie sicher, dass Sie Telegram-Bot abbrechen möchten?"), ].iter().cloned().collect(); } From 6d2e985593fc392874cb06a43e504a64674e9c70 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Wed, 3 Jul 2024 06:12:19 +0000 Subject: [PATCH 191/335] res/vcpkg: Bump aom to 3.9.1, opus to 1.5.1 and libvpx to 1.14.1 (#8555) * res/vcpkg: drop old opus port overlay Signed-off-by: Vasyl Gello * res/vcpkg: bump aom to 3.9.1 Signed-off-by: Vasyl Gello * res/vcpkg: bump libvpx to 1.14.1 Signed-off-by: Vasyl Gello --------- Signed-off-by: Vasyl Gello --- res/vcpkg/aom/aom-rename-static.diff | 19 ------ res/vcpkg/aom/portfile.cmake | 10 +++- res/vcpkg/aom/vcpkg.json | 3 +- .../0003-add-uwp-v142-and-v143-support.patch | 16 ++--- res/vcpkg/libvpx/0005-fix-arm64-build.patch | 13 ----- res/vcpkg/libvpx/portfile.cmake | 17 +++--- res/vcpkg/libvpx/vcpkg.json | 5 +- res/vcpkg/opus/fix-pkgconfig-version.patch | 15 ----- res/vcpkg/opus/portfile.cmake | 58 ------------------- res/vcpkg/opus/vcpkg.json | 23 -------- 10 files changed, 28 insertions(+), 151 deletions(-) delete mode 100644 res/vcpkg/aom/aom-rename-static.diff delete mode 100644 res/vcpkg/libvpx/0005-fix-arm64-build.patch delete mode 100644 res/vcpkg/opus/fix-pkgconfig-version.patch delete mode 100644 res/vcpkg/opus/portfile.cmake delete mode 100644 res/vcpkg/opus/vcpkg.json diff --git a/res/vcpkg/aom/aom-rename-static.diff b/res/vcpkg/aom/aom-rename-static.diff deleted file mode 100644 index dfb911f21c0..00000000000 --- a/res/vcpkg/aom/aom-rename-static.diff +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 8f459f39c4..d8c1bb2b02 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -286,12 +286,12 @@ add_library(aom ${target_objs_aom} $) - - if(BUILD_SHARED_LIBS) - add_library(aom_static STATIC ${target_objs_aom} $) -- set_target_properties(aom_static PROPERTIES OUTPUT_NAME aom) -+ set_target_properties(aom_static PROPERTIES OUTPUT_NAME aom_static) - if(MSVC OR (WIN32 AND NOT MINGW)) - # Fix race condition on the export library file between the two versions. - # Affects MSVC in all three flavors (stock, Clang/CL, LLVM-- the latter sets - # MSVC and MINGW both to FALSE). -- set_target_properties(aom PROPERTIES ARCHIVE_OUTPUT_NAME "aom_dll") -+ set_target_properties(aom PROPERTIES ARCHIVE_OUTPUT_NAME "aom") - endif() - - if(NOT MSVC) diff --git a/res/vcpkg/aom/portfile.cmake b/res/vcpkg/aom/portfile.cmake index 2d05ffac8a4..0167a92631b 100644 --- a/res/vcpkg/aom/portfile.cmake +++ b/res/vcpkg/aom/portfile.cmake @@ -11,9 +11,8 @@ vcpkg_add_to_path(${PERL_PATH}) vcpkg_from_git( OUT_SOURCE_PATH SOURCE_PATH URL "https://aomedia.googlesource.com/aom" - REF 6054fae218eda6e53e1e3b4f7ef0fff4877c7bf1 # v3.7.0 + REF 8ad484f8a18ed1853c094e7d3a4e023b2a92df28 # 3.9.1 PATCHES - aom-rename-static.diff aom-uninitialized-pointer.diff # Can be dropped when https://bugs.chromium.org/p/aomedia/issues/detail?id=3029 is merged into the upstream aom-install.diff @@ -47,6 +46,13 @@ vcpkg_copy_pdbs() vcpkg_fixup_pkgconfig() +if(VCPKG_TARGET_IS_WINDOWS) + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/lib/pkgconfig/aom.pc" " -lm" "") + if(NOT VCPKG_BUILD_TYPE) + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/aom.pc" " -lm" "") + endif() +endif() + # Move cmake configs vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/${PORT}) diff --git a/res/vcpkg/aom/vcpkg.json b/res/vcpkg/aom/vcpkg.json index 7fc53a398f6..78ccc898909 100644 --- a/res/vcpkg/aom/vcpkg.json +++ b/res/vcpkg/aom/vcpkg.json @@ -1,6 +1,7 @@ { "name": "aom", - "version-semver": "3.7.0", + "version-semver": "3.9.1", + "port-version": 0, "description": "AV1 codec library", "homepage": "https://aomedia.googlesource.com/aom", "license": "BSD-2-Clause", diff --git a/res/vcpkg/libvpx/0003-add-uwp-v142-and-v143-support.patch b/res/vcpkg/libvpx/0003-add-uwp-v142-and-v143-support.patch index 43cebde3116..32222238c98 100644 --- a/res/vcpkg/libvpx/0003-add-uwp-v142-and-v143-support.patch +++ b/res/vcpkg/libvpx/0003-add-uwp-v142-and-v143-support.patch @@ -85,19 +85,19 @@ index 58bb66b..b4cad6c 100644 fi fi diff --git a/configure b/configure -index ae289f7..78f5fc1 100644 +index b212e07..1a9fa98 100755 --- a/configure +++ b/configure -@@ -103,6 +103,8 @@ all_platforms="${all_platforms} arm64-darwin20-gcc" - all_platforms="${all_platforms} arm64-darwin21-gcc" +@@ -104,6 +104,8 @@ all_platforms="${all_platforms} arm64-darwin21-gcc" all_platforms="${all_platforms} arm64-darwin22-gcc" + all_platforms="${all_platforms} arm64-darwin23-gcc" all_platforms="${all_platforms} arm64-linux-gcc" +all_platforms="${all_platforms} arm64-uwp-vs16" +all_platforms="${all_platforms} arm64-uwp-vs17" all_platforms="${all_platforms} arm64-win64-gcc" all_platforms="${all_platforms} arm64-win64-vs15" all_platforms="${all_platforms} arm64-win64-vs16" -@@ -112,6 +114,8 @@ all_platforms="${all_platforms} armv7-darwin-gcc" #neon Cortex-A8 +@@ -115,6 +117,8 @@ all_platforms="${all_platforms} armv7-darwin-gcc" #neon Cortex-A8 all_platforms="${all_platforms} armv7-linux-rvct" #neon Cortex-A8 all_platforms="${all_platforms} armv7-linux-gcc" #neon Cortex-A8 all_platforms="${all_platforms} armv7-none-rvct" #neon Cortex-A8 @@ -106,7 +106,7 @@ index ae289f7..78f5fc1 100644 all_platforms="${all_platforms} armv7-win32-gcc" all_platforms="${all_platforms} armv7-win32-vs14" all_platforms="${all_platforms} armv7-win32-vs15" -@@ -143,6 +147,8 @@ all_platforms="${all_platforms} x86-linux-gcc" +@@ -146,6 +150,8 @@ all_platforms="${all_platforms} x86-linux-gcc" all_platforms="${all_platforms} x86-linux-icc" all_platforms="${all_platforms} x86-os2-gcc" all_platforms="${all_platforms} x86-solaris-gcc" @@ -115,7 +115,7 @@ index ae289f7..78f5fc1 100644 all_platforms="${all_platforms} x86-win32-gcc" all_platforms="${all_platforms} x86-win32-vs14" all_platforms="${all_platforms} x86-win32-vs15" -@@ -167,6 +173,8 @@ all_platforms="${all_platforms} x86_64-iphonesimulator-gcc" +@@ -171,6 +177,8 @@ all_platforms="${all_platforms} x86_64-iphonesimulator-gcc" all_platforms="${all_platforms} x86_64-linux-gcc" all_platforms="${all_platforms} x86_64-linux-icc" all_platforms="${all_platforms} x86_64-solaris-gcc" @@ -124,7 +124,7 @@ index ae289f7..78f5fc1 100644 all_platforms="${all_platforms} x86_64-win64-gcc" all_platforms="${all_platforms} x86_64-win64-vs14" all_platforms="${all_platforms} x86_64-win64-vs15" -@@ -491,11 +499,10 @@ process_targets() { +@@ -503,11 +511,10 @@ process_targets() { ! enabled multithread && DIST_DIR="${DIST_DIR}-nomt" ! enabled install_docs && DIST_DIR="${DIST_DIR}-nodocs" DIST_DIR="${DIST_DIR}-${tgt_isa}-${tgt_os}" @@ -140,7 +140,7 @@ index ae289f7..78f5fc1 100644 if [ -f "${source_path}/build/make/version.sh" ]; then ver=`"$source_path/build/make/version.sh" --bare "$source_path"` DIST_DIR="${DIST_DIR}-${ver}" -@@ -584,6 +591,10 @@ process_detect() { +@@ -596,6 +603,10 @@ process_detect() { # Specialize windows and POSIX environments. case $toolchain in diff --git a/res/vcpkg/libvpx/0005-fix-arm64-build.patch b/res/vcpkg/libvpx/0005-fix-arm64-build.patch deleted file mode 100644 index 76c0c8171f7..00000000000 --- a/res/vcpkg/libvpx/0005-fix-arm64-build.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c b/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c -index 33753f7..997775a 100644 ---- a/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c -+++ b/vp9/encoder/arm/neon/vp9_diamond_search_sad_neon.c -@@ -220,7 +220,7 @@ int vp9_diamond_search_sad_neon(const MACROBLOCK *x, - // Look up the component cost of the residual motion vector - { - uint32_t cost[4]; -- int16_t __attribute__((aligned(16))) rowcol[8]; -+ DECLARE_ALIGNED(16, int16_t, rowcol[8]); - vst1q_s16(rowcol, v_diff_mv_w); - - // Note: This is a use case for gather instruction diff --git a/res/vcpkg/libvpx/portfile.cmake b/res/vcpkg/libvpx/portfile.cmake index 30f79431492..96eab871739 100644 --- a/res/vcpkg/libvpx/portfile.cmake +++ b/res/vcpkg/libvpx/portfile.cmake @@ -4,28 +4,25 @@ vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO webmproject/libvpx REF "v${VERSION}" - SHA512 49706838563c92fab7334376848d0f374efcbc1729ef511e967c908fd2ecd40e8d197f1d85da6553b3a7026bdbc17e5a76595319858af26ce58cb9a4c3854897 + SHA512 3e3bfad3d035c0bc3db7cb5a194d56d3c90f5963fb1ad527ae5252054e7c48ce2973de1346c97d94b59f7a95d4801bec44214cce10faf123f92b36fca79a8d1e HEAD_REF master PATCHES 0002-Fix-nasm-debug-format-flag.patch 0003-add-uwp-v142-and-v143-support.patch 0004-remove-library-suffixes.patch - 0005-fix-arm64-build.patch # Upstream commit: https://github.com/webmproject/libvpx/commit/858a8c611f4c965078485860a6820e2135e6611b ) -vcpkg_find_acquire_program(PERL) - -get_filename_component(PERL_EXE_PATH ${PERL} DIRECTORY) - if(CMAKE_HOST_WIN32) - vcpkg_acquire_msys(MSYS_ROOT PACKAGES make) - set(BASH ${MSYS_ROOT}/usr/bin/bash.exe) - set(ENV{PATH} "${MSYS_ROOT}/usr/bin;$ENV{PATH};${PERL_EXE_PATH}") + vcpkg_acquire_msys(MSYS_ROOT PACKAGES make perl) + set(ENV{PATH} "${MSYS_ROOT}/usr/bin;$ENV{PATH}") else() - set(BASH /bin/bash) + vcpkg_find_acquire_program(PERL) + get_filename_component(PERL_EXE_PATH ${PERL} DIRECTORY) set(ENV{PATH} "${MSYS_ROOT}/usr/bin:$ENV{PATH}:${PERL_EXE_PATH}") endif() +find_program(BASH NAME bash HINTS ${MSYS_ROOT}/usr/bin REQUIRED NO_CACHE) + vcpkg_find_acquire_program(NASM) get_filename_component(NASM_EXE_PATH ${NASM} DIRECTORY) vcpkg_add_to_path(${NASM_EXE_PATH}) diff --git a/res/vcpkg/libvpx/vcpkg.json b/res/vcpkg/libvpx/vcpkg.json index 0a11ff7eab8..ca4a47d309b 100644 --- a/res/vcpkg/libvpx/vcpkg.json +++ b/res/vcpkg/libvpx/vcpkg.json @@ -1,6 +1,7 @@ { "name": "libvpx", - "version": "1.13.1", + "version": "1.14.1", + "port-version": 0, "description": "The reference software implementation for the video coding formats VP8 and VP9.", "homepage": "https://github.com/webmproject/libvpx", "license": "BSD-3-Clause", @@ -12,7 +13,7 @@ { "name": "vcpkg-msbuild", "host": true, - "platform": "windows" + "platform": "windows & !mingw" } ], "features": { diff --git a/res/vcpkg/opus/fix-pkgconfig-version.patch b/res/vcpkg/opus/fix-pkgconfig-version.patch deleted file mode 100644 index ef9f7229c41..00000000000 --- a/res/vcpkg/opus/fix-pkgconfig-version.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/cmake/OpusPackageVersion.cmake b/cmake/OpusPackageVersion.cmake -index 447ce3b..15ebd8e 100644 ---- a/cmake/OpusPackageVersion.cmake -+++ b/cmake/OpusPackageVersion.cmake -@@ -4,7 +4,9 @@ endif() - set(__opus_version INCLUDED) - - function(get_package_version PACKAGE_VERSION PROJECT_VERSION) -- -+ set(PACKAGE_VERSION "0" CACHE STRING "opus package version") -+ set(PROJECT_VERSION "0" CACHE STRING "opus project version") -+ return() - find_package(Git) - if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") - execute_process(COMMAND ${GIT_EXECUTABLE} diff --git a/res/vcpkg/opus/portfile.cmake b/res/vcpkg/opus/portfile.cmake deleted file mode 100644 index f4fb2ac9429..00000000000 --- a/res/vcpkg/opus/portfile.cmake +++ /dev/null @@ -1,58 +0,0 @@ -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO xiph/opus - REF "v${VERSION}" - SHA512 86df35cd62ebf3551b2739effb8f818d635656d91d386d7d600a424a92c4c0d6bfbc3986f1ec6cf4950910ac87b28dc9640b9df3b9a6a5a75eb37ae71782b72e - HEAD_REF master - PATCHES fix-pkgconfig-version.patch -) - -vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS - FEATURES - avx AVX_SUPPORTED -) - -set(ADDITIONAL_OPUS_OPTIONS "") -if(VCPKG_TARGET_IS_MINGW) - set(STACK_PROTECTOR OFF) - string(APPEND VCPKG_C_FLAGS "-D_FORTIFY_SOURCE=0") - string(APPEND VCPKG_CXX_FLAGS "-D_FORTIFY_SOURCE=0") -elseif(VCPKG_TARGET_IS_EMSCRIPTEN) - set(STACK_PROTECTOR OFF) -else() - set(STACK_PROTECTOR ON) -endif() - -if((VCPKG_TARGET_IS_MINGW AND VCPKG_TARGET_ARCHITECTURE MATCHES "^arm") OR - (VCPKG_TARGET_IS_LINUX AND VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") OR - (VCPKG_TARGET_IS_ANDROID AND VCPKG_TARGET_ARCHITECTURE STREQUAL "arm" AND VCPKG_CMAKE_CONFIGURE_OPTIONS MATCHES "ANDROID_ARM_NEON")) - message(STATUS "Disabling ARM NEON and intrinsics on ${TARGET_TRIPLET}") - list(APPEND ADDITIONAL_OPUS_OPTIONS "-DOPUS_DISABLE_INTRINSICS=ON -DCOMPILER_SUPPORTS_NEON=OFF") # for HEAD (and future Opus release) -endif() - -vcpkg_cmake_configure( - SOURCE_PATH "${SOURCE_PATH}" - OPTIONS ${FEATURE_OPTIONS} - -DPACKAGE_VERSION=${VERSION} - -DOPUS_STACK_PROTECTOR=${STACK_PROTECTOR} - -DOPUS_INSTALL_PKG_CONFIG_MODULE=ON - -DOPUS_INSTALL_CMAKE_CONFIG_MODULE=ON - -DOPUS_BUILD_PROGRAMS=OFF - -DOPUS_BUILD_TESTING=OFF - ${ADDITIONAL_OPUS_OPTIONS} - MAYBE_UNUSED_VARIABLES - OPUS_USE_NEON - OPUS_DISABLE_INTRINSICS -) -vcpkg_cmake_install() -vcpkg_copy_pdbs() - -vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/Opus) -vcpkg_fixup_pkgconfig(SYSTEM_LIBRARIES m) - - -file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/cmake" - "${CURRENT_PACKAGES_DIR}/lib/cmake" - "${CURRENT_PACKAGES_DIR}/debug/include") - -file(INSTALL "${SOURCE_PATH}/COPYING" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright) diff --git a/res/vcpkg/opus/vcpkg.json b/res/vcpkg/opus/vcpkg.json deleted file mode 100644 index 1b23ca5375c..00000000000 --- a/res/vcpkg/opus/vcpkg.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "opus", - "version": "1.4", - "port-version": 1, - "description": "Totally open, royalty-free, highly versatile audio codec", - "homepage": "https://github.com/xiph/opus", - "license": "BSD-3-Clause", - "dependencies": [ - { - "name": "vcpkg-cmake", - "host": true - }, - { - "name": "vcpkg-cmake-config", - "host": true - } - ], - "features": { - "avx": { - "description": "Builds the library with avx instruction set" - } - } -} From d00582e929605b804ec26410cbe9cfde10c86895 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 3 Jul 2024 14:20:41 +0800 Subject: [PATCH 192/335] linux x11 rgb565 capture (#8580) Signed-off-by: 21pages --- libs/scrap/src/common/convert.rs | 118 +++++++++++++++++++++---------- libs/scrap/src/common/mod.rs | 17 +++++ libs/scrap/src/common/x11.rs | 3 +- libs/scrap/src/x11/capturer.rs | 2 +- libs/scrap/src/x11/display.rs | 8 +++ libs/scrap/src/x11/ffi.rs | 35 ++++++++- libs/scrap/src/x11/iter.rs | 27 +++++-- 7 files changed, 165 insertions(+), 45 deletions(-) diff --git a/libs/scrap/src/common/convert.rs b/libs/scrap/src/common/convert.rs index ba246c69495..d3883192891 100644 --- a/libs/scrap/src/common/convert.rs +++ b/libs/scrap/src/common/convert.rs @@ -32,13 +32,16 @@ pub fn convert_to_yuv( dst_fmt.h ); } - if src_pixfmt == crate::Pixfmt::BGRA || src_pixfmt == crate::Pixfmt::RGBA { + if src_pixfmt == crate::Pixfmt::BGRA + || src_pixfmt == crate::Pixfmt::RGBA + || src_pixfmt == crate::Pixfmt::RGB565LE + { // stride is calculated, not real, so we need to check it - if src_stride[0] < src_width * 4 { + if src_stride[0] < src_width * src_pixfmt.bytes_per_pixel() { bail!( - "src_stride[0] < src_width * 4: {} < {}", + "src_stride too small: {} < {}", src_stride[0], - src_width * 4 + src_width * src_pixfmt.bytes_per_pixel() ); } if src.len() < src_stride[0] * src_height { @@ -51,19 +54,26 @@ pub fn convert_to_yuv( } } let align = |x: usize| (x + 63) / 64 * 64; + let unsupported = format!( + "unsupported pixfmt conversion: {src_pixfmt:?} -> {:?}", + dst_fmt.pixfmt + ); match (src_pixfmt, dst_fmt.pixfmt) { - (crate::Pixfmt::BGRA, crate::Pixfmt::I420) | (crate::Pixfmt::RGBA, crate::Pixfmt::I420) => { + (crate::Pixfmt::BGRA, crate::Pixfmt::I420) + | (crate::Pixfmt::RGBA, crate::Pixfmt::I420) + | (crate::Pixfmt::RGB565LE, crate::Pixfmt::I420) => { let dst_stride_y = dst_fmt.stride[0]; let dst_stride_uv = dst_fmt.stride[1]; dst.resize(dst_fmt.h * dst_stride_y * 2, 0); // waste some memory to ensure memory safety let dst_y = dst.as_mut_ptr(); let dst_u = dst[dst_fmt.u..].as_mut_ptr(); let dst_v = dst[dst_fmt.v..].as_mut_ptr(); - let f = if src_pixfmt == crate::Pixfmt::BGRA { - ARGBToI420 - } else { - ABGRToI420 + let f = match src_pixfmt { + crate::Pixfmt::BGRA => ARGBToI420, + crate::Pixfmt::RGBA => ABGRToI420, + crate::Pixfmt::RGB565LE => RGB565ToI420, + _ => bail!(unsupported), }; call_yuv!(f( src.as_ptr(), @@ -78,7 +88,9 @@ pub fn convert_to_yuv( src_height as _, )); } - (crate::Pixfmt::BGRA, crate::Pixfmt::NV12) | (crate::Pixfmt::RGBA, crate::Pixfmt::NV12) => { + (crate::Pixfmt::BGRA, crate::Pixfmt::NV12) + | (crate::Pixfmt::RGBA, crate::Pixfmt::NV12) + | (crate::Pixfmt::RGB565LE, crate::Pixfmt::NV12) => { let dst_stride_y = dst_fmt.stride[0]; let dst_stride_uv = dst_fmt.stride[1]; dst.resize( @@ -87,14 +99,33 @@ pub fn convert_to_yuv( ); let dst_y = dst.as_mut_ptr(); let dst_uv = dst[dst_fmt.u..].as_mut_ptr(); - let f = if src_pixfmt == crate::Pixfmt::BGRA { - ARGBToNV12 - } else { - ABGRToNV12 + let (input, input_stride) = match src_pixfmt { + crate::Pixfmt::BGRA => (src.as_ptr(), src_stride[0]), + crate::Pixfmt::RGBA => (src.as_ptr(), src_stride[0]), + crate::Pixfmt::RGB565LE => { + let mid_stride = src_width * 4; + mid_data.resize(mid_stride * src_height, 0); + call_yuv!(RGB565ToARGB( + src.as_ptr(), + src_stride[0] as _, + mid_data.as_mut_ptr(), + mid_stride as _, + src_width as _, + src_height as _, + )); + (mid_data.as_ptr(), mid_stride) + } + _ => bail!(unsupported), + }; + let f = match src_pixfmt { + crate::Pixfmt::BGRA => ARGBToNV12, + crate::Pixfmt::RGBA => ABGRToNV12, + crate::Pixfmt::RGB565LE => ARGBToNV12, + _ => bail!(unsupported), }; call_yuv!(f( - src.as_ptr(), - src_stride[0] as _, + input, + input_stride as _, dst_y, dst_stride_y as _, dst_uv, @@ -103,7 +134,9 @@ pub fn convert_to_yuv( src_height as _, )); } - (crate::Pixfmt::BGRA, crate::Pixfmt::I444) | (crate::Pixfmt::RGBA, crate::Pixfmt::I444) => { + (crate::Pixfmt::BGRA, crate::Pixfmt::I444) + | (crate::Pixfmt::RGBA, crate::Pixfmt::I444) + | (crate::Pixfmt::RGB565LE, crate::Pixfmt::I444) => { let dst_stride_y = dst_fmt.stride[0]; let dst_stride_u = dst_fmt.stride[1]; let dst_stride_v = dst_fmt.stride[2]; @@ -115,23 +148,39 @@ pub fn convert_to_yuv( let dst_y = dst.as_mut_ptr(); let dst_u = dst[dst_fmt.u..].as_mut_ptr(); let dst_v = dst[dst_fmt.v..].as_mut_ptr(); - let src = if src_pixfmt == crate::Pixfmt::BGRA { - src - } else { - mid_data.resize(src.len(), 0); - call_yuv!(ABGRToARGB( - src.as_ptr(), - src_stride[0] as _, - mid_data.as_mut_ptr(), - src_stride[0] as _, - src_width as _, - src_height as _, - )); - mid_data + let (input, input_stride) = match src_pixfmt { + crate::Pixfmt::BGRA => (src.as_ptr(), src_stride[0]), + crate::Pixfmt::RGBA => { + mid_data.resize(src.len(), 0); + call_yuv!(ABGRToARGB( + src.as_ptr(), + src_stride[0] as _, + mid_data.as_mut_ptr(), + src_stride[0] as _, + src_width as _, + src_height as _, + )); + (mid_data.as_ptr(), src_stride[0]) + } + crate::Pixfmt::RGB565LE => { + let mid_stride = src_width * 4; + mid_data.resize(mid_stride * src_height, 0); + call_yuv!(RGB565ToARGB( + src.as_ptr(), + src_stride[0] as _, + mid_data.as_mut_ptr(), + mid_stride as _, + src_width as _, + src_height as _, + )); + (mid_data.as_ptr(), mid_stride) + } + _ => bail!(unsupported), }; + call_yuv!(ARGBToI444( - src.as_ptr(), - src_stride[0] as _, + input, + input_stride as _, dst_y, dst_stride_y as _, dst_u, @@ -143,10 +192,7 @@ pub fn convert_to_yuv( )); } _ => { - bail!( - "convert not support, {src_pixfmt:?} -> {:?}", - dst_fmt.pixfmt - ); + bail!(unsupported); } } Ok(()) diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 085d1e501fd..164f7157de3 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -59,6 +59,7 @@ pub enum ImageFormat { ABGR, ARGB, } + #[repr(C)] pub struct ImageRgb { pub raw: Vec, @@ -208,11 +209,27 @@ impl<'a> EncodeInput<'a> { pub enum Pixfmt { BGRA, RGBA, + RGB565LE, I420, NV12, I444, } +impl Pixfmt { + pub fn bpp(&self) -> usize { + match self { + Pixfmt::BGRA | Pixfmt::RGBA => 32, + Pixfmt::RGB565LE => 16, + Pixfmt::I420 | Pixfmt::NV12 => 12, + Pixfmt::I444 => 24, + } + } + + pub fn bytes_per_pixel(&self) -> usize { + (self.bpp() + 7) / 8 + } +} + #[derive(Debug, Clone)] pub struct EncodeYuvFormat { pub pixfmt: Pixfmt, diff --git a/libs/scrap/src/common/x11.rs b/libs/scrap/src/common/x11.rs index 56beaa46ed5..2a7c19c47d9 100644 --- a/libs/scrap/src/common/x11.rs +++ b/libs/scrap/src/common/x11.rs @@ -23,9 +23,10 @@ impl TraitCapturer for Capturer { fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result> { let width = self.width(); let height = self.height(); + let pixfmt = self.0.display().pixfmt(); Ok(Frame::PixelBuffer(PixelBuffer::new( self.0.frame()?, - Pixfmt::BGRA, + pixfmt, width, height, ))) diff --git a/libs/scrap/src/x11/capturer.rs b/libs/scrap/src/x11/capturer.rs index 4b0e0060449..550bfb346f1 100644 --- a/libs/scrap/src/x11/capturer.rs +++ b/libs/scrap/src/x11/capturer.rs @@ -17,7 +17,7 @@ impl Capturer { pub fn new(display: Display) -> io::Result { // Calculate dimensions. - let pixel_width = 4; + let pixel_width = display.pixfmt().bytes_per_pixel(); let rect = display.rect(); let size = (rect.w as usize) * (rect.h as usize) * pixel_width; diff --git a/libs/scrap/src/x11/display.rs b/libs/scrap/src/x11/display.rs index a33903caab2..0bc18b3b870 100644 --- a/libs/scrap/src/x11/display.rs +++ b/libs/scrap/src/x11/display.rs @@ -2,6 +2,7 @@ use std::rc::Rc; use super::ffi::*; use super::Server; +use crate::Pixfmt; #[derive(Debug)] pub struct Display { @@ -10,6 +11,7 @@ pub struct Display { rect: Rect, root: xcb_window_t, name: String, + pixfmt: Pixfmt, } #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] @@ -27,6 +29,7 @@ impl Display { rect: Rect, root: xcb_window_t, name: String, + pixfmt: Pixfmt, ) -> Display { Display { server, @@ -34,6 +37,7 @@ impl Display { rect, root, name, + pixfmt, } } @@ -59,4 +63,8 @@ impl Display { pub fn name(&self) -> String { self.name.clone() } + + pub fn pixfmt(&self) -> Pixfmt { + self.pixfmt + } } diff --git a/libs/scrap/src/x11/ffi.rs b/libs/scrap/src/x11/ffi.rs index 3337d6e44f0..370ce78b70b 100644 --- a/libs/scrap/src/x11/ffi.rs +++ b/libs/scrap/src/x11/ffi.rs @@ -82,12 +82,24 @@ extern "C" { pub fn xcb_get_atom_name_name_length(reply: *const xcb_get_atom_name_reply_t) -> i32; pub fn xcb_shm_query_version(c: *mut xcb_connection_t) -> xcb_shm_query_version_cookie_t; - + pub fn xcb_shm_query_version_reply( c: *mut xcb_connection_t, cookie: xcb_shm_query_version_cookie_t, e: *mut *mut xcb_generic_error_t, ) -> *const xcb_shm_query_version_reply_t; + + pub fn xcb_get_geometry_unchecked( + c: *mut xcb_connection_t, + drawable: xcb_drawable_t, + ) -> xcb_get_geometry_cookie_t; + + pub fn xcb_get_geometry_reply( + c: *mut xcb_connection_t, + cookie: xcb_get_geometry_cookie_t, + e: *mut *mut xcb_generic_error_t, + ) -> *mut xcb_get_geometry_reply_t; + } pub const XCB_IMAGE_FORMAT_Z_PIXMAP: u8 = 2; @@ -195,6 +207,12 @@ pub struct xcb_void_cookie_t { pub sequence: u32, } +#[repr(C)] +#[derive(Clone, Copy)] +pub struct xcb_get_geometry_cookie_t { + pub sequence: u32, +} + #[repr(C)] pub struct xcb_generic_error_t { pub response_type: u8, @@ -248,3 +266,18 @@ pub struct xcb_shm_query_version_reply_t { pub pixmap_format: u8, pub pad0: [u8; 15], } + +#[repr(C)] +pub struct xcb_get_geometry_reply_t { + pub response_type: u8, + pub depth: u8, + pub sequence: u16, + pub length: u32, + pub root: xcb_window_t, + pub x: i16, + pub y: i16, + pub width: u16, + pub height: u16, + pub border_width: u16, + pub pad0: [u8; 2], +} diff --git a/libs/scrap/src/x11/iter.rs b/libs/scrap/src/x11/iter.rs index 387687847aa..f800d5aa8ae 100644 --- a/libs/scrap/src/x11/iter.rs +++ b/libs/scrap/src/x11/iter.rs @@ -2,6 +2,7 @@ use std::ffi::CString; use std::ptr; use std::rc::Rc; +use crate::Pixfmt; use hbb_common::libc; use super::ffi::*; @@ -66,7 +67,7 @@ impl Iterator for DisplayIter { unsafe { let data = &*inner.data; let name = get_atom_name(self.server.raw(), data.name); - + let pixfmt = get_pixfmt(self.server.raw(), root).unwrap_or(Pixfmt::BGRA); let display = Display::new( self.server.clone(), data.primary != 0, @@ -78,6 +79,7 @@ impl Iterator for DisplayIter { }, root, name, + pixfmt, ); xcb_randr_monitor_info_next(inner); @@ -102,11 +104,7 @@ fn get_atom_name(conn: *mut xcb_connection_t, atom: xcb_atom_t) -> String { } unsafe { let mut e: *mut xcb_generic_error_t = std::ptr::null_mut(); - let reply = xcb_get_atom_name_reply( - conn, - xcb_get_atom_name(conn, atom), - &mut e as _, - ); + let reply = xcb_get_atom_name_reply(conn, xcb_get_atom_name(conn, atom), &mut e as _); if reply == std::ptr::null() { return empty; } @@ -121,3 +119,20 @@ fn get_atom_name(conn: *mut xcb_connection_t, atom: xcb_atom_t) -> String { empty } } + +unsafe fn get_pixfmt(conn: *mut xcb_connection_t, root: xcb_window_t) -> Option { + let geo_cookie = xcb_get_geometry_unchecked(conn, root); + let geo = xcb_get_geometry_reply(conn, geo_cookie, ptr::null_mut()); + if geo.is_null() { + return None; + } + let depth = (*geo).depth; + libc::free(geo as _); + // now only support little endian + // https://github.com/FFmpeg/FFmpeg/blob/a9c05eb657d0d05f3ac79fe9973581a41b265a5e/libavdevice/xcbgrab.c#L519 + match depth { + 16 => Some(Pixfmt::RGB565LE), + 32 => Some(Pixfmt::BGRA), + _ => None, + } +} From e294dafe7c8dbc1e0096db5add0b194767713d6b Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Wed, 3 Jul 2024 07:53:17 +0000 Subject: [PATCH 193/335] Bump vcpkg baseline to 2024.06.15 (#8582) * res/vcpkg: drop old opus port overlay Signed-off-by: Vasyl Gello * res/vcpkg: bump aom to 3.9.1 Signed-off-by: Vasyl Gello * res/vcpkg: bump libvpx to 1.14.1 Signed-off-by: Vasyl Gello * Bump vcpkg baseline to 2024.06.15 Signed-off-by: Vasyl Gello --------- Signed-off-by: Vasyl Gello Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> --- .github/workflows/flutter-build.yml | 4 ++-- .github/workflows/playground.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 223b2c08f73..28eb5a894eb 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -22,8 +22,8 @@ env: FLUTTER_ELINUX_VERSION: "3.16.9" TAG_NAME: "${{ inputs.upload-tag }}" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - # vcpkg version: 2024.03.25 - VCPKG_COMMIT_ID: "a34c873a9717a888f58dc05268dea15592c2f0ff" + # vcpkg version: 2024.06.15 + VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" VERSION: "1.2.7" NDK_VERSION: "r26d" #signing keys env variable checks diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index f0c1e4bd15d..b28a2050ee0 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -16,8 +16,8 @@ env: FLUTTER_ELINUX_VERSION: "3.16.9" TAG_NAME: "nightly" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - # vcpkg version: 2024.03.25 - VCPKG_COMMIT_ID: "a34c873a9717a888f58dc05268dea15592c2f0ff" + # vcpkg version: 2024.06.15 + VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" VERSION: "1.2.7" NDK_VERSION: "r26d" #signing keys env variable checks From a6febb281659e2f12de404c1e7894a2d6cbe5224 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Wed, 3 Jul 2024 09:12:21 +0000 Subject: [PATCH 194/335] [WIP] Fix F-Droid version file workflow (#8583) --- .github/workflows/fdroid.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/fdroid.yml b/.github/workflows/fdroid.yml index 49fb8c53e15..2dbc3a93620 100644 --- a/.github/workflows/fdroid.yml +++ b/.github/workflows/fdroid.yml @@ -1,4 +1,4 @@ -name: Fdroid +name: Fdroid version file generation on: workflow_dispatch: @@ -11,15 +11,16 @@ on: jobs: # https://gitlab.com/fdroid/fdroiddata/-/blob/master/metadata/com.carriez.flutter_hbb.yml - # https://gitlab.com/basilgello/rustdesk-fdroid-versioner + # Finds latest release and transforms F-Droid vereion code from version as follows: + # X.Y.Z-A => X * 1e9 + Y * 1e6 + Z * 1e3 + A update-fdroid-version-file: name: Publish RustDesk version file for F-Droid updater runs-on: ubuntu-latest steps: - name: Generate RustDesk version file run: | - UPSTREAM_VERNAME="$GITHUB_REF_NAME" - UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr -d '.')" + UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')" + UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000000 * $MAJOR + 1000000 * $MINOR + 1000 * $PATCH + $REV ))"; done)" echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt shell: bash From 95f4274eca5ed2bf7e9c817fd1f278d547f26c08 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Wed, 3 Jul 2024 09:57:58 +0000 Subject: [PATCH 195/335] vcpkg: sort triplets and restore local opus port for 1.5.1 (#8585) * ci/flutter-build: Sort out triplets in job matrices Signed-off-by: Vasyl Gello * Restore local port for opus 1.5.1 Signed-off-by: Vasyl Gello --------- Signed-off-by: Vasyl Gello --- .github/workflows/flutter-build.yml | 26 +++++---- res/vcpkg/opus/fix-pkgconfig-version.patch | 15 ++++++ res/vcpkg/opus/portfile.cmake | 61 ++++++++++++++++++++++ res/vcpkg/opus/vcpkg.json | 22 ++++++++ 4 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 res/vcpkg/opus/fix-pkgconfig-version.patch create mode 100644 res/vcpkg/opus/portfile.cmake create mode 100644 res/vcpkg/opus/vcpkg.json diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 28eb5a894eb..b9c608dba8d 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -58,7 +58,7 @@ jobs: job: # - { target: i686-pc-windows-msvc , os: windows-2022 } # - { target: x86_64-pc-windows-gnu , os: windows-2022 } - - { target: x86_64-pc-windows-msvc, os: windows-2022, arch: x86_64 } + - { target: x86_64-pc-windows-msvc, os: windows-2022, arch: x86_64, vcpkg-triplet: x64-windows-static } # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } steps: - name: Export GitHub Actions cache environment variables @@ -109,7 +109,7 @@ jobs: - name: Install vcpkg dependencies run: | - $VCPKG_ROOT/vcpkg install --triplet x64-windows-static --x-install-root="$VCPKG_ROOT/installed" + $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - name: Build rustdesk @@ -212,7 +212,7 @@ jobs: job: # - { target: i686-pc-windows-msvc , os: windows-2022 } # - { target: x86_64-pc-windows-gnu , os: windows-2022 } - - { target: i686-pc-windows-msvc, os: windows-2022, arch: x86 } + - { target: i686-pc-windows-msvc, os: windows-2022, arch: x86, vcpkg-triplet: x86-windows-static } # - { target: aarch64-pc-windows-msvc, os: windows-2022 } steps: - name: Export GitHub Actions cache environment variables @@ -249,7 +249,7 @@ jobs: - name: Install vcpkg dependencies run: | - $VCPKG_ROOT/vcpkg install --triplet x86-windows-static --x-install-root="$VCPKG_ROOT/installed" + $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - name: Build rustdesk @@ -408,6 +408,7 @@ jobs: arch: aarch64, target: aarch64-apple-ios, os: macos-13, + vcpkg-triplet: arm64-ios, } steps: - name: Export GitHub Actions cache environment variables @@ -435,7 +436,7 @@ jobs: - name: Install vcpkg dependencies run: | - $VCPKG_ROOT/vcpkg install --triplet arm64-ios --x-install-root="$VCPKG_ROOT/installed" + $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - name: Install Rust toolchain @@ -969,6 +970,7 @@ jobs: distro: ubuntu18.04, on: ubuntu-20.04, deb_arch: amd64, + vcpkg-triplet: x64-linux, } - { arch: aarch64, @@ -976,6 +978,7 @@ jobs: distro: ubuntu18.04, on: [self-hosted, Linux, ARM64], deb_arch: arm64, + vcpkg-triplet: arm64-linux, } steps: - name: Export GitHub Actions cache environment variables @@ -1043,14 +1046,7 @@ jobs: - name: Install vcpkg dependencies if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' run: | - case ${{ matrix.job.target }} in - aarch64-unknown-linux-gnu) - $VCPKG_ROOT/vcpkg install --triplet arm64-linux --x-install-root="$VCPKG_ROOT/installed" - ;; - x86_64-unknown-linux-gnu) - $VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed" - ;; - esac + $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - name: Restore bridge files @@ -1281,6 +1277,7 @@ jobs: distro: ubuntu18.04, deb_arch: amd64, sciter_arch: x64, + vcpkg-triplet: x64-linux, } - { arch: armv7, @@ -1289,6 +1286,7 @@ jobs: distro: ubuntu18.04-rustdesk, deb_arch: armhf, sciter_arch: arm32, + vcpkg-triplet: arm-linux, } steps: - name: Export GitHub Actions cache environment variables @@ -1344,7 +1342,7 @@ jobs: - name: Install vcpkg dependencies run: | cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake - $VCPKG_ROOT/vcpkg install --triplet arm-linux --x-install-root="$VCPKG_ROOT/installed" + $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - uses: rustdesk-org/run-on-arch-action@amd64-support diff --git a/res/vcpkg/opus/fix-pkgconfig-version.patch b/res/vcpkg/opus/fix-pkgconfig-version.patch new file mode 100644 index 00000000000..ef9f7229c41 --- /dev/null +++ b/res/vcpkg/opus/fix-pkgconfig-version.patch @@ -0,0 +1,15 @@ +diff --git a/cmake/OpusPackageVersion.cmake b/cmake/OpusPackageVersion.cmake +index 447ce3b..15ebd8e 100644 +--- a/cmake/OpusPackageVersion.cmake ++++ b/cmake/OpusPackageVersion.cmake +@@ -4,7 +4,9 @@ endif() + set(__opus_version INCLUDED) + + function(get_package_version PACKAGE_VERSION PROJECT_VERSION) +- ++ set(PACKAGE_VERSION "0" CACHE STRING "opus package version") ++ set(PROJECT_VERSION "0" CACHE STRING "opus project version") ++ return() + find_package(Git) + if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git") + execute_process(COMMAND ${GIT_EXECUTABLE} diff --git a/res/vcpkg/opus/portfile.cmake b/res/vcpkg/opus/portfile.cmake new file mode 100644 index 00000000000..3e37dc1d8c2 --- /dev/null +++ b/res/vcpkg/opus/portfile.cmake @@ -0,0 +1,61 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO xiph/opus + REF "v${VERSION}" + SHA512 ba79ad035993e7bc4c09b7d77964ba913eb0b2be33305e8a04a8c49aaab21c4d96ac828e31ae45484896105851fdfc8c305c63c8400e4481dd76c62a1c12286b + HEAD_REF main + PATCHES fix-pkgconfig-version.patch +) + +vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS + FEATURES + avx2 AVX2_SUPPORTED +) + +set(ADDITIONAL_OPUS_OPTIONS "") +if(VCPKG_TARGET_IS_MINGW) + set(STACK_PROTECTOR OFF) + string(APPEND VCPKG_C_FLAGS "-D_FORTIFY_SOURCE=0") + string(APPEND VCPKG_CXX_FLAGS "-D_FORTIFY_SOURCE=0") + if(VCPKG_TARGET_ARCHITECTURE MATCHES "^(ARM|arm)64$") + list(APPEND ADDITIONAL_OPUS_OPTIONS "-DOPUS_USE_NEON=OFF") # for version 1.3.1 (remove for future Opus release) + list(APPEND ADDITIONAL_OPUS_OPTIONS "-DOPUS_DISABLE_INTRINSICS=ON") # for HEAD (and future Opus release) + endif() +elseif(VCPKG_TARGET_IS_EMSCRIPTEN) + set(STACK_PROTECTOR OFF) +else() + set(STACK_PROTECTOR ON) +endif() + +if((VCPKG_TARGET_IS_LINUX AND VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") OR + (VCPKG_TARGET_IS_ANDROID AND VCPKG_TARGET_ARCHITECTURE STREQUAL "arm" AND VCPKG_CMAKE_CONFIGURE_OPTIONS MATCHES "ANDROID_ARM_NEON")) + message(STATUS "Disabling ARM NEON and intrinsics on ${TARGET_TRIPLET}") + list(APPEND ADDITIONAL_OPUS_OPTIONS "-DOPUS_DISABLE_INTRINSICS=ON -DCOMPILER_SUPPORTS_NEON=OFF") # for HEAD (and future Opus release) +endif() + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS ${FEATURE_OPTIONS} + -DPACKAGE_VERSION=${VERSION} + -DOPUS_STACK_PROTECTOR=${STACK_PROTECTOR} + -DOPUS_INSTALL_PKG_CONFIG_MODULE=ON + -DOPUS_INSTALL_CMAKE_CONFIG_MODULE=ON + -DOPUS_BUILD_PROGRAMS=OFF + -DOPUS_BUILD_TESTING=OFF + ${ADDITIONAL_OPUS_OPTIONS} + MAYBE_UNUSED_VARIABLES + OPUS_USE_NEON + OPUS_DISABLE_INTRINSICS +) +vcpkg_cmake_install() +vcpkg_copy_pdbs() + +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/Opus) +vcpkg_fixup_pkgconfig(SYSTEM_LIBRARIES m) + + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/lib/cmake" + "${CURRENT_PACKAGES_DIR}/lib/cmake" + "${CURRENT_PACKAGES_DIR}/debug/include") + +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING") diff --git a/res/vcpkg/opus/vcpkg.json b/res/vcpkg/opus/vcpkg.json new file mode 100644 index 00000000000..4edeaa1e042 --- /dev/null +++ b/res/vcpkg/opus/vcpkg.json @@ -0,0 +1,22 @@ +{ + "name": "opus", + "version": "1.5.1", + "description": "Totally open, royalty-free, highly versatile audio codec", + "homepage": "https://github.com/xiph/opus", + "license": "BSD-3-Clause", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ], + "features": { + "avx2": { + "description": "Builds the library with avx2 instruction set" + } + } +} From 9349210a87d8838ae805a7bf8e85aef77408d5de Mon Sep 17 00:00:00 2001 From: Kleofass <4000163+Kleofass@users.noreply.github.com> Date: Wed, 3 Jul 2024 15:40:57 +0300 Subject: [PATCH 196/335] Update lv.rs (#8591) --- src/lang/lv.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 65384bc932b..1f2271edb72 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -618,16 +618,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nekad"), ("During controlled", "Lietošanas laikā"), ("During service is on", "Kamēr pakalpojums ir ieslēgts"), - ("Capture screen using DirectX", ""), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("Capture screen using DirectX", "Tvert ekrānu, izmantojot DirectX"), + ("Back", "Atpakaļ"), + ("Apps", "Lietotnes"), + ("Volume up", "Skaļāk"), + ("Volume down", "Klusāk"), + ("Power", "Ieslēgšana"), + ("Telegram bot", "Telegram robots"), + ("enable-bot-tip", "Ja iespējojat šo funkciju, varat saņemt 2FA kodu no sava robota. Tas var darboties arī kā savienojuma paziņojums."), + ("enable-bot-desc", "1. Atveriet tērzēšanu ar @BotFather.\n2. Nosūtiet komandu \"/newbot\". Pēc šīs darbības pabeigšanas jūs saņemsit pilnvaru.\n3. Sāciet tērzēšanu ar jaunizveidoto robotprogrammatūru. Lai to aktivizētu, nosūtiet ziņojumu, kas sākas ar slīpsvītru (\"/\"), piemēram, \"/hello\".\n"), + ("cancel-2fa-confirm-tip", "Vai tiešām vēlaties atcelt 2FA?"), + ("cancel-bot-confirm-tip", "Vai tiešām vēlaties atcelt Telegram robotu?"), ].iter().cloned().collect(); } From 92f570831d2ae7a772fa7dde0e764cf562416c68 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 3 Jul 2024 21:37:25 +0800 Subject: [PATCH 197/335] return x11 for tty session --- libs/hbb_common/src/platform/linux.rs | 29 ++++----------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 220512c0d31..34855f235d4 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -85,40 +85,19 @@ pub fn get_display_server_of_session(session: &str) -> String { run_loginctl(Some(vec!["show-session", "-p", "Type", session])) // Check session type of the session { - let display_server = String::from_utf8_lossy(&output.stdout) + String::from_utf8_lossy(&output.stdout) .replace("Type=", "") .trim_end() .into(); - if display_server == "tty" { - // If the type is tty... - if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "TTY", session])) - // 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 \"{tty}.\\\\+Xorg\"")) - // And check if Xorg is running on that tty - { - if xorg_results.trim_end() != "" { - // If it is, manually return "x11", otherwise return tty - return "x11".to_owned(); - } - } - } - } - display_server } else { "".to_owned() }; if display_server.is_empty() || display_server == "tty" { - // loginctl has not given the expected output. try something else. if let Ok(sestype) = std::env::var("XDG_SESSION_TYPE") { - display_server = sestype; + if !sestype.is_empty() { + return sestype.to_lowercase(); + } } - } - if display_server == "" { display_server = "x11".to_owned(); } display_server.to_lowercase() From bea65f873963401b72a34a305e38948b84c17571 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 3 Jul 2024 21:47:13 +0800 Subject: [PATCH 198/335] fix ci --- libs/hbb_common/src/platform/linux.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 34855f235d4..5e03b6816e4 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -88,7 +88,7 @@ pub fn get_display_server_of_session(session: &str) -> String { String::from_utf8_lossy(&output.stdout) .replace("Type=", "") .trim_end() - .into(); + .into() } else { "".to_owned() }; From 94addb162b4811b9e34fc5817a509ce1157f4a5c Mon Sep 17 00:00:00 2001 From: jxdv Date: Thu, 4 Jul 2024 03:43:09 +0000 Subject: [PATCH 199/335] update sk && cz tr (#8595) * update sk translations * update cz translations --- src/lang/cs.rs | 4 ++-- src/lang/sk.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/cs.rs b/src/lang/cs.rs index be42f514fca..3b68bbcb7ed 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -627,7 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram bot"), ("enable-bot-tip", "Pokud tuto funkci povolíte, můžete od svého bota obdržet kód 2FA. Může také fungovat jako oznámení o připojení."), ("enable-bot-desc", "1, Otevřete chat s @BotFather.\n2, Pošlete příkaz \"/newbot\". Po dokončení tohoto kroku obdržíte token.\n3, Spusťte chat s nově vytvořeným botem. Pro jeho aktivaci odešlete zprávu začínající lomítkem vpřed (\"/\"), například \"/hello\".\n"), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("cancel-2fa-confirm-tip", "Jste si jisti, že chcete zrušit 2FA?"), + ("cancel-bot-confirm-tip", "Jste si jisti, že chcete zrušit bota Telegramu?"), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index c7da5843331..2ee8d2a3bf8 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -627,7 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram bot"), ("enable-bot-tip", "Ak túto funkciu povolíte, kód 2FA môžete dostať od svojho bota. Môže fungovať aj ako upozornenie na pripojenie."), ("enable-bot-desc", "1, Otvorte chat s @BotFather.\n2, Odošlite príkaz \"/newbot\". Po dokončení tohto kroku dostanete token.\n3, Spustite chat s novo vytvoreným botom. Odošlite správu začínajúcu lomítkom vpred (\"/\"), napríklad \"/hello\", aby ste ho aktivovali.\n"), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("cancel-2fa-confirm-tip", "Ste si istí, že chcete zrušiť službu 2FA?"), + ("cancel-bot-confirm-tip", "Ste si istí, že chcete zrušiť bota Telegramu?"), ].iter().cloned().collect(); } From 86ff768241638823fe50a15d225fc1b82d9a7b7e Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 4 Jul 2024 20:18:53 +0800 Subject: [PATCH 200/335] clear unwrap (#8605) Signed-off-by: fufesou --- libs/clipboard/src/platform/fuse.rs | 8 ++++++-- libs/clipboard/src/platform/mod.rs | 4 ++-- libs/clipboard/src/platform/unix/local_file.rs | 8 ++++---- libs/clipboard/src/platform/unix/mod.rs | 6 ++---- libs/clipboard/src/platform/unix/url.rs | 8 ++++---- libs/clipboard/src/platform/unix/x11.rs | 8 +++++++- libs/clipboard/src/platform/windows.rs | 12 ++++++------ src/clipboard.rs | 8 ++++---- src/flutter.rs | 9 +++------ src/hbbs_http/http_client.rs | 18 ++++++++++-------- src/server.rs | 7 ++++++- 11 files changed, 54 insertions(+), 42 deletions(-) diff --git a/libs/clipboard/src/platform/fuse.rs b/libs/clipboard/src/platform/fuse.rs index 950dff958ff..c5fe60f56e3 100644 --- a/libs/clipboard/src/platform/fuse.rs +++ b/libs/clipboard/src/platform/fuse.rs @@ -795,7 +795,11 @@ impl FuseNode { conn_id: desc.conn_id, stream_id: rand::random(), index: inode as usize - 2, - name: desc.name.to_str().unwrap().to_owned(), + name: desc + .name + .to_str() + .map(|s| s.to_string()) + .unwrap_or_default(), parent: None, attributes: InodeAttributes::from_description(inode, desc), children: Vec::new(), @@ -1140,7 +1144,7 @@ mod fuse_test { } fn build_single_file(prefix: &str) { - let raw_name = "衬衫的价格为 9 镑 15 便士.txt"; + let raw_name = "simple_test_file.txt"; let f_name = if prefix == "" { raw_name.to_string() } else { diff --git a/libs/clipboard/src/platform/mod.rs b/libs/clipboard/src/platform/mod.rs index 0a2988d2cfd..2be4ce809da 100644 --- a/libs/clipboard/src/platform/mod.rs +++ b/libs/clipboard/src/platform/mod.rs @@ -52,9 +52,9 @@ pub fn create_cliprdr_context( log::warn!("umount {:?} may fail: {:?}", mnt_path, e); } - let unix_ctx = unix::ClipboardContext::new(timeout, mnt_path.parse().unwrap())?; + let unix_ctx = unix::ClipboardContext::new(timeout, mnt_path.parse()?)?; log::debug!("start cliprdr FUSE"); - unix_ctx.run().expect("failed to start cliprdr FUSE"); + unix_ctx.run()?; Ok(Box::new(unix_ctx) as Box<_>) } diff --git a/libs/clipboard/src/platform/unix/local_file.rs b/libs/clipboard/src/platform/unix/local_file.rs index c9186c4faf2..e24712efa4d 100644 --- a/libs/clipboard/src/platform/unix/local_file.rs +++ b/libs/clipboard/src/platform/unix/local_file.rs @@ -113,7 +113,7 @@ impl LocalFile { let win32_time = self .last_write_time .duration_since(std::time::UNIX_EPOCH) - .unwrap() + .unwrap_or_default() .as_nanos() as u64 / 100 + LDAP_EPOCH_DELTA; @@ -188,7 +188,7 @@ impl LocalFile { pub fn read_exact_at(&mut self, buf: &mut [u8], offset: u64) -> Result<(), CliprdrError> { self.load_handle()?; - let handle = self.handle.as_mut().unwrap(); + let handle = self.handle.as_mut()?; if offset != self.offset.load(Ordering::Relaxed) { handle @@ -238,9 +238,9 @@ pub(super) fn construct_file_list(paths: &[PathBuf]) -> Result, C })?; if mt.is_dir() { - let dir = std::fs::read_dir(path).unwrap(); + let dir = std::fs::read_dir(path)?; for entry in dir { - let entry = entry.unwrap(); + let entry = entry?; let path = entry.path(); constr_file_lst(&path, file_list, visited)?; } diff --git a/libs/clipboard/src/platform/unix/mod.rs b/libs/clipboard/src/platform/unix/mod.rs index a58361f82b2..9a086109473 100644 --- a/libs/clipboard/src/platform/unix/mod.rs +++ b/libs/clipboard/src/platform/unix/mod.rs @@ -383,13 +383,11 @@ impl ClipboardContext { let file_contents_id = fmt_lst .iter() .find(|(_, name)| name == FILECONTENTS_FORMAT_NAME) - .map(|(id, _)| *id) - .unwrap(); + .map(|(id, _)| *id)?; let file_descriptor_id = fmt_lst .iter() .find(|(_, name)| name == FILEDESCRIPTORW_FORMAT_NAME) - .map(|(id, _)| *id) - .unwrap(); + .map(|(id, _)| *id)?; add_remote_format(FILECONTENTS_FORMAT_NAME, file_contents_id); add_remote_format(FILEDESCRIPTORW_FORMAT_NAME, file_descriptor_id); diff --git a/libs/clipboard/src/platform/unix/url.rs b/libs/clipboard/src/platform/unix/url.rs index ce97810c4ad..2ae520f4dfc 100644 --- a/libs/clipboard/src/platform/unix/url.rs +++ b/libs/clipboard/src/platform/unix/url.rs @@ -7,9 +7,9 @@ use crate::CliprdrError; // url encode and decode is needed const ENCODE_SET: percent_encoding::AsciiSet = percent_encoding::CONTROLS.add(b' ').remove(b'/'); -pub(super) fn encode_path_to_uri(path: &PathBuf) -> String { - let encoded = percent_encoding::percent_encode(path.to_str().unwrap().as_bytes(), &ENCODE_SET) - .to_string(); +pub(super) fn encode_path_to_uri(path: &PathBuf) -> io::Result { + let encoded = + percent_encoding::percent_encode(path.to_str()?.as_bytes(), &ENCODE_SET).to_string(); format!("file://{}", encoded) } @@ -54,7 +54,7 @@ mod uri_test { #[test] fn test_conversion() { let path = std::path::PathBuf::from("/home/rustdesk/pictures/🖼️.png"); - let uri = super::encode_path_to_uri(&path); + let uri = super::encode_path_to_uri(&path).unwrap(); assert_eq!( uri, "file:///home/rustdesk/pictures/%F0%9F%96%BC%EF%B8%8F.png" diff --git a/libs/clipboard/src/platform/unix/x11.rs b/libs/clipboard/src/platform/unix/x11.rs index 4ca3a2c0b50..41b64264044 100644 --- a/libs/clipboard/src/platform/unix/x11.rs +++ b/libs/clipboard/src/platform/unix/x11.rs @@ -89,7 +89,13 @@ impl SysClipboard for X11Clipboard { fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> { *self.former_file_list.lock() = paths.to_vec(); - let uri_list: Vec = paths.iter().map(encode_path_to_uri).collect(); + let uri_list: Vec = { + let mut v = Vec::new(); + for path in paths { + v.push(encode_path_to_uri(path)?); + } + v + }; let uri_list = uri_list.join("\n"); let text_uri_list_data = uri_list.as_bytes().to_vec(); let gnome_copied_files_data = ["copy\n".as_bytes(), uri_list.as_bytes()].concat(); diff --git a/libs/clipboard/src/platform/windows.rs b/libs/clipboard/src/platform/windows.rs index 1148112b589..8fc917c6fee 100644 --- a/libs/clipboard/src/platform/windows.rs +++ b/libs/clipboard/src/platform/windows.rs @@ -5,16 +5,16 @@ #![allow(non_snake_case)] #![allow(deref_nullptr)] -use std::{ - boxed::Box, - ffi::{CStr, CString}, - result::Result, -}; use crate::{ allow_err, send_data, ClipboardFile, CliprdrError, CliprdrServiceContext, ResultType, ERR_CODE_INVALID_PARAMETER, ERR_CODE_SERVER_FUNCTION_NONE, VEC_MSG_CHANNEL, }; use hbb_common::log; +use std::{ + boxed::Box, + ffi::{CStr, CString}, + result::Result, +}; // only used error code will be recorded here /// success @@ -779,7 +779,7 @@ pub fn server_format_list( } else { let n = match CString::new(format.1) { Ok(n) => n, - Err(_) => CString::new("").unwrap(), + Err(_) => CString::new("").unwrap_or_default(), }; CLIPRDR_FORMAT { formatId: format.0 as UINT32, diff --git a/src/clipboard.rs b/src/clipboard.rs index 973f948f654..9dbdfddda20 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -60,7 +60,7 @@ fn parse_plain_uri_list(v: Vec) -> Result { #[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] impl ClipboardContext { - pub fn new() -> Result { + pub fn new(_listen: bool) -> Result { let clipboard = get_clipboard()?; let string_getter = clipboard .getter @@ -95,14 +95,14 @@ impl ClipboardContext { .load(clip, self.string_getter, prop, TIMEOUT) .map_err(|e| e.to_string())?; - let file_urls = get_clipboard()?.load(clip, self.text_uri_list, prop, TIMEOUT); + let file_urls = get_clipboard()?.load(clip, self.text_uri_list, prop, TIMEOUT)?; - if file_urls.is_err() || file_urls.as_ref().unwrap().is_empty() { + if file_urls.is_err() || file_urls.as_ref().is_empty() { log::trace!("clipboard get text, no file urls"); return String::from_utf8(text_content).map_err(|e| e.to_string()); } - let file_urls = parse_plain_uri_list(file_urls.unwrap())?; + let file_urls = parse_plain_uri_list(file_urls)?; let text_content = String::from_utf8(text_content).map_err(|e| e.to_string())?; diff --git a/src/flutter.rs b/src/flutter.rs index 0cce2ae714c..d1e44008b86 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1069,12 +1069,9 @@ impl FlutterHandler { // 1. "display 1" will not send the event. // 2. "displays 0&1" will not send the event. Because it uses texutre render for now. if !is_sent { - self.display_rgbas - .write() - .unwrap() - .get_mut(&display) - .unwrap() - .valid = false; + if let Some(rgba_data) = self.display_rgbas.write().unwrap().get_mut(&display) { + rgba_data.valid = false; + } } } diff --git a/src/hbbs_http/http_client.rs b/src/hbbs_http/http_client.rs index c4bb2452cdf..944e84ae6ff 100644 --- a/src/hbbs_http/http_client.rs +++ b/src/hbbs_http/http_client.rs @@ -24,14 +24,16 @@ macro_rules! configure_http_client { if let Some(auth) = proxy.intercept.maybe_auth() { let basic_auth = format!("Basic {}", auth.get_basic_authorization()); - builder = builder.default_headers( - vec![( - reqwest::header::PROXY_AUTHORIZATION, - basic_auth.parse().unwrap(), - )] - .into_iter() - .collect(), - ); + if let Ok(auth) = basic_auth.parse() { + builder = builder.default_headers( + vec![( + reqwest::header::PROXY_AUTHORIZATION, + auth, + )] + .into_iter() + .collect(), + ); + } } builder.build().unwrap_or_else(|e| { info!("Failed to create a proxied client: {}", e); diff --git a/src/server.rs b/src/server.rs index e1f5d634cf8..fc33188f46b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -360,7 +360,12 @@ impl Server { self.services .keys() .filter(|k| { - Self::is_video_service_name(k) && self.services.get(*k).unwrap().is_subed(conn_id) + Self::is_video_service_name(k) + && self + .services + .get(*k) + .map(|s| s.is_subed(conn_id)) + .unwrap_or(false) }) .count() } From dfc224ec015183ac210f2bc1087802eb3f368a58 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 4 Jul 2024 20:20:25 +0800 Subject: [PATCH 201/335] fix: #8599 (#8603) Signed-off-by: fufesou --- src/keyboard.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 6f7001e93b6..21da622c4b6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -457,16 +457,29 @@ pub fn is_letter_rdev_key(key: &rdev::Key) -> bool { ) } +// https://github.com/rustdesk/rustdesk/issues/8599 +// We just add these keys as letter keys. +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn is_letter_rdev_key_ex(key: &rdev::Key) -> bool { + matches!( + key, + Key::LeftBracket | Key::RightBracket | Key::SemiColon | Key::Quote | Key::Comma | Key::Dot + ) +} + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_numpad_key(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if is_numpad_rdev_key(&key)) } +// Check is letter key for lock modes. +// Only letter keys need to check and send Lock key state. #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn is_letter_key(event: &Event) -> bool { - matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if is_letter_rdev_key(&key)) +fn is_letter_key_4_lock_modes(event: &Event) -> bool { + matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if (is_letter_rdev_key(&key) || is_letter_rdev_key_ex(&key))) } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -588,7 +601,7 @@ pub fn event_to_key_events( let is_numpad_key = is_numpad_key(&event); #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate || is_numpad_key { - let is_letter_key = is_letter_key(&event); + let is_letter_key = is_letter_key_4_lock_modes(&event); for key_event in &mut key_events { if let Some(lock_modes) = _lock_modes { parse_add_lock_modes_modifiers(key_event, lock_modes, is_numpad_key, is_letter_key); From 4eeee5b7ee4d1651a94638c20cf1d9ba020ed2f3 Mon Sep 17 00:00:00 2001 From: Nevaran Date: Thu, 4 Jul 2024 15:21:06 +0300 Subject: [PATCH 202/335] Update bg.rs (#8611) Added more translated parts, some might need more context to be more accurate --- src/lang/bg.rs | 506 ++++++++++++++++++++++++------------------------- 1 file changed, 253 insertions(+), 253 deletions(-) diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 8283a7dbd58..7a40985b4f5 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -1,143 +1,143 @@ lazy_static::lazy_static! { pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ - ("Status", ""), - ("Your Desktop", ""), + ("Status", "Статус"), + ("Your Desktop", "Твоят Работен Плот"), ("desk_tip", "Вашият работен плот може да бъде достъпен с този идентификационен код и парола."), - ("Password", ""), - ("Ready", ""), - ("Established", ""), + ("Password", "Парола"), + ("Ready", "Готово"), + ("Established", "Установен"), ("connecting_status", "Свързване с RustDesk мрежата..."), - ("Enable service", ""), - ("Start service", ""), - ("Service is running", ""), - ("Service is not running", ""), + ("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", ""), - ("Remove", ""), - ("Refresh random password", ""), - ("Set your own password", ""), - ("Enable keyboard/mouse", ""), - ("Enable clipboard", ""), - ("Enable file transfer", ""), - ("Enable TCP tunneling", ""), - ("IP Whitelisting", ""), + ("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", ""), - ("Your new ID", ""), - ("length %min% to %max%", ""), - ("starts with a letter", ""), - ("allowed characters", ""), + ("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", ""), + ("Website", "Уебсайт"), + ("About", "Относно програмата"), ("Slogan_tip", "Направено от сърце в този хаотичен свят!"), - ("Privacy Statement", ""), - ("Mute", ""), + ("Privacy Statement", "Декларация за поверителност"), + ("Mute", "Без звук"), ("Build Date", "Дата на изграждане"), - ("Version", ""), - ("Home", ""), + ("Version", "Версия"), + ("Home", "Начало"), ("Audio Input", "Аудио вход"), - ("Enhancements", ""), + ("Enhancements", "Подобрения"), ("Hardware Codec", "Хардуерен кодек"), - ("Adaptive bitrate", ""), + ("Adaptive bitrate", "Адаптивен битрейт"), ("ID Server", "ID сървър"), ("Relay Server", "Релейн сървър"), ("API Server", "API сървър"), ("invalid_http", "трябва да започва с http:// или https://"), - ("Invalid IP", ""), - ("Invalid format", ""), + ("Invalid IP", "Невалиден IP"), + ("Invalid format", "Невалиден формат"), ("server_not_support", "Все още не се поддържа от сървъра"), - ("Not available", ""), - ("Too frequent", ""), - ("Cancel", ""), - ("Skip", ""), - ("Close", ""), - ("Retry", ""), - ("OK", ""), + ("Not available", "Не е наличен"), + ("Too frequent", "Твърде често"), + ("Cancel", "Отказ"), + ("Skip", "Пропускане"), + ("Close", "Затвори"), + ("Retry", "Опитайте отново"), + ("OK", "Добре"), ("Password Required", "Изисква се парола"), - ("Please enter your password", ""), - ("Remember password", ""), + ("Please enter your password", "Моля въведете паролата си"), + ("Remember password", "Запомни паролата"), ("Wrong Password", "Грешна парола"), - ("Do you want to enter again?", ""), + ("Do you want to enter again?", "Искате ли да въведете отново?"), ("Connection Error", "Грешка при свързване"), - ("Error", ""), - ("Reset by the peer", ""), - ("Connecting...", ""), - ("Connection in progress. Please wait.", ""), - ("Please try 1 minute later", ""), + ("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", ""), + ("Successful", "Успешен опит"), + ("Connected, waiting for image...", "Свързано, чака се изображение..."), + ("Name", "Име"), + ("Type", "Тип"), + ("Modified", "Променен"), + ("Size", "Размер"), ("Show Hidden Files", "Показване на скрити файлове"), - ("Receive", ""), - ("Send", ""), + ("Receive", "Получаване"), + ("Send", "Пращане"), ("Refresh File", "Опресняване на файла"), - ("Local", ""), - ("Remote", ""), + ("Local", "Локално"), + ("Remote", "Отдалечено"), ("Remote Computer", "Отдалечен компютър"), ("Local Computer", "Локален компютър"), ("Confirm Delete", "Потвърдете изтриването"), - ("Delete", ""), - ("Properties", ""), + ("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", ""), + ("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", ""), + ("Deleting", "Изтриване"), + ("files", "файлове"), ("Waiting", ""), - ("Finished", ""), - ("Speed", ""), + ("Finished", "Готово"), + ("Speed", "Скорост"), ("Custom Image Quality", "Персонализирано качество на изображението"), - ("Privacy mode", ""), - ("Block user input", ""), - ("Unblock user input", ""), + ("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", ""), - ("Failed to connect to rendezvous server", ""), - ("Please try later", ""), - ("Remote desktop is offline", ""), - ("Key mismatch", ""), + ("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", "Неуспешно свързване със сървъра за рандеву"), + ("Please try later", "Моля опитайте по-късно"), + ("Remote desktop is offline", "Отдалеченият работен плот е офлайн"), + ("Key mismatch", "Ключово несъответствие"), ("Timeout", ""), ("Failed to connect to relay server", ""), ("Failed to connect via rendezvous server", ""), @@ -146,89 +146,89 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Set Password", "Задайте парола"), ("OS Password", "Парола на Операционната система"), ("install_tip", "Поради UAC, RustDesk в някои случай не може да работи правилно като отдалечена достъп. За да заобиколите UAC, моля, щракнете върху бутона по-долу, за да инсталирате RustDesk в системата."), - ("Click to upgrade", ""), - ("Click to download", ""), - ("Click to update", ""), - ("Configure", ""), + ("Click to upgrade", "Кликнете, за да надстроите"), + ("Click to download", "Кликнете, за да изтеглите"), + ("Click to update", "Кликнете, за да актуализирате"), + ("Configure", "Конфигуриране"), ("config_acc", "За да управлявате вашия работен плот дистанционно, трябва да предоставите на RustDesk разрешения \"Достъпност\"."), ("config_screen", "In order to access your Desktop remotely, you need to grant RustDesk \"Screen Recording\" permissions."), - ("Installing ...", ""), - ("Install", ""), - ("Installation", ""), + ("Installing ...", "Инсталиране..."), + ("Install", "Инсталирай"), + ("Installation", "Инсталация"), ("Installation Path", "Инсталационен път"), - ("Create start menu shortcuts", ""), - ("Create desktop icon", ""), + ("Create start menu shortcuts", "Създайте преки пътища в менюто 'Старт'."), + ("Create desktop icon", "Създайте икона на работния плот"), ("agreement_tip", "Стартирайки инсталацията, вие приемате лицензионното споразумение."), ("Accept and Install", "Приемете и инсталирайте"), ("End-user license agreement", ""), - ("Generating ...", ""), - ("Your installation is lower version.", ""), + ("Generating ...", "Генериране..."), + ("Your installation is lower version.", "Вашата инсталация е по-ниска версия."), ("not_close_tcp_tip", "Не затваряйте този прозорец, докато използвате тунела"), - ("Listening ...", ""), + ("Listening ...", "Слушане..."), ("Remote Host", "Отдалечен хост"), ("Remote Port", "Отдалечен порт"), - ("Action", ""), - ("Add", ""), + ("Action", "Действие"), + ("Add", "Добави"), ("Local Port", "Локален порт"), ("Local Address", "Локален адрес"), ("Change Local Port", "Промяна на локалният порт"), ("setup_server_tip", "За по-бърза връзка, моля направете свой собствен сървър"), ("Too short, at least 6 characters.", ""), ("The confirmation is not identical.", ""), - ("Permissions", ""), - ("Accept", ""), - ("Dismiss", ""), - ("Disconnect", ""), + ("Permissions", "Разрешения"), + ("Accept", "Приеми"), + ("Dismiss", "Отхвърляне"), + ("Disconnect", "Прекъснете връзката"), ("Enable file copy and paste", ""), - ("Connected", ""), - ("Direct and encrypted connection", ""), - ("Relayed and encrypted connection", ""), - ("Direct and unencrypted connection", ""), - ("Relayed and unencrypted connection", ""), + ("Connected", "Свързан"), + ("Direct and encrypted connection", "Директна и криптирана връзка"), + ("Relayed and encrypted connection", "Препредадена и криптирана връзка"), + ("Direct and unencrypted connection", "Директна и некриптирана връзка"), + ("Relayed and unencrypted connection", "Препредадена и некриптирана връзка"), ("Enter Remote ID", "Въведете дистанционно ID"), - ("Enter your password", ""), + ("Enter your password", "Въведете паролата си"), ("Logging in...", ""), - ("Enable RDP session sharing", ""), + ("Enable RDP session sharing", "Активирайте споделянето на RDP сесия"), ("Auto Login", "Автоматично вписване (Валидно само ако зададете \"Заключване след края на сесията\")"), - ("Enable direct IP access", ""), - ("Rename", ""), - ("Space", ""), - ("Create desktop shortcut", ""), + ("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", ""), - ("Reboot required", ""), - ("Unsupported display server", ""), + ("Please enter the folder name", "Моля, въведете името на папката"), + ("Fix it", "Оправи го"), + ("Warning", "Внимание"), + ("Login screen using Wayland is not supported", "Екранът за влизане с помощта на Wayland не се поддържа"), + ("Reboot required", "Изисква се рестартиране"), + ("Unsupported display server", "Неподдържан сървър за дисплея"), ("x11 expected", ""), - ("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", ""), + ("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", ""), + ("Logout", "Излез от профила си"), + ("Tags", "Етикети"), + ("Search ID", "Търсене на ID"), ("whitelist_sep", "Разделени със запетая, точка и запетая, интервали или нов ред"), - ("Add ID", ""), - ("Add Tag", "Добавяне на етикет"), - ("Unselect all tags", ""), - ("Network error", ""), - ("Username missed", ""), - ("Password missed", ""), + ("Add ID", "Добави ID"), + ("Add Tag", "Добави етикет"), + ("Unselect all tags", "Премахнете избора на всички етикети"), + ("Network error", "Мрежова грешка"), + ("Username missed", "Пропуснато потребителско име"), + ("Password missed", "Пропусната парола"), ("Wrong credentials", "Wrong username or password"), ("The verification code is incorrect or has expired", ""), ("Edit Tag", "Edit tag"), @@ -236,19 +236,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Favorites", ""), ("Add to Favorites", "Добави към любими"), ("Remove from Favorites", "Премахване от любими"), - ("Empty", ""), + ("Empty", "Празно"), ("Invalid folder name", ""), ("Socks5 Proxy", "Socks5 прокси"), ("Socks5/Http(s) Proxy", "Socks5/Http(s) прокси"), ("Discovered", ""), ("install_daemon_tip", "За стартиране с компютъра трябва да инсталирате системна услуга."), - ("Remote ID", ""), - ("Paste", ""), - ("Paste here?", ""), + ("Remote ID", "Дистанционно ID"), + ("Paste", "Постави"), + ("Paste here?", "Постави тук?"), ("Are you sure to close the connection?", "Сигурни ли сте, че искате да затворите връзката?"), ("Download new version", ""), - ("Touch mode", ""), - ("Mouse mode", ""), + ("Touch mode", "Режим тъч (сензорен)"), + ("Mouse mode", "Режим мишка"), ("One-Finger Tap", "Докосване с един пръст"), ("Left Mouse", "Ляв бутон на мишката"), ("One-Long Tap", "Едно дълго докосване"), @@ -268,16 +268,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Note", ""), ("Connection", ""), ("Share Screen", "Сподели екран"), - ("Chat", ""), - ("Total", ""), - ("items", ""), - ("Selected", ""), + ("Chat", "Чат"), + ("Total", "Обшо"), + ("items", "елементи"), + ("Selected", "Избрано"), ("Screen Capture", "Заснемане на екрана"), ("Input Control", "Контрол на въвеждане"), ("Audio Capture", "Аудио записване"), ("File Connection", "Файлова връзка"), ("Screen Connection", "Свързване на екрана"), - ("Do you accept?", ""), + ("Do you accept?", "Приемате ли?"), ("Open System Setting", "Отворете системната настройка"), ("How to get Android input permission?", ""), ("android_input_permission_tip1", "За да може отдалечено устройство да управлява вашето Android устройство чрез мишка или докосване, трябва да разрешите на RustDesk да използва услугата \"Достъпност\"."), @@ -288,46 +288,46 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_version_audio_tip", "Текущата версия на Android не поддържа аудио заснемане, моля, актуализирайте устройството с Android 10 или по-нова версия."), ("android_start_service_tip", "Докоснете [Start service] или активирайте разрешение [Screen Capture], за да стартирате услугата за споделяне на екрана."), ("android_permission_may_not_change_tip", "Разрешенията за установени връзки може да не се променят незабавно, докато не се свържете отново."), - ("Account", ""), - ("Overwrite", ""), + ("Account", "Акаунт"), + ("Overwrite", "Презаписване"), ("This file exists, skip or overwrite this file?", ""), - ("Quit", ""), - ("Help", ""), - ("Failed", ""), - ("Succeeded", ""), - ("Someone turns on privacy mode, exit", ""), - ("Unsupported", ""), + ("Quit", "Излез"), + ("Help", "Помощ"), + ("Failed", "Неуспешно"), + ("Succeeded", "Успешно"), + ("Someone turns on privacy mode, exit", "Някой включва режим на поверителност, излезте"), + ("Unsupported", "Не се поддържа"), ("Peer denied", ""), ("Please install plugins", ""), ("Peer exit", ""), ("Failed to turn off", ""), ("Turned off", ""), - ("Language", ""), + ("Language", "Език"), ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", "Игнорирай оптимизациите на батерията"), ("android_open_battery_optimizations_tip", "Ако искате да деактивирате тази функция, моля, отидете на следващата страница с настройки на приложението RustDesk, намерете и въведете [Battery], премахнете отметката от [Unrestricted]"), - ("Start on boot", ""), + ("Start on boot", "Стартирайте при зареждане"), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", ""), ("Legacy mode", ""), ("Map mode", ""), - ("Translate mode", ""), - ("Use permanent password", ""), - ("Use both passwords", ""), - ("Set permanent password", ""), - ("Enable remote restart", ""), - ("Restart remote device", ""), - ("Are you sure you want to restart", ""), - ("Restarting remote device", ""), + ("Translate mode", "Режим на превод"), + ("Use permanent password", "Използвайте постоянна парола"), + ("Use both passwords", "Използвайте и двете пароли"), + ("Set permanent password", "Задайте постоянна парола"), + ("Enable remote restart", "Разрешете отдалечено рестартиране"), + ("Restart remote device", "Рестартирайте отдалеченото устройство"), + ("Are you sure you want to restart", "Сигурни ли сте, че искате да рестартирате"), + ("Restarting remote device", "Рестартира се отдалечено устройство"), ("remote_restarting_tip", "Отдалеченото устройство се рестартира, моля, затворете това съобщение и се свържете отново с постоянна парола след известно време"), - ("Copied", ""), + ("Copied", "Копирано"), ("Exit Fullscreen", "Изход от цял екран"), - ("Fullscreen", ""), + ("Fullscreen", "Цял екран"), ("Mobile Actions", "Мобилни действия"), ("Select Monitor", "Изберете монитор"), ("Control Actions", "Контролни действия"), ("Display Settings", "Настройки на дисплея"), - ("Ratio", ""), + ("Ratio", "Съотношение"), ("Image Quality", "Качество на изображението"), ("Scroll Style", "Стил на превъртане"), ("Show Toolbar", "Показване на лентата с инструменти"), @@ -336,46 +336,46 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relay Connection", "Релейна връзка"), ("Secure Connection", "Защитена връзка"), ("Insecure Connection", "Незащитена връзка"), - ("Scale original", ""), - ("Scale adaptive", ""), - ("General", ""), - ("Security", ""), - ("Theme", ""), + ("Scale original", "Оригинален мащаб"), + ("Scale adaptive", "Адаптивно мащабиране"), + ("General", "Основен"), + ("Security", "Сигурност"), + ("Theme", "Тема"), ("Dark Theme", "Тъмна тема"), ("Light Theme", "Светла тема"), - ("Dark", ""), - ("Light", ""), + ("Dark", "Тъмна"), + ("Light", "Светла"), ("Follow System", "Следвай системата"), - ("Enable hardware codec", ""), + ("Enable hardware codec", "Активиране на хардуерен кодек"), ("Unlock Security Settings", "Отключи настройките за сигурност"), - ("Enable audio", ""), + ("Enable audio", "Разрешете аудиото"), ("Unlock Network Settings", "Отключи мрежовите настройки"), - ("Server", ""), + ("Server", "Сървър"), ("Direct IP Access", "Директен IP достъп"), - ("Proxy", ""), - ("Apply", ""), + ("Proxy", "Прокси"), + ("Apply", "Приложи"), ("Disconnect all devices?", ""), - ("Clear", ""), + ("Clear", "Изчисти"), ("Audio Input Device", "Аудио входно устройство"), ("Use IP Whitelisting", "Използвайте бял списък с IP адреси"), - ("Network", ""), + ("Network", "Мрежа"), ("Pin Toolbar", "Фиксиране на лентата с инструменти"), ("Unpin Toolbar", "Откачване на лентата с инструменти"), - ("Recording", ""), - ("Directory", ""), + ("Recording", "Записване"), + ("Directory", "Директория"), ("Automatically record incoming sessions", ""), - ("Change", ""), + ("Change", "Промени"), ("Start session recording", ""), ("Stop session recording", ""), ("Enable recording session", ""), - ("Enable LAN discovery", ""), - ("Deny LAN discovery", ""), - ("Write a message", ""), - ("Prompt", ""), + ("Enable LAN discovery", "Активирайте откриване в LAN"), + ("Deny LAN discovery", "Забранете откриване в LAN"), + ("Write a message", "Напишете съобщение"), + ("Prompt", "Подкана"), ("Please wait for confirmation of UAC...", ""), ("elevated_foreground_window_tip", "Текущият прозорец на отдалечения работен плот изисква по-високи привилегии за работа, така че временно не може да използва мишката и клавиатурата. Можете да поискате от отдалечения потребител да минимизира текущия прозорец или да щракнете върху бутона за повдигане в прозореца за управление на връзката. За да избегнете този проблем, се препоръчва да инсталирате софтуера на отдалеченото устройство."), - ("Disconnected", ""), - ("Other", ""), + ("Disconnected", "Прекъсната връзка"), + ("Other", "Други"), ("Confirm before closing multiple tabs", ""), ("Keyboard Settings", "Настройки на клавиатурата"), ("Full Access", "Пълен достъп"), @@ -384,15 +384,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", ""), ("JumpLink", "Преглед"), ("Please Select the screen to be shared(Operate on the peer side).", "Моля, изберете екрана, който да бъде споделен (Работете от страна на партньора)."), - ("Show RustDesk", ""), - ("This PC", ""), - ("or", ""), - ("Continue with", ""), - ("Elevate", ""), - ("Zoom cursor", ""), - ("Accept sessions via password", ""), - ("Accept sessions via click", ""), - ("Accept sessions via both", ""), + ("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", ""), @@ -402,10 +402,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("hide_cm_tip", "Разрешете скриването само ако приемате сесии чрез парола и използвате постоянна парола"), ("wayland_experiment_tip", "Wayland support is in experimental stage, please use X11 if you require unattended access."), ("Right click to select tabs", ""), - ("Skipped", ""), + ("Skipped", "Пропуснато"), ("Add to address book", ""), - ("Group", ""), - ("Search", ""), + ("Group", "Група"), + ("Search", "Търсене"), ("Closed manually by web console", ""), ("Local keyboard type", ""), ("Select local keyboard type", ""), @@ -414,7 +414,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("config_input", "За да контролирате отдалечен работен плот с клавиатура, трябва да предоставите на RustDesk разрешения \"Input Monitoring\"."), ("config_microphone", "За да говорите дистанционно, трябва да предоставите на RustDesk разрешения \"Запис на звук\"."), ("request_elevation_tip", "Можете също така да поискате повишаване на привилегии, ако има някой от отдалечената страна."), - ("Wait", ""), + ("Wait", "Изчакайте"), ("Elevation Error", "Грешка при повишаване на привилегии"), ("Ask the remote user for authentication", ""), ("Choose this if the remote account is administrator", ""), @@ -438,21 +438,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Default Scroll Style", "Стил на превъртане по подразбиране"), ("Default Image Quality", "Качество на изображението по подразбиране"), ("Default Codec", "Кодек по подразбиране"), - ("Bitrate", ""), - ("FPS", ""), - ("Auto", ""), + ("Bitrate", "Битрейт"), + ("FPS", "Кадри в секунда"), + ("Auto", "Автоматично"), ("Other Default Options", "Други опции по подразбиране"), ("Voice call", ""), ("Text chat", ""), ("Stop voice call", ""), ("relay_hint_tip", "Може да не е възможно да се свържете директно; можете да опитате да се свържете чрез реле. Освен това, ако искате да използвате реле при първия си опит, добавете наставка \"/r\" към идентификатора или да изберете опцията \"Винаги свързване чрез реле\" в картата на последните сесии, ако съществува."), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), - ("No transfers in progress", ""), + ("Reconnect", "Свържете се отново"), + ("Codec", "Кодек"), + ("Resolution", "Резолюция"), + ("No transfers in progress", "Не се извършват трансфери"), ("Set one-time password length", ""), ("RDP Settings", "RDP настройки"), - ("Sort by", ""), + ("Sort by", "Сортирай по"), ("New Connection", "Ново свързване"), ("Restore", ""), ("Minimize", ""), @@ -465,7 +465,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("eg: admin", ""), ("Empty Username", "Празно потребителско име"), ("Empty Password", "Празна парола"), - ("Me", ""), + ("Me", "Аз"), ("identical_file_tip", "Този файл е идентичен с този на партньора."), ("show_monitors_tip", "Показване на мониторите в лентата с инструменти"), ("View Mode", "Режим на преглед"), @@ -494,22 +494,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Update", ""), ("Enable", ""), ("Disable", ""), - ("Options", ""), + ("Options", "Настроики"), ("resolution_original_tip", "Оригинална резолюция"), ("resolution_fit_local_tip", "Напасване към локална разделителна способност"), ("resolution_custom_tip", "Персонализирана разделителна способност"), - ("Collapse toolbar", ""), + ("Collapse toolbar", "Свиване на лентата с инструменти"), ("Accept and Elevate", "Приемете и повишаване на привилегии"), ("accept_and_elevate_btn_tooltip", "Приемете връзката и повишете UAC разрешенията."), ("clipboard_wait_response_timeout_tip", "Времето за изчакване на отговор за копиране изтече."), ("Incoming connection", ""), ("Outgoing connection", ""), - ("Exit", ""), - ("Open", ""), + ("Exit", "Излез"), + ("Open", "Отвори"), ("logout_tip", "Сигурни ли сте, че искате да излезете?"), - ("Service", ""), - ("Start", ""), - ("Stop", ""), + ("Service", "Услуга"), + ("Start", "Стартиране"), + ("Stop", "Спиране"), ("exceed_max_devices", "Достигнахте максималния брой управлявани устройства."), ("Sync with recent sessions", ""), ("Sort tags", ""), @@ -537,7 +537,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("scam_title", "Възможно е да сте ИЗМАМЕНИ!"), ("scam_text1", "Ако разговаряте по телефона с някой, когото НЕ ПОЗНАВАТЕ и НЯМАТЕ ДОВЕРИЕ, който ви е помолил да използвате RustDesk и да стартирате услугата, не продължавайте и затворете незабавно."), ("scam_text2", "Те вероятно са измамник, който се опитва да открадне вашите пари или друга лична информация."), - ("Don't show again", ""), + ("Don't show again", "Не показвай отново"), ("I Agree", ""), ("Decline", ""), ("Timeout in minutes", ""), @@ -562,7 +562,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Virtual display", ""), ("Plug out all", ""), ("True color (4:4:4)", ""), - ("Enable blocking user input", ""), + ("Enable blocking user input", "Разрешаване на блокиране на потребителско въвеждане"), ("id_input_tip", "Можете да въведете ID, директен IP адрес или домейн с порт (:).\nАко искате да получите достъп до устройство на друг сървър, моля, добавете адреса на сървъра (@?key=), например\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nАко искате да получите достъп до устройство на обществен сървър, моля, въведете \"@public\" , ключът не е необходим за публичен сървър"), ("privacy_mode_impl_mag_tip", "Режим 1"), ("privacy_mode_impl_virtual_display_tip", "Режим 2"), @@ -574,7 +574,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Swap control-command key", ""), ("swap-left-right-mouse", "Разменете левия и десния бутон на мишката"), ("2FA code", "Код за Двуфакторна удостоверяване"), - ("More", ""), + ("More", "Повече"), ("enable-2fa-title", "Активиране на двуфакторно удостоверяване"), ("enable-2fa-desc", "Моля, настройте вашия удостоверител сега. Можете да използвате приложение за удостоверяване като Authy, Microsoft или Google Authenticator на вашия телефон или настолен компютър.\n\nСканирайте QR кода с вашето приложение и въведете кода, който приложението ви показва, за да активирате двуфакторно удостоверяване."), ("wrong-2fa-code", "е може да се потвърди кодът. Проверете дали настройките за код и локалното време са правилни"), @@ -619,7 +619,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), - ("Back", ""), + ("Back", "Назад"), ("Apps", ""), ("Volume up", ""), ("Volume down", ""), From c2bd1b8965a1288a11d15ab1eb5a77357966eb32 Mon Sep 17 00:00:00 2001 From: solokot Date: Thu, 4 Jul 2024 15:21:18 +0300 Subject: [PATCH 203/335] Update ru.rs (#8610) --- src/lang/ru.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d05145cd2a5..02eca034cc1 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -627,7 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram-бот"), ("enable-bot-tip", "Если включено, можно получать код двухфакторной аутентификации от бота. Он также может выполнять функцию уведомления о подключении."), ("enable-bot-desc", "1) Откройте чат с @BotFather.\n2) Отправьте команду \"/newbot\". После выполнения этого шага вы получите токен.\n3) Начните чат с вашим только что созданным ботом. Отправьте сообщение, начинающееся с прямой косой черты (\"/\"), например, \"/hello\", чтобы его активировать.\n"), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("cancel-2fa-confirm-tip", "Отключить двухфакторную аутентификацию?"), + ("cancel-bot-confirm-tip", "Отключить Telegram-бота?"), ].iter().cloned().collect(); } From f8f26862678420bae8e9a9db58c9d5c04d0cdfbe Mon Sep 17 00:00:00 2001 From: flusheDData <116861809+flusheDData@users.noreply.github.com> Date: Thu, 4 Jul 2024 14:21:28 +0200 Subject: [PATCH 204/335] Update es.rs (#8609) * Update es.rs New terms added * Update es.rs Mistype correction --- src/lang/es.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lang/es.rs b/src/lang/es.rs index ca2d991070e..b938840f99d 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -547,7 +547,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("upgrade_rustdesk_server_pro_to_{}_tip", "¡Por favor, actualiza RustDesk Server Pro a la versión {} o superior"), ("pull_group_failed_tip", "No se ha podido refrescar el grupo"), ("Filter by intersection", "Filtrar por intersección"), - ("Remove wallpaper during incoming sessions", "Quitar el fonde de pantalla durante sesiones entrantes"), + ("Remove wallpaper during incoming sessions", "Quitar el fondo de pantalla durante sesiones entrantes"), ("Test", "Probar"), ("display_is_plugged_out_msg", "La pantalla está desconectada, cambia a la principal."), ("No displays", "No hay pantallas"), @@ -618,16 +618,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Never", "Nunca"), ("During controlled", "Mientras está siendo controlado"), ("During service is on", "Mientras el servicio está activo"), - ("Capture screen using DirectX", ""), - ("Back", ""), + ("Capture screen using DirectX", "Capturar pantalla con DirectX"), + ("Back", "Atrás"), ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("Volume up", "Bajar volumen"), + ("Volume down", "Subir volumen"), + ("Power", "Encendido"), + ("Telegram bot", "Bot de Telegram"), + ("enable-bot-tip", "Si activas esta característica puedes recibir código 2FA de tu bot. También puede funcionar como notificación de conexión."), + ("enable-bot-desc", "1, Abre un chat con @BotFather.\n2, Envía el comando \"/newbot\". Recibirás un token tras completar esta paso.\n3, Inicia un chat con tu bot recién creado. Envía un mensaje que comience con una barra (\"/\") como \"/hola\" para activarlo.\n"), + ("cancel-2fa-confirm-tip", "¿Seguro que quieres cancelar 2FA?"), + ("cancel-bot-confirm-tip", "¿Seguro que quieres cancelar el bot de Telegram?"), ].iter().cloned().collect(); } From a9015bcf703e01931c0f7a3ecd633e5fdfddcf6e Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 4 Jul 2024 21:23:08 +0800 Subject: [PATCH 205/335] feat: clipboard svg (#8608) * feat: clipboard svg Signed-off-by: fufesou * fix: is_last_plain, reset on clipboard event Signed-off-by: fufesou --------- Signed-off-by: fufesou --- Cargo.lock | 276 ++++++++++++++++++++++++++++++++++++++++++++++- src/clipboard.rs | 96 +++++++++++------ 2 files changed, 337 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b25f5cb5f3..a56357f19c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arboard" version = "3.4.0" -source = "git+https://github.com/rustdesk-org/arboard#61b448d8261fb313d67a61d03fc130bd738db396" +source = "git+https://github.com/rustdesk-org/arboard#98a1be0cab8355dd91a629b5dee8b428714cd902" dependencies = [ "clipboard-win", "core-graphics 0.23.2", @@ -234,11 +234,24 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "parking_lot", + "resvg", "windows-sys 0.48.0", "wl-clipboard-rs", "x11rb 0.13.1", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-broadcast" version = "0.5.1" @@ -1513,6 +1526,12 @@ dependencies = [ "dasp_sample", ] +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + [[package]] name = "dbus" version = "0.9.7" @@ -1672,7 +1691,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.4", + "libloading 0.7.4", ] [[package]] @@ -2062,6 +2081,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "flume" version = "0.11.0" @@ -2118,6 +2143,29 @@ dependencies = [ "libm", ] +[[package]] +name = "fontconfig-parser" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" +dependencies = [ + "roxmltree 0.19.0", +] + +[[package]] +name = "fontdb" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -3165,6 +3213,12 @@ dependencies = [ "tiff", ] +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + [[package]] name = "impersonate_system" version = "0.1.0" @@ -3408,6 +3462,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "kurbo" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c" +dependencies = [ + "arrayvec", + "smallvec", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -3713,6 +3777,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -4659,9 +4732,15 @@ version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" dependencies = [ - "siphasher", + "siphasher 0.2.3", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.5" @@ -5335,6 +5414,31 @@ dependencies = [ "winreg 0.50.0", ] +[[package]] +name = "resvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" +dependencies = [ + "gif", + "jpeg-decoder", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -5359,6 +5463,18 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "roxmltree" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rpassword" version = "2.1.0" @@ -5737,6 +5853,22 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.18" @@ -6027,12 +6159,27 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -6042,6 +6189,15 @@ dependencies = [ "autocfg 1.3.0", ] +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -6112,6 +6268,15 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + [[package]] name = "strsim" version = "0.8.0" @@ -6173,6 +6338,16 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "svgtypes" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c" +dependencies = [ + "kurbo", + "siphasher 1.0.1", +] + [[package]] name = "syn" version = "0.15.44" @@ -6512,6 +6687,32 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.6.1" @@ -6803,6 +7004,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + [[package]] name = "typenum" version = "1.17.0" @@ -6875,6 +7082,18 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -6890,12 +7109,30 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "unicode-script" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" + [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.13" @@ -6958,6 +7195,33 @@ dependencies = [ "log", ] +[[package]] +name = "usvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher 1.0.1", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "utf16string" version = "0.2.0" @@ -7956,6 +8220,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "zbus" version = "3.15.2" diff --git a/src/clipboard.rs b/src/clipboard.rs index 9dbdfddda20..bb20f3b32ff 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -14,6 +14,7 @@ use hbb_common::{ pub const CLIPBOARD_NAME: &'static str = "clipboard"; pub const CLIPBOARD_INTERVAL: u64 = 333; +const FAKE_SVG_WIDTH: usize = 999999; lazy_static::lazy_static! { pub static ref CONTENT: Arc> = Default::default(); @@ -142,6 +143,13 @@ pub fn check_clipboard( let content = ctx2.get(); if let Ok(content) = content { if !content.is_empty() { + if matches!(content, ClipboardData::Text(_)) { + // Skip the text if the last content is image-svg/html + if ctx2.is_last_plain { + return None; + } + } + let changed = content != *old.lock().unwrap(); if changed { log::info!("{} update found on {}", CLIPBOARD_NAME, side); @@ -212,26 +220,26 @@ impl ClipboardData { match self { ClipboardData::Empty => true, ClipboardData::Text(s) => s.is_empty(), - ClipboardData::Image(a, _) => a.bytes.is_empty(), + ClipboardData::Image(a, _) => a.bytes().is_empty(), } } fn from_msg(clipboard: Clipboard) -> Self { - let is_image = clipboard.width > 0 && clipboard.height > 0; + let is_image = clipboard.width > 0; let data = if clipboard.compress { decompress(&clipboard.content) } else { clipboard.content.into() }; if is_image { - ClipboardData::Image( - arboard::ImageData { - bytes: data.into(), - width: clipboard.width as _, - height: clipboard.height as _, - }, - 0, - ) + // We cannot use data.start_with(b" { - let compressed = compress_func(&a.bytes); - let compress = compressed.len() < a.bytes.len(); + let compressed = compress_func(&a.bytes()); + let compress = compressed.len() < a.bytes().len(); let content = if compress { compressed } else { - a.bytes.to_vec() + a.bytes().to_vec() + }; + let (w, h) = match a { + arboard::ImageData::Rgba(a) => (a.width, a.height), + arboard::ImageData::Svg(_) => (FAKE_SVG_WIDTH as _, 0 as _), }; msg.set_clipboard(Clipboard { compress, content: content.into(), - width: a.width as _, - height: a.height as _, + width: w as _, + height: h as _, ..Default::default() }); } @@ -285,9 +297,13 @@ impl PartialEq for ClipboardData { fn eq(&self, other: &Self) -> bool { match (self, other) { (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, - (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => { - a.width == b.width && a.height == b.height && a.bytes == b.bytes - } + (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => match (a, b) { + (arboard::ImageData::Rgba(a), arboard::ImageData::Rgba(b)) => { + a.width == b.width && a.height == b.height && a.bytes == b.bytes + } + (arboard::ImageData::Svg(a), arboard::ImageData::Svg(b)) => a == b, + _ => false, + }, (ClipboardData::Empty, ClipboardData::Empty) => true, _ => false, } @@ -295,7 +311,12 @@ impl PartialEq for ClipboardData { } #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] -pub struct ClipboardContext(arboard::Clipboard, (Arc, u64), Option); +pub struct ClipboardContext { + inner: arboard::Clipboard, + counter: (Arc, u64), + shutdown: Option, + is_last_plain: bool, +} #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] #[allow(unreachable_code)] @@ -367,13 +388,18 @@ impl ClipboardContext { shutdown = Some(st); } } - Ok(ClipboardContext(board, (change_count, 0), shutdown)) + Ok(ClipboardContext { + inner: board, + counter: (change_count, 0), + shutdown, + is_last_plain: false, + }) } #[inline] pub fn change_count(&self) -> u64 { - debug_assert!(self.2.is_some()); - self.1 .0.load(Ordering::SeqCst) + debug_assert!(self.shutdown.is_some()); + self.counter.0.load(Ordering::SeqCst) } pub fn get(&mut self) -> ResultType { @@ -381,22 +407,28 @@ impl ClipboardContext { let _lock = ARBOARD_MTX.lock().unwrap(); // only for image for the time being, // because I do not want to change behavior of text clipboard for the time being - if cn != self.1 .1 { - self.1 .1 = cn; - if let Ok(image) = self.0.get_image() { - if image.width > 0 && image.height > 0 { - return Ok(ClipboardData::image(image)); - } + if cn != self.counter.1 { + self.is_last_plain = false; + self.counter.1 = cn; + if let Ok(image) = self.inner.get_image() { + // Both text and image svg may be set by some applications + // But we only want to send the svg content. + // + // We can't call `get_text()` and store current text in `old` in outer scope, + // because it may be updated later than svg. + // Then the text will still be sent and replace the image svg content. + self.is_last_plain = matches!(image, arboard::ImageData::Svg(_)); + return Ok(ClipboardData::image(image.clone())); } } - Ok(ClipboardData::Text(self.0.get_text()?)) + Ok(ClipboardData::Text(self.inner.get_text()?)) } fn set(&mut self, data: &ClipboardData) -> ResultType<()> { let _lock = ARBOARD_MTX.lock().unwrap(); match data { - ClipboardData::Text(s) => self.0.set_text(s)?, - ClipboardData::Image(a, _) => self.0.set_image(a.clone())?, + ClipboardData::Text(s) => self.inner.set_text(s)?, + ClipboardData::Image(a, _) => self.inner.set_image(a.clone())?, _ => {} } Ok(()) @@ -405,7 +437,7 @@ impl ClipboardContext { impl Drop for ClipboardContext { fn drop(&mut self) { - if let Some(shutdown) = self.2.take() { + if let Some(shutdown) = self.shutdown.take() { let _ = shutdown.signal(); } } From 92d0fe1c3f8a2c3f34fa7f2a8d7a51b45ad25f28 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 4 Jul 2024 21:31:19 +0800 Subject: [PATCH 206/335] Revert "feat: clipboard svg (#8608)" (#8612) This reverts commit a9015bcf703e01931c0f7a3ecd633e5fdfddcf6e. --- Cargo.lock | 276 +---------------------------------------------- src/clipboard.rs | 96 ++++++----------- 2 files changed, 35 insertions(+), 337 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a56357f19c3..4b25f5cb5f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arboard" version = "3.4.0" -source = "git+https://github.com/rustdesk-org/arboard#98a1be0cab8355dd91a629b5dee8b428714cd902" +source = "git+https://github.com/rustdesk-org/arboard#61b448d8261fb313d67a61d03fc130bd738db396" dependencies = [ "clipboard-win", "core-graphics 0.23.2", @@ -234,24 +234,11 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "parking_lot", - "resvg", "windows-sys 0.48.0", "wl-clipboard-rs", "x11rb 0.13.1", ] -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "async-broadcast" version = "0.5.1" @@ -1526,12 +1513,6 @@ dependencies = [ "dasp_sample", ] -[[package]] -name = "data-url" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" - [[package]] name = "dbus" version = "0.9.7" @@ -1691,7 +1672,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.7.4", + "libloading 0.8.4", ] [[package]] @@ -2081,12 +2062,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - [[package]] name = "flume" version = "0.11.0" @@ -2143,29 +2118,6 @@ dependencies = [ "libm", ] -[[package]] -name = "fontconfig-parser" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" -dependencies = [ - "roxmltree 0.19.0", -] - -[[package]] -name = "fontdb" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - [[package]] name = "foreign-types" version = "0.3.2" @@ -3213,12 +3165,6 @@ dependencies = [ "tiff", ] -[[package]] -name = "imagesize" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" - [[package]] name = "impersonate_system" version = "0.1.0" @@ -3462,16 +3408,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "kurbo" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c" -dependencies = [ - "arrayvec", - "smallvec", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -3777,15 +3713,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memmap2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.6.5" @@ -4732,15 +4659,9 @@ version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" dependencies = [ - "siphasher 0.2.3", + "siphasher", ] -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project" version = "1.1.5" @@ -5414,31 +5335,6 @@ dependencies = [ "winreg 0.50.0", ] -[[package]] -name = "resvg" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" -dependencies = [ - "gif", - "jpeg-decoder", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", -] - -[[package]] -name = "rgb" -version = "0.8.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.8" @@ -5463,18 +5359,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "roxmltree" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - [[package]] name = "rpassword" version = "2.1.0" @@ -5853,22 +5737,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" -[[package]] -name = "rustybuzz" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" -dependencies = [ - "bitflags 2.6.0", - "bytemuck", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - [[package]] name = "ryu" version = "1.0.18" @@ -6159,27 +6027,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "simplecss" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" -dependencies = [ - "log", -] - [[package]] name = "siphasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - [[package]] name = "slab" version = "0.4.9" @@ -6189,15 +6042,6 @@ dependencies = [ "autocfg 1.3.0", ] -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - [[package]] name = "smallvec" version = "1.13.2" @@ -6268,15 +6112,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - [[package]] name = "strsim" version = "0.8.0" @@ -6338,16 +6173,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "svgtypes" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c" -dependencies = [ - "kurbo", - "siphasher 1.0.1", -] - [[package]] name = "syn" version = "0.15.44" @@ -6687,32 +6512,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if 1.0.0", - "log", - "png", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - [[package]] name = "tinyvec" version = "1.6.1" @@ -7004,12 +6803,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "ttf-parser" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" - [[package]] name = "typenum" version = "1.17.0" @@ -7082,18 +6875,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" - -[[package]] -name = "unicode-ccc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -7109,30 +6890,12 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-properties" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" - -[[package]] -name = "unicode-script" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" - [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" version = "0.1.13" @@ -7195,33 +6958,6 @@ dependencies = [ "log", ] -[[package]] -name = "usvg" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" -dependencies = [ - "base64 0.22.1", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree 0.20.0", - "rustybuzz", - "simplecss", - "siphasher 1.0.1", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - [[package]] name = "utf16string" version = "0.2.0" @@ -8220,12 +7956,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - [[package]] name = "zbus" version = "3.15.2" diff --git a/src/clipboard.rs b/src/clipboard.rs index bb20f3b32ff..9dbdfddda20 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -14,7 +14,6 @@ use hbb_common::{ pub const CLIPBOARD_NAME: &'static str = "clipboard"; pub const CLIPBOARD_INTERVAL: u64 = 333; -const FAKE_SVG_WIDTH: usize = 999999; lazy_static::lazy_static! { pub static ref CONTENT: Arc> = Default::default(); @@ -143,13 +142,6 @@ pub fn check_clipboard( let content = ctx2.get(); if let Ok(content) = content { if !content.is_empty() { - if matches!(content, ClipboardData::Text(_)) { - // Skip the text if the last content is image-svg/html - if ctx2.is_last_plain { - return None; - } - } - let changed = content != *old.lock().unwrap(); if changed { log::info!("{} update found on {}", CLIPBOARD_NAME, side); @@ -220,26 +212,26 @@ impl ClipboardData { match self { ClipboardData::Empty => true, ClipboardData::Text(s) => s.is_empty(), - ClipboardData::Image(a, _) => a.bytes().is_empty(), + ClipboardData::Image(a, _) => a.bytes.is_empty(), } } fn from_msg(clipboard: Clipboard) -> Self { - let is_image = clipboard.width > 0; + let is_image = clipboard.width > 0 && clipboard.height > 0; let data = if clipboard.compress { decompress(&clipboard.content) } else { clipboard.content.into() }; if is_image { - // We cannot use data.start_with(b" { - let compressed = compress_func(&a.bytes()); - let compress = compressed.len() < a.bytes().len(); + let compressed = compress_func(&a.bytes); + let compress = compressed.len() < a.bytes.len(); let content = if compress { compressed } else { - a.bytes().to_vec() - }; - let (w, h) = match a { - arboard::ImageData::Rgba(a) => (a.width, a.height), - arboard::ImageData::Svg(_) => (FAKE_SVG_WIDTH as _, 0 as _), + a.bytes.to_vec() }; msg.set_clipboard(Clipboard { compress, content: content.into(), - width: w as _, - height: h as _, + width: a.width as _, + height: a.height as _, ..Default::default() }); } @@ -297,13 +285,9 @@ impl PartialEq for ClipboardData { fn eq(&self, other: &Self) -> bool { match (self, other) { (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, - (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => match (a, b) { - (arboard::ImageData::Rgba(a), arboard::ImageData::Rgba(b)) => { - a.width == b.width && a.height == b.height && a.bytes == b.bytes - } - (arboard::ImageData::Svg(a), arboard::ImageData::Svg(b)) => a == b, - _ => false, - }, + (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => { + a.width == b.width && a.height == b.height && a.bytes == b.bytes + } (ClipboardData::Empty, ClipboardData::Empty) => true, _ => false, } @@ -311,12 +295,7 @@ impl PartialEq for ClipboardData { } #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] -pub struct ClipboardContext { - inner: arboard::Clipboard, - counter: (Arc, u64), - shutdown: Option, - is_last_plain: bool, -} +pub struct ClipboardContext(arboard::Clipboard, (Arc, u64), Option); #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] #[allow(unreachable_code)] @@ -388,18 +367,13 @@ impl ClipboardContext { shutdown = Some(st); } } - Ok(ClipboardContext { - inner: board, - counter: (change_count, 0), - shutdown, - is_last_plain: false, - }) + Ok(ClipboardContext(board, (change_count, 0), shutdown)) } #[inline] pub fn change_count(&self) -> u64 { - debug_assert!(self.shutdown.is_some()); - self.counter.0.load(Ordering::SeqCst) + debug_assert!(self.2.is_some()); + self.1 .0.load(Ordering::SeqCst) } pub fn get(&mut self) -> ResultType { @@ -407,28 +381,22 @@ impl ClipboardContext { let _lock = ARBOARD_MTX.lock().unwrap(); // only for image for the time being, // because I do not want to change behavior of text clipboard for the time being - if cn != self.counter.1 { - self.is_last_plain = false; - self.counter.1 = cn; - if let Ok(image) = self.inner.get_image() { - // Both text and image svg may be set by some applications - // But we only want to send the svg content. - // - // We can't call `get_text()` and store current text in `old` in outer scope, - // because it may be updated later than svg. - // Then the text will still be sent and replace the image svg content. - self.is_last_plain = matches!(image, arboard::ImageData::Svg(_)); - return Ok(ClipboardData::image(image.clone())); + if cn != self.1 .1 { + self.1 .1 = cn; + if let Ok(image) = self.0.get_image() { + if image.width > 0 && image.height > 0 { + return Ok(ClipboardData::image(image)); + } } } - Ok(ClipboardData::Text(self.inner.get_text()?)) + Ok(ClipboardData::Text(self.0.get_text()?)) } fn set(&mut self, data: &ClipboardData) -> ResultType<()> { let _lock = ARBOARD_MTX.lock().unwrap(); match data { - ClipboardData::Text(s) => self.inner.set_text(s)?, - ClipboardData::Image(a, _) => self.inner.set_image(a.clone())?, + ClipboardData::Text(s) => self.0.set_text(s)?, + ClipboardData::Image(a, _) => self.0.set_image(a.clone())?, _ => {} } Ok(()) @@ -437,7 +405,7 @@ impl ClipboardContext { impl Drop for ClipboardContext { fn drop(&mut self) { - if let Some(shutdown) = self.shutdown.take() { + if let Some(shutdown) = self.2.take() { let _ = shutdown.signal(); } } From 8747b9847fd1800a38dd0ba106be89857043ff39 Mon Sep 17 00:00:00 2001 From: "Thadah D. Denyse" Date: Fri, 5 Jul 2024 03:30:07 +0200 Subject: [PATCH 207/335] Add basque language (#8597) * Add basque language * Update lang.rs --- src/lang.rs | 3 + src/lang/eu.rs | 635 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 638 insertions(+) create mode 100644 src/lang/eu.rs diff --git a/src/lang.rs b/src/lang.rs index e8096bb2446..b269d8631fc 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -14,6 +14,7 @@ mod en; mod eo; mod es; mod et; +mod eu; mod fa; mod fr; mod he; @@ -55,6 +56,7 @@ pub const LANGS: &[(&str, &str)] = &[ ("pt", "Português"), ("es", "Español"), ("et", "Eesti keel"), + ("eu", "Euskara"), ("hu", "Magyar"), ("bg", "Български"), ("be", "Беларуская"), @@ -126,6 +128,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "nl" => nl::T.deref(), "es" => es::T.deref(), "et" => et::T.deref(), + "eu" => eu::T.deref(), "hu" => hu::T.deref(), "ru" => ru::T.deref(), "eo" => eo::T.deref(), diff --git a/src/lang/eu.rs b/src/lang/eu.rs new file mode 100644 index 00000000000..47a209239a6 --- /dev/null +++ b/src/lang/eu.rs @@ -0,0 +1,635 @@ +lazy_static::lazy_static! { + pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Egoera"), + ("Your Desktop", "Zure mahaigaina"), + ("desk_tip", "Mahaigainera zure ID eta pasahitzarekin sartu zaitezke"), + ("Password", "Pasahitza"), + ("Ready", "Prest"), + ("Established", "Ezarrita"), + ("connecting_status", "RustDesk sarera konektatzen..."), + ("Enable service", "Gaitu zerbitzua"), + ("Start service", "Hasi zerbitzua"), + ("Service is running", "Zerbitzua martxan dago"), + ("Service is not running", "Zerbitzua ez dago martxan"), + ("not_ready_status", "Ez dago prest. Mesedez, egiaztatu zure konexioa"), + ("Control Remote Desktop", "Kontrolatu urruneko mahaigaina"), + ("Transfer file", "Transferitu fitxategia"), + ("Connect", "Konektatu"), + ("Recent sessions", "Azken saioak"), + ("Address book", "Helbide-liburua"), + ("Confirmation", "Berrespena"), + ("TCP tunneling", "TCP tunela"), + ("Remove", "Kendu"), + ("Refresh random password", "Freskatu ausazko pasahitza"), + ("Set your own password", "Ezarri zure pasahitza"), + ("Enable keyboard/mouse", "Gaitu teklatua/sagua"), + ("Enable clipboard", "Gaitu arbela"), + ("Enable file transfer", "Gaitu fitxategien transferentzia"), + ("Enable TCP tunneling", "Gaitu TCP tunela"), + ("IP Whitelisting", "Onartutako IP helbideak"), + ("ID/Relay Server", "ID/Relay zerbitzaria"), + ("Import server config", "Inportatu zerbitzariaren konfigurazioa"), + ("Export Server Config", "Esportatu zerbitzariaren konfigurazioa"), + ("Import server configuration successfully", "Zerbitzariaren konfigurazioa ondo inportatu da"), + ("Export server configuration successfully", "Zerbitzariaren konfigurazioa ondo esportatu da"), + ("Invalid server configuration", "Zerbitzariaren konfigurazioa baliogabea da"), + ("Clipboard is empty", "Arbela hutsik dago"), + ("Stop service", "Gelditu zerbitzua"), + ("Change ID", "Aldatu IDa"), + ("Your new ID", "Zure ID berria"), + ("length %min% to %max%", "%min%(e)tik %max% arteko luzera"), + ("starts with a letter", "hizki batekin hasten da"), + ("allowed characters", "onartutako karaktereak"), + ("id_change_tip", "Soilik a-z, A-Z, 0-9 eta _ (barra baxua) karaktereak daude onartuta. Lehen hizkia a-z, A-Z izan behar da. Luzera 6 eta 16 artekoa izan behar da."), + ("Website", "Webgunea"), + ("About", "Honi buruz"), + ("Slogan_tip", "Bihotzez eginda mundu kaotiko honetan!"), + ("Privacy Statement", "Pribatutasun-politika"), + ("Mute", "Mututu"), + ("Build Date", "Konpilazio-data"), + ("Version", "Bertsioa"), + ("Home", "Hasiera"), + ("Audio Input", "Audio sarrera"), + ("Enhancements", "Hobespenak"), + ("Hardware Codec", "Hardware kodeka"), + ("Adaptive bitrate", "Bit-emari moldagarria"), + ("ID Server", "ID zerbitzaria"), + ("Relay Server", "Relay zerbitzaria"), + ("API Server", "API zerbitzaria"), + ("invalid_http", "http:// edo https://-rekin hasi behar da"), + ("Invalid IP", "IP baliogabea"), + ("Invalid format", "Formatu baliogabea"), + ("server_not_support", "Oraindik ez dago zerbitzariarengatik onartuta"), + ("Not available", "Ez dago eskuragarri"), + ("Too frequent", "Sarriegia"), + ("Cancel", "Utzi"), + ("Skip", "Saltatu"), + ("Close", "Itxi"), + ("Retry", "Saiatu berriro"), + ("OK", "Ondo"), + ("Password Required", "Pasahitza beharrezkoa da"), + ("Please enter your password", "Mesedez, sartu zure pasahitza"), + ("Remember password", "Gogoratu pasahitza"), + ("Wrong Password", "Pasahitz okerra"), + ("Do you want to enter again?", "Berriro sartu nahi zara?"), + ("Connection Error", "Konexio errorea"), + ("Error", "Errorea"), + ("Reset by the peer", "Konexioa parekidearengatik berrezarrita"), + ("Connecting...", "Konektatzen..."), + ("Connection in progress. Please wait.", "Konexioa abian da. Itxaron mesedez."), + ("Please try 1 minute later", "Mesedez, saiatu berrito minutu bat pasata"), + ("Login Error", "Saio-hasiera errorea"), + ("Successful", "Arrakastatsua"), + ("Connected, waiting for image...", "Konektatuta, irudiaren zain..."), + ("Name", "Izena"), + ("Type", "Mota"), + ("Modified", "Aldatua"), + ("Size", "Tamaina"), + ("Show Hidden Files", "Erakutsi ezkutuko fitxategiak"), + ("Receive", "Jaso"), + ("Send", "Bidali"), + ("Refresh File", "Freskatu fitxategia"), + ("Local", "Lokala"), + ("Remote", "Urrunekoa"), + ("Remote Computer", "Urruneko ordenagailua"), + ("Local Computer", "Ordenagailu lokala"), + ("Confirm Delete", "Berretsi ezabapena"), + ("Delete", "Ezabatu"), + ("Properties", "Ezaugarriak"), + ("Multi Select", "Multi-hautapena"), + ("Select All", "Hautatu guztiak"), + ("Unselect All", "Desautatu denak"), + ("Empty Directory", "Karpeta hutsa"), + ("Not an empty directory", "Ez da karpeta huts bat"), + ("Are you sure you want to delete this file?", "Ziur zaude fitxategi hau ezabatu nahi duzula?"), + ("Are you sure you want to delete this empty directory?", "Ziur zaude karpeta huts hau ezabatu nahi duzula?"), + ("Are you sure you want to delete the file of this directory?", "Ziur zaude karpeta honen fitxategia ezabatu nahi duzula?"), + ("Do this for all conflicts", "Egin hau gatazka guztietarako"), + ("This is irreversible!", "Hau ezin da atzera bueltatu!"), + ("Deleting", "Ezabatzen"), + ("files", "fitxategiak"), + ("Waiting", "Zain"), + ("Finished", "Bukatuta"), + ("Speed", "Abiadura"), + ("Custom Image Quality", "Irudi kalitate pertsonalizatua"), + ("Privacy mode", "Pribatutasun modua"), + ("Block user input", "Blokeatu erabiltzailearen sarrera"), + ("Unblock user input", "Desblokeatu erabiltzailearen sarrera"), + ("Adjust Window", "Doitu leihoa"), + ("Original", "Originala"), + ("Shrink", "Txikitu"), + ("Stretch", "Luzatu"), + ("Scrollbar", "Korritze-barra"), + ("ScrollAuto", "Korritze automatikoa"), + ("Good image quality", "Irudi kalitate ona"), + ("Balanced", "Orekatua"), + ("Optimize reaction time", "Optimizatu erreakzio-denbiora"), + ("Custom", "Pertsonalizatua"), + ("Show remote cursor", "Erakutsi urruneko kurtsorea"), + ("Show quality monitor", "Erakutsi kalitate monitorea"), + ("Disable clipboard", "Desgaitu arbela"), + ("Lock after session end", "Blokeatu sesioa amaitu ostean"), + ("Insert", "Sartu"), + ("Insert Lock", "Sarrera-blokeoa"), + ("Refresh", "Freskatu"), + ("ID does not exist", "IDa ez da existitzen"), + ("Failed to connect to rendezvous server", "Topaketa zerbitzarira konektatzeak huts egin du"), + ("Please try later", "Mesedez, saiatu berriro geroago"), + ("Remote desktop is offline", "Urruneko mahaigaina lineaz kanpo dago"), + ("Key mismatch", "Gakoak ez datoz bat"), + ("Timeout", "Denbora-muga"), + ("Failed to connect to relay server", "Igorpen zerbitzarira konektatzeak huts egin du"), + ("Failed to connect via rendezvous server", "Topaketa zerbitzariaren bidez konektatzeak huts egin du"), + ("Failed to connect via relay server", "Igorpen zerbitzariaren bidez konektatzeak huts egin du"), + ("Failed to make direct connection to remote desktop", "Urruneko mahaigainera zuzeneko konexio bat ezartzeak huts egin du"), + ("Set Password", "Ezarri pasahitza"), + ("OS Password", "Sistema eragilearen pasahitza"), + ("install_tip", "Erabiltzaile Kontuen Kontrolarengatik, RustDesk ezin du ondo funtzionatu urruneko mahaigainean. EKK saihesteko, mesedez, egin klik azpiko botoian RustDesk sistema mailan instalatzeko."), + ("Click to upgrade", "Egin klik bertsio-berritzeko"), + ("Click to download", "Egin klik deskargatzeko"), + ("Click to update", "Egin klik eguneratzeko"), + ("Configure", "Konfiguratu"), + ("config_acc", "Zure mahaigaina urrunetik kontrolatzeko, RustDesk-i \"Irisgarritasuna\" baimenak eman behar dituzu."), + ("config_screen", "Zure mahaigaina kanpotik kontrolatzeko, RustDesk-i \"Pantaila grabatu\" baimena eman behar duzu."), + ("Installing ...", "Instalantzen..."), + ("Install", "Instalatu"), + ("Installation", "Instalazioa"), + ("Installation Path", "Instalazio bide-izena"), + ("Create start menu shortcuts", "Sortu hasiera-menuko lasterbideak"), + ("Create desktop icon", "Sortu mahaigaineko ikonoa"), + ("agreement_tip", "Instalazioa hastean, lizentzia-kontratua onartzen duzu."), + ("Accept and Install", "Onartu eta instalatu"), + ("End-user license agreement", "Azken erabiltzailearen lizentzia akordioa"), + ("Generating ...", "Sortzen..."), + ("Your installation is lower version.", "Zure instalazioak bertsio zaharragoa du."), + ("not_close_tcp_tip", "Ez itxi leiho hau tunela erabili bitartean"), + ("Listening ...", "Entzuten..."), + ("Remote Host", "Urruneko ostalaria"), + ("Remote Port", "Urruneko ataka"), + ("Action", "Ekintza"), + ("Add", "Gehitu"), + ("Local Port", "Ataka lokala"), + ("Local Address", "Helbide lokala"), + ("Change Local Port", "Aldatu ataka lokala"), + ("setup_server_tip", "Konexio azkarragorako, konfiguratu zure zerbitzaria"), + ("Too short, at least 6 characters.", "Laburregia, 6 karaktere gutxienez."), + ("The confirmation is not identical.", "Berrespena ez dator bat."), + ("Permissions", "Baimenak"), + ("Accept", "Onartu"), + ("Dismiss", "Ezeztatu"), + ("Disconnect", "Deskonektatu"), + ("Enable file copy and paste", "Gaitu fitxategien kopiatze eta itsastea"), + ("Connected", "Konektatuta"), + ("Direct and encrypted connection", "Zifratutako konexio zuzena"), + ("Relayed and encrypted connection", "Zifratutako konexio igorria"), + ("Direct and unencrypted connection", "Zifratu gabeko konexio zuzena"), + ("Relayed and unencrypted connection", "Zifratu gabeko konexio igorria"), + ("Enter Remote ID", "Sartu urruneko IDa"), + ("Enter your password", "Sartu zure pasahitza"), + ("Logging in...", "Saioa hasten..."), + ("Enable RDP session sharing", "Gaitu RDP saio-partekatzea"), + ("Auto Login", "Saio-haste automatikoa"), + ("Enable direct IP access", "Gaitu IP sarbide zuzena"), + ("Rename", "Berrizendatu"), + ("Space", "Zuriunea"), + ("Create desktop shortcut", "Sortu mahaigaineko lasterbidea"), + ("Change Path", "Aldatu bide-izena"), + ("Create Folder", "Sortu karpeta"), + ("Please enter the folder name", "Mesedez, sartu karpetaren izena"), + ("Fix it", "Konpondu"), + ("Warning", "Oharra"), + ("Login screen using Wayland is not supported", "Saio-hasiera Wayland erabilita ez dago onartuta"), + ("Reboot required", "Berrabiaraztea beharrezkoa"), + ("Unsupported display server", "Bistaratze-zerbitzaria ez da bateragarria"), + ("x11 expected", "x11 espero zen"), + ("Port", "Ataka"), + ("Settings", "Ezarpenak"), + ("Username", "Erabiltzaile-izena"), + ("Invalid port", "Ataka baliogabea"), + ("Closed manually by the peer", "Parekideak konexioa eskuz itxi du"), + ("Enable remote configuration modification", "Gaitu urruneko konfigurazio-aldaketak"), + ("Run without install", "Exekutatu instalatu gabe"), + ("Connect via relay", "Konektatu igorpen-zerbitzari batetik"), + ("Always connect via relay", "Konektatu beti igorpen-zerbitzari batetik"), + ("whitelist_tip", "Baimendutako IPak soilik konektatu daitezke mahaigain honetara"), + ("Login", "Saio-hasiera"), + ("Verify", "Egiaztatu"), + ("Remember me", "Gogoratu"), + ("Trust this device", "Gailu honetaz fidatu"), + ("Verification code", "Egiaztapen-kodea"), + ("verification_tip", "Egiaztapen-kode bat bidali da erregistratutako helbide elektronikora. Sartu egiaztapen-kodea saio-hasiera jarraitzeko."), + ("Logout", "Saioa bukatu"), + ("Tags", "Etiketak"), + ("Search ID", "Bilatu IDa"), + ("whitelist_sep", "Koma, puntu koma, zuriune edo lerro berriengatik banatuta"), + ("Add ID", "Gehitu IDa"), + ("Add Tag", "Gehitu etiketa"), + ("Unselect all tags", "Desautatu etiketa guztiak"), + ("Network error", "Sare-errorea"), + ("Username missed", "Erabiltzaile-izena ahaztu duzu"), + ("Password missed", "Pasahitza ahaztu duzu"), + ("Wrong credentials", "Kredentzial baliogabeak"), + ("The verification code is incorrect or has expired", "Egiaztapen-kodea baliogabe edo iraungitua da"), + ("Edit Tag", "Editatu etiketa"), + ("Forget Password", "Ahaztu pasahitza"), + ("Favorites", "Gogokoenak"), + ("Add to Favorites", "Gehitu gogokoenetara"), + ("Remove from Favorites", "Kendu gpgokoenetatik"), + ("Empty", "Hutsik"), + ("Invalid folder name", "Karpeta-izen baliogabea"), + ("Socks5 Proxy", "Socks5 proxia"), + ("Socks5/Http(s) Proxy", "Socks5/Http(s) proxia"), + ("Discovered", "Aurkituta"), + ("install_daemon_tip", "Ordenagailua pizterakoan hasteko, sistemaren zerbitzua instalatu behar duzu."), + ("Remote ID", "Urruneko IDa"), + ("Paste", "Itsatsi"), + ("Paste here?", "Itsatsi hemen?"), + ("Are you sure to close the connection?", "Ziur zaude konexioa itxi nahi duzula?"), + ("Download new version", "Deskargatu bertsio berria"), + ("Touch mode", "Ukipen modua"), + ("Mouse mode", "Sagu modua"), + ("One-Finger Tap", "Hatz bakarreko ukipena"), + ("Left Mouse", "Ezkerreko botoia"), + ("One-Long Tap", "Ukipen luzea"), + ("Two-Finger Tap", "Bi hatzeko ukipena"), + ("Right Mouse", "Eskuineko botoia"), + ("One-Finger Move", "Hatz bakarreko mugimendua"), + ("Double Tap & Move", "Bi aldiz ukitu eta mugitu"), + ("Mouse Drag", "Saguarekin arrastatu"), + ("Three-Finger vertically", "Hiru hatz bertikalki"), + ("Mouse Wheel", "Saguaren gurpila"), + ("Two-Finger Move", "Bi hatzeko mugimendua"), + ("Canvas Move", "Oihal-mugimendua"), + ("Pinch to Zoom", "Atximurkatu zoom egiteko"), + ("Canvas Zoom", "Oihal-zooma"), + ("Reset canvas", "Berrezarri oihala"), + ("No permission of file transfer", "Ez duzu baimenik fitxategiak transferitzeko"), + ("Note", "Nota"), + ("Connection", "Konexioa"), + ("Share Screen", "Partekatu pantaila"), + ("Chat", "Txata"), + ("Total", "Guztira"), + ("items", "elementuak"), + ("Selected", "hautatuta"), + ("Screen Capture", "Pantaila-grabazioa"), + ("Input Control", "Sarrera-kontrola"), + ("Audio Capture", "Audio-grabazioa"), + ("File Connection", "Fitxategi-konexioa"), + ("Screen Connection", "Pantaila-konexioa"), + ("Do you accept?", "Onartzen al duzu?"), + ("Open System Setting", "Ireki sistemaren ezarpenak"), + ("How to get Android input permission?", "Nola lortu dezaket Android sarrera-baimena?"), + ("android_input_permission_tip1", "Urruneko gailu batek zure Android gailua saguaren edo ukipenaren bidez kontrolatzeko, RustDesk \"Irisgarritasuna\" zerbitzua erabiltzeko baimena eman behar diozu."), + ("android_input_permission_tip2", "Joan hurrengo irekiko den sistemaren konfigurazio orrira, bilatu eta sartu [Instalatutako zerbitzuak], aktibatu [RustDesk Input] zerbitzua."), + ("android_new_connection_tip", "Kontrol-eskaera berri bat jaso da uneko gailuarentzat."), + ("android_service_will_start_tip", "Pantaila-argazkia gaitzen baduzu, zerbitzua automatikoki abiaraziko da, eta beste gailu batzuek gailu honetatik konexioa eskatzeko aukera izango dute."), + ("android_stop_service_tip", "Zerbitzua ixteak ezarritako konexio guztiak automatikoki itxiko ditu."), + ("android_version_audio_tip", "Uneko Android bertsioak ez du audioa grabatzea onartzen; mesedez, eguneratu Android 10 edo berrira."), + ("android_start_service_tip", "Sakatu [Hasi zerbitzua] edo gaitu [Pantaila-grabazioa] baimena pantaila-partekatzea hasteko."), + ("android_permission_may_not_change_tip", "Ezarritako konexioen baimenak ez dira aldatuko berriro konektatu arte."), + ("Account", "Kontua"), + ("Overwrite", "Berridatzi"), + ("This file exists, skip or overwrite this file?", "Fitxategi hau existitzen da dagoeneko, saltatu edo berridatzi nahi duzu?"), + ("Quit", "Irten"), + ("Help", "Laguntza"), + ("Failed", "Huts egin du"), + ("Succeeded", "Arrakastatsua izan da"), + ("Someone turns on privacy mode, exit", "Norbaitek pribatutasun modua hasten du, irten"), + ("Unsupported", "Ez da onartzen"), + ("Peer denied", "Parekidea ukatuta"), + ("Please install plugins", "Mesedez, instalatu plugin hauek"), + ("Peer exit", "Parekidea irten da"), + ("Failed to turn off", "Itzaltzeak huts egin du"), + ("Turned off", "Itzalita"), + ("Language", "Hizkuntza"), + ("Keep RustDesk background service", "Mantendu RustDesk atzeko planoko zerbitzu bezala"), + ("Ignore Battery Optimizations", "Ezikusi bateria optimizazioak"), + ("android_open_battery_optimizations_tip", "Ezaugarri hau desgaitu nahi baduzu, joan zaitez RustDesk aplikazioaren ezarpen orrira, bilatu eta saltu [Bateria] orrira eta kendu [Mugarik gabe]"), + ("Start on boot", "Hasi abiaraztean"), + ("Start the screen sharing service on boot, requires special permissions", "Hasi pantaila-partekatze zerbitzua abiaraztean, baimen bereziak behar ditu"), + ("Connection not allowed", "Konexioa ez dago baimenduta"), + ("Legacy mode", "Legatu-modua"), + ("Map mode", "Mapa modua"), + ("Translate mode", "Itzultze modua"), + ("Use permanent password", "Erabili betirako pasahitza"), + ("Use both passwords", "Erabili bi pasahitzak"), + ("Set permanent password", "Ezarri betirako pasahitza"), + ("Enable remote restart", "Gaitu urruneko berrabiaraztea"), + ("Restart remote device", "Berrabiarazi urruneko gailua"), + ("Are you sure you want to restart", "Ziur zaude berrabiarazi nahi duzula?"), + ("Restarting remote device", "Urruneko gailua berrabiarazten"), + ("remote_restarting_tip", "Urruneko gailua berrabiarazten dabil. Mesedez, itxi mezu hau eta konektatu betirako pasahitzarekin une bat pasa ostean."), + ("Copied", "Kopiatuta"), + ("Exit Fullscreen", "Irten pantaila osotik"), + ("Fullscreen", "Pantaila osoa"), + ("Mobile Actions", "Mugikor-ekintzak"), + ("Select Monitor", "Hautatu monitorea"), + ("Control Actions", "Kontrol-ekintzak"), + ("Display Settings", "Pantailaren ezarpenak"), + ("Ratio", "Erlazioa"), + ("Image Quality", "Irudiaren kalitatea"), + ("Scroll Style", "Korritze estiloa"), + ("Show Toolbar", "Erakutsi tresna-barra"), + ("Hide Toolbar", "Ezkutatu tresna-barra"), + ("Direct Connection", "Konexio zuzena"), + ("Relay Connection", "Konexio igorria"), + ("Secure Connection", "Konexio segurua"), + ("Insecure Connection", "Konexio ez-segurua"), + ("Scale original", "Jatorrizko eskala"), + ("Scale adaptive", "Eskala moldagarria"), + ("General", "Orokorra"), + ("Security", "Segurtasuna"), + ("Theme", "Itxura"), + ("Dark Theme", "Itxura iluna"), + ("Light Theme", "Itxura argia"), + ("Dark", "Iluna"), + ("Light", "Argia"), + ("Follow System", "Jarraitu sistemaren itxura"), + ("Enable hardware codec", "Gaitu hardware kodeka"), + ("Unlock Security Settings", "Desblokeatu segurtasun ezarpenak"), + ("Enable audio", "Gaitu audioa"), + ("Unlock Network Settings", "Desblokeatu sare-ezarpenak"), + ("Server", "Zerbitzaria"), + ("Direct IP Access", "IP sarbide zuzena"), + ("Proxy", "Proxia"), + ("Apply", "Aplikatu"), + ("Disconnect all devices?", "Deskonektatu gailu guztiak?"), + ("Clear", "Garbitu"), + ("Audio Input Device", "Audio sarrera gailua"), + ("Use IP Whitelisting", "Erabili IP onartuen zerrenda"), + ("Network", "Sarea"), + ("Pin Toolbar", "Ainguratu tresna-barra"), + ("Unpin Toolbar", "Aingura kendu tresna-barrari"), + ("Recording", "Grabatzen"), + ("Directory", "Direktorioa"), + ("Automatically record incoming sessions", "Automatikoki grabatu sarrerako saioak"), + ("Change", "Aldatu"), + ("Start session recording", "Hasi saioaren grabaketa"), + ("Stop session recording", "Gelditu saioaren grabaketa"), + ("Enable recording session", "Gaitu saioen grabaketa"), + ("Enable LAN discovery", "Gaitu LAN ezagutza"), + ("Deny LAN discovery", "Ukatu LAN ezagutza"), + ("Write a message", "Idatzi mezu bat"), + ("Prompt", "Testu-gonbita"), + ("Please wait for confirmation of UAC...", "Mesedez, itxaron UAC berrespenari"), + ("elevated_foreground_window_tip", "Mahaigaineko uneko urruneko leihoak goi mailako baimenak behar ditu funtzionatzeko. Beraz, ezin duzu sagua eta teklatua erabili aldi baterako. Urruneko erabiltzaileari uneko leihoa minizatu edo kudeatze-leihoan maila igotzeko botoia erabili dezan eskatu diezaiokezu. Arazo hau saihesteko, programa instalatzea gomendatzen da urruneko gailuan."), + ("Disconnected", "Deskonektatuta"), + ("Other", "Besteak"), + ("Confirm before closing multiple tabs", "Berretsi fitxa ugari itxi baino lehen"), + ("Keyboard Settings", "Teklatuaren ezarpenak"), + ("Full Access", "Sarbide osoa"), + ("Screen Share", "Pantailaren partekatzea"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland Ubuntu 21.04 edo bertsio berriagoa behar du."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland-ek linux banaketa berriago bat behar du. Saiatu X11 mahaigainarekin edo aldatu zure sistema eragilea."), + ("JumpLink", "Ikusi"), + ("Please Select the screen to be shared(Operate on the peer side).", "Mesedez, hautatu partekatuko den pantaila (Kudeatu parekidearen aldean)"), + ("Show RustDesk", "Erakutsi RustDesk"), + ("This PC", "PC hau"), + ("or", "edo"), + ("Continue with", "Jarraitu honekin"), + ("Elevate", "Igo maila"), + ("Zoom cursor", "Handitu kurtsorea"), + ("Accept sessions via password", "Onartu saioak pasahitzaren bidez"), + ("Accept sessions via click", "Onartu saioak klikaren bidez"), + ("Accept sessions via both", "Onatu saioak bientzako"), + ("Please wait for the remote side to accept your session request...", "Mesedez, itxaron urruneko aldeak zure saio eskaera onartu dezan..."), + ("One-time Password", "Aldi bateko pasahitza"), + ("Use one-time password", "Erabili aldi baterako pasahitza"), + ("One-time password length", "Aldi baterako pasahitzaren luzera"), + ("Request access to your device", "Eskatu sarrera zure gailura"), + ("Hide connection management window", "Ezkutatu konexio kudeatze leihoa"), + ("hide_cm_tip", "Utzi ezkutatzen saioak pasahitzarekin onartzen badira eta pasahitza betirakoa bada"), + ("wayland_experiment_tip", "Wayland euskarria oraindik fase esperimentalean dago, mesedez, erabili X11 arretarik gabeko sarrera behar baduzu."), + ("Right click to select tabs", "Eskuineko klika fitxak hautatzeko"), + ("Skipped", "Saltatuta"), + ("Add to address book", "Gehitu helbide-liburura"), + ("Group", "Taldea"), + ("Search", "Bilatu"), + ("Closed manually by web console", "Web kontsolarengatik eskuz itxita"), + ("Local keyboard type", "Teklatu mota lokala"), + ("Select local keyboard type", "Hautatu teklatu mota lokala"), + ("software_render_tip", "Nvidia bideo-txartela baduzu eta urruneko leihoa berehala ixten bada, nouveau driver-a instalatzea eta software errenderizazioa hautatzea lagundu dezake. Aplikazioa berrabiarazi behar da."), + ("Always use software rendering", "Erabili software bidezko errenderizazioa beti"), + ("config_input", "Urruneko mahaigaina teklatuaren bidez kontrolatzeko, RustDesk-i \"Sarrera monitorizazioa\" baimena eman behar diozu."), + ("config_microphone", "Urrunetik hitz egin ahal izateko, RustDeski-i \"Grabatu audioa\" baimena eman behar diozu."), + ("request_elevation_tip", "Pribilegioen maila igotzea eskatu ahal duzu ere norbait badago urruneko aldean"), + ("Wait", "Itxaron"), + ("Elevation Error", "Maila-igotze errorea"), + ("Ask the remote user for authentication", "Eskatu autentifikazioa urruneko erabiltzaileari"), + ("Choose this if the remote account is administrator", "Aukeratu urruneko kontua administratzailea bada"), + ("Transmit the username and password of administrator", "Igorri administratzailearen erabiltzailea eta pasahitza"), + ("still_click_uac_tip", "Oraindik beharrezkoa da urruneko erabiltzaileak Ondo botoiari klik egitea exekutatzen ari den RustDesk UAC leihoan"), + ("Request Elevation", "Eskatu pribilegioen maila igotzea"), + ("wait_accept_uac_tip", "Mesedez, itxaron urruneko erabiltzaileak UAC onartu arte."), + ("Elevate successfully", "Maila igotzea ondo joan da"), + ("uppercase", "maiuskula"), + ("lowercase", "minuskula"), + ("digit", "zenbakia"), + ("special character", "karaktere berezia"), + ("length>=8", "luzera>=8"), + ("Weak", "Ahula"), + ("Medium", "Ertaina"), + ("Strong", "Indartsua"), + ("Switch Sides", "Aldatu aldeak"), + ("Please confirm if you want to share your desktop?", "Mesedez, berretsi zure mahaigaina partekatu nahi duzula"), + ("Display", "Pantaila"), + ("Default View Style", "Ikuspen estilo lehenetsia"), + ("Default Scroll Style", "Korritze estilo lehenetsia"), + ("Default Image Quality", "Irudi kalitate lehenetsia"), + ("Default Codec", "Kodek lehenetsia"), + ("Bitrate", "Bit-tasa"), + ("FPS", "FPS"), + ("Auto", "Auto"), + ("Other Default Options", "Beste aukera lehenetsiak"), + ("Voice call", "Ahots-deia"), + ("Text chat", "Testu-txata"), + ("Stop voice call", "Gelditu ahots-deia"), + ("relay_hint_tip", "Posible da ezinezkoa izatea zuzen konektatzea. Igorpen zerbitzari baten bidez konektatzen saiatu zaitezke. Horrez gain, igorpena lehen aldiz erabili nahi baduzu, \"/r\" atzitu dezakezu IDari edo \"Beti igorpen zerbitzari baten bidez konektatu\" aukeratu azken saioen txartelan existitzen bada."), + ("Reconnect", "Berriro konektatu"), + ("Codec", "Kodeka"), + ("Resolution", "Bereizmena"), + ("No transfers in progress", "Ez dago transferentziarik abian"), + ("Set one-time password length", "Ezarri aldi baterako pasahitzaren luzera"), + ("RDP Settings", "RDP ezarpenak"), + ("Sort by", "Ordenatu honengatik"), + ("New Connection", "Konexio berria"), + ("Restore", "Berrezarri"), + ("Minimize", "Minimizatu"), + ("Maximize", "Maximizatu"), + ("Your Device", "Zure gailua"), + ("empty_recent_tip", "Ups, ez dago azken saiorik!\nBerri bat planifikatzeko ordua da."), + ("empty_favorite_tip", "Parekide gogokorik gabe oraindik?\nBilatu norbait konektatzeko eta gehitu zure gogokoetara!"), + ("empty_lan_tip", "Ai ez, badirudi ez duzula parekiderik aurkitu oraindik."), + ("empty_address_book_tip", "Badirudi ez dagoela parekiderik zure helbide-liburuan."), + ("eg: admin", "adib. admin"), + ("Empty Username", "Erabiltzaile-izena hutsik"), + ("Empty Password", "Pasahitza hutsik"), + ("Me", "Ni"), + ("identical_file_tip", "Fitxategi hau parekidearen berdina da."), + ("show_monitors_tip", "Erakutsi monitoreak tresna-barran"), + ("View Mode", "Ikuspen modua"), + ("login_linux_tip", "Urruneko Linux kontu batera hasi behar duzu saioa X mahaigain saio bat gaitzeko"), + ("verify_rustdesk_password_tip", "Berretsi RustDesk pasahitza"), + ("remember_account_tip", "Gogoratu kontu hau"), + ("os_account_desk_tip", "Kontu hau bururik gabe urruneko SE hasi eta mahaigaineko saioa gaitzeko erabiltzen da"), + ("OS Account", "SE kontua"), + ("another_user_login_title_tip", "Beste erabiltzaile batek saioa hasi du dagoeneko"), + ("another_user_login_text_tip", "Deskonektatu"), + ("xorg_not_found_title_tip", "Ez da Xorg aurkitu"), + ("xorg_not_found_text_tip", "Mesedez, instalatu ezazu Xorg"), + ("no_desktop_title_tip", "Ez dago mahaigainik eskuragarri"), + ("no_desktop_text_tip", "Mesedez, instalatu ezazu GNOME Desktop"), + ("No need to elevate", "Ez da beharrezkoa pribilegioen maila igotzea"), + ("System Sound", "Sistemaren soinua"), + ("Default", "Lehenetsia"), + ("New RDP", "RDP berria"), + ("Fingerprint", "Hatz-marka"), + ("Copy Fingerprint", "Kopiatu hatz-marka"), + ("no fingerprints", "hatz-markarik ez"), + ("Select a peer", "Hautatu parekidea"), + ("Select peers", "Hautatu parekideak"), + ("Plugins", "Pluginak"), + ("Uninstall", "Desinstalatu"), + ("Update", "Eguneratu"), + ("Enable", "Gaitu"), + ("Disable", "Desgaitu"), + ("Options", "Aukerak"), + ("resolution_original_tip", "Jatorrizko bereizmena"), + ("resolution_fit_local_tip", "Bereizmen lokala egokitu"), + ("resolution_custom_tip", "Bereizmen pertsonalizatua"), + ("Collapse toolbar", "Ezkutatu tresna-barra"), + ("Accept and Elevate", "Onartu eta igo maila"), + ("accept_and_elevate_btn_tooltip", "Konexioa onartu eta UAC baimenak mailaz igo."), + ("clipboard_wait_response_timeout_tip", "Kopiatzeko denbora-muga gainditu da."), + ("Incoming connection", "Sarrerako konexioa"), + ("Outgoing connection", "Irteerako konexioa"), + ("Exit", "Irten"), + ("Open", "Ireki"), + ("logout_tip", "Saioa itxi nahi duzu?"), + ("Service", "Zerbitzua"), + ("Start", "Hasi"), + ("Stop", "Gelditu"), + ("exceed_max_devices", "Kudeatutako gailuen mugara heldu zara."), + ("Sync with recent sessions", "Sinkronizatu azken saioekin"), + ("Sort tags", "Ordenatu etiketak"), + ("Open connection in new tab", "Ireki konexioa fitxa berri batean"), + ("Move tab to new window", "Mugitu fitxa leiho berri batera"), + ("Can not be empty", "Ezin da hutsik egon"), + ("Already exists", "Dagoeneko existitzen da"), + ("Change Password", "Aldatu pasahitza"), + ("Refresh Password", "Freskatu pasahitza"), + ("ID", "ID"), + ("Grid View", "Sareta-ikuspegia"), + ("List View", "Zerrenda-ikuspegia"), + ("Select", "Hautatu"), + ("Toggle Tags", "Aldatu etiketak"), + ("pull_ab_failed_tip", "Ezin izan da direktorio freskatu"), + ("push_ab_failed_tip", "Ezin izan da zerbitzariko direktorioarekin sinkronizatu"), + ("synced_peer_readded_tip", "Azken saioetako gailuak direktorioarekin sinkronizatuko dira"), + ("Change Color", "Aldatu kolorea"), + ("Primary Color", "Kolore nagusia"), + ("HSV Color", "HSV kolorea"), + ("Installation Successful!", "Instalazioa ondo joan da"), + ("Installation failed!", "Instalazioak huts egin du"), + ("Reverse mouse wheel", "Inbertitu saguaren gurpila"), + ("{} sessions", "{} saio"), + ("scam_title", "IRUZURTUA izan zintezke!"), + ("scam_text1", "Ezagutzen eta fidatzen EZ duzun norbaitekin telefonoz bazaude eta RustDesk erabiltzeko eta zerbitzua abiarazteko eskatu badizute, ez ezazu egin eta eskegi berehala."), + ("scam_text2", "Ziurrenik zure dirua edo informazio pribatua lapurtzen saiatzen ari diren iruzurgileak dira."), + ("Don't show again", "Ez erakutsi berriro"), + ("I Agree", "Onartzen dut"), + ("Decline", "Ukatu"), + ("Timeout in minutes", "Denbora-muga minututan"), + ("auto_disconnect_option_tip", "Itxi sarrerako konexioak automatikoki erabiltzailearen jarduera faltagatik."), + ("Connection failed due to inactivity", "Konexioak huts egin du jarduera faltagatik"), + ("Check for software update on startup", "Egiaztatu software eguneraketak abiatzerakoan"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "Mesedez, bertsio-berritu RustDesk Server Pro {} bertsiora edo berriago batera!"), + ("pull_group_failed_tip", "Ezin izan da taldea freskatu"), + ("Filter by intersection", "Iragazi bidegurutzez"), + ("Remove wallpaper during incoming sessions", "Kendu horma-papera sarrerako saioetan"), + ("Test", "Probatu"), + ("display_is_plugged_out_msg", "Pantaila deskonektatuta dago, aldatu nagusira."), + ("No displays", "Ez dago pantailarik"), + ("elevated_switch_display_msg", "Aldatu pantaila nagusira pantaila ugari ez daudelako goi mailako moduan onartuta"), + ("Open in new window", "Ireki leiho berrian"), + ("Show displays as individual windows", "Erakutsi pantailak banakako leiho gisa"), + ("Use all my displays for the remote session", "Erabili nire pantaila guztiak urruneko saiorako"), + ("selinux_tip", "SELinux zure gailuan gaituta dago. Honek RustDesk kontrolatutako alde gisa dagoenean behar bezala ez exekutatzea ekarri dezake."), + ("Change view", "Aldatu ikuspegia"), + ("Big tiles", "Baldosa handiak"), + ("Small tiles", "Baldosa txikiak"), + ("List", "Zerrenda"), + ("Virtual display", "Pantaila birtuala"), + ("Plug out all", "Deskonektatu dena"), + ("True color (4:4:4)", "Kolore erreala (4:4:4)"), + ("Enable blocking user input", "Gaitu erabiltzailearen sarreraren blokeoa"), + ("id_input_tip", "ID bat, IP zuzena edo ataka duen domeinu bat sar dezakezu (:).\nBeste zerbitzari bateko gailu batera sartu nahi baduzu, erantsi zerbitzariaren helbidea (@?key=), adibidez,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nZerbitzari publiko bateko gailuan sartu nahi baduzu, idatzi \"@public\", gakoa ez da zerbitzari publikorako beharrezkoa.\n\nLehen konexioan igorpen-zerbitzari bat erabiltzea behartu nahi baduzu, gehitu \"/r\" IDaren amaieran, adibidez, \"9123456234/r\"."), + ("privacy_mode_impl_mag_tip", "1 modua"), + ("privacy_mode_impl_virtual_display_tip", "2 modua"), + ("Enter privacy mode", "Sartu modu pribatuan"), + ("Exit privacy mode", "Irten modu pribatutik"), + ("idd_not_support_under_win10_2004_tip", "Zeharkako pantaila kudeatzailea ez dago onartuta. Windows 10, 2004 bertsioa edo berriagoa behar da."), + ("switch_display_elevated_connections_tip", "Pantaila nagusia ez den batera aldatzea ez da onartzen goi mailako moduan hainbat konexio daudenean. Mesedez, saiatu berriro instalatu ondoren hainbat pantaila kontrolatu nahi badituzu."), + ("input_source_1_tip", "Sarrera-iturri 1"), + ("input_source_2_tip", "Sarrera-iturri 2"), + ("capture_display_elevated_connections_tip", "Pantaila anitz harrapatzea goi mailako pribilegioekin ez da onartzen. Mesedez, saiatu berriro instalatu ondoren hainbat pantaila kontrolatu nahi badituzu."), + ("Swap control-command key", "Aldatu kontrol-komando teklak"), + ("swap-left-right-mouse", "Aldatu saguaren ezker-eskuin botoiak"), + ("2FA code", "2FA kodea"), + ("More", "Gehiago"), + ("enable-2fa-title", "Gaitu bi faktoreko autentifikazioa"), + ("enable-2fa-desc", "Konfiguratu orain zure autentifikatzailea. Authy, Microsoft edo Google Authenticator bezalako aplikazio autentifikatzaile bat erabil dezakezu telefonoan edo mahaigainean.\n\nEskaneatu QR kodea zure aplikazioarekin eta idatzi aplikazioak erakusten duen kodea bi faktoreko autentifikazioa gaitzeko."), + ("wrong-2fa-code", "Ezin da kodea egiaztatu. Egiaztatu kodea eta tokiko orduaren ezarpenak zuzenak direla"), + ("enter-2fa-title", "Bi faktoreko autentifikazioa"), + ("Email verification code must be 6 characters.", "Posta elektronikoa egiaztatzeko kodeak 6 karaktere izan behar ditu."), + ("2FA code must be 6 digits.", "2FA kodeak 6 digitu izan behar ditu."), + ("Multiple Windows sessions found", "Windows saio anitz aurkitu dira"), + ("Please select the session you want to connect to", "Mesedez, aukeratu konektatu nahi duzun saioa"), + ("powered_by_me", "RustDesk-ek egina"), + ("outgoing_only_desk_tip", "Edizio pertsonalizatua da hau.\nBeste gailuetara konekta zaitezke, baina beste gailu batzuk ezin dira zure gailura konektatu."), + ("preset_password_warning", "Edizio pertsonalizatu hau aurrez ezarritako pasahitz batekin dator. Pasahitz hau ezagutzen duenak zure gailuaren kontrol osoa lor dezake. Hau espero ez bazenuen, desinstalatu softwarea berehala."), + ("Security Alert", "Segurtasun Alerta"), + ("My address book", "Nire helbide-liburua"), + ("Personal", "Pertsonala"), + ("Owner", "Jabea"), + ("Set shared password", "Ezarri pasahitz partekatua"), + ("Exist in", "Bertan existitzen da"), + ("Read-only", "Irakurtzeko soilik"), + ("Read/Write", "Irakurri/Idatzi"), + ("Full Control", "Kontrol Osoa"), + ("share_warning_tip", "Goiko eremuak partekatuak eta besteengatik ikusgai daude."), + ("Everyone", "Denek"), + ("ab_web_console_tip", "Gehiago web kontsolan"), + ("allow-only-conn-window-open-tip", "Baimendu konexioa RustDesk leihoa irekita badago"), + ("no_need_privacy_mode_no_physical_displays_tip", "Ez dago pantaila fisikorik, ez dago pribatutasun modua erabili beharrik."), + ("Follow remote cursor", "Jarraitu urruneko kurtsorea"), + ("Follow remote window focus", "Jarraitu urruneko leiho fokua"), + ("default_proxy_tip", "Lehenetsitako protokoloa eta ataka Socks5 eta 1080 dira"), + ("no_audio_input_device_tip", "Ez da aurkitu audio sarrerako gailurik."), + ("Incoming", "Sarrerakoa"), + ("Outgoing", "Irteerakoa"), + ("Clear Wayland screen selection", "Garbitu Wayland pantaila-hautapena"), + ("clear_Wayland_screen_selection_tip", "Pantaila hautapena garbitu ondoren, partekatzeko pantaila hauta dezakezu."), + ("confirm_clear_Wayland_screen_selection_tip", "Ziur Wayland pantaila-hautapena garbituko duzula?"), + ("android_new_voice_call_tip", "Ahots-dei eskaera berri bat jaso da. Onartzen baduzu, audioa ahots bidezko komunikaziora aldatuko da."), + ("texture_render_tip", "Erabili testura-errenderizazioa irudiak leunagoak egiteko."), + ("Use texture rendering", "Erabili testura-errenderizazioa"), + ("Floating window", "Leiho flotagarria"), + ("floating_window_tip", "RustDesk atzeko planoko zerbitzua mantentzen laguntzen du"), + ("Keep screen on", "Mantendu pantaila piztuta"), + ("Never", "Inoiz"), + ("During controlled", "Kontrolatu bitartean"), + ("During service is on", "Zerbitzua aktibo dagoen bitartean"), + ("Capture screen using DirectX", "Harrapatu pantaila DirectX erabiliz"), + ("Back", "Atzera"), + ("Apps", "Aplikazioak"), + ("Volume up", "Igo bolumena"), + ("Volume down", "Jaitsi bolumena"), + ("Power", "Piztu/Itzali"), + ("Telegram bot", "Telegrameko bot-a"), + ("enable-bot-tip", "Ezaugarri hau gaitzen baduzu, zure bot-etik 2FA kodea jaso dezakezu. Konexio jakinarazpenetarako ere balio dezake."), + ("enable-bot-desc", "1, Ireki txat bat @BotFather bot-arekin.\n2, Bidali \"/newbot\" agindua. Token bat jasoko duzu urrats honen ostean.\n3, Hasi txat bat zure bot berriarekin. Bidali mezu bat aurreko barra batekin (\"/\") \"/kaixo\" bezala gaitzeko.\n"), + ].iter().cloned().collect(); + } + \ No newline at end of file From 0511cdbb21868345e5d2bc120fea1117661802eb Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 5 Jul 2024 09:30:59 +0800 Subject: [PATCH 208/335] feat: clipboard svg (#8615) Signed-off-by: fufesou --- Cargo.lock | 276 ++++++++++++++++++++++++++++++++++++++++++++++- src/clipboard.rs | 96 +++++++++++------ 2 files changed, 337 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b25f5cb5f3..cfc0f8560ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arboard" version = "3.4.0" -source = "git+https://github.com/rustdesk-org/arboard#61b448d8261fb313d67a61d03fc130bd738db396" +source = "git+https://github.com/rustdesk-org/arboard#27b4e503caa70ec6306e5270461429f2cf907ad6" dependencies = [ "clipboard-win", "core-graphics 0.23.2", @@ -234,11 +234,24 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "parking_lot", + "resvg", "windows-sys 0.48.0", "wl-clipboard-rs", "x11rb 0.13.1", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-broadcast" version = "0.5.1" @@ -1513,6 +1526,12 @@ dependencies = [ "dasp_sample", ] +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + [[package]] name = "dbus" version = "0.9.7" @@ -1672,7 +1691,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.4", + "libloading 0.7.4", ] [[package]] @@ -2062,6 +2081,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "flume" version = "0.11.0" @@ -2118,6 +2143,29 @@ dependencies = [ "libm", ] +[[package]] +name = "fontconfig-parser" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" +dependencies = [ + "roxmltree 0.19.0", +] + +[[package]] +name = "fontdb" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -3165,6 +3213,12 @@ dependencies = [ "tiff", ] +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + [[package]] name = "impersonate_system" version = "0.1.0" @@ -3408,6 +3462,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "kurbo" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c" +dependencies = [ + "arrayvec", + "smallvec", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -3713,6 +3777,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -4659,9 +4732,15 @@ version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" dependencies = [ - "siphasher", + "siphasher 0.2.3", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.5" @@ -5335,6 +5414,31 @@ dependencies = [ "winreg 0.50.0", ] +[[package]] +name = "resvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" +dependencies = [ + "gif", + "jpeg-decoder", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -5359,6 +5463,18 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "roxmltree" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rpassword" version = "2.1.0" @@ -5737,6 +5853,22 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.18" @@ -6027,12 +6159,27 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -6042,6 +6189,15 @@ dependencies = [ "autocfg 1.3.0", ] +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -6112,6 +6268,15 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + [[package]] name = "strsim" version = "0.8.0" @@ -6173,6 +6338,16 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "svgtypes" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c" +dependencies = [ + "kurbo", + "siphasher 1.0.1", +] + [[package]] name = "syn" version = "0.15.44" @@ -6512,6 +6687,32 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.6.1" @@ -6803,6 +7004,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + [[package]] name = "typenum" version = "1.17.0" @@ -6875,6 +7082,18 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -6890,12 +7109,30 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "unicode-script" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" + [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.13" @@ -6958,6 +7195,33 @@ dependencies = [ "log", ] +[[package]] +name = "usvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher 1.0.1", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "utf16string" version = "0.2.0" @@ -7956,6 +8220,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "zbus" version = "3.15.2" diff --git a/src/clipboard.rs b/src/clipboard.rs index 9dbdfddda20..0db9f59c144 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -14,6 +14,7 @@ use hbb_common::{ pub const CLIPBOARD_NAME: &'static str = "clipboard"; pub const CLIPBOARD_INTERVAL: u64 = 333; +const FAKE_SVG_WIDTH: usize = 999999; lazy_static::lazy_static! { pub static ref CONTENT: Arc> = Default::default(); @@ -142,6 +143,13 @@ pub fn check_clipboard( let content = ctx2.get(); if let Ok(content) = content { if !content.is_empty() { + if matches!(content, ClipboardData::Text(_)) { + // Skip the text if the last content is image-svg/html + if ctx2.is_last_plain { + return None; + } + } + let changed = content != *old.lock().unwrap(); if changed { log::info!("{} update found on {}", CLIPBOARD_NAME, side); @@ -212,26 +220,26 @@ impl ClipboardData { match self { ClipboardData::Empty => true, ClipboardData::Text(s) => s.is_empty(), - ClipboardData::Image(a, _) => a.bytes.is_empty(), + ClipboardData::Image(a, _) => a.bytes().is_empty(), } } fn from_msg(clipboard: Clipboard) -> Self { - let is_image = clipboard.width > 0 && clipboard.height > 0; + let is_image = clipboard.width > 0; let data = if clipboard.compress { decompress(&clipboard.content) } else { clipboard.content.into() }; if is_image { - ClipboardData::Image( - arboard::ImageData { - bytes: data.into(), - width: clipboard.width as _, - height: clipboard.height as _, - }, - 0, - ) + // We cannot use data.start_with(b" { - let compressed = compress_func(&a.bytes); - let compress = compressed.len() < a.bytes.len(); + let compressed = compress_func(&a.bytes()); + let compress = compressed.len() < a.bytes().len(); let content = if compress { compressed } else { - a.bytes.to_vec() + a.bytes().to_vec() + }; + let (w, h) = match a { + arboard::ImageData::Rgba(a) => (a.width, a.height), + arboard::ImageData::Svg(_) => (FAKE_SVG_WIDTH as _, 0 as _), }; msg.set_clipboard(Clipboard { compress, content: content.into(), - width: a.width as _, - height: a.height as _, + width: w as _, + height: h as _, ..Default::default() }); } @@ -285,9 +297,13 @@ impl PartialEq for ClipboardData { fn eq(&self, other: &Self) -> bool { match (self, other) { (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, - (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => { - a.width == b.width && a.height == b.height && a.bytes == b.bytes - } + (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => match (a, b) { + (arboard::ImageData::Rgba(a), arboard::ImageData::Rgba(b)) => { + a.width == b.width && a.height == b.height && a.bytes == b.bytes + } + (arboard::ImageData::Svg(a), arboard::ImageData::Svg(b)) => a == b, + _ => false, + }, (ClipboardData::Empty, ClipboardData::Empty) => true, _ => false, } @@ -295,7 +311,12 @@ impl PartialEq for ClipboardData { } #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] -pub struct ClipboardContext(arboard::Clipboard, (Arc, u64), Option); +pub struct ClipboardContext { + inner: arboard::Clipboard, + counter: (Arc, u64), + shutdown: Option, + is_last_plain: bool, +} #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] #[allow(unreachable_code)] @@ -367,13 +388,18 @@ impl ClipboardContext { shutdown = Some(st); } } - Ok(ClipboardContext(board, (change_count, 0), shutdown)) + Ok(ClipboardContext { + inner: board, + counter: (change_count, 0), + shutdown, + is_last_plain: false, + }) } #[inline] pub fn change_count(&self) -> u64 { - debug_assert!(self.2.is_some()); - self.1 .0.load(Ordering::SeqCst) + debug_assert!(self.shutdown.is_some()); + self.counter.0.load(Ordering::SeqCst) } pub fn get(&mut self) -> ResultType { @@ -381,22 +407,28 @@ impl ClipboardContext { let _lock = ARBOARD_MTX.lock().unwrap(); // only for image for the time being, // because I do not want to change behavior of text clipboard for the time being - if cn != self.1 .1 { - self.1 .1 = cn; - if let Ok(image) = self.0.get_image() { - if image.width > 0 && image.height > 0 { - return Ok(ClipboardData::image(image)); - } + if cn != self.counter.1 { + self.is_last_plain = false; + self.counter.1 = cn; + if let Ok(image) = self.inner.get_image() { + // Both text and image svg may be set by some applications + // But we only want to send the svg content. + // + // We can't call `get_text()` and store current text in `old` in outer scope, + // because it may be updated later than svg. + // Then the text will still be sent and replace the image svg content. + self.is_last_plain = matches!(image, arboard::ImageData::Svg(_)); + return Ok(ClipboardData::image(image)); } } - Ok(ClipboardData::Text(self.0.get_text()?)) + Ok(ClipboardData::Text(self.inner.get_text()?)) } fn set(&mut self, data: &ClipboardData) -> ResultType<()> { let _lock = ARBOARD_MTX.lock().unwrap(); match data { - ClipboardData::Text(s) => self.0.set_text(s)?, - ClipboardData::Image(a, _) => self.0.set_image(a.clone())?, + ClipboardData::Text(s) => self.inner.set_text(s)?, + ClipboardData::Image(a, _) => self.inner.set_image(a.clone())?, _ => {} } Ok(()) @@ -405,7 +437,7 @@ impl ClipboardContext { impl Drop for ClipboardContext { fn drop(&mut self) { - if let Some(shutdown) = self.2.take() { + if let Some(shutdown) = self.shutdown.take() { let _ = shutdown.signal(); } } From 48efdcf1f0e61d9c5993d516b7c4247e5b7a1867 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 5 Jul 2024 19:26:20 +0800 Subject: [PATCH 209/335] add remove-preset-password-warning-of --- flutter/lib/common.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 5c927521388..61571f04bd6 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3301,6 +3301,13 @@ Widget buildPresetPasswordWarning() { return Text( 'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error } else if (snapshot.hasData && snapshot.data == true) { + final idServer = + bind.mainGetOptionSync(key: 'custom-rendezvous-server'); + if (idServer.isNotEmpty && + bind.mainGetLocalOption(key: "remove-preset-password-warning-of") == + idServer) { + return SizedBox.shrink(); + } return Container( color: Colors.yellow, child: Column( From d4f3a872762954506e68e30dad9d472806fdbad8 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 5 Jul 2024 20:09:26 +0800 Subject: [PATCH 210/335] change to remove-preset-password-warning --- flutter/lib/common.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 61571f04bd6..390d75ff438 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3301,11 +3301,8 @@ Widget buildPresetPasswordWarning() { return Text( 'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error } else if (snapshot.hasData && snapshot.data == true) { - final idServer = - bind.mainGetOptionSync(key: 'custom-rendezvous-server'); - if (idServer.isNotEmpty && - bind.mainGetLocalOption(key: "remove-preset-password-warning-of") == - idServer) { + if (bind.mainGetLocalOption(key: "remove-preset-password-warning") == + 'Y') { return SizedBox.shrink(); } return Container( From 90df80ed78be3873b41e8d5dab46a44b38dc24c3 Mon Sep 17 00:00:00 2001 From: Vasilis553 <108126638+Vasilis553@users.noreply.github.com> Date: Sat, 6 Jul 2024 06:35:29 +0300 Subject: [PATCH 211/335] Update el.rs (#8628) * Update el.rs * Update el.rs --- src/lang/el.rs | 66 +++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/lang/el.rs b/src/lang/el.rs index eab1c7c1bc9..995f159308f 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -564,8 +564,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("True color (4:4:4)", ""), ("Enable blocking user input", "Ενεργοποίηση αποκλεισμού χειρισμού από τον χρήστη"), ("id_input_tip", "Μπορείτε να εισάγετε ενα ID, μια διεύθυνση IP, ή ένα όνομα τομέα με την αντίστοιχη πόρτα (:).\nΑν θέλετε να συνδεθείτε σε μια συσκευή σε άλλο διακομιστή, παρακαλώ να προσθέσετε και την διεύθυνση του διακομιστή (@?key=), για παράδειγμα,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nΑν θέλετε να συνδεθείτε σε κάποιο δημόσιο διακομιστή, προσθέστε το όνομά του \"@public\", η παράμετρος key δεν απαιτείται για τους δημόσιους διακομιστές."), - ("privacy_mode_impl_mag_tip", "Mode 1"), - ("privacy_mode_impl_virtual_display_tip", "Mode 2"), + ("privacy_mode_impl_mag_tip", "Προφύλαξη Οθόνης"), + ("privacy_mode_impl_virtual_display_tip", "Εικονική Οθόνη"), ("Enter privacy mode", "Ενεργοποίηση λειτουργίας απορρήτου"), ("Exit privacy mode", "Διακοπή λειτουργίας απορρήτου"), ("idd_not_support_under_win10_2004_tip", "Το πρόγραμμα οδήγησης έμμεσης οθόνης δεν υποστηρίζεται. Απαιτείτε λειτουργικό σύστημα Windows 10 έκδοση 2004 ή νεότερο."), @@ -575,37 +575,37 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("swap-left-right-mouse", "Εναλλαγή αριστερό-δεξί κουμπί του ποντικιού"), ("2FA code", "κωδικός 2FA"), ("More", "Περισσότερα"), - ("enable-2fa-title", ""), - ("enable-2fa-desc", ""), - ("wrong-2fa-code", ""), - ("enter-2fa-title", ""), - ("Email verification code must be 6 characters.", ""), - ("2FA code must be 6 digits.", ""), + ("enable-2fa-title", "Ενεργοποίηση Πιστοποίησης Δύο Παραγόντων"), + ("enable-2fa-desc", "Ρυθμίστε τον έλεγχο ταυτότητας τώρα. Μπορείτε να χρησιμοποιήσετε μια εφαρμογή ελέγχου ταυτότητας όπως Authy, Microsoft ή Google Authenticator στο τηλέφωνο ή στην επιφάνεια εργασίας σας.Σαρώστε τον κωδικό QR με την εφαρμογή σας και εισαγάγετε τον κωδικό που εμφανίζει η εφαρμογή σας για να ενεργοποιήσετε τον έλεγχο ταυτότητας δύο παραγόντων."), + ("wrong-2fa-code", "Δεν είναι δυνατή η επαλήθευση του κωδικού. Ελέγξτε ότι ο κωδικός και οι ρυθμίσεις τοπικής ώρας είναι σωστές"), + ("enter-2fa-title", "Έλεγχος ταυτότητας δύο παραγόντων"), + ("Email verification code must be 6 characters.", "Ο κωδικός επαλήθευσης email πρέπει να είναι εως 6 χαρακτήρες"), + ("2FA code must be 6 digits.", "Ο κωδικός 2FA πρέπει να είναι 6ψήφιος."), ("Multiple Windows sessions found", ""), - ("Please select the session you want to connect to", ""), - ("powered_by_me", ""), + ("Please select the session you want to connect to", "Επιλέξτε τη συνεδρία στην οποία θέλετε να συνδεθείτε"), + ("powered_by_me", "Με την υποστήριξη της RustDesk"), ("outgoing_only_desk_tip", ""), - ("preset_password_warning", ""), - ("Security Alert", ""), - ("My address book", ""), - ("Personal", ""), - ("Owner", ""), - ("Set shared password", ""), + ("preset_password_warning", "προειδοποίηση προκαθορισμένου κωδικού πρόσβασης"), + ("Security Alert", "Ειδοποίηση ασφαλείας"), + ("My address book", "Το βιβλίο διευθύνσεών μου"), + ("Personal", "Προσωπικό"), + ("Owner", "Ιδιοκτήτης"), + ("Set shared password", "Ορίστε κοινόχρηστο κωδικό πρόσβασης"), ("Exist in", ""), - ("Read-only", ""), + ("Read-only", "Μόνο για ανάγνωση"), ("Read/Write", ""), - ("Full Control", ""), - ("share_warning_tip", ""), + ("Full Control", "Πλήρης Έλεγχος"), + ("share_warning_tip", "Τα παραπάνω πεδία είναι κοινόχρηστα και ορατά σε άλλους."), ("Everyone", ""), ("ab_web_console_tip", ""), - ("allow-only-conn-window-open-tip", ""), - ("no_need_privacy_mode_no_physical_displays_tip", ""), + ("allow-only-conn-window-open-tip", "Να επιτρέπεται η σύνδεση μόνο εάν το παράθυρο RustDesk είναι ανοιχτό"), + ("no_need_privacy_mode_no_physical_displays_tip", "Δεν υπάρχουν φυσικές οθόνες, δεν χρειάζεται να χρησιμοποιήσετε τη λειτουργία απορρήτου."), ("Follow remote cursor", ""), ("Follow remote window focus", ""), ("default_proxy_tip", ""), ("no_audio_input_device_tip", ""), - ("Incoming", ""), - ("Outgoing", ""), + ("Incoming", "Εισερχόμενη"), + ("Outgoing", "Εξερχόμενη"), ("Clear Wayland screen selection", ""), ("clear_Wayland_screen_selection_tip", ""), ("confirm_clear_Wayland_screen_selection_tip", ""), @@ -614,20 +614,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use texture rendering", ""), ("Floating window", ""), ("floating_window_tip", ""), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), + ("Keep screen on", "Διατήρηση οθόνης Ανοιχτή"), + ("Never", "Ποτέ"), + ("During controlled", "Κατα την διάρκεια απομακρυσμένου ελέγχου"), + ("During service is on", "Κατα την εκκίνηση της υπηρεσίας Rustdesk"), ("Capture screen using DirectX", ""), - ("Back", ""), - ("Apps", ""), + ("Back", "Πίσω"), + ("Apps", "Εφαρμογές"), ("Volume up", ""), ("Volume down", ""), ("Power", ""), ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("enable-bot-tip", "Εάν ενεργοποιήσετε αυτήν τη δυνατότητα, μπορείτε να λάβετε τον κωδικό 2FA από το bot σας. Μπορεί επίσης να λειτουργήσει ως ειδοποίηση σύνδεσης."), + ("enable-bot-desc", "1, Ανοίξτε μια συνομιλία με τον @BotFather., Στείλτε την εντολή "/newbot". Θα λάβετε ένα διακριτικό αφού ολοκληρώσετε αυτό το βήμα.3, Ξεκινήστε μια συνομιλία με το bot που μόλις δημιουργήσατε. Στείλτε ένα μήνυμα που αρχίζει με κάθετο ("/") όπως "/hello" για να το ενεργοποιήσετε."), + ("cancel-2fa-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το 2FA;"), + ("cancel-bot-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το Telegram bot;"), ].iter().cloned().collect(); } From ac88121c4ad673067967f8ce8a8e4f517ebe4935 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 6 Jul 2024 11:46:33 +0800 Subject: [PATCH 212/335] fix ci --- src/lang/el.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/el.rs b/src/lang/el.rs index 995f159308f..0618d031a82 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -626,7 +626,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Power", ""), ("Telegram bot", ""), ("enable-bot-tip", "Εάν ενεργοποιήσετε αυτήν τη δυνατότητα, μπορείτε να λάβετε τον κωδικό 2FA από το bot σας. Μπορεί επίσης να λειτουργήσει ως ειδοποίηση σύνδεσης."), - ("enable-bot-desc", "1, Ανοίξτε μια συνομιλία με τον @BotFather., Στείλτε την εντολή "/newbot". Θα λάβετε ένα διακριτικό αφού ολοκληρώσετε αυτό το βήμα.3, Ξεκινήστε μια συνομιλία με το bot που μόλις δημιουργήσατε. Στείλτε ένα μήνυμα που αρχίζει με κάθετο ("/") όπως "/hello" για να το ενεργοποιήσετε."), + ("enable-bot-desc", "1, Ανοίξτε μια συνομιλία με τον @BotFather., Στείλτε την εντολή \"/newbot\". Θα λάβετε ένα διακριτικό αφού ολοκληρώσετε αυτό το βήμα.3, Ξεκινήστε μια συνομιλία με το bot που μόλις δημιουργήσατε. Στείλτε ένα μήνυμα που αρχίζει με κάθετο (\"/\") όπως \"/hello\" για να το ενεργοποιήσετε."), ("cancel-2fa-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το 2FA;"), ("cancel-bot-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το Telegram bot;"), ].iter().cloned().collect(); From 8621b9343619ad50a696673ab801f0307558bf12 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 6 Jul 2024 23:10:50 +0800 Subject: [PATCH 213/335] add display-name option for https://github.com/rustdesk/rustdesk-server-pro/issues/277 --- libs/hbb_common/src/config.rs | 4 ++++ src/client.rs | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 711d5af37dd..88a24d1b324 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2122,6 +2122,8 @@ pub mod keys { pub const OPTION_DISABLE_GROUP_PANEL: &str = "disable-group-panel"; pub const OPTION_PRE_ELEVATE_SERVICE: &str = "pre-elevate-service"; + pub const OPTION_DISPLAY_NAME: &str = "display-name"; + // proxy settings // The following options are not real keys, they are just used for custom client advanced settings. // The real keys are in Config2::socks. @@ -2185,6 +2187,8 @@ pub mod keys { OPTION_KEEP_SCREEN_ON, OPTION_DISABLE_GROUP_PANEL, OPTION_PRE_ELEVATE_SERVICE, + OPTION_DISPLAY_NAME, + "remove-preset-password-warning", ]; // DEFAULT_SETTINGS, OVERWRITE_SETTINGS pub const KEYS_SETTINGS: &[&str] = &[ diff --git a/src/client.rs b/src/client.rs index c3fe10688cf..4c3f53b0324 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2019,11 +2019,15 @@ impl LoginConfigHandler { } else { (my_id, self.id.clone()) }; + let mut display_name = LocalConfig::get_option(&config::keys::OPTION_DISPLAY_NAME); + if display_name.is_empty() { + display_name = crate::username(); + } let mut lr = LoginRequest { username: pure_id, password: password.into(), my_id, - my_name: crate::username(), + my_name: display_name, option: self.get_option_message(true).into(), session_id: self.session_id, version: crate::VERSION.to_string(), From 024220e58acd6d1c74c93010b86da6f709655a5f Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 6 Jul 2024 23:41:53 +0800 Subject: [PATCH 214/335] add more fine-grained hide options for https://github.com/rustdesk/rustdesk-server-pro/issues/276 --- .../desktop/pages/desktop_setting_page.dart | 24 +++++++++++++------ libs/hbb_common/src/config.rs | 4 ++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 7c955e6fb4d..8ff41758508 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -173,10 +173,15 @@ class _DesktopSettingPageState extends State } List _children() { + final hideSecurity = + bind.mainGetLocalOption(key: "hide-security-settings") == 'Y'; + final hideNetwork = + bind.mainGetLocalOption(key: "hide-network-settings") == 'Y'; final children = [ _General(), - if (!bind.isOutgoingOnly() && !bind.isDisableSettings()) _Safety(), - if (!bind.isDisableSettings()) _Network(), + if (!bind.isOutgoingOnly() && !bind.isDisableSettings() && !hideSecurity) + _Safety(), + if (!bind.isDisableSettings() && !hideNetwork) _Network(), if (!bind.isIncomingOnly()) _Display(), if (!isWeb && !bind.isIncomingOnly() && bind.pluginFeatureIsEnabled()) _Plugin(), @@ -1266,6 +1271,10 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { super.build(context); bool enabled = !locked; final scrollController = ScrollController(); + final hideServer = + bind.mainGetLocalOption(key: "hide-server-settings") == 'Y'; + final hideProxy = + bind.mainGetLocalOption(key: "hide-proxy-settings") == 'Y'; return DesktopScrollWrapper( scrollController: scrollController, child: ListView( @@ -1279,11 +1288,12 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { AbsorbPointer( absorbing: locked, child: Column(children: [ - server(enabled), - _Card(title: 'Proxy', children: [ - _Button('Socks5/Http(s) Proxy', changeSocks5Proxy, - enabled: enabled), - ]), + if (!hideServer) server(enabled), + if (!hideProxy) + _Card(title: 'Proxy', children: [ + _Button('Socks5/Http(s) Proxy', changeSocks5Proxy, + enabled: enabled), + ]), ]), ), ]).marginOnly(bottom: _kListViewBottomMargin)); diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 88a24d1b324..0778a487dc7 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2189,6 +2189,10 @@ pub mod keys { OPTION_PRE_ELEVATE_SERVICE, OPTION_DISPLAY_NAME, "remove-preset-password-warning", + "hide-security-settings", + "hide-network-settings", + "hide-server-settings", + "hide-proxy-settings", ]; // DEFAULT_SETTINGS, OVERWRITE_SETTINGS pub const KEYS_SETTINGS: &[&str] = &[ From 53dbc2fa6f1b46a11adad7d2a2ce52013041c837 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 7 Jul 2024 09:25:27 +0800 Subject: [PATCH 215/335] add OPTION_DISABLE_UDP --- libs/hbb_common/src/config.rs | 2 ++ src/rendezvous_mediator.rs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 0778a487dc7..d1cac63951c 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2099,6 +2099,7 @@ pub mod keys { pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture"; pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str = "enable-android-software-encoding-half-scale"; + pub const OPTION_DISABLE_UDP: &str = "disable-udp"; // flutter local options pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState"; @@ -2231,6 +2232,7 @@ pub mod keys { OPTION_PRESET_ADDRESS_BOOK_TAG, OPTION_ENABLE_DIRECTX_CAPTURE, OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE, + OPTION_DISABLE_UDP, ]; } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index a27f95315e6..ceb4057c702 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -392,7 +392,10 @@ impl RendezvousMediator { } else { false }; - if (cfg!(debug_assertions) && option_env!("TEST_TCP").is_some()) || is_http_proxy { + if (cfg!(debug_assertions) && option_env!("TEST_TCP").is_some()) + || is_http_proxy + || Config::get_option(config::keys::OPTION_DISABLE_UDP) == "Y" + { Self::start_tcp(server, host).await } else { Self::start_udp(server, host).await From 7d961d895b4964e9c8da9f93ffcf4fe9874ed773 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 7 Jul 2024 16:35:57 +0800 Subject: [PATCH 216/335] add assign function in device.py for user/strategy/ab --- res/devices.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/res/devices.py b/res/devices.py index 246a432dfc3..4259abd4162 100755 --- a/res/devices.py +++ b/res/devices.py @@ -91,11 +91,24 @@ def delete(url, token, guid, id): return check(response) +def assign(url, token, guid, id, type, value): + print("assign", id, type, value) + if type != "ab" and type != "strategy_name" and type != "user_name": + print("Invalid type, it must be 'ab', 'strategy_name' or 'user_name'") + return + data = {"type": type, "value": value} + headers = {"Authorization": f"Bearer {token}"} + response = requests.post( + f"{url}/api/devices/{guid}/assign", headers=headers, json=data + ) + return check(response) + + def main(): parser = argparse.ArgumentParser(description="Device manager") parser.add_argument( "command", - choices=["view", "disable", "enable", "delete"], + choices=["view", "disable", "enable", "delete", "assign"], help="Command to execute", ) parser.add_argument("--url", required=True, help="URL of the API") @@ -106,6 +119,10 @@ def main(): parser.add_argument("--device_name", help="Device name") parser.add_argument("--user_name", help="User name") parser.add_argument("--group_name", help="Group name") + parser.add_argument( + "--assign_to", + help="=, e.g. user_name=mike, strategy_name=test, ab=ab1, ab=ab1,tag1", + ) parser.add_argument( "--offline_days", type=int, help="Offline duration in days, e.g., 7" ) @@ -137,6 +154,16 @@ def main(): for device in devices: response = delete(args.url, args.token, device["guid"], device["id"]) print(response) + elif args.command == "assign": + if "=" not in args.assign_to: + print("Invalid assign_to format, it must be =") + return + type, value = args.assign_to.split("=", 1) + for device in devices: + response = assign( + args.url, args.token, device["guid"], device["id"], type, value + ) + print(response) if __name__ == "__main__": From 3c7e24c6055c02b683f0c594549f40efc43049cb Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 8 Jul 2024 11:38:42 +0800 Subject: [PATCH 217/335] add MOUSE_MOVE_TIME to key event, may work for https://github.com/rustdesk/rustdesk/issues/8633 when window resume via keyboard rather than mouse --- .../lib/desktop/pages/desktop_tab_page.dart | 18 ++++++++++++++++++ src/server/connection.rs | 2 ++ 2 files changed, 20 insertions(+) diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 0f7e77c810b..9c42ab46595 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -8,6 +8,7 @@ import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:get/get.dart'; import 'package:window_manager/window_manager.dart'; +// import 'package:flutter/services.dart'; import '../../common/shared_state.dart'; @@ -41,6 +42,7 @@ class _DesktopTabPageState extends State final tabController = DesktopTabController(tabType: DesktopTabType.main); final RxBool _block = false.obs; + // bool mouseIn = false; @override void didChangeAppLifecycleState(AppLifecycleState state) { @@ -53,6 +55,7 @@ class _DesktopTabPageState extends State @override void initState() { super.initState(); + // HardwareKeyboard.instance.addHandler(_handleKeyEvent); WidgetsBinding.instance.addObserver(this); Get.put(tabController); RemoteCountState.init(); @@ -78,8 +81,19 @@ class _DesktopTabPageState extends State } } + /* + bool _handleKeyEvent(KeyEvent event) { + if (!mouseIn && event is KeyDownEvent) { + print('key down: ${event.logicalKey}'); + shouldBeBlocked(_block, canBeBlocked); + } + return false; // allow it to propagate + } + */ + @override void dispose() { + // HardwareKeyboard.instance.removeHandler(_handleKeyEvent); WidgetsBinding.instance.removeObserver(this); Get.delete(); @@ -105,8 +119,12 @@ class _DesktopTabPageState extends State ))); widget() => MouseRegion( onEnter: (_) async { + // mouseIn = true; await shouldBeBlocked(_block, canBeBlocked); }, + onExit: (_) { + // mouseIn = false; + }, child: FocusScope(child: tabWidget, canRequestFocus: !_block.value)); return isMacOS || kUseCompatibleUiMode ? Obx(() => widget()) diff --git a/src/server/connection.rs b/src/server/connection.rs index 5fac698a118..cc43650e026 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1999,6 +1999,8 @@ impl Connection { if is_enter(&me) { CLICK_TIME.store(get_time(), Ordering::SeqCst); } + // https://github.com/rustdesk/rustdesk/issues/8633 + MOUSE_MOVE_TIME.store(get_time(), Ordering::SeqCst); // handle all down as press // fix unexpected repeating key on remote linux, seems also fix abnormal alt/shift, which // make sure all key are released From 2af799f46e4c7b4e5e694e91db124918ca85b700 Mon Sep 17 00:00:00 2001 From: FastAct <93490087+FastAct@users.noreply.github.com> Date: Mon, 8 Jul 2024 06:09:50 +0200 Subject: [PATCH 218/335] Update nl.rs (#8632) --- src/lang/nl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/nl.rs b/src/lang/nl.rs index fce7668a025..c4981c3ffad 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -627,7 +627,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Telegram bot", "Telegram bot"), ("enable-bot-tip", "Als u deze functie inschakelt, kunt u een 2FA-code ontvangen van uw bot. Het kan ook fungeren als een verbindingsmelding."), ("enable-bot-desc", "1, Open een chat met @BotFather.\n2, Verzend het commando \"/newbot\". Als deze stap voltooid is, ontvang je een token.\n3, Start een chat met de nieuw aangemaakte bot. Om hem te activeren stuurt u een bericht dat begint met een schuine streep (\"/\"), bijvoorbeeld \"/hello\".\n"), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("cancel-2fa-confirm-tip", "Weet je zeker dat je 2FA wilt annuleren?"), + ("cancel-bot-confirm-tip", "Weet je zeker dat je de Telegram-bot wilt annuleren?"), ].iter().cloned().collect(); } From d093fdc25604d5b4ffa626e22a3018d2e50ddea7 Mon Sep 17 00:00:00 2001 From: XLion Date: Mon, 8 Jul 2024 12:10:02 +0800 Subject: [PATCH 219/335] Translation (#8634) * Update tw.rs * Update en.rs: Using `.` instead of `,` * Update tw.rs: Fix formatting --- src/lang/en.rs | 2 +- src/lang/tw.rs | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lang/en.rs b/src/lang/en.rs index 02055724551..1e1d5708c35 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -228,7 +228,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("texture_render_tip", "Use texture rendering to make the pictures smoother. You could try disabling this option if you encounter rendering issues."), ("floating_window_tip", "It helps to keep RustDesk background service"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), - ("enable-bot-desc", "1, Open a chat with @BotFather.\n2, Send the command \"/newbot\". You will receive a token after completing this step.\n3, Start a chat with your newly created bot. Send a message beginning with a forward slash (\"/\") like \"/hello\" to activate it.\n"), + ("enable-bot-desc", "1. Open a chat with @BotFather.\n2. Send the command \"/newbot\". You will receive a token after completing this step.\n3. Start a chat with your newly created bot. Send a message beginning with a forward slash (\"/\") like \"/hello\" to activate it.\n"), ("cancel-2fa-confirm-tip", "Are you sure you want to cancel 2FA?"), ("cancel-bot-confirm-tip", "Are you sure you want to cancel Telegram bot?"), ].iter().cloned().collect(); diff --git a/src/lang/tw.rs b/src/lang/tw.rs index d2bb51b7f26..09b4411086a 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -619,15 +619,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", "被控期間"), ("During service is on", "服務開啟期間"), ("Capture screen using DirectX", "使用 DirectX 擷取螢幕"), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), + ("Back", "返回"), + ("Apps", "應用程式"), + ("Volume up", "提高音量"), + ("Volume down", "降低音量"), + ("Power", "電源"), + ("Telegram bot", "Telegram 機器人"), + ("enable-bot-tip", "如果您啟用此功能,您可以從您的機器人接收二步驟驗證碼,亦可作為連線通知之用。"), + ("enable-bot-desc", "1. 開啟與 @BotFather 的對話。\n2. 傳送指令 \"/newbot\"。 您將會在完成此步驟後收到權杖 (Token)。\n3. 開始與您剛創立的機器人的對話。 傳送一則以正斜槓 (\"/\") 開頭的訊息來啟用它,例如 \"/hello\"。"), + ("cancel-2fa-confirm-tip", "確定要取消二步驟驗證嗎?"), + ("cancel-bot-confirm-tip", "確定要取消 Telegram 機器人嗎?"), ].iter().cloned().collect(); } From c02b4f994afd27be3cd178b641486239614ce682 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 8 Jul 2024 14:45:18 +0800 Subject: [PATCH 220/335] fix two tray icon on macos --- src/tray.rs | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/tray.rs b/src/tray.rs index 01f91dbdc13..18d6d7e91ad 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -18,7 +18,7 @@ pub fn make_tray() -> hbb_common::ResultType<()> { use tao::event_loop::{ControlFlow, EventLoopBuilder}; use tray_icon::{ menu::{Menu, MenuEvent, MenuItem}, - TrayIconBuilder, TrayIconEvent as TrayEvent, + TrayIcon, TrayIconBuilder, TrayIconEvent as TrayEvent, }; let icon; #[cfg(target_os = "macos")] @@ -63,15 +63,7 @@ pub fn make_tray() -> hbb_common::ResultType<()> { ) } }; - let _tray_icon = Some( - TrayIconBuilder::new() - .with_menu(Box::new(tray_menu)) - .with_tooltip(tooltip(0)) - .with_icon(icon) - .with_icon_as_template(true) // mac only - .build()?, - ); - let _tray_icon = Arc::new(Mutex::new(_tray_icon)); + let mut _tray_icon: Arc>> = Default::default(); let menu_channel = MenuEvent::receiver(); let tray_channel = TrayEvent::receiver(); @@ -114,11 +106,38 @@ pub fn make_tray() -> hbb_common::ResultType<()> { use tao::platform::macos::EventLoopExtMacOS; event_loop.set_activation_policy(tao::platform::macos::ActivationPolicy::Accessory); } - event_loop.run(move |_event, _, control_flow| { + event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::WaitUntil( std::time::Instant::now() + std::time::Duration::from_millis(100), ); + if let tao::event::Event::NewEvents(tao::event::StartCause::Init) = event { + // We create the icon once the event loop is actually running + // to prevent issues like https://github.com/tauri-apps/tray-icon/issues/90 + let tray = TrayIconBuilder::new() + .with_menu(Box::new(tray_menu.clone())) + .with_tooltip(tooltip(0)) + .with_icon(icon.clone()) + .with_icon_as_template(true) // mac only + .build(); + match tray { + Ok(tray) => _tray_icon = Arc::new(Mutex::new(Some(tray))), + Err(err) => { + log::error!("Failed to create tray icon: {}", err); + } + }; + + // We have to request a redraw here to have the icon actually show up. + // Tao only exposes a redraw method on the Window so we use core-foundation directly. + #[cfg(target_os = "macos")] + unsafe { + use core_foundation::runloop::{CFRunLoopGetMain, CFRunLoopWakeUp}; + + let rl = CFRunLoopGetMain(); + CFRunLoopWakeUp(rl); + } + } + if let Ok(event) = menu_channel.try_recv() { if event.id == quit_i.id() { /* failed in windows, seems no permission to check system process From eb5ab4d7d90fe8146df5f24065dfca36b856a75b Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:59:29 +0200 Subject: [PATCH 221/335] Update Italian language (#8645) --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 626c6f7884f..5c49e8840cc 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -626,7 +626,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Power", "Alimentazione"), ("Telegram bot", "Bot Telgram"), ("enable-bot-tip", "If you enable this feature, you can receive the 2FA code from your bot. It can also function as a connection notification."), - ("enable-bot-desc", "1, apri una chat con @BotFather.\n2, Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3, Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), + ("enable-bot-desc", "1. apri una chat con @BotFather.\n2. Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3. Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), ("cancel-2fa-confirm-tip", "Sei sicuro di voler annullare 2FA?"), ("cancel-bot-confirm-tip", "Sei sicuro di voler annulare Telegram?"), ].iter().cloned().collect(); From af66d2a73b25e4a942c575f8d66f8961ca524111 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 8 Jul 2024 20:08:05 +0800 Subject: [PATCH 222/335] main window add block mask, cm add keyboard block (#8640) * block window body only so user can still click minisize button. * cm doesn't show mask * Remove focusable Offstage in tabbar_widget.dart Signed-off-by: 21pages --- flutter/lib/common.dart | 22 +- .../lib/desktop/pages/desktop_tab_page.dart | 14 +- flutter/lib/desktop/pages/server_page.dart | 32 ++- .../lib/desktop/widgets/tabbar_widget.dart | 229 ++++++++++-------- 4 files changed, 162 insertions(+), 135 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 390d75ff438..0c46836e4e7 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2715,20 +2715,26 @@ Future shouldBeBlocked(RxBool block, WhetherUseRemoteBlock? use) async { } typedef WhetherUseRemoteBlock = Future Function(); -Widget buildRemoteBlock({required Widget child, WhetherUseRemoteBlock? use}) { - var block = false.obs; +Widget buildRemoteBlock( + {required Widget child, + required RxBool block, + required bool mask, + WhetherUseRemoteBlock? use}) { return Obx(() => MouseRegion( onEnter: (_) async { await shouldBeBlocked(block, use); }, onExit: (event) => block.value = false, child: Stack(children: [ - child, - Offstage( - offstage: !block.value, - child: Container( - color: Colors.black.withOpacity(0.5), - )), + // scope block tab + FocusScope(child: child, canRequestFocus: !block.value), + // mask block click, cm not block click and still use check_click_time to avoid block local click + if (mask) + Offstage( + offstage: !block.value, + child: Container( + color: Colors.black.withOpacity(0.5), + )), ]), )); } diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 9c42ab46595..a981be01a33 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -116,22 +116,14 @@ class _DesktopTabPageState extends State isClose: false, ), ), + blockTab: _block, ))); - widget() => MouseRegion( - onEnter: (_) async { - // mouseIn = true; - await shouldBeBlocked(_block, canBeBlocked); - }, - onExit: (_) { - // mouseIn = false; - }, - child: FocusScope(child: tabWidget, canRequestFocus: !_block.value)); return isMacOS || kUseCompatibleUiMode - ? Obx(() => widget()) + ? Obx(() => tabWidget) : Obx( () => DragToResizeArea( resizeEdgeSize: stateGlobal.resizeEdgeSize.value, - child: widget(), + child: tabWidget, ), ); } diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 4af54b9af29..43f9c2bd3cd 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -104,7 +104,18 @@ class ConnectionManager extends StatefulWidget { State createState() => ConnectionManagerState(); } -class ConnectionManagerState extends State { +class ConnectionManagerState extends State + with WidgetsBindingObserver { + final RxBool _block = false.obs; + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + shouldBeBlocked(_block, null); + } + } + @override void initState() { gFFI.serverModel.updateClientState(); @@ -127,9 +138,16 @@ class ConnectionManagerState extends State { } }; gFFI.chatModel.isConnManager = true; + WidgetsBinding.instance.addObserver(this); super.initState(); } + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + @override Widget build(BuildContext context) { final serverModel = Provider.of(context); @@ -165,6 +183,7 @@ class ConnectionManagerState extends State { selectedBorderColor: MyTheme.accent, maxLabelWidth: 100, tail: null, //buildScrollJumper(), + blockTab: _block, selectedTabBackgroundColor: Theme.of(context).hintColor.withOpacity(0), tabBuilder: (key, icon, label, themeConf) { @@ -208,14 +227,9 @@ class ConnectionManagerState extends State { builder: (_, model, child) => SizedBox( width: realChatPageWidth, child: buildRemoteBlock( - child: Container( - decoration: BoxDecoration( - border: Border( - right: BorderSide( - color: Theme.of(context) - .dividerColor))), - child: buildSidePage()), - ), + child: buildSidePage(), + block: _block, + mask: true), )), SizedBox( width: realClosedWidth, diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index fe851984707..c7d9e5493bf 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -248,6 +248,7 @@ class DesktopTab extends StatelessWidget { final Color? selectedTabBackgroundColor; final Color? unSelectedTabBackgroundColor; final Color? selectedBorderColor; + final RxBool? blockTab; final DesktopTabController controller; @@ -277,6 +278,7 @@ class DesktopTab extends StatelessWidget { this.selectedTabBackgroundColor, this.unSelectedTabBackgroundColor, this.selectedBorderColor, + this.blockTab, }) : super(key: key) { tabType = controller.tabType; isMainWindow = tabType == DesktopTabType.main || @@ -292,10 +294,10 @@ class DesktopTab extends StatelessWidget { @override Widget build(BuildContext context) { return Column(children: [ - Obx(() => Offstage( - offstage: !stateGlobal.showTabBar.isTrue || - (kUseCompatibleUiMode && isHideSingleItem()), - child: SizedBox( + Obx(() { + if (stateGlobal.showTabBar.isTrue && + !(kUseCompatibleUiMode && isHideSingleItem())) { + return SizedBox( height: _kTabBarHeight, child: Column( children: [ @@ -308,7 +310,11 @@ class DesktopTab extends StatelessWidget { ), ], ), - ))), + ); + } else { + return Offstage(); + } + }), Expanded( child: pageViewBuilder != null ? pageViewBuilder!(_buildPageView()) @@ -317,10 +323,15 @@ class DesktopTab extends StatelessWidget { } Widget _buildBlock({required Widget child}) { - if (tabType != DesktopTabType.main) { + if (blockTab != null) { + return buildRemoteBlock( + child: child, + block: blockTab!, + use: canBeBlocked, + mask: tabType == DesktopTabType.main); + } else { return child; } - return buildRemoteBlock(child: child, use: canBeBlocked); } List _tabWidgets = []; @@ -703,72 +714,69 @@ class WindowActionPanelState extends State return Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Obx(() => Offstage( - offstage: - !(showTabDowndown() && existingInvisibleTab().isNotEmpty), - child: _TabDropDownButton( - controller: widget.tabController, - labelGetter: widget.labelGetter, - tabkeys: existingInvisibleTab()), - )), - Offstage(offstage: widget.tail == null, child: widget.tail), - Offstage( - offstage: kUseCompatibleUiMode, - child: Row( + Obx(() { + if (showTabDowndown() && existingInvisibleTab().isNotEmpty) { + return _TabDropDownButton( + controller: widget.tabController, + labelGetter: widget.labelGetter, + tabkeys: existingInvisibleTab()); + } else { + return Offstage(); + } + }), + if (widget.tail != null) widget.tail!, + if (!kUseCompatibleUiMode) + Row( children: [ - Offstage( - offstage: !widget.showMinimize || isMacOS, - child: ActionIcon( - message: 'Minimize', - icon: IconFont.min, - onTap: () { - if (widget.isMainWindow) { - windowManager.minimize(); - } else { - WindowController.fromWindowId(kWindowId!).minimize(); - } - }, - isClose: false, - )), - Offstage( - offstage: !widget.showMaximize || isMacOS, - child: Obx(() => ActionIcon( - message: stateGlobal.isMaximized.isTrue - ? 'Restore' - : 'Maximize', - icon: stateGlobal.isMaximized.isTrue - ? IconFont.restore - : IconFont.max, - onTap: bind.isIncomingOnly() && isInHomePage() - ? null - : _toggleMaximize, - isClose: false, - ))), - Offstage( - offstage: !widget.showClose || isMacOS, - child: ActionIcon( - message: 'Close', - icon: IconFont.close, - onTap: () async { - final res = await widget.onClose?.call() ?? true; - if (res) { - // hide for all window - // note: the main window can be restored by tray icon - Future.delayed(Duration.zero, () async { - if (widget.isMainWindow) { - await windowManager.close(); - } else { - await WindowController.fromWindowId(kWindowId!) - .close(); - } - }); - } - }, - isClose: true, - )) + if (widget.showMinimize && !isMacOS) + ActionIcon( + message: 'Minimize', + icon: IconFont.min, + onTap: () { + if (widget.isMainWindow) { + windowManager.minimize(); + } else { + WindowController.fromWindowId(kWindowId!).minimize(); + } + }, + isClose: false, + ), + if (widget.showMaximize && !isMacOS) + Obx(() => ActionIcon( + message: stateGlobal.isMaximized.isTrue + ? 'Restore' + : 'Maximize', + icon: stateGlobal.isMaximized.isTrue + ? IconFont.restore + : IconFont.max, + onTap: bind.isIncomingOnly() && isInHomePage() + ? null + : _toggleMaximize, + isClose: false, + )), + if (widget.showClose && !isMacOS) + ActionIcon( + message: 'Close', + icon: IconFont.close, + onTap: () async { + final res = await widget.onClose?.call() ?? true; + if (res) { + // hide for all window + // note: the main window can be restored by tray icon + Future.delayed(Duration.zero, () async { + if (widget.isMainWindow) { + await windowManager.close(); + } else { + await WindowController.fromWindowId(kWindowId!) + .close(); + } + }); + } + }, + isClose: true, + ) ], ), - ), ], ); } @@ -1172,22 +1180,26 @@ class _CloseButton extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( - width: _kIconSize, - child: Offstage( - offstage: !visible, - child: InkWell( - hoverColor: MyTheme.tabbar(context).closeHoverColor, - customBorder: const CircleBorder(), - onTap: () => onClose(), - child: Icon( - Icons.close, - size: _kIconSize, - color: tabSelected - ? MyTheme.tabbar(context).selectedIconColor - : MyTheme.tabbar(context).unSelectedIconColor, - ), - ), - )).paddingOnly(left: 10); + width: _kIconSize, + child: () { + if (visible) { + return InkWell( + hoverColor: MyTheme.tabbar(context).closeHoverColor, + customBorder: const CircleBorder(), + onTap: () => onClose(), + child: Icon( + Icons.close, + size: _kIconSize, + color: tabSelected + ? MyTheme.tabbar(context).selectedIconColor + : MyTheme.tabbar(context).unSelectedIconColor, + ), + ); + } else { + return Offstage(); + } + }()) + .paddingOnly(left: 10); } } @@ -1341,27 +1353,30 @@ class _TabDropDownButtonState extends State<_TabDropDownButton> { child: InkWell(child: Text(label)), ), Obx( - () => Offstage( - offstage: !(tabInfo?.onTabCloseButton != null && - menuHover.value), - child: InkWell( - onTap: () { - tabInfo?.onTabCloseButton?.call(); - if (Navigator.of(context).canPop()) { - Navigator.of(context).pop(); - } - }, - child: MouseRegion( - cursor: SystemMouseCursors.click, - onHover: (event) => - setState(() => btnHover.value = true), - onExit: (event) => - setState(() => btnHover.value = false), - child: Icon(Icons.close, - color: - btnHover.value ? Colors.red : null))), - ), - ) + () { + if (tabInfo?.onTabCloseButton != null && + menuHover.value) { + return InkWell( + onTap: () { + tabInfo?.onTabCloseButton?.call(); + if (Navigator.of(context).canPop()) { + Navigator.of(context).pop(); + } + }, + child: MouseRegion( + cursor: SystemMouseCursors.click, + onHover: (event) => + setState(() => btnHover.value = true), + onExit: (event) => + setState(() => btnHover.value = false), + child: Icon(Icons.close, + color: + btnHover.value ? Colors.red : null))); + } else { + return Offstage(); + } + }, + ), ], ), ), From 02572e903244d86e48d520a129135350a875b5a1 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Mon, 8 Jul 2024 21:51:16 +0800 Subject: [PATCH 223/335] fix: obx, no rx value (#8652) Signed-off-by: fufesou --- flutter/lib/desktop/pages/desktop_tab_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index a981be01a33..ce4d02e375c 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -119,7 +119,7 @@ class _DesktopTabPageState extends State blockTab: _block, ))); return isMacOS || kUseCompatibleUiMode - ? Obx(() => tabWidget) + ? tabWidget : Obx( () => DragToResizeArea( resizeEdgeSize: stateGlobal.resizeEdgeSize.value, From d007408061867f46a0a38b81def843bd8986b788 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Tue, 9 Jul 2024 06:45:08 +0300 Subject: [PATCH 224/335] Factor out F-Droid buildscript (#8625) Now RustDesk maintainers can make proactive changes in F-Droid packaging script so F-Droid side is (fully) automated. Signed-off-by: Vasyl Gello --- .github/workflows/fdroid.yml | 6 +- .github/workflows/flutter-build.yml | 7 + flutter/build_fdroid.sh | 550 ++++++++++++++++++ .../patches/0000-flutter-android-x86.patch | 16 + .../patches/0001-x86-no-debuggable.patch | 24 + 5 files changed, 600 insertions(+), 3 deletions(-) create mode 100755 flutter/build_fdroid.sh create mode 100644 res/fdroid/patches/0000-flutter-android-x86.patch create mode 100644 res/fdroid/patches/0001-x86-no-debuggable.patch diff --git a/.github/workflows/fdroid.yml b/.github/workflows/fdroid.yml index 2dbc3a93620..77eb145bbca 100644 --- a/.github/workflows/fdroid.yml +++ b/.github/workflows/fdroid.yml @@ -11,8 +11,8 @@ on: jobs: # https://gitlab.com/fdroid/fdroiddata/-/blob/master/metadata/com.carriez.flutter_hbb.yml - # Finds latest release and transforms F-Droid vereion code from version as follows: - # X.Y.Z-A => X * 1e9 + Y * 1e6 + Z * 1e3 + A + # Finds latest release and transforms F-Droid version code from version as follows: + # X.Y.Z-A => X * 1e6 + Y * 1e4 + Z * 1e2 + A update-fdroid-version-file: name: Publish RustDesk version file for F-Droid updater runs-on: ubuntu-latest @@ -20,7 +20,7 @@ jobs: - name: Generate RustDesk version file run: | UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')" - UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000000 * $MAJOR + 1000000 * $MINOR + 1000 * $PATCH + $REV ))"; done)" + UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000 * $MAJOR + 10000 * $MINOR + 100 * $PATCH + $REV ))"; done)" echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt shell: bash diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index b9c608dba8d..e7a438997cd 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -10,6 +10,13 @@ on: type: string default: "nightly" +# NOTE: F-Droid builder script 'flutter/build_fdroid.sh' reads environment +# variables from this workflow! +# +# It does NOT read build steps, however, so please fix 'flutter/build_fdroid.sh +# whenever you add changes to Android CI build action ('build-rustdesk-android') +# in this file! + env: WIN_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh new file mode 100755 index 00000000000..6f04b80010c --- /dev/null +++ b/flutter/build_fdroid.sh @@ -0,0 +1,550 @@ +#!/bin/bash + +set -x + +# +# Script to build F-Droid release of RustDesk +# +# Copyright (C) 2024, The RustDesk Authors +# 2024, Vasyl Gello +# + +# The script is invoked by F-Droid builder system ste-by-step. +# +# It accepts the following arguments: +# +# - versionName from https://github.com/rustdesk/rustdesk/releases/download/fdroid-version/rustdesk-version.txt +# - versionCode from https://github.com/rustdesk/rustdesk/releases/download/fdroid-version/rustdesk-version.txt +# - Android architecture to build APK for: armeabi-v7a arm64-v8av x86 x86_64 +# - The build step to execute: +# +# + sudo-deps: as root, install needed Debian packages into builder VM +# + prebuild: patch sources and do other stuff before the build +# + build: perform actual build of APK file +# + +# Parse command-line arguments + +VERNAME="${1}" +VERCODE="${2}" +ANDROID_ABI="${3}" +BUILDSTEP="${4}" + +if [ -z "${VERNAME}" ] || [ -z "${VERCODE}" ] || [ -z "${ANDROID_ABI}" ] || + [ -z "${BUILDSTEP}" ]; then + echo "ERROR: Command-line arguments are all required to be non-empty!" >&2 + exit 1 +fi + +# Set various architecture-specific identifiers + +case "${ANDROID_ABI}" in +arm64-v8a) + FLUTTER_TARGET=android-arm64 + NDK_TARGET=aarch64-linux-android + RUST_TARGET=aarch64-linux-android +# RUSTDESK_FEATURES='flutter,hwcodec' + RUSTDESK_FEATURES='flutter' + ;; +armeabi-v7a) + FLUTTER_TARGET=android-arm + NDK_TARGET=arm-linux-androideabi + RUST_TARGET=armv7-linux-androideabi +# RUSTDESK_FEATURES='flutter,hwcodec' + RUSTDESK_FEATURES='flutter' + ;; +x86_64) + FLUTTER_TARGET=android-x64 + NDK_TARGET=x86_64-linux-android + RUST_TARGET=x86_64-linux-android + RUSTDESK_FEATURES='flutter' + ;; +x86) + FLUTTER_TARGET=android-x86 + NDK_TARGET=i686-linux-android + RUST_TARGET=i686-linux-android + RUSTDESK_FEATURES='flutter' + ;; +*) + echo "ERROR: Unknown Android ABI '${ANDROID_ABI}'!" >&2 + exit 1 + ;; +esac + +# Export necessary variables + +export PATH="${PATH}:${HOME}/flutter/bin:${HOME}/depot_tools" + +export VCPKG_ROOT="${HOME}/vcpkg" + +# Now act depending on build step + +case "${BUILDSTEP}" in +sudo-deps) + # sudo-deps: as root, install needed Debian packages into builder VM + + set -e + + export DEBIAN_FRONTEND=noninteractive + + apt-get update + + apt-get -yq full-upgrade + + apt-get -yq install \ + clang \ + cmake \ + curl \ + fakeroot \ + gcc \ + g++ \ + git \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libgtk-3-dev \ + make \ + nasm \ + ninja-build \ + openjdk-17-jdk-headless \ + pkg-config \ + tar \ + unzip \ + wget \ + xz-utils \ + yasm \ + yq \ + zip + + update-alternatives --auto java + + ;; +prebuild) + # prebuild: patch sources and do other stuff before the build + + # + # Extract required versions for NDK, Rust, Flutter from + # '.github/workflows/flutter-build.yml' + # + + CARGO_NDK_VERSION="$(yq -r \ + .env.CARGO_NDK_VERSION \ + .github/workflows/flutter-build.yml)" + + FLUTTER_VERSION="$(yq -r \ + .env.ANDROID_FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + if [ -z "${FLUTTER_VERSION}" ]; then + FLUTTER_VERSION="$(yq -r \ + .env.FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + fi + + FLUTTER_RUST_BRIDGE_VERSION="$(yq -r \ + .env.FLUTTER_RUST_BRIDGE_VERSION \ + .github/workflows/flutter-build.yml)" + + NDK_VERSION="$(yq -r \ + .env.NDK_VERSION \ + .github/workflows/flutter-build.yml)" + + RUST_VERSION="$(yq -r \ + .env.RUST_VERSION \ + .github/workflows/flutter-build.yml)" + + VCPKG_COMMIT_ID="$(yq -r \ + .env.VCPKG_COMMIT_ID \ + .github/workflows/flutter-build.yml)" + + if [ -z "${CARGO_NDK_VERSION}" ] || [ -z "${FLUTTER_VERSION}" ] || + [ -z "${FLUTTER_RUST_BRIDGE_VERSION}" ] || + [ -z "${NDK_VERSION}" ] || [ -z "${RUST_VERSION}" ] || + [ -z "${VCPKG_COMMIT_ID}" ]; then + echo "ERROR: Can not identify all required versions!" >&2 + exit 1 + fi + + export ANDROID_NDK_ROOT="${HOME}/android-ndk-${NDK_VERSION}" + export ANDROID_NDK_HOME="${HOME}/android-ndk-${NDK_VERSION}" + + # + # Install the components + # + + set -e + + # Install Android NDK + + if [ ! -d "${ANDROID_NDK_ROOT}" ]; then + pushd "${HOME}" + + wget \ + -q \ + "https://dl.google.com/android/repository/android-ndk-${NDK_VERSION}-linux.zip" + + unzip "android-ndk-${NDK_VERSION}-linux.zip" 1>/dev/null + + rm "android-ndk-${NDK_VERSION}-linux.zip" + + popd # ${HOME} + fi + + # Install Flutter + + if [ ! -f "${HOME}/flutter/bin/flutter" ]; then + pushd "${HOME}" + + wget \ + -q \ + "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" + tar xf "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" + rm "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" + + popd # ${HOME} + + git config --global --add safe.directory "${HOME}/flutter" + + flutter --disable-telemetry + dart --disable-telemetry + fi + + # Install Rust + + if [ ! -f "${HOME}/rustup/rustup-init.sh" ]; then + mkdir "${HOME}/rustup" + pushd "${HOME}/rustup" + wget -O rustup-init.sh 'https://sh.rustup.rs' + popd + fi + + pushd "${HOME}/rustup" + bash rustup-init.sh -y \ + --target "${RUST_TARGET}" \ + --default-toolchain "${RUST_VERSION}" + popd + + if ! command -v cargo 1>/dev/null 2>&1; then + . "${HOME}/.cargo/env" + fi + + # Install cargo-ndk + + cargo install \ + cargo-ndk \ + --version "${CARGO_NDK_VERSION}" + + # Install rust bridge generator + + cargo install cargo-expand + cargo install flutter_rust_bridge_codegen \ + --version "${FLUTTER_RUST_BRIDGE_VERSION}" \ + --features "uuid" + + # Populate native vcpkg dependencies + + if [ ! -d "${VCPKG_ROOT}" ]; then + pushd "${HOME}" + + git clone \ + https://github.com/Microsoft/vcpkg.git + + pushd vcpkg + + git reset --hard "${VCPKG_COMMIT_ID}" + + sh bootstrap-vcpkg.sh -disableMetrics + + popd # vcpkg + + popd # ${HOME} + fi + + # Install depot-tools for x86 + + if [ "${ANDROID_ABI}" = "x86" ]; then + if [ ! -d "${HOME}/depot_tools" ]; then + pushd "${HOME}" + + git clone \ + --depth 1 \ + https://chromium.googlesource.com/chromium/tools/depot_tools.git + + popd # ${HOME} + fi + fi + + # Patch the RustDesk sources + + git apply res/fdroid/patches/*.patch + + sed \ + -i \ + -e '/gms/d' \ + flutter/android/build.gradle \ + flutter/android/app/build.gradle + + sed \ + -i \ + -e '/firebase_analytics/d' \ + flutter/pubspec.yaml + + sed \ + -i \ + -e '/ firebase/,/ version/d' \ + flutter/pubspec.lock + + sed \ + -i \ + -e '/firebase/Id' \ + flutter/lib/main.dart + + if [ "${FLUTTER_VERSION}" = "3.13.9" ]; then + # Fix for android 3.13.9 + # https://github.com/rustdesk/rustdesk/blob/285e974d1a52c891d5fcc28e963d724e085558bc/.github/workflows/flutter-build.yml#L862 + + sed \ + -i \ + -e 's/uni_links_desktop/#uni_links_desktop/g' \ + flutter/pubspec.yaml + + set -- + + while read -r _1; do + set -- "$@" "${_1}" + done 0<<.a +$(find flutter/lib/ -type f -name "*dart*") +.a + + sed \ + -i \ + -e 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' \ + "$@" + + set -- + fi + + sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" flutter-sdk/.gclient + + ;; +build) + # build: perform actual build of APK file + + set -e + + # + # Extract required versions for NDK, Rust, Flutter from + # '.github/workflows/flutter-build.yml' + # + + FLUTTER_VERSION="$(yq -r \ + .env.ANDROID_FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + if [ -z "${FLUTTER_VERSION}" ]; then + FLUTTER_VERSION="$(yq -r \ + .env.FLUTTER_VERSION \ + .github/workflows/flutter-build.yml)" + fi + + NDK_VERSION="$(yq -r \ + .env.NDK_VERSION \ + .github/workflows/flutter-build.yml)" + + export ANDROID_NDK_ROOT="${HOME}/android-ndk-${NDK_VERSION}" + export ANDROID_NDK_HOME="${HOME}/android-ndk-${NDK_VERSION}" + + if ! command -v cargo 1>/dev/null 2>&1; then + . "${HOME}/.cargo/env" + fi + + # Download Flutter dependencies + + pushd flutter + + flutter packages pub get + + popd # flutter + + # Generate FFI bindings + + flutter_rust_bridge_codegen \ + --rust-input ./src/flutter_ffi.rs \ + --dart-output ./flutter/lib/generated_bridge.dart + + # Build host android deps + + bash flutter/build_android_deps.sh "${ANDROID_ABI}" + + # Build rustdesk lib + + cargo ndk \ + --platform 21 \ + --target "${RUST_TARGET}" \ + --bindgen \ + build \ + --release \ + --features "${RUSTDESK_FEATURES}" + + mkdir -p "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}" + + cp "target/${RUST_TARGET}/release/liblibrustdesk.so" \ + "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}/librustdesk.so" + + cp "${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/${NDK_TARGET}/libc++_shared.so" \ + "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}/" + + "${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip" \ + "flutter/android/app/src/main/jniLibs/${ANDROID_ABI}"/* + + # Build flutter-jit-release for x86 + + if [ "${ANDROID_ABI}" = "x86" ]; then + pushd flutter-sdk + + echo "## Sync flutter engine sources" + echo "### We need fakeroot because chromium base image is unpacked with weird uid/gid ownership" + + sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" .gclient + + export FAKEROOTDONTTRYCHOWN=1 + + fakeroot gclient sync + + unset FAKEROOTDONTTRYCHOWN + + pushd src + + echo "## Patch away Google Play dependencies" + + rm \ + flutter/shell/platform/android/io/flutter/app/FlutterPlayStoreSplitApplication.java \ + flutter/shell/platform/android/io/flutter/embedding/engine/deferredcomponents/PlayStoreDeferredComponentManager.java flutter/shell/platform/android/io/flutter/embedding/android/FlutterPlayStoreSplitApplication.java + + sed \ + -i \ + -e '/PlayStore/d' \ + flutter/tools/android_lint/project.xml \ + flutter/shell/platform/android/BUILD.gn + + sed \ + -i \ + -e '/com.google.android.play/d' \ + flutter/tools/androidx/files.json + + echo "## Configure android engine build" + + flutter/tools/gn \ + --android --android-cpu x86 --runtime-mode=jit_release \ + --no-goma --no-enable-unittests + + echo "## Perform android engine build" + + ninja -C out/android_jit_release_x86 1>/dev/null + + echo "## Configure host engine build" + + flutter/tools/gn \ + --android-cpu x86 --runtime-mode=jit_release \ + --no-goma --no-enable-unittests + + echo "## Perform android engine build" + + ninja -C out/host_jit_release_x86 1>/dev/null + + echo "## Rename host engine" + + mv out/host_jit_release_x86 out/host_jit_release + + echo "## Mimic jit_release engine to debug to use with flutter build apk" + + pushd out/android_jit_release_x86 + + sed \ + -e 's/jit_release/debug/' \ + flutter_embedding_jit_release.maven-metadata.xml \ + 1>flutter_embedding_debug.maven-metadata.xml + + sed \ + -e 's/jit_release/debug/' \ + flutter_embedding_jit_release.pom \ + 1>flutter_embedding_debug.pom + + sed \ + -e 's/jit_release/debug/' \ + x86_jit_release.maven-metadata.xml \ + 1>x86_debug.maven-metadata.xml + + sed \ + -e 's/jit_release/debug/' \ + x86_jit_release.pom \ + 1>x86_debug.pom + + cp -a \ + flutter_embedding_jit_release-sources.jar \ + flutter_embedding_debug-sources.jar + + cp -a \ + flutter_embedding_jit_release.jar \ + flutter_embedding_debug.jar + + cp -a \ + x86_jit_release.jar \ + x86_debug.jar + + popd # out/android_jit_release_x86 + + popd # src + + popd # flutter-sdk + + echo "# Clean up intermediate engine files and show free space" + + rm -rf \ + flutter-sdk/src/out/android_jit_release_x86/obj \ + flutter-sdk/src/out/host_jit_release/obj + + mv flutter-sdk/src/out flutter-out + + rm -rf flutter-sdk + + mkdir -p flutter-sdk/src/ + + mv flutter-out flutter-sdk/src/out + fi + + # Build the apk + + pushd flutter + + if [ "${ANDROID_ABI}" = "x86" ]; then + flutter build apk \ + --local-engine-src-path="$(readlink -mf "../flutter-sdk/src")" \ + --local-engine=android_jit_release_x86 \ + --debug \ + --build-number="${VERCODE}" \ + --build-name="${VERNAME}" \ + --target-platform "${FLUTTER_TARGET}" + else + flutter build apk \ + --release \ + --build-number="${VERCODE}" \ + --build-name="${VERNAME}" \ + --target-platform "${FLUTTER_TARGET}" + fi + + popd # flutter + + rm -rf flutter-sdk + + # Special step for fdroiddata CI builds to remove .gitconfig + + rm -f /home/vagrant/.gitconfig + + ;; +*) + echo "ERROR: Unknown build step '${BUILDSTEP}'!" >&2 + exit 1 + ;; +esac + +# Report success + +echo "All done!" diff --git a/res/fdroid/patches/0000-flutter-android-x86.patch b/res/fdroid/patches/0000-flutter-android-x86.patch new file mode 100644 index 00000000000..1bb4436e830 --- /dev/null +++ b/res/fdroid/patches/0000-flutter-android-x86.patch @@ -0,0 +1,16 @@ +diff --git a/flutter-sdk/.gclient b/flutter-sdk/.gclient +new file mode 100644 +index 0000000..fd12886 +--- /dev/null ++++ b/flutter-sdk/.gclient +@@ -0,0 +1,10 @@ ++solutions = [ ++ { ++ "managed": False, ++ "name": "src/flutter", ++ "url": "https://github.com/flutter/engine.git@FLUTTER_VERSION_PLACEHOLDER", ++ "custom_deps": {}, ++ "deps_file": "DEPS", ++ "safesync_url": "", ++ }, ++] diff --git a/res/fdroid/patches/0001-x86-no-debuggable.patch b/res/fdroid/patches/0001-x86-no-debuggable.patch new file mode 100644 index 00000000000..c160d60d2e6 --- /dev/null +++ b/res/fdroid/patches/0001-x86-no-debuggable.patch @@ -0,0 +1,24 @@ +diff --git a/flutter/android/app/build.gradle b/flutter/android/app/build.gradle +index f4dc69e..6b835fd 100644 +--- a/flutter/android/app/build.gradle ++++ b/flutter/android/app/build.gradle +@@ -67,6 +67,19 @@ android { + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules' + } + } ++ ++ applicationVariants.all { variant -> ++ variant.outputs.each { output -> ++ output.processManifest.doLast { task -> ++ def outputDir = multiApkManifestOutputDirectory.asFile.get() ++ File manifestOutFile = new File(outputDir, "AndroidManifest.xml") ++ if (manifestOutFile.exists()) { ++ def newFileContents = manifestOutFile.getText('UTF-8').replace("android:debuggable=\"true\"", "") ++ manifestOutFile.write(newFileContents, 'UTF-8') ++ } ++ } ++ } ++ } + } + + flutter { From 8a370e640a972cf2595331b67f893e667de7ee68 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 9 Jul 2024 13:45:08 +0800 Subject: [PATCH 225/335] add hide-username-on-card, https://github.com/rustdesk/rustdesk-server-pro/issues/284#issuecomment-2216521407 --- flutter/lib/common/widgets/peer_card.dart | 16 ++++++++++++---- flutter/lib/consts.dart | 2 ++ libs/hbb_common/src/config.rs | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 0fc4360690e..9f828c1906c 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -22,6 +22,8 @@ enum PeerUiType { grid, tile, list } final peerCardUiType = PeerUiType.grid.obs; +bool? hideUsernameOnCard; + class _PeerCard extends StatefulWidget { final Peer peer; final PeerTabIndex tab; @@ -130,8 +132,11 @@ class _PeerCardState extends State<_PeerCard> Widget _buildPeerTile( BuildContext context, Peer peer, Rx? deco) { - final name = - '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; + hideUsernameOnCard ??= + bind.mainGetLocalOption(key: kHideUsernameOnCard) == 'Y'; + final name = hideUsernameOnCard == true + ? peer.hostname + : '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; final greyStyle = TextStyle( fontSize: 11, color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); @@ -239,8 +244,11 @@ class _PeerCardState extends State<_PeerCard> Widget _buildPeerCard( BuildContext context, Peer peer, Rx deco) { - final name = - '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; + hideUsernameOnCard ??= + bind.mainGetLocalOption(key: kHideUsernameOnCard) == 'Y'; + final name = hideUsernameOnCard == true + ? peer.hostname + : '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; final child = Card( color: Colors.transparent, elevation: 0, diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index b78d298e5e9..036a1dd3c82 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -303,6 +303,8 @@ const kRequestIgnoreBatteryOptimizations = const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW"; const kAndroid13Notification = "android.permission.POST_NOTIFICATIONS"; +const kHideUsernameOnCard = "hide-username-on-card"; + /// Android channel invoke type key class AndroidChannel { static final kStartAction = "start_action"; diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index d1cac63951c..15208783668 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2194,6 +2194,7 @@ pub mod keys { "hide-network-settings", "hide-server-settings", "hide-proxy-settings", + "hide-username-on-card", ]; // DEFAULT_SETTINGS, OVERWRITE_SETTINGS pub const KEYS_SETTINGS: &[&str] = &[ From 6f74080a2d0b128e5895b6ba966628203b001530 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Tue, 9 Jul 2024 09:36:05 +0300 Subject: [PATCH 226/335] build_fdroid: Move sudo-deps back to f-droid spec (#8659) ... as requested by linsui here: https://gitlab.com/fdroid/fdroiddata/-/merge_requests/15343#note_1988918695 Signed-off-by: Vasyl Gello --- flutter/build_fdroid.sh | 42 ++++------------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh index 6f04b80010c..418e8368d37 100755 --- a/flutter/build_fdroid.sh +++ b/flutter/build_fdroid.sh @@ -79,45 +79,11 @@ export VCPKG_ROOT="${HOME}/vcpkg" # Now act depending on build step -case "${BUILDSTEP}" in -sudo-deps) - # sudo-deps: as root, install needed Debian packages into builder VM - - set -e - - export DEBIAN_FRONTEND=noninteractive - - apt-get update - - apt-get -yq full-upgrade - - apt-get -yq install \ - clang \ - cmake \ - curl \ - fakeroot \ - gcc \ - g++ \ - git \ - libgstreamer1.0-dev \ - libgstreamer-plugins-base1.0-dev \ - libgtk-3-dev \ - make \ - nasm \ - ninja-build \ - openjdk-17-jdk-headless \ - pkg-config \ - tar \ - unzip \ - wget \ - xz-utils \ - yasm \ - yq \ - zip - - update-alternatives --auto java +# NOTE: F-Droid maintainers require explicit declaration of dependencies +# as root via `Builds.sudo` F-Droid metadata directive: +# https://gitlab.com/fdroid/fdroiddata/-/merge_requests/15343#note_1988918695 - ;; +case "${BUILDSTEP}" in prebuild) # prebuild: patch sources and do other stuff before the build From 0bb4d43e9ea9d9dfb9c46c8d27d1a97cd0ad6bea Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 9 Jul 2024 15:19:16 +0800 Subject: [PATCH 227/335] add custom client option to allow remote cm modification (#8660) Signed-off-by: 21pages --- flutter/lib/consts.dart | 1 + flutter/lib/desktop/pages/server_page.dart | 25 ++++++++++++++++------ libs/hbb_common/src/config.rs | 2 ++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 036a1dd3c82..ff3017634ff 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -135,6 +135,7 @@ const String kOptionAllowLinuxHeadless = "allow-linux-headless"; const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper"; const String kOptionStopService = "stop-service"; const String kOptionDirectxCapture = "enable-directx-capture"; +const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification"; const String kOptionToggleViewOnly = "view-only"; diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 43f9c2bd3cd..1e63c71bdbd 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -112,7 +112,9 @@ class ConnectionManagerState extends State void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed) { - shouldBeBlocked(_block, null); + if (!allowRemoteCMModification()) { + shouldBeBlocked(_block, null); + } } } @@ -183,7 +185,7 @@ class ConnectionManagerState extends State selectedBorderColor: MyTheme.accent, maxLabelWidth: 100, tail: null, //buildScrollJumper(), - blockTab: _block, + blockTab: allowRemoteCMModification() ? null : _block, selectedTabBackgroundColor: Theme.of(context).hintColor.withOpacity(0), tabBuilder: (key, icon, label, themeConf) { @@ -226,10 +228,12 @@ class ConnectionManagerState extends State Consumer( builder: (_, model, child) => SizedBox( width: realChatPageWidth, - child: buildRemoteBlock( - child: buildSidePage(), - block: _block, - mask: true), + child: allowRemoteCMModification() + ? buildSidePage() + : buildRemoteBlock( + child: buildSidePage(), + block: _block, + mask: true), )), SizedBox( width: realClosedWidth, @@ -1057,6 +1061,10 @@ class _CmControlPanel extends StatelessWidget { } void checkClickTime(int id, Function() callback) async { + if (allowRemoteCMModification()) { + callback(); + return; + } var clickCallbackTime = DateTime.now().millisecondsSinceEpoch; await bind.cmCheckClickTime(connId: id); Timer(const Duration(milliseconds: 120), () async { @@ -1065,6 +1073,11 @@ void checkClickTime(int id, Function() callback) async { }); } +bool allowRemoteCMModification() { + return option2bool(kOptionAllowRemoteCmModification, + bind.mainGetLocalOption(key: kOptionAllowRemoteCmModification)); +} + class _FileTransferLogPage extends StatefulWidget { _FileTransferLogPage({Key? key}) : super(key: key); diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 15208783668..07e6fee1b7b 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2109,6 +2109,7 @@ pub mod keys { pub const OPTION_FLUTTER_PEER_TAB_VISIBLE: &str = "peer-tab-visible"; pub const OPTION_FLUTTER_PEER_CARD_UI_TYLE: &str = "peer-card-ui-type"; pub const OPTION_FLUTTER_CURRENT_AB_NAME: &str = "current-ab-name"; + pub const OPTION_ALLOW_REMOTE_CM_MODIFICATION: &str = "allow-remote-cm-modification"; // android floating window options pub const OPTION_DISABLE_FLOATING_WINDOW: &str = "disable-floating-window"; @@ -2195,6 +2196,7 @@ pub mod keys { "hide-server-settings", "hide-proxy-settings", "hide-username-on-card", + OPTION_ALLOW_REMOTE_CM_MODIFICATION, ]; // DEFAULT_SETTINGS, OVERWRITE_SETTINGS pub const KEYS_SETTINGS: &[&str] = &[ From f07936a91196154a0544e217458085c00652bb44 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 9 Jul 2024 18:01:30 +0800 Subject: [PATCH 228/335] wayland not call check_get_displays_changed_msg (#8637) * wayland not call check_get_displays_changed_msg For wayland, call Display::all() in video service will cause block, I don't know the reason. Signed-off-by: 21pages * Update display_service.rs --------- Signed-off-by: 21pages Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> --- libs/scrap/src/common/linux.rs | 1 + src/server/display_service.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libs/scrap/src/common/linux.rs b/libs/scrap/src/common/linux.rs index e70ac6ca5b0..0f1e9b62248 100644 --- a/libs/scrap/src/common/linux.rs +++ b/libs/scrap/src/common/linux.rs @@ -59,6 +59,7 @@ impl Display { }) } + // Call this function carefully for wayland, it may cause blocking pub fn all() -> io::Result> { Ok(if super::is_x11() { x11::Display::all()? diff --git a/src/server/display_service.rs b/src/server/display_service.rs index 0f54188a85e..aa226099dc3 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -184,6 +184,14 @@ fn check_get_displays_changed_msg() -> Option { } pub fn check_displays_changed() -> ResultType<()> { + #[cfg(target_os = "linux")] + { + // For wayland, call Display::all() in video service will cause block, reproduced by refresh, I don't know the reason. + // block, or even crash here, https://github.com/rustdesk/rustdesk/blob/0bb4d43e9ea9d9dfb9c46c8d27d1a97cd0ad6bea/libs/scrap/src/wayland/pipewire.rs#L235 + if !is_x11() { + return Ok(()); + } + } check_update_displays(&try_get_displays()?); Ok(()) } @@ -394,7 +402,8 @@ pub fn try_get_displays_(add_amyuni_headless: bool) -> ResultType> let mut displays = Display::all()?; // Do not add virtual display if the platform is not installed or the virtual display is not supported. - if !crate::platform::is_installed() || !virtual_display_manager::is_virtual_display_supported() { + if !crate::platform::is_installed() || !virtual_display_manager::is_virtual_display_supported() + { return Ok(displays); } From e2d217a1384862b050837763d1ba85e5f1b38b55 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 9 Jul 2024 22:10:39 +0800 Subject: [PATCH 229/335] fix wayland Display::all() comment (#8664) Signed-off-by: 21pages --- libs/scrap/src/common/linux.rs | 2 +- src/server/display_service.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/scrap/src/common/linux.rs b/libs/scrap/src/common/linux.rs index 0f1e9b62248..4e83e6e7c21 100644 --- a/libs/scrap/src/common/linux.rs +++ b/libs/scrap/src/common/linux.rs @@ -59,7 +59,7 @@ impl Display { }) } - // Call this function carefully for wayland, it may cause blocking + // Currently, wayland need to call wayland::clear() before call Display::all() pub fn all() -> io::Result> { Ok(if super::is_x11() { x11::Display::all()? diff --git a/src/server/display_service.rs b/src/server/display_service.rs index aa226099dc3..71772468c64 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -186,7 +186,7 @@ fn check_get_displays_changed_msg() -> Option { pub fn check_displays_changed() -> ResultType<()> { #[cfg(target_os = "linux")] { - // For wayland, call Display::all() in video service will cause block, reproduced by refresh, I don't know the reason. + // Currently, wayland need to call wayland::clear() before call Display::all(), otherwise it will cause // block, or even crash here, https://github.com/rustdesk/rustdesk/blob/0bb4d43e9ea9d9dfb9c46c8d27d1a97cd0ad6bea/libs/scrap/src/wayland/pipewire.rs#L235 if !is_x11() { return Ok(()); From 011647511c0f47a9261ee82e7b0d4a5cad99c4e0 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:05:25 +0800 Subject: [PATCH 230/335] feat: clipboard, multi format (#8672) * feat: clipboard, multi format Signed-off-by: fufesou * inline Signed-off-by: fufesou --------- Signed-off-by: fufesou --- Cargo.lock | 280 +---------------- Cargo.toml | 2 +- libs/clipboard/src/platform/mod.rs | 3 + libs/hbb_common/protos/message.proto | 14 + src/client.rs | 65 ++-- src/client/io_loop.rs | 16 +- src/clipboard.rs | 446 ++++++++++++++++----------- src/flutter.rs | 13 + src/server/clipboard_service.rs | 16 +- src/server/connection.rs | 22 +- 10 files changed, 376 insertions(+), 501 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfc0f8560ae..bb905da1add 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arboard" version = "3.4.0" -source = "git+https://github.com/rustdesk-org/arboard#27b4e503caa70ec6306e5270461429f2cf907ad6" +source = "git+https://github.com/rustdesk-org/arboard#58d2b6a5c3af3d1aa15481ddff1bf70551f35a48" dependencies = [ "clipboard-win", "core-graphics 0.23.2", @@ -234,24 +234,11 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "parking_lot", - "resvg", "windows-sys 0.48.0", "wl-clipboard-rs", "x11rb 0.13.1", ] -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "async-broadcast" version = "0.5.1" @@ -1000,9 +987,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ "error-code", ] @@ -1526,12 +1513,6 @@ dependencies = [ "dasp_sample", ] -[[package]] -name = "data-url" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" - [[package]] name = "dbus" version = "0.9.7" @@ -1691,7 +1672,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.7.4", + "libloading 0.8.4", ] [[package]] @@ -2081,12 +2062,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - [[package]] name = "flume" version = "0.11.0" @@ -2143,29 +2118,6 @@ dependencies = [ "libm", ] -[[package]] -name = "fontconfig-parser" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" -dependencies = [ - "roxmltree 0.19.0", -] - -[[package]] -name = "fontdb" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser", -] - [[package]] name = "foreign-types" version = "0.3.2" @@ -3213,12 +3165,6 @@ dependencies = [ "tiff", ] -[[package]] -name = "imagesize" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" - [[package]] name = "impersonate_system" version = "0.1.0" @@ -3462,16 +3408,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "kurbo" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c" -dependencies = [ - "arrayvec", - "smallvec", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -3777,15 +3713,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memmap2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] - [[package]] name = "memoffset" version = "0.6.5" @@ -4732,15 +4659,9 @@ version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" dependencies = [ - "siphasher 0.2.3", + "siphasher", ] -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project" version = "1.1.5" @@ -5414,31 +5335,6 @@ dependencies = [ "winreg 0.50.0", ] -[[package]] -name = "resvg" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" -dependencies = [ - "gif", - "jpeg-decoder", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", -] - -[[package]] -name = "rgb" -version = "0.8.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.8" @@ -5463,18 +5359,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "roxmltree" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - [[package]] name = "rpassword" version = "2.1.0" @@ -5853,22 +5737,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" -[[package]] -name = "rustybuzz" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" -dependencies = [ - "bitflags 2.6.0", - "bytemuck", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - [[package]] name = "ryu" version = "1.0.18" @@ -6159,27 +6027,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "simplecss" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" -dependencies = [ - "log", -] - [[package]] name = "siphasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - [[package]] name = "slab" version = "0.4.9" @@ -6189,15 +6042,6 @@ dependencies = [ "autocfg 1.3.0", ] -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - [[package]] name = "smallvec" version = "1.13.2" @@ -6268,15 +6112,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - [[package]] name = "strsim" version = "0.8.0" @@ -6338,16 +6173,6 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" -[[package]] -name = "svgtypes" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c" -dependencies = [ - "kurbo", - "siphasher 1.0.1", -] - [[package]] name = "syn" version = "0.15.44" @@ -6687,32 +6512,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if 1.0.0", - "log", - "png", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - [[package]] name = "tinyvec" version = "1.6.1" @@ -7004,12 +6803,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "ttf-parser" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" - [[package]] name = "typenum" version = "1.17.0" @@ -7082,18 +6875,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" - -[[package]] -name = "unicode-ccc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -7109,30 +6890,12 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-properties" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" - -[[package]] -name = "unicode-script" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" - [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" version = "0.1.13" @@ -7195,33 +6958,6 @@ dependencies = [ "log", ] -[[package]] -name = "usvg" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" -dependencies = [ - "base64 0.22.1", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree 0.20.0", - "rustybuzz", - "simplecss", - "siphasher 1.0.1", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - [[package]] name = "utf16string" version = "0.2.0" @@ -8220,12 +7956,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - [[package]] name = "zbus" version = "3.15.2" diff --git a/Cargo.toml b/Cargo.toml index 0c54897e1c8..ef1da853a01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } ctrlc = "3.2" # arboard = { version = "3.4.0", features = ["wayland-data-control"] } -arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control", "image-data"] } +arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] } clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" } system_shutdown = "4.0" diff --git a/libs/clipboard/src/platform/mod.rs b/libs/clipboard/src/platform/mod.rs index 2be4ce809da..5db27112973 100644 --- a/libs/clipboard/src/platform/mod.rs +++ b/libs/clipboard/src/platform/mod.rs @@ -1,3 +1,4 @@ +#[cfg(any(target_os = "linux", target_os = "macos"))] use crate::{CliprdrError, CliprdrServiceContext}; #[cfg(target_os = "windows")] @@ -63,8 +64,10 @@ pub fn create_cliprdr_context( return Ok(Box::new(DummyCliprdrContext {}) as Box<_>); } +#[cfg(any(target_os = "linux", target_os = "macos"))] struct DummyCliprdrContext {} +#[cfg(any(target_os = "linux", target_os = "macos"))] impl CliprdrServiceContext for DummyCliprdrContext { fn set_is_stopped(&mut self) -> Result<(), CliprdrError> { Ok(()) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index f346a72288c..d7a8cf0a7cd 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -81,6 +81,7 @@ message LoginRequest { uint64 session_id = 10; string version = 11; OSLogin os_login = 12; + string my_platform = 13; } message Auth2FA { @@ -315,13 +316,25 @@ message Hash { string challenge = 2; } +enum ClipboardFormat { + Text = 0; + Rtf = 1; + Html = 2; + ImageRgba = 21; + ImagePng = 22; + ImageSvg = 23; +} + message Clipboard { bool compress = 1; bytes content = 2; int32 width = 3; int32 height = 4; + ClipboardFormat format = 5; } +message MultiClipboards { repeated Clipboard clipboards = 1; } + enum FileType { Dir = 0; DirLink = 2; @@ -816,5 +829,6 @@ message Message { PeerInfo peer_info = 25; PointerDeviceEvent pointer_device_event = 26; Auth2FA auth_2fa = 27; + MultiClipboards multi_clipboards = 28; } } diff --git a/src/client.rs b/src/client.rs index 4c3f53b0324..4357c007451 100644 --- a/src/client.rs +++ b/src/client.rs @@ -65,7 +65,7 @@ use crate::{ }; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::clipboard::{check_clipboard, CLIPBOARD_INTERVAL}; +use crate::clipboard::{check_clipboard, ClipboardSide, CLIPBOARD_INTERVAL}; #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::ui_session_interface::SessionPermissionConfig; @@ -136,18 +136,11 @@ lazy_static::lazy_static! { #[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { static ref ENIGO: Arc> = Arc::new(Mutex::new(enigo::Enigo::new())); - static ref OLD_CLIPBOARD_DATA: Arc> = Default::default(); static ref TEXT_CLIPBOARD_STATE: Arc> = Arc::new(Mutex::new(TextClipboardState::new())); } const PUBLIC_SERVER: &str = "public"; -#[inline] -#[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn get_old_clipboard_text() -> Arc> { - OLD_CLIPBOARD_DATA.clone() -} - #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_key_state(key: enigo::Key) -> bool { use enigo::KeyboardControllable; @@ -719,7 +712,9 @@ impl Client { // // If clipboard update is detected, the text will be sent to all sessions by `send_text_clipboard_msg`. #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn try_start_clipboard(_ctx: Option) -> Option> { + fn try_start_clipboard( + client_clip_ctx: Option, + ) -> Option> { let mut clipboard_lock = TEXT_CLIPBOARD_STATE.lock().unwrap(); if clipboard_lock.running { return None; @@ -740,15 +735,8 @@ impl Client { continue; } - if let Some(msg) = check_clipboard(&mut ctx, Some(OLD_CLIPBOARD_DATA.clone())) { - #[cfg(feature = "flutter")] - crate::flutter::send_text_clipboard_msg(msg); - #[cfg(not(feature = "flutter"))] - if let Some(ctx) = &_ctx { - if ctx.cfg.is_text_clipboard_required() { - let _ = ctx.tx.send(Data::Message(msg)); - } - } + if let Some(msg) = check_clipboard(&mut ctx, ClipboardSide::Client) { + Self::send_msg(&client_clip_ctx, msg); } if !is_sent { @@ -765,15 +753,39 @@ impl Client { } #[inline] + #[cfg(feature = "flutter")] #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn get_current_clipboard_msg() -> Option { - let data = &*OLD_CLIPBOARD_DATA.lock().unwrap(); - if data.is_empty() { - None - } else { - Some(data.create_msg()) + fn send_msg(_ctx: &Option, msg: Message) { + crate::flutter::send_text_clipboard_msg(msg); + } + + #[cfg(not(feature = "flutter"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + fn send_msg(ctx: &Option, msg: Message) { + if let Some(ctx) = &ctx { + if ctx.cfg.is_text_clipboard_required() { + if let Some(pi) = ctx.cfg.lc.read().unwrap().peer_info.as_ref() { + if let Some(message::Union::MultiClipboards(multi_clipboards)) = &msg.union { + if let Some(msg_out) = crate::clipboard::get_msg_if_not_support_multi_clip( + &pi.version, + &pi.platform, + multi_clipboards, + ) { + let _ = ctx.tx.send(Data::Message(msg_out)); + return; + } + } + } + let _ = ctx.tx.send(Data::Message(msg)); + } } } + + #[inline] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + fn get_current_clipboard_msg(peer_version: &str, peer_platform: &str) -> Option { + crate::clipboard::get_cache_msg(peer_version, peer_platform) + } } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -2023,11 +2035,16 @@ impl LoginConfigHandler { if display_name.is_empty() { display_name = crate::username(); } + #[cfg(not(target_os = "android"))] + let my_platform = whoami::platform().to_string(); + #[cfg(target_os = "android")] + let my_platform = "Android".into(); let mut lr = LoginRequest { username: pure_id, password: password.into(), my_id, my_name: display_name, + my_platform, option: self.get_option_message(true).into(), session_id: self.session_id, version: crate::VERSION.to_string(), diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 19074bbd1a0..ac630cda9e3 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -41,7 +41,7 @@ use crate::client::{ new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::clipboard::{update_clipboard, CLIPBOARD_INTERVAL}; +use crate::clipboard::{update_clipboard, ClipboardSide, CLIPBOARD_INTERVAL}; use crate::common::{get_default_sound_input, set_sound_input}; use crate::ui_session_interface::{InvokeUiSession, Session}; #[cfg(not(any(target_os = "ios")))] @@ -1118,6 +1118,8 @@ impl Remote { } } Some(login_response::Union::PeerInfo(pi)) => { + let peer_version = pi.version.clone(); + let peer_platform = pi.platform.clone(); self.handler.handle_peer_info(pi); self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) { @@ -1139,7 +1141,9 @@ impl Remote { } #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Some(msg_out) = Client::get_current_clipboard_msg() { + if let Some(msg_out) = + Client::get_current_clipboard_msg(&peer_version, &peer_platform) + { let sender = self.sender.clone(); let permission_config = self.handler.get_permission_config(); tokio::spawn(async move { @@ -1180,7 +1184,7 @@ impl Remote { Some(message::Union::Clipboard(cb)) => { if !self.handler.lc.read().unwrap().disable_clipboard.v { #[cfg(not(any(target_os = "android", target_os = "ios")))] - update_clipboard(cb, Some(crate::client::get_old_clipboard_text())); + update_clipboard(vec![cb], ClipboardSide::Client); #[cfg(any(target_os = "android", target_os = "ios"))] { let content = if cb.compress { @@ -1194,6 +1198,12 @@ impl Remote { } } } + Some(message::Union::MultiClipboards(_mcb)) => { + if !self.handler.lc.read().unwrap().disable_clipboard.v { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + update_clipboard(_mcb.clipboards, ClipboardSide::Client); + } + } #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] Some(message::Union::Cliprdr(clip)) => { self.handle_cliprdr_msg(clip); diff --git a/src/clipboard.rs b/src/clipboard.rs index 0db9f59c144..e0cf85afac2 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -3,24 +3,32 @@ use std::sync::{ Arc, Mutex, }; +use arboard::{ClipboardData, ClipboardFormat}; use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; -use hbb_common::{ - allow_err, - compress::{compress as compress_func, decompress}, - log, - message_proto::*, - ResultType, -}; +use hbb_common::{log, message_proto::*, ResultType}; pub const CLIPBOARD_NAME: &'static str = "clipboard"; pub const CLIPBOARD_INTERVAL: u64 = 333; -const FAKE_SVG_WIDTH: usize = 999999; + +// This format is used to store the flag in the clipboard. +const RUSTDESK_CLIPBOARD_OWNER_FORMAT: &'static str = "dyn.com.rustdesk.owner"; lazy_static::lazy_static! { - pub static ref CONTENT: Arc> = Default::default(); static ref ARBOARD_MTX: Arc> = Arc::new(Mutex::new(())); + // cache the clipboard msg + static ref LAST_MULTI_CLIPBOARDS: Arc> = Arc::new(Mutex::new(MultiClipboards::new())); } +const SUPPORTED_FORMATS: &[ClipboardFormat] = &[ + ClipboardFormat::Text, + ClipboardFormat::Html, + ClipboardFormat::Rtf, + ClipboardFormat::ImageRgba, + ClipboardFormat::ImagePng, + ClipboardFormat::ImageSvg, + ClipboardFormat::Special(RUSTDESK_CLIPBOARD_OWNER_FORMAT), +]; + #[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] static X11_CLIPBOARD: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); @@ -126,58 +134,40 @@ impl ClipboardContext { } } -pub fn check_clipboard( - ctx: &mut Option, - old: Option>>, -) -> Option { +pub fn check_clipboard(ctx: &mut Option, side: ClipboardSide) -> Option { if ctx.is_none() { *ctx = ClipboardContext::new(true).ok(); } let ctx2 = ctx.as_mut()?; - let side = if old.is_none() { "host" } else { "client" }; - let old = if let Some(old) = old { - old - } else { - CONTENT.clone() - }; - let content = ctx2.get(); + let content = ctx2.get(side); if let Ok(content) = content { if !content.is_empty() { - if matches!(content, ClipboardData::Text(_)) { - // Skip the text if the last content is image-svg/html - if ctx2.is_last_plain { - return None; - } - } - - let changed = content != *old.lock().unwrap(); - if changed { - log::info!("{} update found on {}", CLIPBOARD_NAME, side); - let msg = content.create_msg(); - *old.lock().unwrap() = content; - return Some(msg); - } + let mut msg = Message::new(); + let clipboards = proto::create_multi_clipboards(content); + msg.set_multi_clipboards(clipboards.clone()); + *LAST_MULTI_CLIPBOARDS.lock().unwrap() = clipboards; + return Some(msg); } } None } -fn update_clipboard_(clipboard: Clipboard, old: Option>>) { - let content = ClipboardData::from_msg(clipboard); - if content.is_empty() { +fn update_clipboard_(multi_clipboards: Vec, side: ClipboardSide) { + let mut to_update_data = proto::from_multi_clipbards(multi_clipboards); + if to_update_data.is_empty() { return; } match ClipboardContext::new(false) { Ok(mut ctx) => { - let side = if old.is_none() { "host" } else { "client" }; - let old = if let Some(old) = old { - old + to_update_data.push(ClipboardData::Special(( + RUSTDESK_CLIPBOARD_OWNER_FORMAT.to_owned(), + side.get_owner_data(), + ))); + if let Err(e) = ctx.set(&to_update_data) { + log::debug!("Failed to set clipboard: {}", e); } else { - CONTENT.clone() - }; - allow_err!(ctx.set(&content)); - *old.lock().unwrap() = content; - log::debug!("{} updated on {}", CLIPBOARD_NAME, side); + log::debug!("{} updated on {}", CLIPBOARD_NAME, side); + } } Err(err) => { log::error!("Failed to create clipboard context: {}", err); @@ -185,137 +175,17 @@ fn update_clipboard_(clipboard: Clipboard, old: Option> } } -pub fn update_clipboard(clipboard: Clipboard, old: Option>>) { +pub fn update_clipboard(multi_clipboards: Vec, side: ClipboardSide) { std::thread::spawn(move || { - update_clipboard_(clipboard, old); + update_clipboard_(multi_clipboards, side); }); } -#[derive(Clone)] -pub enum ClipboardData { - Text(String), - Image(arboard::ImageData<'static>, u64), - Empty, -} - -impl Default for ClipboardData { - fn default() -> Self { - ClipboardData::Empty - } -} - -impl ClipboardData { - fn image(image: arboard::ImageData<'static>) -> ClipboardData { - let hash = 0; - /* - use std::hash::{DefaultHasher, Hash, Hasher}; - let mut hasher = DefaultHasher::new(); - image.bytes.hash(&mut hasher); - let hash = hasher.finish(); - */ - ClipboardData::Image(image, hash) - } - - pub fn is_empty(&self) -> bool { - match self { - ClipboardData::Empty => true, - ClipboardData::Text(s) => s.is_empty(), - ClipboardData::Image(a, _) => a.bytes().is_empty(), - } - } - - fn from_msg(clipboard: Clipboard) -> Self { - let is_image = clipboard.width > 0; - let data = if clipboard.compress { - decompress(&clipboard.content) - } else { - clipboard.content.into() - }; - if is_image { - // We cannot use data.start_with(b" Message { - let mut msg = Message::new(); - - match self { - ClipboardData::Text(s) => { - let compressed = compress_func(s.as_bytes()); - let compress = compressed.len() < s.as_bytes().len(); - let content = if compress { - compressed - } else { - s.clone().into_bytes() - }; - msg.set_clipboard(Clipboard { - compress, - content: content.into(), - ..Default::default() - }); - } - ClipboardData::Image(a, _) => { - let compressed = compress_func(&a.bytes()); - let compress = compressed.len() < a.bytes().len(); - let content = if compress { - compressed - } else { - a.bytes().to_vec() - }; - let (w, h) = match a { - arboard::ImageData::Rgba(a) => (a.width, a.height), - arboard::ImageData::Svg(_) => (FAKE_SVG_WIDTH as _, 0 as _), - }; - msg.set_clipboard(Clipboard { - compress, - content: content.into(), - width: w as _, - height: h as _, - ..Default::default() - }); - } - _ => {} - } - msg - } -} - -impl PartialEq for ClipboardData { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, - (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => match (a, b) { - (arboard::ImageData::Rgba(a), arboard::ImageData::Rgba(b)) => { - a.width == b.width && a.height == b.height && a.bytes == b.bytes - } - (arboard::ImageData::Svg(a), arboard::ImageData::Svg(b)) => a == b, - _ => false, - }, - (ClipboardData::Empty, ClipboardData::Empty) => true, - _ => false, - } - } -} - #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] pub struct ClipboardContext { inner: arboard::Clipboard, counter: (Arc, u64), shutdown: Option, - is_last_plain: bool, } #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] @@ -392,7 +262,6 @@ impl ClipboardContext { inner: board, counter: (change_count, 0), shutdown, - is_last_plain: false, }) } @@ -402,35 +271,29 @@ impl ClipboardContext { self.counter.0.load(Ordering::SeqCst) } - pub fn get(&mut self) -> ResultType { + pub fn get(&mut self, side: ClipboardSide) -> ResultType> { let cn = self.change_count(); let _lock = ARBOARD_MTX.lock().unwrap(); - // only for image for the time being, - // because I do not want to change behavior of text clipboard for the time being if cn != self.counter.1 { - self.is_last_plain = false; self.counter.1 = cn; - if let Ok(image) = self.inner.get_image() { - // Both text and image svg may be set by some applications - // But we only want to send the svg content. - // - // We can't call `get_text()` and store current text in `old` in outer scope, - // because it may be updated later than svg. - // Then the text will still be sent and replace the image svg content. - self.is_last_plain = matches!(image, arboard::ImageData::Svg(_)); - return Ok(ClipboardData::image(image)); + let data = self.inner.get_formats(SUPPORTED_FORMATS)?; + if !data.is_empty() { + for c in data.iter() { + if let ClipboardData::Special((_, d)) = c { + if side.is_owner(d) { + return Ok(vec![]); + } + } + } } + return Ok(data); } - Ok(ClipboardData::Text(self.inner.get_text()?)) + Ok(vec![]) } - fn set(&mut self, data: &ClipboardData) -> ResultType<()> { + fn set(&mut self, data: &[ClipboardData]) -> ResultType<()> { let _lock = ARBOARD_MTX.lock().unwrap(); - match data { - ClipboardData::Text(s) => self.inner.set_text(s)?, - ClipboardData::Image(a, _) => self.inner.set_image(a.clone())?, - _ => {} - } + self.inner.set_formats(data)?; Ok(()) } } @@ -442,3 +305,210 @@ impl Drop for ClipboardContext { } } } + +pub fn is_support_multi_clipboard(peer_version: &str, peer_platform: &str) -> bool { + use hbb_common::get_version_number; + get_version_number(peer_version) >= get_version_number("1.2.7") + && !["", "Android", &whoami::Platform::Ios.to_string()].contains(&peer_platform) +} + +pub fn get_cache_msg(peer_version: &str, peer_platform: &str) -> Option { + let multi_clipboards = LAST_MULTI_CLIPBOARDS.lock().unwrap().clone(); + if multi_clipboards.clipboards.is_empty() { + return None; + } + + let mut msg = Message::new(); + if is_support_multi_clipboard(peer_version, peer_platform) { + msg.set_multi_clipboards(multi_clipboards); + } else { + for clipboard in multi_clipboards.clipboards.iter() { + if clipboard.format.enum_value() == Ok(hbb_common::message_proto::ClipboardFormat::Text) + { + msg.set_clipboard(clipboard.clone()); + break; + } + } + } + Some(msg) +} + +pub fn reset_cache() { + *LAST_MULTI_CLIPBOARDS.lock().unwrap() = MultiClipboards::new(); +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum ClipboardSide { + Host, + Client, +} + +impl ClipboardSide { + // 01: the clipboard is owned by the host + // 10: the clipboard is owned by the client + fn get_owner_data(&self) -> Vec { + match self { + ClipboardSide::Host => vec![0b01], + ClipboardSide::Client => vec![0b10], + } + } + + fn is_owner(&self, data: &[u8]) -> bool { + if data.len() == 0 { + return false; + } + match self { + ClipboardSide::Host => data[0] & 0b01 == 0b01, + ClipboardSide::Client => data[0] & 0b10 == 0b10, + } + } +} + +impl std::fmt::Display for ClipboardSide { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ClipboardSide::Host => write!(f, "host"), + ClipboardSide::Client => write!(f, "client"), + } + } +} + +pub use proto::get_msg_if_not_support_multi_clip; +mod proto { + use arboard::ClipboardData; + use hbb_common::{ + compress::{compress as compress_func, decompress}, + message_proto::{Clipboard, ClipboardFormat, Message, MultiClipboards}, + }; + + fn plain_to_proto(s: String, format: ClipboardFormat) -> Clipboard { + let compressed = compress_func(s.as_bytes()); + let compress = compressed.len() < s.as_bytes().len(); + let content = if compress { + compressed + } else { + s.bytes().collect::>() + }; + Clipboard { + compress, + content: content.into(), + format: format.into(), + ..Default::default() + } + } + + fn image_to_proto(a: arboard::ImageData) -> Clipboard { + match &a { + arboard::ImageData::Rgba(rgba) => { + let compressed = compress_func(&a.bytes()); + let compress = compressed.len() < a.bytes().len(); + let content = if compress { + compressed + } else { + a.bytes().to_vec() + }; + Clipboard { + compress, + content: content.into(), + width: rgba.width as _, + height: rgba.height as _, + format: ClipboardFormat::ImageRgba.into(), + ..Default::default() + } + } + arboard::ImageData::Png(png) => Clipboard { + compress: false, + content: png.to_owned().to_vec().into(), + format: ClipboardFormat::ImagePng.into(), + ..Default::default() + }, + arboard::ImageData::Svg(_) => { + let compressed = compress_func(&a.bytes()); + let compress = compressed.len() < a.bytes().len(); + let content = if compress { + compressed + } else { + a.bytes().to_vec() + }; + Clipboard { + compress, + content: content.into(), + format: ClipboardFormat::ImageSvg.into(), + ..Default::default() + } + } + } + } + + fn clipboard_data_to_proto(data: ClipboardData) -> Option { + let d = match data { + ClipboardData::Text(s) => plain_to_proto(s, ClipboardFormat::Text), + ClipboardData::Rtf(s) => plain_to_proto(s, ClipboardFormat::Rtf), + ClipboardData::Html(s) => plain_to_proto(s, ClipboardFormat::Html), + ClipboardData::Image(a) => image_to_proto(a), + _ => return None, + }; + Some(d) + } + + pub fn create_multi_clipboards(vec_data: Vec) -> MultiClipboards { + MultiClipboards { + clipboards: vec_data + .into_iter() + .filter_map(clipboard_data_to_proto) + .collect(), + ..Default::default() + } + } + + fn from_clipboard(clipboard: Clipboard) -> Option { + let data = if clipboard.compress { + decompress(&clipboard.content) + } else { + clipboard.content.into() + }; + match clipboard.format.enum_value() { + Ok(ClipboardFormat::Text) => String::from_utf8(data).ok().map(ClipboardData::Text), + Ok(ClipboardFormat::Rtf) => String::from_utf8(data).ok().map(ClipboardData::Rtf), + Ok(ClipboardFormat::Html) => String::from_utf8(data).ok().map(ClipboardData::Html), + Ok(ClipboardFormat::ImageRgba) => Some(ClipboardData::Image(arboard::ImageData::rgba( + clipboard.width as _, + clipboard.height as _, + data.into(), + ))), + Ok(ClipboardFormat::ImagePng) => { + Some(ClipboardData::Image(arboard::ImageData::png(data.into()))) + } + Ok(ClipboardFormat::ImageSvg) => Some(ClipboardData::Image(arboard::ImageData::svg( + std::str::from_utf8(&data).unwrap_or_default(), + ))), + _ => None, + } + } + + pub fn from_multi_clipbards(multi_clipboards: Vec) -> Vec { + multi_clipboards + .into_iter() + .filter_map(from_clipboard) + .collect() + } + + pub fn get_msg_if_not_support_multi_clip( + version: &str, + platform: &str, + multi_clipboards: &MultiClipboards, + ) -> Option { + if crate::clipboard::is_support_multi_clipboard(version, platform) { + return None; + } + let mut msg = Message::new(); + // Find the first text clipboard and send it. + for clipboard in multi_clipboards.clipboards.iter() { + if clipboard.format.enum_value() == Ok(ClipboardFormat::Text) { + msg.set_clipboard(clipboard.clone()); + break; + } + } + Some(msg) + } +} diff --git a/src/flutter.rs b/src/flutter.rs index d1e44008b86..773abb3f248 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1256,6 +1256,19 @@ pub fn update_text_clipboard_required() { pub fn send_text_clipboard_msg(msg: Message) { for s in sessions::get_sessions() { if s.is_text_clipboard_required() { + // Check if the client supports multi clipboards + if let Some(message::Union::MultiClipboards(multi_clipboards)) = &msg.union { + let version = s.ui_handler.peer_info.read().unwrap().version.clone(); + let platform = s.ui_handler.peer_info.read().unwrap().platform.clone(); + if let Some(msg_out) = crate::clipboard::get_msg_if_not_support_multi_clip( + &version, + &platform, + multi_clipboards, + ) { + s.send(Data::Message(msg_out)); + continue; + } + } s.send(Data::Message(msg.clone())); } } diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index eeeea4999c6..7faa2de3dbe 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -1,7 +1,7 @@ use super::*; pub use crate::clipboard::{ - check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME, - CONTENT, + check_clipboard, get_cache_msg, ClipboardContext, ClipboardSide, + CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME, }; #[derive(Default)] @@ -11,7 +11,7 @@ struct State { impl super::service::Reset for State { fn reset(&mut self) { - *CONTENT.lock().unwrap() = Default::default(); + crate::clipboard::reset_cache(); self.ctx = None; } @@ -34,14 +34,14 @@ pub fn new() -> GenericService { } fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> { - if let Some(msg) = check_clipboard(&mut state.ctx, None) { + if let Some(msg) = check_clipboard(&mut state.ctx, ClipboardSide::Host) { sp.send(msg); } sp.snapshot(|sps| { - let data = CONTENT.lock().unwrap().clone(); - if !data.is_empty() { - let msg_out = data.create_msg(); - sps.send_shared(Arc::new(msg_out)); + // Just create a message with multi clipboards here + // The actual peer version and peer platform will be checked again before sending. + if let Some(msg) = get_cache_msg("1.2.7", "Windows") { + sps.send_shared(Arc::new(msg)); } Ok(()) })?; diff --git a/src/server/connection.rs b/src/server/connection.rs index cc43650e026..26533fcf436 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1,6 +1,6 @@ use super::{input_service::*, *}; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::clipboard::update_clipboard; +use crate::clipboard::{update_clipboard, ClipboardSide}; #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] use crate::clipboard_file::*; #[cfg(target_os = "android")] @@ -685,8 +685,19 @@ impl Connection { msg = Arc::new(new_msg); } } + Some(message::Union::MultiClipboards(_multi_clipboards)) => { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Some(msg_out) = crate::clipboard::get_msg_if_not_support_multi_clip(&conn.lr.version, &conn.lr.my_platform, _multi_clipboards) { + if let Err(err) = conn.stream.send(&msg_out).await { + conn.on_close(&err.to_string(), false).await; + break; + } + continue; + } + } _ => {} } + let msg: &Message = &msg; if let Err(err) = conn.stream.send(msg).await { conn.on_close(&err.to_string(), false).await; @@ -2053,7 +2064,14 @@ impl Connection { { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.clipboard { - update_clipboard(_cb, None); + update_clipboard(vec![_cb], ClipboardSide::Host); + } + } + Some(message::Union::MultiClipboards(_mcb)) => + { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if self.clipboard { + update_clipboard(_mcb.clipboards, ClipboardSide::Host); } } Some(message::Union::Cliprdr(_clip)) => From ef4d84657ba80a91d821c201f9402bc7039d6342 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:36:29 +0800 Subject: [PATCH 231/335] Revert "feat: clipboard, multi format (#8672)" (#8673) This reverts commit 011647511c0f47a9261ee82e7b0d4a5cad99c4e0. --- Cargo.lock | 280 ++++++++++++++++- Cargo.toml | 2 +- libs/clipboard/src/platform/mod.rs | 3 - libs/hbb_common/protos/message.proto | 14 - src/client.rs | 65 ++-- src/client/io_loop.rs | 16 +- src/clipboard.rs | 446 +++++++++++---------------- src/flutter.rs | 13 - src/server/clipboard_service.rs | 16 +- src/server/connection.rs | 22 +- 10 files changed, 501 insertions(+), 376 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bb905da1add..cfc0f8560ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -224,7 +224,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arboard" version = "3.4.0" -source = "git+https://github.com/rustdesk-org/arboard#58d2b6a5c3af3d1aa15481ddff1bf70551f35a48" +source = "git+https://github.com/rustdesk-org/arboard#27b4e503caa70ec6306e5270461429f2cf907ad6" dependencies = [ "clipboard-win", "core-graphics 0.23.2", @@ -234,11 +234,24 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "parking_lot", + "resvg", "windows-sys 0.48.0", "wl-clipboard-rs", "x11rb 0.13.1", ] +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-broadcast" version = "0.5.1" @@ -987,9 +1000,9 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "5.4.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" dependencies = [ "error-code", ] @@ -1513,6 +1526,12 @@ dependencies = [ "dasp_sample", ] +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + [[package]] name = "dbus" version = "0.9.7" @@ -1672,7 +1691,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.4", + "libloading 0.7.4", ] [[package]] @@ -2062,6 +2081,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "flume" version = "0.11.0" @@ -2118,6 +2143,29 @@ dependencies = [ "libm", ] +[[package]] +name = "fontconfig-parser" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" +dependencies = [ + "roxmltree 0.19.0", +] + +[[package]] +name = "fontdb" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32eac81c1135c1df01d4e6d4233c47ba11f6a6d07f33e0bba09d18797077770" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -3165,6 +3213,12 @@ dependencies = [ "tiff", ] +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + [[package]] name = "impersonate_system" version = "0.1.0" @@ -3408,6 +3462,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "kurbo" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5aa9f0f96a938266bdb12928a67169e8d22c6a786fda8ed984b85e6ba93c3c" +dependencies = [ + "arrayvec", + "smallvec", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -3713,6 +3777,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -4659,9 +4732,15 @@ version = "0.7.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" dependencies = [ - "siphasher", + "siphasher 0.2.3", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.5" @@ -5335,6 +5414,31 @@ dependencies = [ "winreg 0.50.0", ] +[[package]] +name = "resvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "944d052815156ac8fa77eaac055220e95ba0b01fa8887108ca710c03805d9051" +dependencies = [ + "gif", + "jpeg-decoder", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7439be6844e40133eda024efd85bf07f59d0dd2f59b10c00dd6cfb92cc5c741" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -5359,6 +5463,18 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "roxmltree" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rpassword" version = "2.1.0" @@ -5737,6 +5853,22 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.6.0", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + [[package]] name = "ryu" version = "1.0.18" @@ -6027,12 +6159,27 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -6042,6 +6189,15 @@ dependencies = [ "autocfg 1.3.0", ] +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -6112,6 +6268,15 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + [[package]] name = "strsim" version = "0.8.0" @@ -6173,6 +6338,16 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "svgtypes" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fae3064df9b89391c9a76a0425a69d124aee9c5c28455204709e72c39868a43c" +dependencies = [ + "kurbo", + "siphasher 1.0.1", +] + [[package]] name = "syn" version = "0.15.44" @@ -6512,6 +6687,32 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.6.1" @@ -6803,6 +7004,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + [[package]] name = "typenum" version = "1.17.0" @@ -6875,6 +7082,18 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -6890,12 +7109,30 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + +[[package]] +name = "unicode-script" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" + [[package]] name = "unicode-segmentation" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.13" @@ -6958,6 +7195,33 @@ dependencies = [ "log", ] +[[package]] +name = "usvg" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84ea542ae85c715f07b082438a4231c3760539d902e11d093847a0b22963032" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher 1.0.1", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "utf16string" version = "0.2.0" @@ -7956,6 +8220,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "zbus" version = "3.15.2" diff --git a/Cargo.toml b/Cargo.toml index ef1da853a01..0c54897e1c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,7 @@ enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } ctrlc = "3.2" # arboard = { version = "3.4.0", features = ["wayland-data-control"] } -arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] } +arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control", "image-data"] } clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" } system_shutdown = "4.0" diff --git a/libs/clipboard/src/platform/mod.rs b/libs/clipboard/src/platform/mod.rs index 5db27112973..2be4ce809da 100644 --- a/libs/clipboard/src/platform/mod.rs +++ b/libs/clipboard/src/platform/mod.rs @@ -1,4 +1,3 @@ -#[cfg(any(target_os = "linux", target_os = "macos"))] use crate::{CliprdrError, CliprdrServiceContext}; #[cfg(target_os = "windows")] @@ -64,10 +63,8 @@ pub fn create_cliprdr_context( return Ok(Box::new(DummyCliprdrContext {}) as Box<_>); } -#[cfg(any(target_os = "linux", target_os = "macos"))] struct DummyCliprdrContext {} -#[cfg(any(target_os = "linux", target_os = "macos"))] impl CliprdrServiceContext for DummyCliprdrContext { fn set_is_stopped(&mut self) -> Result<(), CliprdrError> { Ok(()) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index d7a8cf0a7cd..f346a72288c 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -81,7 +81,6 @@ message LoginRequest { uint64 session_id = 10; string version = 11; OSLogin os_login = 12; - string my_platform = 13; } message Auth2FA { @@ -316,25 +315,13 @@ message Hash { string challenge = 2; } -enum ClipboardFormat { - Text = 0; - Rtf = 1; - Html = 2; - ImageRgba = 21; - ImagePng = 22; - ImageSvg = 23; -} - message Clipboard { bool compress = 1; bytes content = 2; int32 width = 3; int32 height = 4; - ClipboardFormat format = 5; } -message MultiClipboards { repeated Clipboard clipboards = 1; } - enum FileType { Dir = 0; DirLink = 2; @@ -829,6 +816,5 @@ message Message { PeerInfo peer_info = 25; PointerDeviceEvent pointer_device_event = 26; Auth2FA auth_2fa = 27; - MultiClipboards multi_clipboards = 28; } } diff --git a/src/client.rs b/src/client.rs index 4357c007451..4c3f53b0324 100644 --- a/src/client.rs +++ b/src/client.rs @@ -65,7 +65,7 @@ use crate::{ }; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::clipboard::{check_clipboard, ClipboardSide, CLIPBOARD_INTERVAL}; +use crate::clipboard::{check_clipboard, CLIPBOARD_INTERVAL}; #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::ui_session_interface::SessionPermissionConfig; @@ -136,11 +136,18 @@ lazy_static::lazy_static! { #[cfg(not(any(target_os = "android", target_os = "ios")))] lazy_static::lazy_static! { static ref ENIGO: Arc> = Arc::new(Mutex::new(enigo::Enigo::new())); + static ref OLD_CLIPBOARD_DATA: Arc> = Default::default(); static ref TEXT_CLIPBOARD_STATE: Arc> = Arc::new(Mutex::new(TextClipboardState::new())); } const PUBLIC_SERVER: &str = "public"; +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn get_old_clipboard_text() -> Arc> { + OLD_CLIPBOARD_DATA.clone() +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_key_state(key: enigo::Key) -> bool { use enigo::KeyboardControllable; @@ -712,9 +719,7 @@ impl Client { // // If clipboard update is detected, the text will be sent to all sessions by `send_text_clipboard_msg`. #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn try_start_clipboard( - client_clip_ctx: Option, - ) -> Option> { + fn try_start_clipboard(_ctx: Option) -> Option> { let mut clipboard_lock = TEXT_CLIPBOARD_STATE.lock().unwrap(); if clipboard_lock.running { return None; @@ -735,8 +740,15 @@ impl Client { continue; } - if let Some(msg) = check_clipboard(&mut ctx, ClipboardSide::Client) { - Self::send_msg(&client_clip_ctx, msg); + if let Some(msg) = check_clipboard(&mut ctx, Some(OLD_CLIPBOARD_DATA.clone())) { + #[cfg(feature = "flutter")] + crate::flutter::send_text_clipboard_msg(msg); + #[cfg(not(feature = "flutter"))] + if let Some(ctx) = &_ctx { + if ctx.cfg.is_text_clipboard_required() { + let _ = ctx.tx.send(Data::Message(msg)); + } + } } if !is_sent { @@ -753,39 +765,15 @@ impl Client { } #[inline] - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn send_msg(_ctx: &Option, msg: Message) { - crate::flutter::send_text_clipboard_msg(msg); - } - - #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn send_msg(ctx: &Option, msg: Message) { - if let Some(ctx) = &ctx { - if ctx.cfg.is_text_clipboard_required() { - if let Some(pi) = ctx.cfg.lc.read().unwrap().peer_info.as_ref() { - if let Some(message::Union::MultiClipboards(multi_clipboards)) = &msg.union { - if let Some(msg_out) = crate::clipboard::get_msg_if_not_support_multi_clip( - &pi.version, - &pi.platform, - multi_clipboards, - ) { - let _ = ctx.tx.send(Data::Message(msg_out)); - return; - } - } - } - let _ = ctx.tx.send(Data::Message(msg)); - } + fn get_current_clipboard_msg() -> Option { + let data = &*OLD_CLIPBOARD_DATA.lock().unwrap(); + if data.is_empty() { + None + } else { + Some(data.create_msg()) } } - - #[inline] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn get_current_clipboard_msg(peer_version: &str, peer_platform: &str) -> Option { - crate::clipboard::get_cache_msg(peer_version, peer_platform) - } } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -2035,16 +2023,11 @@ impl LoginConfigHandler { if display_name.is_empty() { display_name = crate::username(); } - #[cfg(not(target_os = "android"))] - let my_platform = whoami::platform().to_string(); - #[cfg(target_os = "android")] - let my_platform = "Android".into(); let mut lr = LoginRequest { username: pure_id, password: password.into(), my_id, my_name: display_name, - my_platform, option: self.get_option_message(true).into(), session_id: self.session_id, version: crate::VERSION.to_string(), diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index ac630cda9e3..19074bbd1a0 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -41,7 +41,7 @@ use crate::client::{ new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::clipboard::{update_clipboard, ClipboardSide, CLIPBOARD_INTERVAL}; +use crate::clipboard::{update_clipboard, CLIPBOARD_INTERVAL}; use crate::common::{get_default_sound_input, set_sound_input}; use crate::ui_session_interface::{InvokeUiSession, Session}; #[cfg(not(any(target_os = "ios")))] @@ -1118,8 +1118,6 @@ impl Remote { } } Some(login_response::Union::PeerInfo(pi)) => { - let peer_version = pi.version.clone(); - let peer_platform = pi.platform.clone(); self.handler.handle_peer_info(pi); self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) { @@ -1141,9 +1139,7 @@ impl Remote { } #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Some(msg_out) = - Client::get_current_clipboard_msg(&peer_version, &peer_platform) - { + if let Some(msg_out) = Client::get_current_clipboard_msg() { let sender = self.sender.clone(); let permission_config = self.handler.get_permission_config(); tokio::spawn(async move { @@ -1184,7 +1180,7 @@ impl Remote { Some(message::Union::Clipboard(cb)) => { if !self.handler.lc.read().unwrap().disable_clipboard.v { #[cfg(not(any(target_os = "android", target_os = "ios")))] - update_clipboard(vec![cb], ClipboardSide::Client); + update_clipboard(cb, Some(crate::client::get_old_clipboard_text())); #[cfg(any(target_os = "android", target_os = "ios"))] { let content = if cb.compress { @@ -1198,12 +1194,6 @@ impl Remote { } } } - Some(message::Union::MultiClipboards(_mcb)) => { - if !self.handler.lc.read().unwrap().disable_clipboard.v { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - update_clipboard(_mcb.clipboards, ClipboardSide::Client); - } - } #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] Some(message::Union::Cliprdr(clip)) => { self.handle_cliprdr_msg(clip); diff --git a/src/clipboard.rs b/src/clipboard.rs index e0cf85afac2..0db9f59c144 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -3,32 +3,24 @@ use std::sync::{ Arc, Mutex, }; -use arboard::{ClipboardData, ClipboardFormat}; use clipboard_master::{CallbackResult, ClipboardHandler, Master, Shutdown}; -use hbb_common::{log, message_proto::*, ResultType}; +use hbb_common::{ + allow_err, + compress::{compress as compress_func, decompress}, + log, + message_proto::*, + ResultType, +}; pub const CLIPBOARD_NAME: &'static str = "clipboard"; pub const CLIPBOARD_INTERVAL: u64 = 333; - -// This format is used to store the flag in the clipboard. -const RUSTDESK_CLIPBOARD_OWNER_FORMAT: &'static str = "dyn.com.rustdesk.owner"; +const FAKE_SVG_WIDTH: usize = 999999; lazy_static::lazy_static! { + pub static ref CONTENT: Arc> = Default::default(); static ref ARBOARD_MTX: Arc> = Arc::new(Mutex::new(())); - // cache the clipboard msg - static ref LAST_MULTI_CLIPBOARDS: Arc> = Arc::new(Mutex::new(MultiClipboards::new())); } -const SUPPORTED_FORMATS: &[ClipboardFormat] = &[ - ClipboardFormat::Text, - ClipboardFormat::Html, - ClipboardFormat::Rtf, - ClipboardFormat::ImageRgba, - ClipboardFormat::ImagePng, - ClipboardFormat::ImageSvg, - ClipboardFormat::Special(RUSTDESK_CLIPBOARD_OWNER_FORMAT), -]; - #[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] static X11_CLIPBOARD: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); @@ -134,40 +126,58 @@ impl ClipboardContext { } } -pub fn check_clipboard(ctx: &mut Option, side: ClipboardSide) -> Option { +pub fn check_clipboard( + ctx: &mut Option, + old: Option>>, +) -> Option { if ctx.is_none() { *ctx = ClipboardContext::new(true).ok(); } let ctx2 = ctx.as_mut()?; - let content = ctx2.get(side); + let side = if old.is_none() { "host" } else { "client" }; + let old = if let Some(old) = old { + old + } else { + CONTENT.clone() + }; + let content = ctx2.get(); if let Ok(content) = content { if !content.is_empty() { - let mut msg = Message::new(); - let clipboards = proto::create_multi_clipboards(content); - msg.set_multi_clipboards(clipboards.clone()); - *LAST_MULTI_CLIPBOARDS.lock().unwrap() = clipboards; - return Some(msg); + if matches!(content, ClipboardData::Text(_)) { + // Skip the text if the last content is image-svg/html + if ctx2.is_last_plain { + return None; + } + } + + let changed = content != *old.lock().unwrap(); + if changed { + log::info!("{} update found on {}", CLIPBOARD_NAME, side); + let msg = content.create_msg(); + *old.lock().unwrap() = content; + return Some(msg); + } } } None } -fn update_clipboard_(multi_clipboards: Vec, side: ClipboardSide) { - let mut to_update_data = proto::from_multi_clipbards(multi_clipboards); - if to_update_data.is_empty() { +fn update_clipboard_(clipboard: Clipboard, old: Option>>) { + let content = ClipboardData::from_msg(clipboard); + if content.is_empty() { return; } match ClipboardContext::new(false) { Ok(mut ctx) => { - to_update_data.push(ClipboardData::Special(( - RUSTDESK_CLIPBOARD_OWNER_FORMAT.to_owned(), - side.get_owner_data(), - ))); - if let Err(e) = ctx.set(&to_update_data) { - log::debug!("Failed to set clipboard: {}", e); + let side = if old.is_none() { "host" } else { "client" }; + let old = if let Some(old) = old { + old } else { - log::debug!("{} updated on {}", CLIPBOARD_NAME, side); - } + CONTENT.clone() + }; + allow_err!(ctx.set(&content)); + *old.lock().unwrap() = content; + log::debug!("{} updated on {}", CLIPBOARD_NAME, side); } Err(err) => { log::error!("Failed to create clipboard context: {}", err); @@ -175,17 +185,137 @@ fn update_clipboard_(multi_clipboards: Vec, side: ClipboardSide) { } } -pub fn update_clipboard(multi_clipboards: Vec, side: ClipboardSide) { +pub fn update_clipboard(clipboard: Clipboard, old: Option>>) { std::thread::spawn(move || { - update_clipboard_(multi_clipboards, side); + update_clipboard_(clipboard, old); }); } +#[derive(Clone)] +pub enum ClipboardData { + Text(String), + Image(arboard::ImageData<'static>, u64), + Empty, +} + +impl Default for ClipboardData { + fn default() -> Self { + ClipboardData::Empty + } +} + +impl ClipboardData { + fn image(image: arboard::ImageData<'static>) -> ClipboardData { + let hash = 0; + /* + use std::hash::{DefaultHasher, Hash, Hasher}; + let mut hasher = DefaultHasher::new(); + image.bytes.hash(&mut hasher); + let hash = hasher.finish(); + */ + ClipboardData::Image(image, hash) + } + + pub fn is_empty(&self) -> bool { + match self { + ClipboardData::Empty => true, + ClipboardData::Text(s) => s.is_empty(), + ClipboardData::Image(a, _) => a.bytes().is_empty(), + } + } + + fn from_msg(clipboard: Clipboard) -> Self { + let is_image = clipboard.width > 0; + let data = if clipboard.compress { + decompress(&clipboard.content) + } else { + clipboard.content.into() + }; + if is_image { + // We cannot use data.start_with(b" Message { + let mut msg = Message::new(); + + match self { + ClipboardData::Text(s) => { + let compressed = compress_func(s.as_bytes()); + let compress = compressed.len() < s.as_bytes().len(); + let content = if compress { + compressed + } else { + s.clone().into_bytes() + }; + msg.set_clipboard(Clipboard { + compress, + content: content.into(), + ..Default::default() + }); + } + ClipboardData::Image(a, _) => { + let compressed = compress_func(&a.bytes()); + let compress = compressed.len() < a.bytes().len(); + let content = if compress { + compressed + } else { + a.bytes().to_vec() + }; + let (w, h) = match a { + arboard::ImageData::Rgba(a) => (a.width, a.height), + arboard::ImageData::Svg(_) => (FAKE_SVG_WIDTH as _, 0 as _), + }; + msg.set_clipboard(Clipboard { + compress, + content: content.into(), + width: w as _, + height: h as _, + ..Default::default() + }); + } + _ => {} + } + msg + } +} + +impl PartialEq for ClipboardData { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (ClipboardData::Text(a), ClipboardData::Text(b)) => a == b, + (ClipboardData::Image(a, _), ClipboardData::Image(b, _)) => match (a, b) { + (arboard::ImageData::Rgba(a), arboard::ImageData::Rgba(b)) => { + a.width == b.width && a.height == b.height && a.bytes == b.bytes + } + (arboard::ImageData::Svg(a), arboard::ImageData::Svg(b)) => a == b, + _ => false, + }, + (ClipboardData::Empty, ClipboardData::Empty) => true, + _ => false, + } + } +} + #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] pub struct ClipboardContext { inner: arboard::Clipboard, counter: (Arc, u64), shutdown: Option, + is_last_plain: bool, } #[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))] @@ -262,6 +392,7 @@ impl ClipboardContext { inner: board, counter: (change_count, 0), shutdown, + is_last_plain: false, }) } @@ -271,29 +402,35 @@ impl ClipboardContext { self.counter.0.load(Ordering::SeqCst) } - pub fn get(&mut self, side: ClipboardSide) -> ResultType> { + pub fn get(&mut self) -> ResultType { let cn = self.change_count(); let _lock = ARBOARD_MTX.lock().unwrap(); + // only for image for the time being, + // because I do not want to change behavior of text clipboard for the time being if cn != self.counter.1 { + self.is_last_plain = false; self.counter.1 = cn; - let data = self.inner.get_formats(SUPPORTED_FORMATS)?; - if !data.is_empty() { - for c in data.iter() { - if let ClipboardData::Special((_, d)) = c { - if side.is_owner(d) { - return Ok(vec![]); - } - } - } + if let Ok(image) = self.inner.get_image() { + // Both text and image svg may be set by some applications + // But we only want to send the svg content. + // + // We can't call `get_text()` and store current text in `old` in outer scope, + // because it may be updated later than svg. + // Then the text will still be sent and replace the image svg content. + self.is_last_plain = matches!(image, arboard::ImageData::Svg(_)); + return Ok(ClipboardData::image(image)); } - return Ok(data); } - Ok(vec![]) + Ok(ClipboardData::Text(self.inner.get_text()?)) } - fn set(&mut self, data: &[ClipboardData]) -> ResultType<()> { + fn set(&mut self, data: &ClipboardData) -> ResultType<()> { let _lock = ARBOARD_MTX.lock().unwrap(); - self.inner.set_formats(data)?; + match data { + ClipboardData::Text(s) => self.inner.set_text(s)?, + ClipboardData::Image(a, _) => self.inner.set_image(a.clone())?, + _ => {} + } Ok(()) } } @@ -305,210 +442,3 @@ impl Drop for ClipboardContext { } } } - -pub fn is_support_multi_clipboard(peer_version: &str, peer_platform: &str) -> bool { - use hbb_common::get_version_number; - get_version_number(peer_version) >= get_version_number("1.2.7") - && !["", "Android", &whoami::Platform::Ios.to_string()].contains(&peer_platform) -} - -pub fn get_cache_msg(peer_version: &str, peer_platform: &str) -> Option { - let multi_clipboards = LAST_MULTI_CLIPBOARDS.lock().unwrap().clone(); - if multi_clipboards.clipboards.is_empty() { - return None; - } - - let mut msg = Message::new(); - if is_support_multi_clipboard(peer_version, peer_platform) { - msg.set_multi_clipboards(multi_clipboards); - } else { - for clipboard in multi_clipboards.clipboards.iter() { - if clipboard.format.enum_value() == Ok(hbb_common::message_proto::ClipboardFormat::Text) - { - msg.set_clipboard(clipboard.clone()); - break; - } - } - } - Some(msg) -} - -pub fn reset_cache() { - *LAST_MULTI_CLIPBOARDS.lock().unwrap() = MultiClipboards::new(); -} - -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum ClipboardSide { - Host, - Client, -} - -impl ClipboardSide { - // 01: the clipboard is owned by the host - // 10: the clipboard is owned by the client - fn get_owner_data(&self) -> Vec { - match self { - ClipboardSide::Host => vec![0b01], - ClipboardSide::Client => vec![0b10], - } - } - - fn is_owner(&self, data: &[u8]) -> bool { - if data.len() == 0 { - return false; - } - match self { - ClipboardSide::Host => data[0] & 0b01 == 0b01, - ClipboardSide::Client => data[0] & 0b10 == 0b10, - } - } -} - -impl std::fmt::Display for ClipboardSide { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ClipboardSide::Host => write!(f, "host"), - ClipboardSide::Client => write!(f, "client"), - } - } -} - -pub use proto::get_msg_if_not_support_multi_clip; -mod proto { - use arboard::ClipboardData; - use hbb_common::{ - compress::{compress as compress_func, decompress}, - message_proto::{Clipboard, ClipboardFormat, Message, MultiClipboards}, - }; - - fn plain_to_proto(s: String, format: ClipboardFormat) -> Clipboard { - let compressed = compress_func(s.as_bytes()); - let compress = compressed.len() < s.as_bytes().len(); - let content = if compress { - compressed - } else { - s.bytes().collect::>() - }; - Clipboard { - compress, - content: content.into(), - format: format.into(), - ..Default::default() - } - } - - fn image_to_proto(a: arboard::ImageData) -> Clipboard { - match &a { - arboard::ImageData::Rgba(rgba) => { - let compressed = compress_func(&a.bytes()); - let compress = compressed.len() < a.bytes().len(); - let content = if compress { - compressed - } else { - a.bytes().to_vec() - }; - Clipboard { - compress, - content: content.into(), - width: rgba.width as _, - height: rgba.height as _, - format: ClipboardFormat::ImageRgba.into(), - ..Default::default() - } - } - arboard::ImageData::Png(png) => Clipboard { - compress: false, - content: png.to_owned().to_vec().into(), - format: ClipboardFormat::ImagePng.into(), - ..Default::default() - }, - arboard::ImageData::Svg(_) => { - let compressed = compress_func(&a.bytes()); - let compress = compressed.len() < a.bytes().len(); - let content = if compress { - compressed - } else { - a.bytes().to_vec() - }; - Clipboard { - compress, - content: content.into(), - format: ClipboardFormat::ImageSvg.into(), - ..Default::default() - } - } - } - } - - fn clipboard_data_to_proto(data: ClipboardData) -> Option { - let d = match data { - ClipboardData::Text(s) => plain_to_proto(s, ClipboardFormat::Text), - ClipboardData::Rtf(s) => plain_to_proto(s, ClipboardFormat::Rtf), - ClipboardData::Html(s) => plain_to_proto(s, ClipboardFormat::Html), - ClipboardData::Image(a) => image_to_proto(a), - _ => return None, - }; - Some(d) - } - - pub fn create_multi_clipboards(vec_data: Vec) -> MultiClipboards { - MultiClipboards { - clipboards: vec_data - .into_iter() - .filter_map(clipboard_data_to_proto) - .collect(), - ..Default::default() - } - } - - fn from_clipboard(clipboard: Clipboard) -> Option { - let data = if clipboard.compress { - decompress(&clipboard.content) - } else { - clipboard.content.into() - }; - match clipboard.format.enum_value() { - Ok(ClipboardFormat::Text) => String::from_utf8(data).ok().map(ClipboardData::Text), - Ok(ClipboardFormat::Rtf) => String::from_utf8(data).ok().map(ClipboardData::Rtf), - Ok(ClipboardFormat::Html) => String::from_utf8(data).ok().map(ClipboardData::Html), - Ok(ClipboardFormat::ImageRgba) => Some(ClipboardData::Image(arboard::ImageData::rgba( - clipboard.width as _, - clipboard.height as _, - data.into(), - ))), - Ok(ClipboardFormat::ImagePng) => { - Some(ClipboardData::Image(arboard::ImageData::png(data.into()))) - } - Ok(ClipboardFormat::ImageSvg) => Some(ClipboardData::Image(arboard::ImageData::svg( - std::str::from_utf8(&data).unwrap_or_default(), - ))), - _ => None, - } - } - - pub fn from_multi_clipbards(multi_clipboards: Vec) -> Vec { - multi_clipboards - .into_iter() - .filter_map(from_clipboard) - .collect() - } - - pub fn get_msg_if_not_support_multi_clip( - version: &str, - platform: &str, - multi_clipboards: &MultiClipboards, - ) -> Option { - if crate::clipboard::is_support_multi_clipboard(version, platform) { - return None; - } - let mut msg = Message::new(); - // Find the first text clipboard and send it. - for clipboard in multi_clipboards.clipboards.iter() { - if clipboard.format.enum_value() == Ok(ClipboardFormat::Text) { - msg.set_clipboard(clipboard.clone()); - break; - } - } - Some(msg) - } -} diff --git a/src/flutter.rs b/src/flutter.rs index 773abb3f248..d1e44008b86 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1256,19 +1256,6 @@ pub fn update_text_clipboard_required() { pub fn send_text_clipboard_msg(msg: Message) { for s in sessions::get_sessions() { if s.is_text_clipboard_required() { - // Check if the client supports multi clipboards - if let Some(message::Union::MultiClipboards(multi_clipboards)) = &msg.union { - let version = s.ui_handler.peer_info.read().unwrap().version.clone(); - let platform = s.ui_handler.peer_info.read().unwrap().platform.clone(); - if let Some(msg_out) = crate::clipboard::get_msg_if_not_support_multi_clip( - &version, - &platform, - multi_clipboards, - ) { - s.send(Data::Message(msg_out)); - continue; - } - } s.send(Data::Message(msg.clone())); } } diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index 7faa2de3dbe..eeeea4999c6 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -1,7 +1,7 @@ use super::*; pub use crate::clipboard::{ - check_clipboard, get_cache_msg, ClipboardContext, ClipboardSide, - CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME, + check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME, + CONTENT, }; #[derive(Default)] @@ -11,7 +11,7 @@ struct State { impl super::service::Reset for State { fn reset(&mut self) { - crate::clipboard::reset_cache(); + *CONTENT.lock().unwrap() = Default::default(); self.ctx = None; } @@ -34,14 +34,14 @@ pub fn new() -> GenericService { } fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> { - if let Some(msg) = check_clipboard(&mut state.ctx, ClipboardSide::Host) { + if let Some(msg) = check_clipboard(&mut state.ctx, None) { sp.send(msg); } sp.snapshot(|sps| { - // Just create a message with multi clipboards here - // The actual peer version and peer platform will be checked again before sending. - if let Some(msg) = get_cache_msg("1.2.7", "Windows") { - sps.send_shared(Arc::new(msg)); + let data = CONTENT.lock().unwrap().clone(); + if !data.is_empty() { + let msg_out = data.create_msg(); + sps.send_shared(Arc::new(msg_out)); } Ok(()) })?; diff --git a/src/server/connection.rs b/src/server/connection.rs index 26533fcf436..cc43650e026 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1,6 +1,6 @@ use super::{input_service::*, *}; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::clipboard::{update_clipboard, ClipboardSide}; +use crate::clipboard::update_clipboard; #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] use crate::clipboard_file::*; #[cfg(target_os = "android")] @@ -685,19 +685,8 @@ impl Connection { msg = Arc::new(new_msg); } } - Some(message::Union::MultiClipboards(_multi_clipboards)) => { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Some(msg_out) = crate::clipboard::get_msg_if_not_support_multi_clip(&conn.lr.version, &conn.lr.my_platform, _multi_clipboards) { - if let Err(err) = conn.stream.send(&msg_out).await { - conn.on_close(&err.to_string(), false).await; - break; - } - continue; - } - } _ => {} } - let msg: &Message = &msg; if let Err(err) = conn.stream.send(msg).await { conn.on_close(&err.to_string(), false).await; @@ -2064,14 +2053,7 @@ impl Connection { { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.clipboard { - update_clipboard(vec![_cb], ClipboardSide::Host); - } - } - Some(message::Union::MultiClipboards(_mcb)) => - { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if self.clipboard { - update_clipboard(_mcb.clipboards, ClipboardSide::Host); + update_clipboard(_cb, None); } } Some(message::Union::Cliprdr(_clip)) => From b5a716501588b35b42bb3fafad730aed97fa28ae Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 11 Jul 2024 12:02:19 +0800 Subject: [PATCH 232/335] feat: android, controlled side, clipboard text (#8677) Signed-off-by: fufesou --- src/server/connection.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index cc43650e026..fd3dd2a032a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2049,11 +2049,27 @@ impl Connection { } self.update_auto_disconnect_timer(); } - Some(message::Union::Clipboard(_cb)) => - { - #[cfg(not(any(target_os = "android", target_os = "ios")))] + Some(message::Union::Clipboard(cb)) => { if self.clipboard { - update_clipboard(_cb, None); + #[cfg(not(any(target_os = "android", target_os = "ios")))] + update_clipboard(cb, None); + #[cfg(all(feature = "flutter", target_os = "android"))] + { + let content = if cb.compress { + hbb_common::compress::decompress(&cb.content) + } else { + cb.content.into() + }; + if let Ok(content) = String::from_utf8(content) { + let data = HashMap::from([ + ("name", "clipboard"), + ("content", &content), + ]); + if let Ok(data) = serde_json::to_string(&data) { + let _ = crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, data); + } + } + } } } Some(message::Union::Cliprdr(_clip)) => From 2391b180468d105663898cb1dc1bf5788f8b84f8 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 11 Jul 2024 23:13:41 +0800 Subject: [PATCH 233/335] fix: ubuntu 18.04, service, Exec format error (#8680) Signed-off-by: fufesou --- res/DEBIAN/postinst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/res/DEBIAN/postinst b/res/DEBIAN/postinst index 6513d6eb099..eeeccaaec8b 100755 --- a/res/DEBIAN/postinst +++ b/res/DEBIAN/postinst @@ -16,6 +16,12 @@ if [ "$1" = configure ]; then parsedVersion=$(echo "${version//./}") mkdir -p /usr/lib/systemd/system/ cp /usr/share/rustdesk/files/systemd/rustdesk.service /usr/lib/systemd/system/rustdesk.service + # try fix error in Ubuntu 18.04 + # Failed to reload rustdesk.service: Unit rustdesk.service is not loaded properly: Exec format error. + # /usr/lib/systemd/system/rustdesk.service:10: Executable path is not absolute: pkill -f "rustdesk --" + if [ -e /usr/bin/pkill ]; then + sed -i "s|pkill|/usr/bin/pkill|g" /usr/lib/systemd/system/rustdesk.service + fi systemctl daemon-reload systemctl enable rustdesk systemctl start rustdesk From d8cee6507d9ea21b5152ad62b77c423f157cf610 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 12 Jul 2024 10:07:55 +0800 Subject: [PATCH 234/335] test old flutter for mac intel freeze issue --- .github/workflows/playground.yml | 161 ++++++++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index b28a2050ee0..0da66dfb95a 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.13.9" + FLUTTER_VERSION: "3.16.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" @@ -31,7 +31,166 @@ env: SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}" jobs: + build-for-macOS: + name: ${{ matrix.job.target }} + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + } + steps: + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@v6 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - 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 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: dtolnay/rust-toolchain@v1 + with: + toolchain: ${{ env.RUST_VERSION }} + targets: ${{ matrix.job.target }} + components: "rustfmt" + + - 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 --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" + 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 --c-output ./flutter/macos/Runner/bridge_generated.h + + - name: Setup vcpkg with Github Actions binary cache + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + $VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed" + + - 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: | + ./build.py --flutter --hwcodec ${{ matrix.job.extra-build-args }} + + - name: create unsigned dmg + if: env.UPLOAD_ARTIFACT == 'true' + run: | + CREATE_DMG="$(command -v create-dmg)" + CREATE_DMG="$(readlink -f "$CREATE_DMG")" + sed -i -e 's/MAXIMUM_UNMOUNTING_ATTEMPTS=3/MAXIMUM_UNMOUNTING_ATTEMPTS=7/' "$CREATE_DMG" + create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app + + - name: Upload unsigned macOS app + if: env.UPLOAD_ARTIFACT == 'true' + uses: actions/upload-artifact@master + with: + name: rustdesk-unsigned-macos-${{ matrix.job.arch }} + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg # can not upload the directory directly or tar.gz, which destroy the link structure, causing the codesign failed + + - name: Codesign app and create signed dmg + if: env.MACOS_P12_BASE64 != null && env.UPLOAD_ARTIFACT == 'true' + run: | + # Patch create-dmg to give more attempts to unmount image + CREATE_DMG="$(command -v create-dmg)" + CREATE_DMG="$(readlink -f "$CREATE_DMG")" + sed -i -e 's/MAXIMUM_UNMOUNTING_ATTEMPTS=3/MAXIMUM_UNMOUNTING_ATTEMPTS=7/' "$CREATE_DMG" + # Unlock keychain + security default-keychain -s rustdesk.keychain + security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain + # start sign the rustdesk.app and dmg + rm -rf *.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 + if: env.UPLOAD_ARTIFACT == 'true' + run: | + for name in rustdesk*??.dmg; do + mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}.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.arch }}-flutter$${{ env.FLUTTER_VERSION }}.dmg + + build-rustdesk-android: + if: false name: build rustdesk android apk ${{ matrix.job.target }} runs-on: ${{ matrix.job.os }} strategy: From 170200fa4997f76339f42284ceae0a11334d08cd Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:14:25 +0800 Subject: [PATCH 235/335] Update playground.yml --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 0da66dfb95a..dee394c65a3 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -10,7 +10,7 @@ env: RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503 CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.16.9" + FLUTTER_VERSION: "3.22.2" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work FLUTTER_ELINUX_VERSION: "3.16.9" From 282ea02ebf6b21bf434f9df05733783f7e965ed5 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:41:13 +0800 Subject: [PATCH 236/335] Update playground.yml --- .github/workflows/playground.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index dee394c65a3..21bc54ba10e 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,6 +43,14 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, + flutter: "3.16.9", + } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.22.2", } steps: - name: Export GitHub Actions cache environment variables @@ -96,7 +104,7 @@ jobs: uses: subosito/flutter-action@v2 with: channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} + flutter-version: ${{ matrix.job.flutter }} - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 @@ -140,7 +148,6 @@ jobs: ./build.py --flutter --hwcodec ${{ matrix.job.extra-build-args }} - name: create unsigned dmg - if: env.UPLOAD_ARTIFACT == 'true' run: | CREATE_DMG="$(command -v create-dmg)" CREATE_DMG="$(readlink -f "$CREATE_DMG")" @@ -148,14 +155,13 @@ jobs: create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app - name: Upload unsigned macOS app - if: env.UPLOAD_ARTIFACT == 'true' uses: actions/upload-artifact@master with: name: rustdesk-unsigned-macos-${{ matrix.job.arch }} path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg # can not upload the directory directly or tar.gz, which destroy the link structure, causing the codesign failed - name: Codesign app and create signed dmg - if: env.MACOS_P12_BASE64 != null && env.UPLOAD_ARTIFACT == 'true' + if: env.MACOS_P12_BASE64 != null run: | # Patch create-dmg to give more attempts to unmount image CREATE_DMG="$(command -v create-dmg)" @@ -173,20 +179,18 @@ jobs: rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json --staple rustdesk-${{ env.VERSION }}.dmg - name: Rename rustdesk - if: env.UPLOAD_ARTIFACT == 'true' run: | for name in rustdesk*??.dmg; do mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}.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.arch }}-flutter$${{ env.FLUTTER_VERSION }}.dmg + rustdesk*-${{ matrix.job.arch }}-flutter$${{ matrix.job.flutter }}.dmg build-rustdesk-android: From 21f41e98a0489ae23c9baa001b51c6fe0505bd99 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 10:44:04 +0800 Subject: [PATCH 237/335] Update playground.yml --- .github/workflows/playground.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 21bc54ba10e..27bed1d1bc9 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -181,7 +181,7 @@ jobs: - name: Rename rustdesk run: | for name in rustdesk*??.dmg; do - mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}.dmg" + mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}-flutter${{ matrix.job.flutter }}.dmg" done - name: Publish DMG package @@ -190,7 +190,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk*-${{ matrix.job.arch }}-flutter$${{ matrix.job.flutter }}.dmg + rustdesk*-${{ matrix.job.arch }}*.dmg build-rustdesk-android: From 0ea88ce6ff8a978df9d9a398e28dc46b04192528 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:01:12 +0800 Subject: [PATCH 238/335] Update playground.yml --- .github/workflows/playground.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 27bed1d1bc9..73156d02688 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -154,12 +154,6 @@ jobs: sed -i -e 's/MAXIMUM_UNMOUNTING_ATTEMPTS=3/MAXIMUM_UNMOUNTING_ATTEMPTS=7/' "$CREATE_DMG" create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app - - name: Upload unsigned macOS app - uses: actions/upload-artifact@master - with: - name: rustdesk-unsigned-macos-${{ matrix.job.arch }} - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.dmg # can not upload the directory directly or tar.gz, which destroy the link structure, causing the codesign failed - - name: Codesign app and create signed dmg if: env.MACOS_P12_BASE64 != null run: | From 821f7245b0c6f7a6fa3d4cd20b80d40aed969b55 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 12 Jul 2024 11:08:51 +0800 Subject: [PATCH 239/335] videotoolbox ram codec (#8683) * h265 encoding: the second frame always fails, use repeat encoding to achieve real-time encoding * h264 encoding: Not supported because encoding fails too frequently, about 50%, with one failure followed by another success. * h264/h265 decoding: No issues found. * Does not support dynamically changing the bitrate and changing the quality by resetting the encoder. Signed-off-by: 21pages --- Cargo.lock | 4 ++-- libs/scrap/src/common/codec.rs | 2 +- libs/scrap/src/common/hwcodec.rs | 22 ++++++++++++---------- src/ipc.rs | 8 ++++---- src/server.rs | 2 +- src/ui_interface.rs | 5 ++--- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfc0f8560ae..7455d04dd27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3087,8 +3087,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.4.18" -source = "git+https://github.com/21pages/hwcodec#4b15d782512f95cb158577853e6cdb67a37502c1" +version = "0.4.19" +source = "git+https://github.com/21pages/hwcodec#852de98224605db7a2495cf276776dabaf8aa139" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 4ef69181ed9..07ff0f91d24 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -838,7 +838,7 @@ impl Decoder { pub fn enable_hwcodec_option() -> bool { use hbb_common::config::keys::OPTION_ENABLE_HWCODEC; - if cfg!(windows) || cfg!(target_os = "linux") || cfg!(target_os = "android") { + if !cfg!(target_os = "ios") { return option2bool( OPTION_ENABLE_HWCODEC, &Config::get_option(OPTION_ENABLE_HWCODEC), diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index c387afcd7f1..4e653215eb6 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -192,19 +192,21 @@ impl EncoderApi for HwRamEncoder { } fn support_abr(&self) -> bool { - ["qsv", "vaapi", "mediacodec"] + ["qsv", "vaapi", "mediacodec", "videotoolbox"] .iter() .all(|&x| !self.config.name.contains(x)) } fn support_changing_quality(&self) -> bool { - ["vaapi", "mediacodec"] + ["vaapi", "mediacodec", "videotoolbox"] .iter() .all(|&x| !self.config.name.contains(x)) } fn latency_free(&self) -> bool { - !self.config.name.contains("mediacodec") + ["mediacodec", "videotoolbox"] + .iter() + .all(|&x| !self.config.name.contains(x)) } fn is_hardware(&self) -> bool { @@ -501,12 +503,12 @@ pub struct HwCodecConfig { // portable: ui start check process, check process send to ui // sciter and unilink: get from ipc server impl HwCodecConfig { - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn set(config: String) { let config = serde_json::from_str(&config).unwrap_or_default(); log::info!("set hwcodec config"); log::debug!("{config:?}"); - #[cfg(windows)] + #[cfg(any(windows, target_os = "macos"))] hbb_common::config::common_store(&config, "_hwcodec"); *CONFIG.lock().unwrap() = Some(config); *CONFIG_SET_BY_IPC.lock().unwrap() = true; @@ -578,7 +580,7 @@ impl HwCodecConfig { ..Default::default() } } - #[cfg(windows)] + #[cfg(any(windows, target_os = "macos"))] { let config = CONFIG.lock().unwrap().clone(); match config { @@ -606,13 +608,13 @@ impl HwCodecConfig { { CONFIG.lock().unwrap().clone().unwrap_or_default() } - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(target_os = "ios")] { HwCodecConfig::default() } } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_set_value() -> Option { let set = CONFIG_SET_BY_IPC.lock().unwrap().clone(); if set { @@ -622,7 +624,7 @@ impl HwCodecConfig { } } - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn already_set() -> bool { CONFIG_SET_BY_IPC.lock().unwrap().clone() } @@ -690,7 +692,7 @@ pub fn check_available_hwcodec() -> String { serde_json::to_string(&c).unwrap_or_default() } -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn start_check_process() { if !enable_hwcodec_option() || HwCodecConfig::already_set() { return; diff --git a/src/ipc.rs b/src/ipc.rs index c344dc54ee0..20e10e1b28f 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -553,12 +553,12 @@ async fn handle(data: Data, stream: &mut Connection) { ); } #[cfg(feature = "hwcodec")] - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] Data::CheckHwcodec => { scrap::hwcodec::start_check_process(); } #[cfg(feature = "hwcodec")] - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] Data::HwCodecConfig(c) => { match c { None => { @@ -1047,7 +1047,7 @@ pub async fn notify_server_to_check_hwcodec() -> ResultType<()> { } #[cfg(feature = "hwcodec")] -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub async fn get_hwcodec_config_from_server() -> ResultType<()> { if !scrap::codec::enable_hwcodec_option() || scrap::hwcodec::HwCodecConfig::already_set() { @@ -1070,7 +1070,7 @@ pub async fn get_hwcodec_config_from_server() -> ResultType<()> { } #[cfg(feature = "hwcodec")] -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn client_get_hwcodec_config_thread(wait_sec: u64) { static ONCE: std::sync::Once = std::sync::Once::new(); if !crate::platform::is_installed() diff --git a/src/server.rs b/src/server.rs index fc33188f46b..6505ad1c2d3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -489,7 +489,7 @@ pub async fn start_server(is_server: bool) { #[cfg(target_os = "windows")] crate::platform::try_kill_broker(); #[cfg(feature = "hwcodec")] - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] scrap::hwcodec::start_check_process(); crate::RendezvousMediator::start_all().await; } else { diff --git a/src/ui_interface.rs b/src/ui_interface.rs index c05f720a7c6..5f85d975860 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -904,8 +904,7 @@ pub fn get_api_server() -> String { #[inline] pub fn has_hwcodec() -> bool { // Has real hardware codec using gpu - (cfg!(feature = "hwcodec") && (cfg!(windows) || cfg!(target_os = "linux"))) - || cfg!(feature = "mediacodec") + (cfg!(feature = "hwcodec") && cfg!(not(target_os = "ios"))) || cfg!(feature = "mediacodec") } #[inline] @@ -1408,7 +1407,7 @@ pub fn verify_bot(token: String) -> String { pub fn check_hwcodec() { #[cfg(feature = "hwcodec")] - #[cfg(any(target_os = "windows", target_os = "linux"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] { use std::sync::Once; static ONCE: Once = Once::new(); From c2716c25094d12456e801b0d36edca19874e5ed2 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 15:44:51 +0800 Subject: [PATCH 240/335] Update playground.yml --- .github/workflows/playground.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 73156d02688..4fb0c7dfc5a 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,14 +43,7 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.16.9", - } - - { - target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel - extra-build-args: "", - arch: x86_64, - flutter: "3.22.2", + flutter: "3.13.9", } steps: - name: Export GitHub Actions cache environment variables @@ -121,6 +114,8 @@ jobs: shell: bash run: | cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" + sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml + pushd flutter/lib; find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd; 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 --c-output ./flutter/macos/Runner/bridge_generated.h From d652b99d5bd1bce91554fa0d9185e087318f4a9d Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 16:33:55 +0800 Subject: [PATCH 241/335] Update playground.yml --- .github/workflows/playground.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 4fb0c7dfc5a..94fc4a3fcaa 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -114,7 +114,6 @@ jobs: shell: bash run: | cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" - sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml pushd flutter/lib; find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd; 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 --c-output ./flutter/macos/Runner/bridge_generated.h From fe513dd96709191fb8aa9a2dfa7a1a1c42a2a920 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:26:00 +0800 Subject: [PATCH 242/335] Update playground.yml --- .github/workflows/playground.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 94fc4a3fcaa..b52a6638511 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -114,7 +114,8 @@ jobs: shell: bash run: | cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" - pushd flutter/lib; find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd; + # below works for mac + pushd flutter/lib; > find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; 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 --c-output ./flutter/macos/Runner/bridge_generated.h From 2c027cdcf592161e1e299a6817075b7062ffe68c Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:55:25 +0800 Subject: [PATCH 243/335] Update playground.yml --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index b52a6638511..7004b53a54b 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -115,7 +115,7 @@ jobs: run: | cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" # below works for mac - pushd flutter/lib; > find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; + pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; 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 --c-output ./flutter/macos/Runner/bridge_generated.h From 1d59a7fe5fe8acb8f13ffe02eedc291625c60eea Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 12 Jul 2024 18:07:27 +0800 Subject: [PATCH 244/335] adjust uni_link_desktop version to make work with old flutter --- flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index dce598bc4ef..c6e1e61cc5f 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -79,7 +79,7 @@ dependencies: git: url: https://github.com/rustdesk-org/flutter_improved_scrolling uni_links: ^0.5.1 - uni_links_desktop: ^0.1.7 + uni_links_desktop: ^0.1.6 # use 0.1.6 to make flutter 3.13 works path: ^1.8.1 auto_size_text: ^3.0.0 bot_toast: ^4.0.3 From 888e993534e1dfbcb7a9a17d28b8e0b9630b1795 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Fri, 12 Jul 2024 22:04:11 +0800 Subject: [PATCH 245/335] fix: unable to close on fullscreen (#8690) Signed-off-by: fufesou --- .../lib/desktop/widgets/tabbar_widget.dart | 399 ++++++++++-------- 1 file changed, 221 insertions(+), 178 deletions(-) diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index c7d9e5493bf..51344111398 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -230,8 +230,7 @@ typedef LabelGetter = Rx Function(String key); int _lastClickTime = DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000; -// ignore: must_be_immutable -class DesktopTab extends StatelessWidget { +class DesktopTab extends StatefulWidget { final bool showLogo; final bool showTitle; final bool showMinimize; @@ -252,12 +251,8 @@ class DesktopTab extends StatelessWidget { final DesktopTabController controller; - Rx get state => controller.state; final _scrollDebounce = Debouncer(delay: Duration(milliseconds: 50)); - late final DesktopTabType tabType; - late final bool isMainWindow; - final RxList invisibleTabKeys = RxList.empty(); DesktopTab({ @@ -279,18 +274,232 @@ class DesktopTab extends StatelessWidget { this.unSelectedTabBackgroundColor, this.selectedBorderColor, this.blockTab, - }) : super(key: key) { - tabType = controller.tabType; - isMainWindow = tabType == DesktopTabType.main || - tabType == DesktopTabType.cm || - tabType == DesktopTabType.install; + }) : super(key: key); + + static RxString tablabelGetter(String peerId) { + final alias = bind.mainGetPeerOptionSync(id: peerId, key: 'alias'); + return RxString(getDesktopTabLabel(peerId, alias)); + } + + @override + State createState() { + return _DesktopTabState(); } +} + +// ignore: must_be_immutable +class _DesktopTabState extends State + with MultiWindowListener, WindowListener { + final _saveFrameDebounce = Debouncer(delay: Duration(seconds: 1)); + Timer? _macOSCheckRestoreTimer; + int _macOSCheckRestoreCounter = 0; + + bool get showLogo => widget.showLogo; + bool get showTitle => widget.showTitle; + bool get showMinimize => widget.showMinimize; + bool get showMaximize => widget.showMaximize; + bool get showClose => widget.showClose; + Widget Function(Widget pageView)? get pageViewBuilder => + widget.pageViewBuilder; + TabMenuBuilder? get tabMenuBuilder => widget.tabMenuBuilder; + Widget? get tail => widget.tail; + Future Function()? get onWindowCloseButton => + widget.onWindowCloseButton; + TabBuilder? get tabBuilder => widget.tabBuilder; + LabelGetter? get labelGetter => widget.labelGetter; + double? get maxLabelWidth => widget.maxLabelWidth; + Color? get selectedTabBackgroundColor => widget.selectedTabBackgroundColor; + Color? get unSelectedTabBackgroundColor => + widget.unSelectedTabBackgroundColor; + Color? get selectedBorderColor => widget.selectedBorderColor; + RxBool? get blockTab => widget.blockTab; + DesktopTabController get controller => widget.controller; + RxList get invisibleTabKeys => widget.invisibleTabKeys; + Debouncer get _scrollDebounce => widget._scrollDebounce; + + Rx get state => controller.state; + + DesktopTabType get tabType => controller.tabType; + bool get isMainWindow => + tabType == DesktopTabType.main || + tabType == DesktopTabType.cm || + tabType == DesktopTabType.install; + + _DesktopTabState() : super(); static RxString tablabelGetter(String peerId) { final alias = bind.mainGetPeerOptionSync(id: peerId, key: 'alias'); return RxString(getDesktopTabLabel(peerId, alias)); } + @override + void initState() { + super.initState(); + DesktopMultiWindow.addListener(this); + windowManager.addListener(this); + + Future.delayed(Duration(milliseconds: 500), () { + if (isMainWindow) { + windowManager.isMaximized().then((maximized) { + if (stateGlobal.isMaximized.value != maximized) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => setState(() => stateGlobal.setMaximized(maximized))); + } + }); + } else { + final wc = WindowController.fromWindowId(kWindowId!); + wc.isMaximized().then((maximized) { + debugPrint("isMaximized $maximized"); + if (stateGlobal.isMaximized.value != maximized) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => setState(() => stateGlobal.setMaximized(maximized))); + } + }); + } + }); + } + + @override + void dispose() { + DesktopMultiWindow.removeListener(this); + windowManager.removeListener(this); + _macOSCheckRestoreTimer?.cancel(); + super.dispose(); + } + + void _setMaximized(bool maximize) { + stateGlobal.setMaximized(maximize); + _saveFrameDebounce.call(_saveFrame); + setState(() {}); + } + + @override + void onWindowFocus() { + stateGlobal.isFocused.value = true; + } + + @override + void onWindowBlur() { + stateGlobal.isFocused.value = false; + } + + @override + void onWindowMinimize() { + stateGlobal.setMinimized(true); + stateGlobal.setMaximized(false); + super.onWindowMinimize(); + } + + @override + void onWindowMaximize() { + stateGlobal.setMinimized(false); + _setMaximized(true); + super.onWindowMaximize(); + } + + @override + void onWindowUnmaximize() { + stateGlobal.setMinimized(false); + _setMaximized(false); + super.onWindowUnmaximize(); + } + + _saveFrame() async { + if (tabType == DesktopTabType.main) { + await saveWindowPosition(WindowType.Main); + } else if (kWindowType != null && kWindowId != null) { + await saveWindowPosition(kWindowType!, windowId: kWindowId); + } + } + + @override + void onWindowMoved() { + _saveFrameDebounce.call(_saveFrame); + super.onWindowMoved(); + } + + @override + void onWindowResized() { + _saveFrameDebounce.call(_saveFrame); + super.onWindowMoved(); + } + + @override + void onWindowClose() async { + mainWindowClose() async => await windowManager.hide(); + notMainWindowClose(WindowController windowController) async { + if (controller.length != 0) { + debugPrint("close not empty multiwindow from taskbar"); + if (isWindows) { + await windowController.show(); + await windowController.focus(); + final res = await onWindowCloseButton?.call() ?? true; + if (!res) return; + } + controller.clear(); + } + await windowController.hide(); + await rustDeskWinManager + .call(WindowType.Main, kWindowEventHide, {"id": kWindowId!}); + } + + macOSWindowClose( + Future Function() checkFullscreen, + Future Function() closeFunc, + ) async { + _macOSCheckRestoreCounter = 0; + _macOSCheckRestoreTimer = + Timer.periodic(Duration(milliseconds: 30), (timer) async { + _macOSCheckRestoreCounter++; + if (!await checkFullscreen() || _macOSCheckRestoreCounter >= 30) { + _macOSCheckRestoreTimer?.cancel(); + _macOSCheckRestoreTimer = null; + Timer(Duration(milliseconds: 700), () async => await closeFunc()); + } + }); + } + + // hide window on close + if (isMainWindow) { + if (rustDeskWinManager.getActiveWindows().contains(kMainWindowId)) { + await rustDeskWinManager.unregisterActiveWindow(kMainWindowId); + } + // macOS specific workaround, the window is not hiding when in fullscreen. + if (isMacOS && await windowManager.isFullScreen()) { + await windowManager.setFullScreen(false); + await macOSWindowClose( + () async => await windowManager.isFullScreen(), + mainWindowClose, + ); + } else { + await mainWindowClose(); + } + } else { + // it's safe to hide the subwindow + final controller = WindowController.fromWindowId(kWindowId!); + if (isMacOS) { + // onWindowClose() maybe called multiple times because of loopCloseWindow() in remote_tab_page.dart. + // use ??= to make sure the value is set on first call. + + if (await onWindowCloseButton?.call() ?? true) { + if (await controller.isFullScreen()) { + await controller.setFullscreen(false); + stateGlobal.setFullscreen(false, procWnd: false); + await macOSWindowClose( + () async => await controller.isFullScreen(), + () async => await notMainWindowClose(controller), + ); + } else { + await notMainWindowClose(controller); + } + } + } else { + await notMainWindowClose(controller); + } + } + super.onWindowClose(); + } + @override Widget build(BuildContext context) { return Column(children: [ @@ -468,7 +677,6 @@ class DesktopTab extends StatelessWidget { // hide simulated action buttons when we in compatible ui mode, because of reusing system title bar. WindowActionPanel( isMainWindow: isMainWindow, - tabType: tabType, state: state, tabController: controller, invisibleTabKeys: invisibleTabKeys, @@ -486,7 +694,6 @@ class DesktopTab extends StatelessWidget { class WindowActionPanel extends StatefulWidget { final bool isMainWindow; - final DesktopTabType tabType; final Rx state; final DesktopTabController tabController; @@ -502,7 +709,6 @@ class WindowActionPanel extends StatefulWidget { const WindowActionPanel( {Key? key, required this.isMainWindow, - required this.tabType, required this.state, required this.tabController, required this.invisibleTabKeys, @@ -520,180 +726,17 @@ class WindowActionPanel extends StatefulWidget { } } -class WindowActionPanelState extends State - with MultiWindowListener, WindowListener { - final _saveFrameDebounce = Debouncer(delay: Duration(seconds: 1)); - Timer? _macOSCheckRestoreTimer; - int _macOSCheckRestoreCounter = 0; - +class WindowActionPanelState extends State { @override void initState() { super.initState(); - DesktopMultiWindow.addListener(this); - windowManager.addListener(this); - - Future.delayed(Duration(milliseconds: 500), () { - if (widget.isMainWindow) { - windowManager.isMaximized().then((maximized) { - if (stateGlobal.isMaximized.value != maximized) { - WidgetsBinding.instance.addPostFrameCallback( - (_) => setState(() => stateGlobal.setMaximized(maximized))); - } - }); - } else { - final wc = WindowController.fromWindowId(kWindowId!); - wc.isMaximized().then((maximized) { - debugPrint("isMaximized $maximized"); - if (stateGlobal.isMaximized.value != maximized) { - WidgetsBinding.instance.addPostFrameCallback( - (_) => setState(() => stateGlobal.setMaximized(maximized))); - } - }); - } - }); } @override void dispose() { - DesktopMultiWindow.removeListener(this); - windowManager.removeListener(this); - _macOSCheckRestoreTimer?.cancel(); super.dispose(); } - void _setMaximized(bool maximize) { - stateGlobal.setMaximized(maximize); - _saveFrameDebounce.call(_saveFrame); - setState(() {}); - } - - @override - void onWindowFocus() { - stateGlobal.isFocused.value = true; - } - - @override - void onWindowBlur() { - stateGlobal.isFocused.value = false; - } - - @override - void onWindowMinimize() { - stateGlobal.setMinimized(true); - stateGlobal.setMaximized(false); - super.onWindowMinimize(); - } - - @override - void onWindowMaximize() { - stateGlobal.setMinimized(false); - _setMaximized(true); - super.onWindowMaximize(); - } - - @override - void onWindowUnmaximize() { - stateGlobal.setMinimized(false); - _setMaximized(false); - super.onWindowUnmaximize(); - } - - _saveFrame() async { - if (widget.tabType == DesktopTabType.main) { - await saveWindowPosition(WindowType.Main); - } else if (kWindowType != null && kWindowId != null) { - await saveWindowPosition(kWindowType!, windowId: kWindowId); - } - } - - @override - void onWindowMoved() { - _saveFrameDebounce.call(_saveFrame); - super.onWindowMoved(); - } - - @override - void onWindowResized() { - _saveFrameDebounce.call(_saveFrame); - super.onWindowMoved(); - } - - @override - void onWindowClose() async { - mainWindowClose() async => await windowManager.hide(); - notMainWindowClose(WindowController controller) async { - if (widget.tabController.length != 0) { - debugPrint("close not empty multiwindow from taskbar"); - if (isWindows) { - await controller.show(); - await controller.focus(); - final res = await widget.onClose?.call() ?? true; - if (!res) return; - } - widget.tabController.clear(); - } - await controller.hide(); - await rustDeskWinManager - .call(WindowType.Main, kWindowEventHide, {"id": kWindowId!}); - } - - macOSWindowClose( - Future Function() checkFullscreen, - Future Function() closeFunc, - ) async { - _macOSCheckRestoreCounter = 0; - _macOSCheckRestoreTimer = - Timer.periodic(Duration(milliseconds: 30), (timer) async { - _macOSCheckRestoreCounter++; - if (!await checkFullscreen() || _macOSCheckRestoreCounter >= 30) { - _macOSCheckRestoreTimer?.cancel(); - _macOSCheckRestoreTimer = null; - Timer(Duration(milliseconds: 700), () async => await closeFunc()); - } - }); - } - - // hide window on close - if (widget.isMainWindow) { - if (rustDeskWinManager.getActiveWindows().contains(kMainWindowId)) { - await rustDeskWinManager.unregisterActiveWindow(kMainWindowId); - } - // macOS specific workaround, the window is not hiding when in fullscreen. - if (isMacOS && await windowManager.isFullScreen()) { - await windowManager.setFullScreen(false); - await macOSWindowClose( - () async => await windowManager.isFullScreen(), - mainWindowClose, - ); - } else { - await mainWindowClose(); - } - } else { - // it's safe to hide the subwindow - final controller = WindowController.fromWindowId(kWindowId!); - if (isMacOS) { - // onWindowClose() maybe called multiple times because of loopCloseWindow() in remote_tab_page.dart. - // use ??= to make sure the value is set on first call. - - if (await widget.onClose?.call() ?? true) { - if (await controller.isFullScreen()) { - await controller.setFullscreen(false); - stateGlobal.setFullscreen(false, procWnd: false); - await macOSWindowClose( - () async => await controller.isFullScreen(), - () async => await notMainWindowClose(controller), - ); - } else { - await notMainWindowClose(controller); - } - } - } else { - await notMainWindowClose(controller); - } - } - super.onWindowClose(); - } - bool showTabDowndown() { return widget.tabController.state.value.tabs.length > 1 && (widget.tabController.tabType == DesktopTabType.remoteScreen || From b30f84623bdd740b2abbaa25cc75543b87a68a3c Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Fri, 12 Jul 2024 23:44:25 +0800 Subject: [PATCH 246/335] Update playground.yml --- .github/workflows/playground.yml | 50 ++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 7004b53a54b..f0c86ec5aeb 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,7 +43,45 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.13.9", + flutter: "3.19.6", + ref: "1ffc10e44ffdc9c8a396ec840b856bd27940b171", + date: "20240419" + } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.16.9", + ref: "b2290f5b4c3fd9233f6550b01c151401cdb3fed3", + date: "20240319" + } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.19.6", + ref: "914da2b86f73a314027833b1940784d657826503", + date: "20240219" + } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.19.6", + ref: "06b3894249c14610c71d565cff1634b4ebb75326", + date: "20240119" + } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.19.6", + ref: "f6509e3fd6917aa976bad2fc684182601ebf2434", + date: "20231219" } steps: - name: Export GitHub Actions cache environment variables @@ -55,7 +93,9 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 - + with: + ref: ${{ matrix.job.ref }} + - name: Import the codesign cert if: env.MACOS_P12_BASE64 != null uses: apple-actions/import-codesign-certs@v1 @@ -114,8 +154,8 @@ jobs: shell: bash run: | cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" - # below works for mac - pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; + # below works for mac to make buildable on 3.13.9 + # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; 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 --c-output ./flutter/macos/Runner/bridge_generated.h @@ -170,7 +210,7 @@ jobs: - name: Rename rustdesk run: | for name in rustdesk*??.dmg; do - mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}-flutter${{ matrix.job.flutter }}.dmg" + mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}-flutter${{ matrix.job.flutter }}-flutter${{ matrix.job.date }}.dmg" done - name: Publish DMG package From c873b69662d9f9fada72ad97f676237f9dce2c2e Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sat, 13 Jul 2024 00:44:08 +0800 Subject: [PATCH 247/335] refact: unwrap offstage for cursor paint (#8693) Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 13 +++++++------ flutter/lib/desktop/widgets/remote_toolbar.dart | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 4d61c373892..f108f74445c 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -506,12 +506,13 @@ class _RemotePageState extends State ]; if (!_ffi.canvasModel.cursorEmbedded) { - paints.add(Obx(() => Offstage( - offstage: _showRemoteCursor.isFalse || _remoteCursorMoved.isFalse, - child: CursorPaint( - id: widget.id, - zoomCursor: _zoomCursor, - )))); + paints + .add(Obx(() => _showRemoteCursor.isFalse || _remoteCursorMoved.isFalse + ? Offstage() + : CursorPaint( + id: widget.id, + zoomCursor: _zoomCursor, + ))); } paints.add( Positioned( diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 53df40fedb0..5b649049cfb 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -6,7 +6,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_hbb/common/widgets/audio_input.dart'; import 'package:flutter_hbb/common/widgets/toolbar.dart'; import 'package:flutter_hbb/models/chat_model.dart'; -import 'package:flutter_hbb/models/input_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; From a532b36e28e1b0abd0071a3a25fb356d08e5d115 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 01:03:43 +0800 Subject: [PATCH 248/335] Update playground.yml --- .github/workflows/playground.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index f0c86ec5aeb..64625517bc4 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -180,7 +180,7 @@ jobs: - name: Build rustdesk run: | - ./build.py --flutter --hwcodec ${{ matrix.job.extra-build-args }} + ./build.py --flutter ${{ matrix.job.extra-build-args }} - name: create unsigned dmg run: | From 06e04143a85908b912ab338aae3026082f0288e1 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 01:26:09 +0800 Subject: [PATCH 249/335] Update playground.yml --- .github/workflows/playground.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 64625517bc4..d469df1e3b4 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -43,7 +43,7 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.19.6", + flutter: "3.16.9", ref: "1ffc10e44ffdc9c8a396ec840b856bd27940b171", date: "20240419" } @@ -61,7 +61,7 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.19.6", + flutter: "3.16.9", ref: "914da2b86f73a314027833b1940784d657826503", date: "20240219" } @@ -70,7 +70,7 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.19.6", + flutter: "3.16.9", ref: "06b3894249c14610c71d565cff1634b4ebb75326", date: "20240119" } @@ -79,7 +79,7 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.19.6", + flutter: "3.16.9", ref: "f6509e3fd6917aa976bad2fc684182601ebf2434", date: "20231219" } From 183ea47ba42f48dc3956e33c1832cee6b7c9b4fb Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 01:29:17 +0800 Subject: [PATCH 250/335] Update playground.yml --- .github/workflows/playground.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index d469df1e3b4..056d5051853 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -153,6 +153,7 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | + rm -rf Cargo.lock cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" # below works for mac to make buildable on 3.13.9 # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; From 294a6ce9bce038f0c4f56e8050fb4aa2f0274541 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 11:15:00 +0800 Subject: [PATCH 251/335] Update playground.yml --- .github/workflows/playground.yml | 34 ++++++++------------------------ 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 056d5051853..e802e60bab4 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -44,44 +44,26 @@ jobs: extra-build-args: "", arch: x86_64, flutter: "3.16.9", - ref: "1ffc10e44ffdc9c8a396ec840b856bd27940b171", - date: "20240419" + ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8", + date: "20231119" } - { target: x86_64-apple-darwin, os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.16.9", - ref: "b2290f5b4c3fd9233f6550b01c151401cdb3fed3", - date: "20240319" - } - - { - target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel - extra-build-args: "", - arch: x86_64, - flutter: "3.16.9", - ref: "914da2b86f73a314027833b1940784d657826503", - date: "20240219" + flutter: "3.13.9", + ref: "c055ba2985516927a348900a8215983d536d1a1e", + date: "20231019" } - { target: x86_64-apple-darwin, os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.16.9", - ref: "06b3894249c14610c71d565cff1634b4ebb75326", - date: "20240119" - } - - { - target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel - extra-build-args: "", - arch: x86_64, - flutter: "3.16.9", - ref: "f6509e3fd6917aa976bad2fc684182601ebf2434", - date: "20231219" + flutter: "3.13.9", + ref: "ab982e86c391dc31aba67e9439c0bd93143b84af", + date: "20231010" } steps: - name: Export GitHub Actions cache environment variables From 9d26fec6312c46699121e7f9ef3509f2241eea65 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 11:33:15 +0800 Subject: [PATCH 252/335] Update playground.yml --- .github/workflows/playground.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index e802e60bab4..a7d27a67200 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -45,6 +45,7 @@ jobs: arch: x86_64, flutter: "3.16.9", ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8", + bridge: "1.80.1", date: "20231119" } - { @@ -54,6 +55,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "c055ba2985516927a348900a8215983d536d1a1e", + bridge: "1.75", date: "20231019" } - { @@ -63,6 +65,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "ab982e86c391dc31aba67e9439c0bd93143b84af", + bridge: "1.75", date: "20231010" } steps: @@ -135,8 +138,7 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | - rm -rf Cargo.lock - cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" + cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" # below works for mac to make buildable on 3.13.9 # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; pushd flutter && flutter pub get && popd From 46605fab1b3917105e280e959bbd731fa0975103 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 14:33:17 +0800 Subject: [PATCH 253/335] Update playground.yml --- .github/workflows/playground.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index a7d27a67200..e0b7d9488e7 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -55,7 +55,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "c055ba2985516927a348900a8215983d536d1a1e", - bridge: "1.75", + bridge: "1.75.1", date: "20231019" } - { @@ -65,7 +65,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "ab982e86c391dc31aba67e9439c0bd93143b84af", - bridge: "1.75", + bridge: "1.75.1", date: "20231010" } steps: From a95a6ab7337fdd40c72dd06d845f36b23e804edd Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:24:01 +0800 Subject: [PATCH 254/335] Update playground.yml --- .github/workflows/playground.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index e0b7d9488e7..425b0815865 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -55,7 +55,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "c055ba2985516927a348900a8215983d536d1a1e", - bridge: "1.75.1", + bridge: "1.79.0", date: "20231019" } - { @@ -65,7 +65,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "ab982e86c391dc31aba67e9439c0bd93143b84af", - bridge: "1.75.1", + bridge: "1.79.0", date: "20231010" } steps: From 883c630206562b1661c8a8336abf6838efd6f09c Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:35:24 +0800 Subject: [PATCH 255/335] Update playground.yml --- .github/workflows/playground.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 425b0815865..c8ede1eef29 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -55,7 +55,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "c055ba2985516927a348900a8215983d536d1a1e", - bridge: "1.79.0", + bridge: "1.75.3", date: "20231019" } - { @@ -65,7 +65,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "ab982e86c391dc31aba67e9439c0bd93143b84af", - bridge: "1.79.0", + bridge: "1.75.3", date: "20231010" } steps: From f5bcc17636c315670a0d22b00e186d64f8dad3eb Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:45:59 +0800 Subject: [PATCH 256/335] Update playground.yml --- .github/workflows/playground.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index c8ede1eef29..9d19b309aaa 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -53,7 +53,7 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.13.9", + flutter: "3.10.6", ref: "c055ba2985516927a348900a8215983d536d1a1e", bridge: "1.75.3", date: "20231019" @@ -63,7 +63,7 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.13.9", + flutter: "3.10.6", ref: "ab982e86c391dc31aba67e9439c0bd93143b84af", bridge: "1.75.3", date: "20231010" From 8a1acedae56da77ea7791cd86cf9fe74dc45e1c8 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:51:51 +0800 Subject: [PATCH 257/335] Update playground.yml --- .github/workflows/playground.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 9d19b309aaa..2ae3f5cf511 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -48,6 +48,26 @@ jobs: bridge: "1.80.1", date: "20231119" } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.13.9", + ref: "8bf57f1293abee463a935aae58f84d01b60f9b74", + bridge: "1.79.0", + date: "20231101" + } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.10.6", + ref: "7480ead76a27eb8bc9dd19c376780cdf015c4c0f", + bridge: "1.75.3", + date: "20231030" + } - { target: x86_64-apple-darwin, os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel From 7a3100a87c4485125c283b305c70c0f66d1c9e31 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 13 Jul 2024 16:08:07 +0800 Subject: [PATCH 258/335] Update playground.yml --- .github/workflows/playground.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 2ae3f5cf511..897462e37f5 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -17,7 +17,7 @@ env: TAG_NAME: "nightly" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.06.15 - VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" + VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1" VERSION: "1.2.7" NDK_VERSION: "r26d" #signing keys env variable checks @@ -166,12 +166,24 @@ jobs: - name: Setup vcpkg with Github Actions binary cache uses: lukka/run-vcpkg@v11 + if: false with: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + if: false run: | $VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed" + + - 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 aom - name: Show version information (Rust, cargo, Clang) shell: bash From 5e8fe239fa334a6159d91c0f8ea748556156d52f Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Sat, 13 Jul 2024 15:46:05 +0300 Subject: [PATCH 259/335] build_fdroid.sh: Fix review points by Licaon_Kter (#8701) * NDK should be downloaded via verified Android SDK's sdkmanager * vcpkg should be built from sources not prebuilt Signed-off-by: Vasyl Gello --- flutter/build_fdroid.sh | 111 +++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh index 418e8368d37..abf311b2f9e 100755 --- a/flutter/build_fdroid.sh +++ b/flutter/build_fdroid.sh @@ -71,6 +71,13 @@ x86) ;; esac +# Check ANDROID_SDK_ROOT and sdkmanager present on PATH + +if [ ! -d "${ANDROID_SDK_ROOT}" ] || ! command -v sdkmanager 1>/dev/null; then + echo "ERROR: Can not find Android SDK!" >&2 + exit 1 +fi + # Export necessary variables export PATH="${PATH}:${HOME}/flutter/bin:${HOME}/depot_tools" @@ -129,8 +136,22 @@ prebuild) exit 1 fi - export ANDROID_NDK_ROOT="${HOME}/android-ndk-${NDK_VERSION}" - export ANDROID_NDK_HOME="${HOME}/android-ndk-${NDK_VERSION}" + # Map NDK version to revision + + NDK_VERSION="$(wget \ + -qO- \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + 'https://api.github.com/repos/android/ndk/releases' | + jq -r ".[] | select(.tag_name == \"${NDK_VERSION}\") | .body | match(\"ndkVersion \\\"(.*)\\\"\").captures[0].string")" + + if [ -z "${NDK_VERSION}" ]; then + echo "ERROR: Can not map Android NDK codename to revision!" >&2 + exit 1 + fi + + export ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}" + export ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}" # # Install the components @@ -141,17 +162,7 @@ prebuild) # Install Android NDK if [ ! -d "${ANDROID_NDK_ROOT}" ]; then - pushd "${HOME}" - - wget \ - -q \ - "https://dl.google.com/android/repository/android-ndk-${NDK_VERSION}-linux.zip" - - unzip "android-ndk-${NDK_VERSION}-linux.zip" 1>/dev/null - - rm "android-ndk-${NDK_VERSION}-linux.zip" - - popd # ${HOME} + sdkmanager --install "ndk;${NDK_VERSION}" fi # Install Flutter @@ -159,27 +170,27 @@ prebuild) if [ ! -f "${HOME}/flutter/bin/flutter" ]; then pushd "${HOME}" - wget \ - -q \ - "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" - tar xf "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" - rm "flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" + git clone https://github.com/flutter/flutter - popd # ${HOME} + pushd flutter + + git reset --hard "${FLUTTER_VERSION}" - git config --global --add safe.directory "${HOME}/flutter" + flutter config --no-analytics - flutter --disable-telemetry - dart --disable-telemetry + popd # flutter + + popd # ${HOME} fi # Install Rust if [ ! -f "${HOME}/rustup/rustup-init.sh" ]; then - mkdir "${HOME}/rustup" - pushd "${HOME}/rustup" - wget -O rustup-init.sh 'https://sh.rustup.rs' - popd + pushd "${HOME}" + + git clone --depth 1 https://github.com/rust-lang/rustup + + popd # ${HOME} fi pushd "${HOME}/rustup" @@ -212,12 +223,36 @@ prebuild) git clone \ https://github.com/Microsoft/vcpkg.git + git clone \ + https://github.com/Microsoft/vcpkg-tool.git + + pushd vcpkg-tool + + mkdir build + + pushd build + + cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -G 'Ninja' \ + -DVCPKG_DEVELOPMENT_WARNINGS=OFF \ + .. + + cmake --build . + + popd # build + + popd # vcpkg-tool pushd vcpkg git reset --hard "${VCPKG_COMMIT_ID}" - sh bootstrap-vcpkg.sh -disableMetrics + cp -a ../vcpkg-tool/build/vcpkg vcpkg + + # disable telemetry + + touch "vcpkg.disable-metrics" popd # vcpkg @@ -314,8 +349,22 @@ build) .env.NDK_VERSION \ .github/workflows/flutter-build.yml)" - export ANDROID_NDK_ROOT="${HOME}/android-ndk-${NDK_VERSION}" - export ANDROID_NDK_HOME="${HOME}/android-ndk-${NDK_VERSION}" + # Map NDK version to revision + + NDK_VERSION="$(wget \ + -qO- \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + 'https://api.github.com/repos/android/ndk/releases' | + jq -r ".[] | select(.tag_name == \"${NDK_VERSION}\") | .body | match(\"ndkVersion \\\"(.*)\\\"\").captures[0].string")" + + if [ -z "${NDK_VERSION}" ]; then + echo "ERROR: Can not map Android NDK codename to revision!" >&2 + exit 1 + fi + + export ANDROID_NDK_HOME="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}" + export ANDROID_NDK_ROOT="${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}" if ! command -v cargo 1>/dev/null 2>&1; then . "${HOME}/.cargo/env" @@ -403,7 +452,7 @@ build) echo "## Perform android engine build" - ninja -C out/android_jit_release_x86 1>/dev/null + ninja -C out/android_jit_release_x86 echo "## Configure host engine build" @@ -413,7 +462,7 @@ build) echo "## Perform android engine build" - ninja -C out/host_jit_release_x86 1>/dev/null + ninja -C out/host_jit_release_x86 echo "## Rename host engine" From 0dc664474af862df7ab97fb368814b5383e50758 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sun, 14 Jul 2024 03:46:48 +0800 Subject: [PATCH 260/335] Update playground.yml --- .github/workflows/playground.yml | 48 +++----------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 897462e37f5..54bd3263ba2 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -17,7 +17,7 @@ env: TAG_NAME: "nightly" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2024.06.15 - VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1" + VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" VERSION: "1.2.7" NDK_VERSION: "r26d" #signing keys env variable checks @@ -43,51 +43,11 @@ jobs: os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel extra-build-args: "", arch: x86_64, - flutter: "3.16.9", + flutter: "3.10.6", ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8", bridge: "1.80.1", date: "20231119" } - - { - target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel - extra-build-args: "", - arch: x86_64, - flutter: "3.13.9", - ref: "8bf57f1293abee463a935aae58f84d01b60f9b74", - bridge: "1.79.0", - date: "20231101" - } - - { - target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel - extra-build-args: "", - arch: x86_64, - flutter: "3.10.6", - ref: "7480ead76a27eb8bc9dd19c376780cdf015c4c0f", - bridge: "1.75.3", - date: "20231030" - } - - { - target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel - extra-build-args: "", - arch: x86_64, - flutter: "3.10.6", - ref: "c055ba2985516927a348900a8215983d536d1a1e", - bridge: "1.75.3", - date: "20231019" - } - - { - target: x86_64-apple-darwin, - os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel - extra-build-args: "", - arch: x86_64, - flutter: "3.10.6", - ref: "ab982e86c391dc31aba67e9439c0bd93143b84af", - bridge: "1.75.3", - date: "20231010" - } steps: - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 @@ -166,22 +126,22 @@ jobs: - name: Setup vcpkg with Github Actions binary cache uses: lukka/run-vcpkg@v11 - if: false with: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies - if: false run: | $VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed" - name: Restore from cache and install vcpkg uses: lukka/run-vcpkg@v7 + if: false with: setupOnly: true vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + if: false run: | $VCPKG_ROOT/vcpkg install libvpx libyuv opus aom From 9adc083def10e4390da2ea3f96aa420a923e6c48 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sun, 14 Jul 2024 03:47:15 +0800 Subject: [PATCH 261/335] Update playground.yml --- .github/workflows/playground.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 54bd3263ba2..67fafba2567 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -48,6 +48,16 @@ jobs: bridge: "1.80.1", date: "20231119" } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.13.9", + ref: "85ddfc0739f052cab0029c46b899b959ee94eeb8", + bridge: "1.80.1", + date: "20231119" + } steps: - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 From d18e95703e0844803f33b9c94a87e2bef14348dc Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 14 Jul 2024 03:49:16 +0800 Subject: [PATCH 262/335] fix: mouse forward back (#8705) Signed-off-by: fufesou --- libs/enigo/src/win/win_impl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs index 0598eb608ec..882dba1267d 100644 --- a/libs/enigo/src/win/win_impl.rs +++ b/libs/enigo/src/win/win_impl.rs @@ -154,8 +154,8 @@ impl MouseControllable for Enigo { } }, match button { - MouseButton::Back => XBUTTON1 as u32 * WHEEL_DELTA as u32, - MouseButton::Forward => XBUTTON2 as u32 * WHEEL_DELTA as u32, + MouseButton::Back => XBUTTON1 as u32, + MouseButton::Forward => XBUTTON2 as u32, _ => 0, }, 0, From 30afe4f779e4263929677d189300992a7e2a0860 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 14 Jul 2024 04:07:02 +0800 Subject: [PATCH 263/335] refact: seperate audio device for voice call (#8703) Signed-off-by: fufesou --- flutter/lib/common/widgets/audio_input.dart | 45 ++++++++++++++----- .../desktop/pages/desktop_setting_page.dart | 6 ++- flutter/lib/desktop/pages/server_page.dart | 6 +-- .../lib/desktop/widgets/remote_toolbar.dart | 45 ++++++++++--------- src/client/io_loop.rs | 14 +++--- src/flutter_ffi.rs | 19 ++++++++ src/ipc.rs | 18 +++++--- src/server/audio_service.rs | 39 ++++++++++++++-- src/server/connection.rs | 44 +++++++++--------- 9 files changed, 162 insertions(+), 74 deletions(-) diff --git a/flutter/lib/common/widgets/audio_input.dart b/flutter/lib/common/widgets/audio_input.dart index 36a0e49722c..1db4391270c 100644 --- a/flutter/lib/common/widgets/audio_input.dart +++ b/flutter/lib/common/widgets/audio_input.dart @@ -2,22 +2,39 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/models/platform_model.dart'; +const _kWindowsSystemSound = 'System Sound'; + typedef AudioINputSetDevice = void Function(String device); typedef AudioInputBuilder = Widget Function( List devices, String currentDevice, AudioINputSetDevice setDevice); class AudioInput extends StatelessWidget { final AudioInputBuilder builder; + final bool isCm; + final bool isVoiceCall; - const AudioInput({Key? key, required this.builder}) : super(key: key); + const AudioInput( + {Key? key, + required this.builder, + required this.isCm, + required this.isVoiceCall}) + : super(key: key); static String getDefault() { if (isWindows) return translate('System Sound'); return ''; } - static Future getValue() async { - String device = await bind.mainGetOption(key: 'audio-input'); + static Future getAudioInput(bool isCm, bool isVoiceCall) { + if (isVoiceCall) { + return bind.getVoiceCallInputDevice(isCm: isCm); + } else { + return bind.mainGetOption(key: 'audio-input'); + } + } + + static Future getValue(bool isCm, bool isVoiceCall) async { + String device = await getAudioInput(isCm, isVoiceCall); if (device.isNotEmpty) { return device; } else { @@ -25,31 +42,39 @@ class AudioInput extends StatelessWidget { } } - static Future setDevice(String device) async { + static Future setDevice( + String device, bool isCm, bool isVoiceCall) async { if (device == getDefault()) device = ''; - await bind.mainSetOption(key: 'audio-input', value: device); + if (isVoiceCall) { + await bind.setVoiceCallInputDevice(isCm: isCm, device: device); + } else { + await bind.mainSetOption(key: 'audio-input', value: device); + } } - static Future> getDevicesInfo() async { + static Future> getDevicesInfo( + bool isCm, bool isVoiceCall) async { List devices = (await bind.mainGetSoundInputs()).toList(); if (isWindows) { - devices.insert(0, translate('System Sound')); + devices.insert(0, translate(_kWindowsSystemSound)); } - String current = await getValue(); + String current = await getValue(isCm, isVoiceCall); return {'devices': devices, 'current': current}; } @override Widget build(BuildContext context) { return futureBuilder( - future: getDevicesInfo(), + future: getDevicesInfo(isCm, isVoiceCall), hasData: (data) { String currentDevice = data['current']; List devices = data['devices'] as List; if (devices.isEmpty) { return const Offstage(); } - return builder(devices, currentDevice, setDevice); + return builder(devices, currentDevice, (devices) { + setDevice(devices, isCm, isVoiceCall); + }); }, ); } diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 8ff41758508..505a98f17db 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -500,7 +500,7 @@ class _GeneralState extends State<_General> { return const Offstage(); } - return AudioInput(builder: (devices, currentDevice, setDevice) { + builder(devices, currentDevice, setDevice) { return _Card(title: 'Audio Input Device', children: [ ...devices.map((device) => _Radio(context, value: device, @@ -511,7 +511,9 @@ class _GeneralState extends State<_General> { setState(() {}); })) ]); - }); + } + + return AudioInput(builder: builder, isCm: false, isVoiceCall: false); } Widget record(BuildContext context) { diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 1e63c71bdbd..1cac2706e2f 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -732,7 +732,7 @@ class _CmControlPanel extends StatelessWidget { child: buildButton(context, color: MyTheme.accent, onClick: null, onTapDown: (details) async { - final devicesInfo = await AudioInput.getDevicesInfo(); + final devicesInfo = await AudioInput.getDevicesInfo(true, true); List devices = devicesInfo['devices'] as List; if (devices.isEmpty) { msgBox( @@ -758,13 +758,13 @@ class _CmControlPanel extends StatelessWidget { value: d, height: 18, padding: EdgeInsets.zero, - onTap: () => AudioInput.setDevice(d), + onTap: () => AudioInput.setDevice(d, true, true), child: IgnorePointer( child: RadioMenuButton( value: d, groupValue: currentDevice, onChanged: (v) { - if (v != null) AudioInput.setDevice(v); + if (v != null) AudioInput.setDevice(v, true, true); }, child: Container( child: Text( diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 5b649049cfb..d90e24f5395 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1984,28 +1984,31 @@ class _VoiceCallMenu extends StatelessWidget { @override Widget build(BuildContext context) { menuChildrenGetter() { - final audioInput = - AudioInput(builder: (devices, currentDevice, setDevice) { - return Column( - children: devices - .map((d) => RdoMenuButton( - child: Container( - child: Text( - d, - overflow: TextOverflow.ellipsis, + final audioInput = AudioInput( + builder: (devices, currentDevice, setDevice) { + return Column( + children: devices + .map((d) => RdoMenuButton( + child: Container( + child: Text( + d, + overflow: TextOverflow.ellipsis, + ), + constraints: BoxConstraints(maxWidth: 250), ), - constraints: BoxConstraints(maxWidth: 250), - ), - value: d, - groupValue: currentDevice, - onChanged: (v) { - if (v != null) setDevice(v); - }, - ffi: ffi, - )) - .toList(), - ); - }); + value: d, + groupValue: currentDevice, + onChanged: (v) { + if (v != null) setDevice(v); + }, + ffi: ffi, + )) + .toList(), + ); + }, + isCm: false, + isVoiceCall: true, + ); return [ audioInput, Divider(), diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 19074bbd1a0..15a779109a6 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -42,7 +42,7 @@ use crate::client::{ }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::clipboard::{update_clipboard, CLIPBOARD_INTERVAL}; -use crate::common::{get_default_sound_input, set_sound_input}; +use crate::common::get_default_sound_input; use crate::ui_session_interface::{InvokeUiSession, Session}; #[cfg(not(any(target_os = "ios")))] use crate::{audio_service, ConnInner, CLIENT_SERVER}; @@ -387,11 +387,12 @@ impl Remote { if self.handler.is_file_transfer() || self.handler.is_port_forward() { return None; } - // Switch to default input device - let default_sound_device = get_default_sound_input(); - if let Some(device) = default_sound_device { - set_sound_input(device); - } + // NOTE: + // The client server and --server both use the same sound input device. + // It's better to distinguish the server side and client side. + // But it' not necessary for now, because it's not a common case. + // And it is immediately known when the input device is changed. + crate::audio_service::set_voice_call_input_device(get_default_sound_input(), false); // iOS does not have this server. #[cfg(not(any(target_os = "ios")))] { @@ -421,6 +422,7 @@ impl Remote { client_conn_inner, false, ); + crate::audio_service::set_voice_call_input_device(None, true); break; } _ => {} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 5371b225eea..3eccfc84d9c 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1319,6 +1319,25 @@ pub fn cm_close_voice_call(id: i32) { crate::ui_cm_interface::close_voice_call(id); } +pub fn set_voice_call_input_device(is_cm: bool, device: String) { + if is_cm { + let _ = crate::ipc::set_config("voice-call-input", device); + } else { + crate::audio_service::set_voice_call_input_device(Some(device), true); + } +} + +pub fn get_voice_call_input_device(is_cm: bool) -> String { + if is_cm { + match crate::ipc::get_config("voice-call-input") { + Ok(Some(device)) => device, + _ => "".to_owned(), + } + } else { + crate::audio_service::get_voice_call_input_device().unwrap_or_default() + } +} + pub fn main_get_last_remote_id() -> String { LocalConfig::get_remote_id() } diff --git a/src/ipc.rs b/src/ipc.rs index 20e10e1b28f..489db38e709 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -308,7 +308,7 @@ pub async fn new_listener(postfix: &str) -> ResultType { } } -pub struct CheckIfRestart(String, Vec, String); +pub struct CheckIfRestart(String, Vec, String, String); impl CheckIfRestart { pub fn new() -> CheckIfRestart { @@ -316,6 +316,7 @@ impl CheckIfRestart { Config::get_option("stop-service"), Config::get_rendezvous_servers(), Config::get_option("audio-input"), + Config::get_option("voice-call-input"), ) } } @@ -329,6 +330,12 @@ impl Drop for CheckIfRestart { if self.2 != Config::get_option("audio-input") { crate::audio_service::restart(); } + if self.3 != Config::get_option("voice-call-input") { + crate::audio_service::set_voice_call_input_device( + Some(Config::get_option("voice-call-input")), + true, + ) + } } } @@ -457,6 +464,8 @@ async fn handle(data: Data, stream: &mut Connection) { } else { None }; + } else if name == "voice-call-input" { + value = crate::audio_service::get_voice_call_input_device(); } else { value = None; } @@ -472,6 +481,8 @@ async fn handle(data: Data, stream: &mut Connection) { Config::set_permanent_password(&value); } else if name == "salt" { Config::set_salt(&value); + } else if name == "voice-call-input" { + crate::audio_service::set_voice_call_input_device(Some(value), true); } else { return; } @@ -488,12 +499,7 @@ async fn handle(data: Data, stream: &mut Connection) { if let Some(v) = value.get("privacy-mode-impl-key") { crate::privacy_mode::switch(v); } - let pre_opts = Config::get_options(); - let new_audio_input = pre_opts.get("audio-input"); Config::set_options(value); - if new_audio_input != pre_opts.get("audio-input") { - crate::audio_service::restart(); - } allow_err!(stream.send(&Data::Options(None)).await); } }, diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index cfe7b457beb..504c0927974 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -22,6 +22,10 @@ pub const NAME: &'static str = "audio"; pub const AUDIO_DATA_SIZE_U8: usize = 960 * 4; // 10ms in 48000 stereo static RESTARTING: AtomicBool = AtomicBool::new(false); +lazy_static::lazy_static! { + static ref VOICE_CALL_INPUT_DEVICE: Arc::>> = Default::default(); +} + #[cfg(not(any(target_os = "linux", target_os = "android")))] pub fn new() -> GenericService { let svc = EmptyExtraFieldService::new(NAME.to_owned(), true); @@ -36,6 +40,33 @@ pub fn new() -> GenericService { svc.sp } +#[inline] +pub fn get_voice_call_input_device() -> Option { + VOICE_CALL_INPUT_DEVICE.lock().unwrap().clone() +} + +#[inline] +pub fn set_voice_call_input_device(device: Option, set_if_present: bool) { + if !set_if_present && VOICE_CALL_INPUT_DEVICE.lock().unwrap().is_some() { + return; + } + + if *VOICE_CALL_INPUT_DEVICE.lock().unwrap() == device { + return; + } + *VOICE_CALL_INPUT_DEVICE.lock().unwrap() = device; + restart(); +} + +#[inline] +fn get_audio_input() -> String { + VOICE_CALL_INPUT_DEVICE + .lock() + .unwrap() + .clone() + .unwrap_or(Config::get_option("audio-input")) +} + pub fn restart() { log::info!("restart the audio service, freezing now..."); if RESTARTING.load(Ordering::SeqCst) { @@ -62,7 +93,7 @@ mod pa_impl { stream .send(&crate::ipc::Data::Config(( "audio-input".to_owned(), - Some(Config::get_option("audio-input")) + Some(super::get_audio_input()) ))) .await ); @@ -132,6 +163,7 @@ mod cpal_impl { } fn run_restart(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> { + println!("REMOVE ME ========================= run_restart"); state.reset(); sp.snapshot(|_sps: ServiceSwap<_>| Ok(()))?; match &state.stream { @@ -198,7 +230,8 @@ mod cpal_impl { #[cfg(windows)] fn get_device() -> ResultType<(Device, SupportedStreamConfig)> { - let audio_input = Config::get_option("audio-input"); + let audio_input = super::get_audio_input(); + println!("REMOVE ME =============================== use audio input: {}", &audio_input); if !audio_input.is_empty() { return get_audio_input(&audio_input); } @@ -219,7 +252,7 @@ mod cpal_impl { #[cfg(not(windows))] fn get_device() -> ResultType<(Device, SupportedStreamConfig)> { - let audio_input = Config::get_option("audio-input"); + let audio_input = super::get_audio_input(); get_audio_input(&audio_input) } diff --git a/src/server/connection.rs b/src/server/connection.rs index fd3dd2a032a..4a383fe356a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -17,7 +17,6 @@ use crate::{ client::{ new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender, }, - common::{get_default_sound_input, set_sound_input}, display_service, ipc, privacy_mode, video_service, VERSION, }; #[cfg(any(target_os = "android", target_os = "ios"))] @@ -218,7 +217,6 @@ pub struct Connection { portable: PortableState, from_switch: bool, voice_call_request_timestamp: Option, - audio_input_device_before_voice_call: Option, options_in_login: Option, #[cfg(not(any(target_os = "ios")))] pressed_modifiers: HashSet, @@ -367,7 +365,6 @@ impl Connection { from_switch: false, audio_sender: None, voice_call_request_timestamp: None, - audio_input_device_before_voice_call: None, options_in_login: None, #[cfg(not(any(target_os = "ios")))] pressed_modifiers: Default::default(), @@ -2061,12 +2058,13 @@ impl Connection { cb.content.into() }; if let Ok(content) = String::from_utf8(content) { - let data = HashMap::from([ - ("name", "clipboard"), - ("content", &content), - ]); + let data = + HashMap::from([("name", "clipboard"), ("content", &content)]); if let Ok(data) = serde_json::to_string(&data) { - let _ = crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, data); + let _ = crate::flutter::push_global_event( + crate::flutter::APP_TYPE_MAIN, + data, + ); } } } @@ -2720,15 +2718,10 @@ impl Connection { if let Some(ts) = self.voice_call_request_timestamp.take() { let msg = new_voice_call_response(ts.get(), accepted); if accepted { - // Backup the default input device. - let audio_input_device = Config::get_option("audio-input"); - log::debug!("Backup the sound input device {}", audio_input_device); - self.audio_input_device_before_voice_call = Some(audio_input_device); - // Switch to default input device - let default_sound_device = get_default_sound_input(); - if let Some(device) = default_sound_device { - set_sound_input(device); - } + crate::audio_service::set_voice_call_input_device( + crate::get_default_sound_input(), + false, + ); self.send_to_cm(Data::StartVoiceCall); } else { self.send_to_cm(Data::CloseVoiceCall("".to_owned())); @@ -2740,12 +2733,7 @@ impl Connection { } pub async fn close_voice_call(&mut self) { - // Restore to the prior audio device. - if let Some(sound_input) = - std::mem::replace(&mut self.audio_input_device_before_voice_call, None) - { - set_sound_input(sound_input); - } + crate::audio_service::set_voice_call_input_device(None, true); // Notify the connection manager that the voice call has been closed. self.send_to_cm(Data::CloseVoiceCall("".to_owned())); } @@ -3035,6 +3023,16 @@ impl Connection { return; } self.closed = true; + // If voice A,B -> C, and A,B has voice call + // B disconnects, C will reset the voice call input. + // + // It may be acceptable, because it's not a common case, + // and it's immediately known when the input device changes. + // C can change the input device manually in cm interface. + // + // We can add a (Vec, input device) to avoid this. + // But it's not necessary now and we have to consider two audio services(client, server). + crate::audio_service::set_voice_call_input_device(None, true); log::info!("#{} Connection closed: {}", self.inner.id(), reason); if lock && self.lock_after_session_end && self.keyboard { #[cfg(not(any(target_os = "android", target_os = "ios")))] From 3367c541b2bf34d76bc6e771e75f8486271623c5 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sun, 14 Jul 2024 04:08:11 +0800 Subject: [PATCH 264/335] Update playground.yml --- .github/workflows/playground.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 67fafba2567..58a7fcabbb2 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -128,6 +128,7 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | + rm -rf Cargo.lock cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" # below works for mac to make buildable on 3.13.9 # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; From 29b8875c1c6f40d1e708a9910472e37364fe1dcd Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sun, 14 Jul 2024 04:28:22 +0800 Subject: [PATCH 265/335] Update playground.yml --- .github/workflows/playground.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index 58a7fcabbb2..bff3d2a019f 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -38,6 +38,24 @@ jobs: fail-fast: false matrix: job: + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.13.9", + ref: "f6509e3fd6917aa976bad2fc684182601ebf2434", + date: "20231219" + } + - { + target: x86_64-apple-darwin, + os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel + extra-build-args: "", + arch: x86_64, + flutter: "3.10.6", + ref: "f6509e3fd6917aa976bad2fc684182601ebf2434", + date: "20231219" + } - { target: x86_64-apple-darwin, os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel From 5f31211db3229419727691ac13ead3c8657609b3 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sun, 14 Jul 2024 04:35:23 +0800 Subject: [PATCH 266/335] Update playground.yml --- .github/workflows/playground.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index bff3d2a019f..ad391131e29 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -146,7 +146,6 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | - rm -rf Cargo.lock cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" # below works for mac to make buildable on 3.13.9 # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; From bed214bd374f8830feeb3952789cd96bf28f3394 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sun, 14 Jul 2024 04:56:03 +0800 Subject: [PATCH 267/335] Update playground.yml --- .github/workflows/playground.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/playground.yml b/.github/workflows/playground.yml index ad391131e29..d788858ab4c 100644 --- a/.github/workflows/playground.yml +++ b/.github/workflows/playground.yml @@ -45,6 +45,7 @@ jobs: arch: x86_64, flutter: "3.13.9", ref: "f6509e3fd6917aa976bad2fc684182601ebf2434", + bridge: "1.80.1", date: "20231219" } - { @@ -54,6 +55,7 @@ jobs: arch: x86_64, flutter: "3.10.6", ref: "f6509e3fd6917aa976bad2fc684182601ebf2434", + bridge: "1.80.1", date: "20231219" } - { @@ -146,6 +148,7 @@ jobs: - name: Install flutter rust bridge deps shell: bash run: | + sed -i '' 's/3.1.0/2.17.0/g' flutter/pubspec.yaml; cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" # below works for mac to make buildable on 3.13.9 # pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd; From d0a54a6cc67cd5721fb9d95197cc46c18ccd5e27 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sun, 14 Jul 2024 05:47:42 +0800 Subject: [PATCH 268/335] hc --- libs/hbb_common/protos/rendezvous.proto | 8 ++ src/client.rs | 158 ++++++++++++++++++------ src/client/io_loop.rs | 7 +- src/common.rs | 3 + src/port_forward.rs | 5 +- src/rendezvous_mediator.rs | 17 +-- 6 files changed, 144 insertions(+), 54 deletions(-) diff --git a/libs/hbb_common/protos/rendezvous.proto b/libs/hbb_common/protos/rendezvous.proto index fac9aa433a6..49d737c69e1 100644 --- a/libs/hbb_common/protos/rendezvous.proto +++ b/libs/hbb_common/protos/rendezvous.proto @@ -21,6 +21,7 @@ message PunchHoleRequest { string licence_key = 3; ConnType conn_type = 4; string token = 5; + string version = 6; } message PunchHole { @@ -62,6 +63,10 @@ message RegisterPk { string old_id = 4; } +message HealthCheck { + string token = 1; +} + message RegisterPkResponse { enum Result { OK = 0; @@ -92,6 +97,7 @@ message PunchHoleResponse { bool is_local = 6; } string other_failure = 7; + int32 feedback = 8; } message ConfigUpdate { @@ -122,6 +128,7 @@ message RelayResponse { string refuse_reason = 6; string version = 7; string request_region = 8; + int32 feedback = 9; } message SoftwareUpdate { string url = 1; } @@ -190,5 +197,6 @@ message RendezvousMessage { OnlineRequest online_request = 23; OnlineResponse online_response = 24; KeyExchange key_exchange = 25; + HealthCheck hc = 26; } } diff --git a/src/client.rs b/src/client.rs index 4c3f53b0324..d0c0c6635da 100644 --- a/src/client.rs +++ b/src/client.rs @@ -22,12 +22,9 @@ use sha2::{Digest, Sha256}; use uuid::Uuid; pub use file_trait::FileManager; -#[cfg(windows)] -use hbb_common::tokio; #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::tokio::sync::mpsc::UnboundedSender; -#[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; use hbb_common::{ allow_err, @@ -42,11 +39,14 @@ use hbb_common::{ protobuf::{Message as _, MessageField}, rand, rendezvous_proto::*, - socket_client, + socket_client::{connect_tcp, connect_tcp_local, ipv4_to_ipv6}, sodiumoxide::{base64, crypto::sign}, tcp::FramedStream, timeout, - tokio::time::Duration, + tokio::{ + self, + time::{interval, Duration, Instant}, + }, AddrMangle, ResultType, Stream, }; pub use helper::*; @@ -226,7 +226,7 @@ impl Client { token: &str, conn_type: ConnType, interface: impl Interface, - ) -> ResultType<(Stream, bool, Option>)> { + ) -> ResultType<((Stream, bool, Option>), (i32, String))> { debug_assert!(peer == interface.get_id()); interface.update_direct(None); interface.update_received(false); @@ -250,25 +250,26 @@ impl Client { token: &str, conn_type: ConnType, interface: impl Interface, - ) -> ResultType<(Stream, bool, Option>)> { + ) -> ResultType<((Stream, bool, Option>), (i32, String))> { if config::is_incoming_only() { bail!("Incoming only mode"); } // to-do: remember the port for each peer, so that we can retry easier if hbb_common::is_ip_str(peer) { return Ok(( - socket_client::connect_tcp(check_port(peer, RELAY_PORT + 1), CONNECT_TIMEOUT) - .await?, - true, - None, + ( + connect_tcp(check_port(peer, RELAY_PORT + 1), CONNECT_TIMEOUT).await?, + true, + None, + ), + (0, "".to_owned()), )); } // Allow connect to {domain}:{port} if hbb_common::is_domain_port_str(peer) { return Ok(( - socket_client::connect_tcp(peer, CONNECT_TIMEOUT).await?, - true, - None, + (connect_tcp(peer, CONNECT_TIMEOUT).await?, true, None), + (0, "".to_owned()), )); } @@ -295,13 +296,13 @@ impl Client { } }; - let mut socket = socket_client::connect_tcp(&*rendezvous_server, CONNECT_TIMEOUT).await; + let mut socket = connect_tcp(&*rendezvous_server, CONNECT_TIMEOUT).await; debug_assert!(!servers.contains(&rendezvous_server)); if socket.is_err() && !servers.is_empty() { log::info!("try the other servers: {:?}", servers); for server in servers { let server = check_port(server, RENDEZVOUS_PORT); - socket = socket_client::connect_tcp(&*server, CONNECT_TIMEOUT).await; + socket = connect_tcp(&*server, CONNECT_TIMEOUT).await; if socket.is_ok() { rendezvous_server = server; break; @@ -327,6 +328,7 @@ impl Client { let mut peer_nat_type = NatType::UNKNOWN_NAT; let my_nat_type = crate::get_nat_type(100).await; let mut is_local = false; + let mut feedback = 0; for i in 1..=3 { log::info!("#{} punch attempt with {}, id: {}", i, my_addr, peer); let mut msg_out = RendezvousMessage::new(); @@ -342,9 +344,11 @@ impl Client { nat_type: nat_type.into(), licence_key: key.to_owned(), conn_type: conn_type.into(), + version: crate::VERSION.to_owned(), ..Default::default() }); socket.send(&msg_out).await?; + // below timeout should not bigger than hbbs's connection timeout. if let Some(msg_in) = crate::get_next_nonkeyexchange_msg(&mut socket, Some(i * 6000)).await { @@ -375,6 +379,7 @@ impl Client { signed_id_pk = ph.pk.into(); relay_server = ph.relay_server; peer_addr = AddrMangle::decode(&ph.socket_addr); + feedback = ph.feedback; log::info!("Hole Punched {} = {}", peer, peer_addr); break; } @@ -395,9 +400,10 @@ impl Client { my_addr.is_ipv4(), ) .await?; + feedback = rr.feedback; let pk = Self::secure_connection(peer, signed_id_pk, key, &mut conn).await?; - return Ok((conn, false, pk)); + return Ok(((conn, false, pk), (feedback, rendezvous_server))); } _ => { log::error!("Unexpected protobuf msg received: {:?}", msg_in); @@ -420,23 +426,26 @@ impl Client { format!("nat_type: {:?}", peer_nat_type) } ); - Self::connect( - my_addr, - peer_addr, - peer, - signed_id_pk, - &relay_server, - &rendezvous_server, - time_used, - peer_nat_type, - my_nat_type, - is_local, - key, - token, - conn_type, - interface, - ) - .await + Ok(( + Self::connect( + my_addr, + peer_addr, + peer, + signed_id_pk, + &relay_server, + &rendezvous_server, + time_used, + peer_nat_type, + my_nat_type, + is_local, + key, + token, + conn_type, + interface, + ) + .await?, + (feedback, rendezvous_server), + )) } /// Connect to the peer. @@ -491,8 +500,7 @@ impl Client { log::info!("peer address: {}, timeout: {}", peer, connect_timeout); let start = std::time::Instant::now(); // NOTICE: Socks5 is be used event in intranet. Which may be not a good way. - let mut conn = - socket_client::connect_tcp_local(peer, Some(local_addr), connect_timeout).await; + let mut conn = connect_tcp_local(peer, Some(local_addr), connect_timeout).await; let mut direct = !conn.is_err(); interface.update_direct(Some(direct)); if interface.is_force_relay() || conn.is_err() { @@ -622,7 +630,7 @@ impl Client { for i in 1..=3 { // use different socket due to current hbbs implementation requiring different nat address for each attempt - let mut socket = socket_client::connect_tcp(rendezvous_server, CONNECT_TIMEOUT) + let mut socket = connect_tcp(rendezvous_server, CONNECT_TIMEOUT) .await .with_context(|| "Failed to connect to rendezvous server")?; @@ -679,8 +687,8 @@ impl Client { conn_type: ConnType, ipv4: bool, ) -> ResultType { - let mut conn = socket_client::connect_tcp( - socket_client::ipv4_to_ipv6(check_port(relay_server, RELAY_PORT), ipv4), + let mut conn = connect_tcp( + ipv4_to_ipv6(check_port(relay_server, RELAY_PORT), ipv4), CONNECT_TIMEOUT, ) .await @@ -3171,3 +3179,75 @@ pub fn check_if_retry(msgtype: &str, title: &str, text: &str, retry_for_relay: b && !text.to_lowercase().contains("manually") && !text.to_lowercase().contains("not allowed"))) } + +pub async fn hc_connection( + feedback: i32, + rendezvous_server: String, + token: &str, +) -> Option> { + if feedback == 0 || rendezvous_server.is_empty() || token.is_empty() { + return None; + } + let (tx, rx) = unbounded_channel::<()>(); + let token = token.to_owned(); + tokio::spawn(async move { + allow_err!(hc_connection_(rendezvous_server, rx, token).await); + }); + Some(tx) +} + +async fn hc_connection_( + rendezvous_server: String, + mut rx: UnboundedReceiver<()>, + token: String, +) -> ResultType<()> { + let mut timer = crate::rustdesk_interval(interval(crate::TIMER_OUT)); + let mut last_recv_msg = Instant::now(); + let mut keep_alive = crate::DEFAULT_KEEP_ALIVE; + + let host = check_port(&rendezvous_server, RENDEZVOUS_PORT); + let mut conn = connect_tcp(host.clone(), CONNECT_TIMEOUT).await?; + let key = crate::get_key(true).await; + crate::secure_tcp(&mut conn, &key).await?; + let mut msg_out = RendezvousMessage::new(); + msg_out.set_hc(HealthCheck { + token, + ..Default::default() + }); + conn.send(&msg_out).await?; + loop { + tokio::select! { + res = rx.recv() => { + if res.is_none() { + log::debug!("HC connection is closed as controlling connection exits"); + break; + } + } + res = conn.next() => { + last_recv_msg = Instant::now(); + let bytes = res.ok_or_else(|| anyhow!("Rendezvous connection is reset by the peer"))??; + if bytes.is_empty() { + conn.send_bytes(bytes::Bytes::new()).await?; + continue; // heartbeat + } + let msg = RendezvousMessage::parse_from_bytes(&bytes)?; + match msg.union { + Some(rendezvous_message::Union::RegisterPkResponse(rpr)) => { + if rpr.keep_alive > 0 { + keep_alive = rpr.keep_alive * 1000; + log::info!("keep_alive: {}ms", keep_alive); + } + } + _ => {} + } + } + _ = timer.tick() => { + // https://www.emqx.com/en/blog/mqtt-keep-alive + if last_recv_msg.elapsed().as_millis() as u64 > keep_alive as u64 * 3 / 2 { + bail!("HC connection is timeout"); + } + } + } + } + Ok(()) +} diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 15a779109a6..27ecc7a9104 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -38,7 +38,7 @@ use hbb_common::{tokio::sync::Mutex as TokioMutex, ResultType}; use scrap::CodecFormat; use crate::client::{ - new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, + self, new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::clipboard::{update_clipboard, CLIPBOARD_INTERVAL}; @@ -134,7 +134,7 @@ impl Remote { ) .await { - Ok((mut peer, direct, pk)) => { + Ok(((mut peer, direct, pk), (feedback, rendezvous_server))) => { self.handler .connection_round_state .lock() @@ -173,6 +173,9 @@ impl Remote { crate::rustdesk_interval(time::interval(Duration::new(1, 0))); let mut fps_instant = Instant::now(); + let _keep_it = + client::hc_connection(feedback, rendezvous_server, token).await; + loop { tokio::select! { res = peer.next() => { diff --git a/src/common.rs b/src/common.rs index fcc65834b09..27135b56826 100644 --- a/src/common.rs +++ b/src/common.rs @@ -53,6 +53,9 @@ pub const PLATFORM_LINUX: &str = "Linux"; pub const PLATFORM_MACOS: &str = "Mac OS"; pub const PLATFORM_ANDROID: &str = "Android"; +pub const TIMER_OUT: Duration = Duration::from_secs(1); +pub const DEFAULT_KEEP_ALIVE: i32 = 60_000; + const MIN_VER_MULTI_UI_SESSION: &str = "1.2.4"; pub mod input { diff --git a/src/port_forward.rs b/src/port_forward.rs index f9d38f4c443..28ac624cd14 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -118,11 +118,14 @@ async fn connect_and_login( } else { ConnType::PORT_FORWARD }; - let (mut stream, direct, _pk) = + let ((mut stream, direct, _pk), (feedback, rendezvous_server)) = Client::start(id, key, token, conn_type, interface.clone()).await?; interface.update_direct(Some(direct)); let mut buffer = Vec::new(); let mut received = false; + + let _keep_it = hc_connection(feedback, rendezvous_server, token).await; + loop { tokio::select! { res = timeout(READ_TIMEOUT, stream.next()) => match res { diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index ceb4057c702..94fe128be04 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -21,11 +21,7 @@ use hbb_common::{ sleep, socket_client::{self, connect_tcp, is_ipv4}, tcp::FramedStream, - tokio::{ - self, select, - sync::Mutex, - time::{interval, Duration}, - }, + tokio::{self, select, sync::Mutex, time::interval}, udp::FramedSocket, AddrMangle, IntoTargetAddr, ResultType, TargetAddr, }; @@ -37,9 +33,6 @@ use crate::{ type Message = RendezvousMessage; -const TIMER_OUT: Duration = Duration::from_secs(1); -const DEFAULT_KEEP_ALIVE: i32 = 60_000; - lazy_static::lazy_static! { static ref SOLVING_PK_MISMATCH: Arc> = Default::default(); } @@ -151,10 +144,10 @@ impl RendezvousMediator { addr: addr.clone(), host: host.clone(), host_prefix: Self::get_host_prefix(&host), - keep_alive: DEFAULT_KEEP_ALIVE, + keep_alive: crate::DEFAULT_KEEP_ALIVE, }; - let mut timer = crate::rustdesk_interval(interval(TIMER_OUT)); + let mut timer = crate::rustdesk_interval(interval(crate::TIMER_OUT)); const MIN_REG_TIMEOUT: i64 = 3_000; const MAX_REG_TIMEOUT: i64 = 30_000; let mut reg_timeout = MIN_REG_TIMEOUT; @@ -337,9 +330,9 @@ impl RendezvousMediator { addr: conn.local_addr().into_target_addr()?, host: host.clone(), host_prefix: Self::get_host_prefix(&host), - keep_alive: DEFAULT_KEEP_ALIVE, + keep_alive: crate::DEFAULT_KEEP_ALIVE, }; - let mut timer = crate::rustdesk_interval(interval(TIMER_OUT)); + let mut timer = crate::rustdesk_interval(interval(crate::TIMER_OUT)); let mut last_register_sent: Option = None; let mut last_recv_msg = Instant::now(); // we won't support connecting to multiple rendzvous servers any more, so we can use a global variable here. From 3a0ece14475b045f3476429eb7222b071b3bef91 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 14 Jul 2024 15:04:35 +0800 Subject: [PATCH 269/335] mobile view mode menu (#8707) * mobile add view mode menu, disable same menus as desktop * show forbidden cursor when not view mode && no keyboard permission && not show remote cursor * hide keyboard/mouse bottom action when keyboard disabled * fix not listen keyboard permission change * mobile missing changing resolution and http proxy setting, will add if needed. Signed-off-by: 21pages --- flutter/lib/common/widgets/toolbar.dart | 12 +++ flutter/lib/mobile/pages/remote_page.dart | 104 ++++++++++++++-------- flutter/lib/models/model.dart | 4 +- flutter/lib/web/bridge.dart | 8 ++ 4 files changed, 91 insertions(+), 37 deletions(-) diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index 2c2da922ab5..bc6a4b22b90 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -650,6 +650,18 @@ Future> toolbarDisplayToggle( v.addAll(toolbarKeyboardToggles(ffi)); } + // view mode (mobile only, desktop is in keyboard menu) + if (isMobile && versionCmp(pi.version, '1.2.0') >= 0) { + v.add(TToggleMenu( + value: ffiModel.viewOnly, + onChanged: (value) async { + if (value == null) return; + await bind.sessionToggleOption( + sessionId: ffi.sessionId, value: kOptionToggleViewOnly); + ffiModel.setViewOnly(id, value); + }, + child: Text(translate('View Mode')))); + } return v; } diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index cd5743d0b2d..77d8eaff3a6 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -277,12 +277,10 @@ class _RemotePageState extends State { }); } - bool get keyboard => gFFI.ffiModel.permissions['keyboard'] != false; - Widget _bottomWidget() => _showGestureHelp ? getGestureHelp() : (_showBar && gFFI.ffiModel.pi.displays.isNotEmpty - ? getBottomAppBar(keyboard) + ? getBottomAppBar() : Offstage()); @override @@ -349,7 +347,7 @@ class _RemotePageState extends State { return Container( color: kColorCanvas, child: isWebDesktop - ? getBodyForDesktopWithListener(keyboard) + ? getBodyForDesktopWithListener() : SafeArea( child: OrientationBuilder(builder: (ctx, orientation) { @@ -381,9 +379,9 @@ class _RemotePageState extends State { } Widget getRawPointerAndKeyBody(Widget child) { - final keyboard = gFFI.ffiModel.permissions['keyboard'] != false; + final ffiModel = Provider.of(context); return RawPointerMouseRegion( - cursor: keyboard ? SystemMouseCursors.none : MouseCursor.defer, + cursor: ffiModel.keyboard ? SystemMouseCursors.none : MouseCursor.defer, inputModel: inputModel, // Disable RawKeyFocusScope before the connecting is established. // The "Delete" key on the soft keyboard may be grabbed when inputting the password dialog. @@ -396,7 +394,8 @@ class _RemotePageState extends State { ); } - Widget getBottomAppBar(bool keyboard) { + Widget getBottomAppBar() { + final ffiModel = Provider.of(context); return BottomAppBar( elevation: 10, color: MyTheme.accent, @@ -422,7 +421,7 @@ class _RemotePageState extends State { }, ) ] + - (isWebDesktop + (isWebDesktop || ffiModel.viewOnly || !ffiModel.keyboard ? [] : gFFI.ffiModel.isPeerAndroid ? [ @@ -534,19 +533,20 @@ class _RemotePageState extends State { ), ]; if (showCursorPaint) { - paints.add(CursorPaint()); + paints.add(CursorPaint(widget.id)); } return paints; }())); } - Widget getBodyForDesktopWithListener(bool keyboard) { + Widget getBodyForDesktopWithListener() { + final ffiModel = Provider.of(context); var paints = [ImagePaint()]; if (showCursorPaint) { final cursor = bind.sessionGetToggleOptionSync( sessionId: sessionId, arg: 'show-remote-cursor'); - if (keyboard || cursor) { - paints.add(CursorPaint()); + if (ffiModel.keyboard || cursor) { + paints.add(CursorPaint(widget.id)); } } return Container( @@ -949,22 +949,34 @@ class ImagePaint extends StatelessWidget { } class CursorPaint extends StatelessWidget { + late final String id; + CursorPaint(this.id); + @override Widget build(BuildContext context) { final m = Provider.of(context); final c = Provider.of(context); + final ffiModel = Provider.of(context); final adjust = gFFI.cursorModel.adjustForKeyboard(); final s = c.scale; double hotx = m.hotx; double hoty = m.hoty; - if (m.image == null) { + var image = m.image; + if (image == null) { if (preDefaultCursor.image != null) { + image = preDefaultCursor.image; hotx = preDefaultCursor.image!.width / 2; hoty = preDefaultCursor.image!.height / 2; } } - - final image = m.image ?? preDefaultCursor.image; + if (preForbiddenCursor.image != null && + !ffiModel.viewOnly && + !ffiModel.keyboard && + !ShowRemoteCursorState.find(id).value) { + image = preForbiddenCursor.image; + hotx = preForbiddenCursor.image!.width / 2; + hoty = preForbiddenCursor.image!.height / 2; + } if (image == null) { return Offstage(); } @@ -1060,22 +1072,40 @@ void showOptions( var codec = (codecRadios.isNotEmpty ? codecRadios[0].groupValue : '').obs; final radios = [ for (var e in viewStyleRadios) - Obx(() => getRadio(e.child, e.value, viewStyle.value, (v) { - e.onChanged?.call(v); - if (v != null) viewStyle.value = v; - })), + Obx(() => getRadio( + e.child, + e.value, + viewStyle.value, + e.onChanged != null + ? (v) { + e.onChanged?.call(v); + if (v != null) viewStyle.value = v; + } + : null)), const Divider(color: MyTheme.border), for (var e in imageQualityRadios) - Obx(() => getRadio(e.child, e.value, imageQuality.value, (v) { - e.onChanged?.call(v); - if (v != null) imageQuality.value = v; - })), + Obx(() => getRadio( + e.child, + e.value, + imageQuality.value, + e.onChanged != null + ? (v) { + e.onChanged?.call(v); + if (v != null) imageQuality.value = v; + } + : null)), const Divider(color: MyTheme.border), for (var e in codecRadios) - Obx(() => getRadio(e.child, e.value, codec.value, (v) { - e.onChanged?.call(v); - if (v != null) codec.value = v; - })), + Obx(() => getRadio( + e.child, + e.value, + codec.value, + e.onChanged != null + ? (v) { + e.onChanged?.call(v); + if (v != null) codec.value = v; + } + : null)), if (codecRadios.isNotEmpty) const Divider(color: MyTheme.border), ]; final rxCursorToggleValues = cursorToggles.map((e) => e.value.obs).toList(); @@ -1086,10 +1116,12 @@ void showOptions( contentPadding: EdgeInsets.zero, visualDensity: VisualDensity.compact, value: rxCursorToggleValues[e.key].value, - onChanged: (v) { - e.value.onChanged?.call(v); - if (v != null) rxCursorToggleValues[e.key].value = v; - }, + onChanged: e.value.onChanged != null + ? (v) { + e.value.onChanged?.call(v); + if (v != null) rxCursorToggleValues[e.key].value = v; + } + : null, title: e.value.child))) .toList(); @@ -1101,10 +1133,12 @@ void showOptions( contentPadding: EdgeInsets.zero, visualDensity: VisualDensity.compact, value: rxToggleValues[e.key].value, - onChanged: (v) { - e.value.onChanged?.call(v); - if (v != null) rxToggleValues[e.key].value = v; - }, + onChanged: e.value.onChanged != null + ? (v) { + e.value.onChanged?.call(v); + if (v != null) rxToggleValues[e.key].value = v; + } + : null, title: e.value.child))) .toList(); final toggles = [ diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 2346efcc6ab..b10488b2931 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -192,7 +192,7 @@ class FfiModel with ChangeNotifier { _permissions[k] = v == 'true'; }); // Only inited at remote page - if (desktopType == DesktopType.remote) { + if (parent.target?.connType == ConnType.defaultConn) { KeyboardEnabledState.find(id).value = _permissions['keyboard'] != false; } debugPrint('updatePermission: $_permissions'); @@ -1726,7 +1726,7 @@ class PredefinedCursor { _image2 = img2.decodePng(base64Decode(png)); if (_image2 != null) { // The png type of forbidden cursor image is `PngColorType.indexed`. - if (isWindows && id == kPreForbiddenCursorId) { + if (id == kPreForbiddenCursorId) { _image2 = _image2!.convert(format: img2.Format.uint8, numChannels: 4); } diff --git a/flutter/lib/web/bridge.dart b/flutter/lib/web/bridge.dart index ce541469193..d4939c804e6 100644 --- a/flutter/lib/web/bridge.dart +++ b/flutter/lib/web/bridge.dart @@ -1614,5 +1614,13 @@ class RustdeskImpl { throw UnimplementedError(); } + bool mainHasValidBotSync({dynamic hint}) { + throw UnimplementedError(); + } + + Future mainVerifyBot({required String token, dynamic hint}) { + throw UnimplementedError(); + } + void dispose() {} } From e2a7e38a397370fc0c1d78553783af0475976f34 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:05:56 +0800 Subject: [PATCH 270/335] fix: build ios (#8709) Signed-off-by: fufesou --- src/client/io_loop.rs | 12 ++++++------ src/flutter_ffi.rs | 16 ++++++++++------ src/server/audio_service.rs | 1 - 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 27ecc7a9104..1280fb80120 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -390,15 +390,15 @@ impl Remote { if self.handler.is_file_transfer() || self.handler.is_port_forward() { return None; } - // NOTE: - // The client server and --server both use the same sound input device. - // It's better to distinguish the server side and client side. - // But it' not necessary for now, because it's not a common case. - // And it is immediately known when the input device is changed. - crate::audio_service::set_voice_call_input_device(get_default_sound_input(), false); // iOS does not have this server. #[cfg(not(any(target_os = "ios")))] { + // NOTE: + // The client server and --server both use the same sound input device. + // It's better to distinguish the server side and client side. + // But it' not necessary for now, because it's not a common case. + // And it is immediately known when the input device is changed. + crate::audio_service::set_voice_call_input_device(get_default_sound_input(), false); // Create a channel to receive error or closed message let (tx, rx) = std::sync::mpsc::channel(); let (tx_audio_data, mut rx_audio_data) = diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 3eccfc84d9c..a7eda9d9ba7 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1319,16 +1319,18 @@ pub fn cm_close_voice_call(id: i32) { crate::ui_cm_interface::close_voice_call(id); } -pub fn set_voice_call_input_device(is_cm: bool, device: String) { - if is_cm { - let _ = crate::ipc::set_config("voice-call-input", device); +pub fn set_voice_call_input_device(_is_cm: bool, _device: String) { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if _is_cm { + let _ = crate::ipc::set_config("voice-call-input", _device); } else { - crate::audio_service::set_voice_call_input_device(Some(device), true); + crate::audio_service::set_voice_call_input_device(Some(_device), true); } } -pub fn get_voice_call_input_device(is_cm: bool) -> String { - if is_cm { +pub fn get_voice_call_input_device(_is_cm: bool) -> String { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if _is_cm { match crate::ipc::get_config("voice-call-input") { Ok(Some(device)) => device, _ => "".to_owned(), @@ -1336,6 +1338,8 @@ pub fn get_voice_call_input_device(is_cm: bool) -> String { } else { crate::audio_service::get_voice_call_input_device().unwrap_or_default() } + #[cfg(any(target_os = "android", target_os = "ios"))] + "".to_owned() } pub fn main_get_last_remote_id() -> String { diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index 504c0927974..b717c66442f 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -163,7 +163,6 @@ mod cpal_impl { } fn run_restart(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> { - println!("REMOVE ME ========================= run_restart"); state.reset(); sp.snapshot(|_sps: ServiceSwap<_>| Ok(()))?; match &state.stream { From 8512c2b2b0457dfb4227287681f8fffd47470457 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 15 Jul 2024 10:47:19 +0800 Subject: [PATCH 271/335] fix lan peers batch deletion (#8715) `peers.map((p) async {})` is not sync and peers are loaded before deletion. ``` let mut peers = config::LanPeers::load().peers; peers.retain(|x| x.id != id); ``` Signed-off-by: 21pages --- flutter/lib/common/widgets/peer_tab_page.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index c89a88de516..ae5de507d53 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -399,9 +399,9 @@ class _PeerTabPageState extends State final peers = model.selectedPeers; switch (model.currentTab) { case 0: - peers.map((p) async { + for (var p in peers) { await bind.mainRemovePeer(id: p.id); - }).toList(); + } await bind.mainLoadRecentPeers(); break; case 1: @@ -413,9 +413,9 @@ class _PeerTabPageState extends State await bind.mainLoadFavPeers(); break; case 2: - peers.map((p) async { + for (var p in peers) { await bind.mainRemoveDiscovered(id: p.id); - }).toList(); + } await bind.mainLoadLanPeers(); break; case 3: From 3f11d9cdb65eecd692a959109ab5df9eeef354b9 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 15 Jul 2024 10:49:09 +0800 Subject: [PATCH 272/335] remove Instant sub (#8714) which cause crash when connect to windows just startup Signed-off-by: 21pages --- src/lan.rs | 6 +++--- src/server/input_service.rs | 12 +++++++++--- src/virtual_display_manager.rs | 10 ++++++---- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/lan.rs b/src/lan.rs index bd337ba9745..666e73c7c3d 100644 --- a/src/lan.rs +++ b/src/lan.rs @@ -283,7 +283,7 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) }); let mut response_set = HashSet::new(); - let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); + let mut last_write_time: Option = None; loop { tokio::select! { data = rx.recv() => match data { @@ -297,11 +297,11 @@ async fn handle_received_peers(mut rx: UnboundedReceiver) } } peers.insert(0, peer); - if last_write_time.elapsed().as_millis() > 300 { + if last_write_time.map(|t| t.elapsed().as_millis() > 300).unwrap_or(true) { config::LanPeers::store(&peers); #[cfg(feature = "flutter")] crate::flutter_ffi::main_load_lan_peers(); - last_write_time = Instant::now(); + last_write_time = Some(Instant::now()); } } None => { diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 245c86244e9..30a885663c5 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -312,7 +312,7 @@ pub fn new_window_focus() -> GenericService { fn update_last_cursor_pos(x: i32, y: i32) { let mut lock = LATEST_SYS_CURSOR_POS.lock().unwrap(); if lock.1 .0 != x || lock.1 .1 != y { - (lock.0, lock.1) = (Instant::now(), (x, y)) + (lock.0, lock.1) = (Some(Instant::now()), (x, y)) } } @@ -411,7 +411,7 @@ lazy_static::lazy_static! { }; static ref KEYS_DOWN: Arc>> = Default::default(); static ref LATEST_PEER_INPUT_CURSOR: Arc> = Default::default(); - static ref LATEST_SYS_CURSOR_POS: Arc> = Arc::new(Mutex::new((Instant::now().sub(MOUSE_MOVE_PROTECTION_TIMEOUT), (INVALID_CURSOR_POS, INVALID_CURSOR_POS)))); + static ref LATEST_SYS_CURSOR_POS: Arc, (i32, i32))>> = Arc::new(Mutex::new((None, (INVALID_CURSOR_POS, INVALID_CURSOR_POS)))); } static EXITING: AtomicBool = AtomicBool::new(false); @@ -808,7 +808,13 @@ fn active_mouse_(conn: i32) -> bool { true /* this method is buggy (not working on macOS, making fast moving mouse event discarded here) and added latency (this is blocking way, must do in async way), so we disable it for now // out of time protection - if LATEST_SYS_CURSOR_POS.lock().unwrap().0.elapsed() > MOUSE_MOVE_PROTECTION_TIMEOUT { + if LATEST_SYS_CURSOR_POS + .lock() + .unwrap() + .0 + .map(|t| t.elapsed() > MOUSE_MOVE_PROTECTION_TIMEOUT) + .unwrap_or(true) + { return true; } diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index f0312196738..37bfeb33427 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -428,7 +428,7 @@ pub mod amyuni_idd { lazy_static::lazy_static! { static ref LOCK: Arc> = Default::default(); - static ref LAST_PLUG_IN_HEADLESS_TIME: Arc> = Arc::new(Mutex::new(Instant::now().sub(Duration::from_secs(100)))); + static ref LAST_PLUG_IN_HEADLESS_TIME: Arc>> = Arc::new(Mutex::new(None)); } fn get_deviceinstaller64_work_dir() -> ResultType>> { @@ -573,10 +573,12 @@ pub mod amyuni_idd { pub fn plug_in_headless() -> ResultType<()> { let mut tm = LAST_PLUG_IN_HEADLESS_TIME.lock().unwrap(); - if tm.elapsed() < Duration::from_secs(3) { - bail!("Plugging in too frequently."); + if let Some(tm) = &mut *tm { + if tm.elapsed() < Duration::from_secs(3) { + bail!("Plugging in too frequently."); + } } - *tm = Instant::now(); + *tm = Some(Instant::now()); drop(tm); let mut is_async = false; From eec879a8010adb2934b2393551ae959aea0cbc79 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Mon, 15 Jul 2024 10:50:54 +0800 Subject: [PATCH 273/335] refact: macos, hide&show on leaving view (#8712) Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 4 ++++ flutter/pubspec.lock | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index f108f74445c..802ef7c4585 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -416,6 +416,10 @@ class _RemotePageState extends State } void leaveView(PointerExitEvent evt) { + if (isMacOS) { + DesktopMultiWindow.hideShow(); + } + if (_ffi.ffiModel.keyboard) { _ffi.inputModel.tryMoveEdgeOnExit(evt.position); } diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 57ea92482a3..1c743423edc 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "60773827434eefe6d01eefa814dca9a032b970b3" + resolved-ref: "53fee59855c44f35381428c9fb7c7678f700d11d" url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From 09466680d306627219ae890326ab5a20a8bb32ac Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 15 Jul 2024 18:53:14 +0800 Subject: [PATCH 274/335] mobile virtual display, resolution menu, proxy setting (#8717) 1. Merge code of mobile and desktop virtual display menu. 2. Mobile add seperate resolution menu, only support changing resolutions. 3. Android add proxy setting Signed-off-by: 21pages --- flutter/lib/common/widgets/toolbar.dart | 104 +++++++++++ flutter/lib/consts.dart | 2 + .../desktop/pages/desktop_setting_page.dart | 87 +++++----- .../lib/desktop/widgets/remote_toolbar.dart | 161 +----------------- flutter/lib/mobile/pages/remote_page.dart | 98 ++++++++++- flutter/lib/mobile/pages/settings_page.dart | 14 +- src/ui_interface.rs | 48 +++--- 7 files changed, 297 insertions(+), 217 deletions(-) diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index bc6a4b22b90..f2b3db7ad87 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -6,6 +6,7 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/common/widgets/dialog.dart'; import 'package:flutter_hbb/consts.dart'; +import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; @@ -802,3 +803,106 @@ List toolbarKeyboardToggles(FFI ffi) { } return v; } + +bool showVirtualDisplayMenu(FFI ffi) { + if (ffi.ffiModel.pi.platform != kPeerPlatformWindows) { + return false; + } + if (!ffi.ffiModel.pi.isInstalled) { + return false; + } + if (ffi.ffiModel.pi.isRustDeskIdd || ffi.ffiModel.pi.isAmyuniIdd) { + return true; + } + return false; +} + +List getVirtualDisplayMenuChildren( + FFI ffi, String id, VoidCallback? clickCallBack) { + if (!showVirtualDisplayMenu(ffi)) { + return []; + } + final pi = ffi.ffiModel.pi; + final privacyModeState = PrivacyModeState.find(id); + if (pi.isRustDeskIdd) { + final virtualDisplays = ffi.ffiModel.pi.RustDeskVirtualDisplays; + final children = []; + for (var i = 0; i < kMaxVirtualDisplayCount; i++) { + children.add(Obx(() => CkbMenuButton( + value: virtualDisplays.contains(i + 1), + onChanged: privacyModeState.isNotEmpty + ? null + : (bool? value) async { + if (value != null) { + bind.sessionToggleVirtualDisplay( + sessionId: ffi.sessionId, index: i + 1, on: value); + clickCallBack?.call(); + } + }, + child: Text('${translate('Virtual display')} ${i + 1}'), + ffi: ffi, + ))); + } + children.add(Divider()); + children.add(Obx(() => MenuButton( + onPressed: privacyModeState.isNotEmpty + ? null + : () { + bind.sessionToggleVirtualDisplay( + sessionId: ffi.sessionId, + index: kAllVirtualDisplay, + on: false); + clickCallBack?.call(); + }, + ffi: ffi, + child: Text(translate('Plug out all')), + ))); + return children; + } + if (pi.isAmyuniIdd) { + final count = ffi.ffiModel.pi.amyuniVirtualDisplayCount; + final children = [ + Obx(() => Row( + children: [ + TextButton( + onPressed: privacyModeState.isNotEmpty || count == 0 + ? null + : () { + bind.sessionToggleVirtualDisplay( + sessionId: ffi.sessionId, index: 0, on: false); + clickCallBack?.call(); + }, + child: Icon(Icons.remove), + ), + Text(count.toString()), + TextButton( + onPressed: privacyModeState.isNotEmpty || count == 4 + ? null + : () { + bind.sessionToggleVirtualDisplay( + sessionId: ffi.sessionId, index: 0, on: true); + clickCallBack?.call(); + }, + child: Icon(Icons.add), + ), + ], + )), + Divider(), + Obx(() => MenuButton( + onPressed: privacyModeState.isNotEmpty || count == 0 + ? null + : () { + bind.sessionToggleVirtualDisplay( + sessionId: ffi.sessionId, + index: kAllVirtualDisplay, + on: false); + clickCallBack?.call(); + }, + ffi: ffi, + child: Text(translate('Plug out all')), + )), + ]; + return children; + } + return []; +} diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index ff3017634ff..b54ff15dd05 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -136,6 +136,8 @@ const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper"; const String kOptionStopService = "stop-service"; const String kOptionDirectxCapture = "enable-directx-capture"; const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification"; +const String kOptionHideServerSetting = "hide-server-settings"; +const String kOptionHideProxySetting = "hide-proxy-settings"; const String kOptionToggleViewOnly = "view-only"; diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 505a98f17db..a8ed3f082f3 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1274,9 +1274,9 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { bool enabled = !locked; final scrollController = ScrollController(); final hideServer = - bind.mainGetLocalOption(key: "hide-server-settings") == 'Y'; + bind.mainGetLocalOption(key: kOptionHideServerSetting) == 'Y'; final hideProxy = - bind.mainGetLocalOption(key: "hide-proxy-settings") == 'Y'; + bind.mainGetLocalOption(key: kOptionHideProxySetting) == 'Y'; return DesktopScrollWrapper( scrollController: scrollController, child: ListView( @@ -2328,35 +2328,40 @@ void changeSocks5Proxy() async { children: [ Row( children: [ - ConstrainedBox( - constraints: const BoxConstraints(minWidth: 140), - child: Align( - alignment: Alignment.centerRight, - child: Row( - children: [ - Text( - translate('Server'), - ).marginOnly(right: 4), - Tooltip( - waitDuration: Duration(milliseconds: 0), - message: translate("default_proxy_tip"), - child: Icon( - Icons.help_outline_outlined, - size: 16, - color: Theme.of(context) - .textTheme - .titleLarge - ?.color - ?.withOpacity(0.5), + if (!isMobile) + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 140), + child: Align( + alignment: Alignment.centerRight, + child: Row( + children: [ + Text( + translate('Server'), + ).marginOnly(right: 4), + Tooltip( + waitDuration: Duration(milliseconds: 0), + message: translate("default_proxy_tip"), + child: Icon( + Icons.help_outline_outlined, + size: 16, + color: Theme.of(context) + .textTheme + .titleLarge + ?.color + ?.withOpacity(0.5), + ), ), - ), - ], - )).marginOnly(right: 10), - ), + ], + )).marginOnly(right: 10), + ), Expanded( child: TextField( decoration: InputDecoration( errorText: proxyMsg.isNotEmpty ? proxyMsg : null, + labelText: isMobile ? translate('Server') : null, + helperText: + isMobile ? translate("default_proxy_tip") : null, + helperMaxLines: isMobile ? 3 : null, ), controller: proxyController, autofocus: true, @@ -2367,15 +2372,19 @@ void changeSocks5Proxy() async { ).marginOnly(bottom: 8), Row( children: [ - ConstrainedBox( - constraints: const BoxConstraints(minWidth: 140), - child: Text( - '${translate("Username")}:', - textAlign: TextAlign.right, - ).marginOnly(right: 10)), + if (!isMobile) + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 140), + child: Text( + '${translate("Username")}:', + textAlign: TextAlign.right, + ).marginOnly(right: 10)), Expanded( child: TextField( controller: userController, + decoration: InputDecoration( + labelText: isMobile ? translate('Username') : null, + ), enabled: !isOptFixed, ), ), @@ -2383,16 +2392,18 @@ void changeSocks5Proxy() async { ).marginOnly(bottom: 8), Row( children: [ - ConstrainedBox( - constraints: const BoxConstraints(minWidth: 140), - child: Text( - '${translate("Password")}:', - textAlign: TextAlign.right, - ).marginOnly(right: 10)), + if (!isMobile) + ConstrainedBox( + constraints: const BoxConstraints(minWidth: 140), + child: Text( + '${translate("Password")}:', + textAlign: TextAlign.right, + ).marginOnly(right: 10)), Expanded( child: Obx(() => TextField( obscureText: obscure.value, decoration: InputDecoration( + labelText: isMobile ? translate('Password') : null, suffixIcon: IconButton( onPressed: () => obscure.value = !obscure.value, icon: Icon(obscure.value diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index d90e24f5395..9db65e072e6 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1052,15 +1052,11 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, screenAdjustor: _screenAdjustor, ), - if (pi.isRustDeskIdd) - _RustDeskVirtualDisplayMenu( - id: widget.id, - ffi: widget.ffi, - ), - if (pi.isAmyuniIdd) - _AmyuniVirtualDisplayMenu( - id: widget.id, + if (showVirtualDisplayMenu(ffi)) + _SubmenuButton( ffi: widget.ffi, + menuChildren: getVirtualDisplayMenuChildren(ffi, id, null), + child: Text(translate("Virtual display")), ), cursorToggles(), Divider(), @@ -1559,155 +1555,6 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } } -class _RustDeskVirtualDisplayMenu extends StatefulWidget { - final String id; - final FFI ffi; - - _RustDeskVirtualDisplayMenu({ - Key? key, - required this.id, - required this.ffi, - }) : super(key: key); - - @override - State<_RustDeskVirtualDisplayMenu> createState() => - _RustDeskVirtualDisplayMenuState(); -} - -class _RustDeskVirtualDisplayMenuState - extends State<_RustDeskVirtualDisplayMenu> { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - if (widget.ffi.ffiModel.pi.platform != kPeerPlatformWindows) { - return Offstage(); - } - if (!widget.ffi.ffiModel.pi.isInstalled) { - return Offstage(); - } - - final virtualDisplays = widget.ffi.ffiModel.pi.RustDeskVirtualDisplays; - final privacyModeState = PrivacyModeState.find(widget.id); - - final children = []; - for (var i = 0; i < kMaxVirtualDisplayCount; i++) { - children.add(Obx(() => CkbMenuButton( - value: virtualDisplays.contains(i + 1), - onChanged: privacyModeState.isNotEmpty - ? null - : (bool? value) async { - if (value != null) { - bind.sessionToggleVirtualDisplay( - sessionId: widget.ffi.sessionId, - index: i + 1, - on: value); - } - }, - child: Text('${translate('Virtual display')} ${i + 1}'), - ffi: widget.ffi, - ))); - } - children.add(Divider()); - children.add(Obx(() => MenuButton( - onPressed: privacyModeState.isNotEmpty - ? null - : () { - bind.sessionToggleVirtualDisplay( - sessionId: widget.ffi.sessionId, - index: kAllVirtualDisplay, - on: false); - }, - ffi: widget.ffi, - child: Text(translate('Plug out all')), - ))); - return _SubmenuButton( - ffi: widget.ffi, - menuChildren: children, - child: Text(translate("Virtual display")), - ); - } -} - -class _AmyuniVirtualDisplayMenu extends StatefulWidget { - final String id; - final FFI ffi; - - _AmyuniVirtualDisplayMenu({ - Key? key, - required this.id, - required this.ffi, - }) : super(key: key); - - @override - State<_AmyuniVirtualDisplayMenu> createState() => - _AmiyuniVirtualDisplayMenuState(); -} - -class _AmiyuniVirtualDisplayMenuState extends State<_AmyuniVirtualDisplayMenu> { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - if (widget.ffi.ffiModel.pi.platform != kPeerPlatformWindows) { - return Offstage(); - } - if (!widget.ffi.ffiModel.pi.isInstalled) { - return Offstage(); - } - - final count = widget.ffi.ffiModel.pi.amyuniVirtualDisplayCount; - final privacyModeState = PrivacyModeState.find(widget.id); - - final children = [ - Obx(() => Row( - children: [ - TextButton( - onPressed: privacyModeState.isNotEmpty || count == 0 - ? null - : () => bind.sessionToggleVirtualDisplay( - sessionId: widget.ffi.sessionId, index: 0, on: false), - child: Icon(Icons.remove), - ), - Text(count.toString()), - TextButton( - onPressed: privacyModeState.isNotEmpty || count == 4 - ? null - : () => bind.sessionToggleVirtualDisplay( - sessionId: widget.ffi.sessionId, index: 0, on: true), - child: Icon(Icons.add), - ), - ], - )), - Divider(), - Obx(() => MenuButton( - onPressed: privacyModeState.isNotEmpty || count == 0 - ? null - : () { - bind.sessionToggleVirtualDisplay( - sessionId: widget.ffi.sessionId, - index: kAllVirtualDisplay, - on: false); - }, - ffi: widget.ffi, - child: Text(translate('Plug out all')), - )), - ]; - - return _SubmenuButton( - ffi: widget.ffi, - menuChildren: children, - child: Text(translate("Virtual display")), - ); - } -} - class _KeyboardMenu extends StatelessWidget { final String id; final FFI ffi; diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 77d8eaff3a6..30598085f40 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -1158,14 +1158,110 @@ void showOptions( ); } + var popupDialogMenus = List.empty(growable: true); + final resolution = getResolutionMenu(gFFI, id); + if (resolution != null) { + popupDialogMenus.add(ListTile( + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + title: resolution.child, + onTap: () { + close(); + resolution.onPressed(); + }, + )); + } + final virtualDisplayMenu = getVirtualDisplayMenu(gFFI, id); + if (virtualDisplayMenu != null) { + popupDialogMenus.add(ListTile( + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + title: virtualDisplayMenu.child, + onTap: () { + close(); + virtualDisplayMenu.onPressed(); + }, + )); + } + if (popupDialogMenus.isNotEmpty) { + popupDialogMenus.add(const Divider(color: MyTheme.border)); + } + return CustomAlertDialog( content: Column( mainAxisSize: MainAxisSize.min, - children: displays + radios + toggles + [privacyModeWidget]), + children: displays + + radios + + popupDialogMenus + + toggles + + [privacyModeWidget]), ); }, clickMaskDismiss: true, backDismiss: true); } +TTextMenu? getVirtualDisplayMenu(FFI ffi, String id) { + if (!showVirtualDisplayMenu(ffi)) { + return null; + } + return TTextMenu( + child: Text(translate("Virtual display")), + onPressed: () { + ffi.dialogManager.show((setState, close, context) { + final children = getVirtualDisplayMenuChildren(ffi, id, close); + return CustomAlertDialog( + title: Text(translate('Virtual display')), + content: Column( + mainAxisSize: MainAxisSize.min, + children: children, + ), + ); + }, clickMaskDismiss: true, backDismiss: true); + }, + ); +} + +TTextMenu? getResolutionMenu(FFI ffi, String id) { + final ffiModel = ffi.ffiModel; + final pi = ffiModel.pi; + final resolutions = pi.resolutions; + final display = pi.tryGetDisplayIfNotAllDisplay(display: pi.currentDisplay); + + final visible = + ffiModel.keyboard && (resolutions.length > 1) && display != null; + if (!visible) return null; + + return TTextMenu( + child: Text(translate("Resolution")), + onPressed: () { + ffi.dialogManager.show((setState, close, context) { + final children = resolutions + .map((e) => getRadio( + Text('${e.width}x${e.height}'), + '${e.width}x${e.height}', + '${display.width}x${display.height}', + (value) { + close(); + bind.sessionChangeResolution( + sessionId: ffi.sessionId, + display: pi.currentDisplay, + width: e.width, + height: e.height, + ); + }, + )) + .toList(); + return CustomAlertDialog( + title: Text(translate('Resolution')), + content: Column( + mainAxisSize: MainAxisSize.min, + children: children, + ), + ); + }, clickMaskDismiss: true, backDismiss: true); + }, + ); +} + void sendPrompt(bool isMac, String key) { final old = isMac ? gFFI.inputModel.command : gFFI.inputModel.ctrl; if (isMac) { diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 909ca0285e5..2fc77634848 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common/widgets/setting_widgets.dart'; +import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:settings_ui/settings_ui.dart'; @@ -83,6 +84,8 @@ class _SettingsState extends State with WidgetsBindingObserver { var _fingerprint = ""; var _buildDate = ""; var _autoDisconnectTimeout = ""; + var _hideServer = false; + var _hideProxy = false; @override void initState() { @@ -109,6 +112,8 @@ class _SettingsState extends State with WidgetsBindingObserver { bind.mainGetOptionSync(key: kOptionAllowAutoDisconnect)); _autoDisconnectTimeout = bind.mainGetOptionSync(key: kOptionAutoDisconnectTimeout); + _hideServer = bind.mainGetLocalOption(key: kOptionHideServerSetting) == 'Y'; + _hideProxy = bind.mainGetLocalOption(key: kOptionHideProxySetting) == 'Y'; () async { var update = false; @@ -553,13 +558,20 @@ class _SettingsState extends State with WidgetsBindingObserver { ], ), SettingsSection(title: Text(translate("Settings")), tiles: [ - if (!disabledSettings) + if (!disabledSettings && !_hideServer) SettingsTile( title: Text(translate('ID/Relay Server')), leading: Icon(Icons.cloud), onPressed: (context) { showServerSettings(gFFI.dialogManager); }), + if (!isIOS && !_hideProxy) + SettingsTile( + title: Text(translate('Socks5/Http(s) Proxy')), + leading: Icon(Icons.network_ping), + onPressed: (context) { + changeSocks5Proxy(); + }), SettingsTile( title: Text(translate('Language')), leading: Icon(Icons.translate), diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 5f85d975860..ead6b4168e8 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -415,33 +415,44 @@ pub fn install_path() -> String { #[inline] pub fn get_socks() -> Vec { - #[cfg(any(target_os = "android", target_os = "ios"))] - return Vec::new(); #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - let s = ipc::get_socks(); - match s { - None => Vec::new(), - Some(s) => { - let mut v = Vec::new(); - v.push(s.proxy); - v.push(s.username); - v.push(s.password); - v - } + let s = ipc::get_socks(); + #[cfg(target_os = "android")] + let s = Config::get_socks(); + #[cfg(target_os = "ios")] + let s: Option = None; + match s { + None => Vec::new(), + Some(s) => { + let mut v = Vec::new(); + v.push(s.proxy); + v.push(s.username); + v.push(s.password); + v } } } #[inline] -#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn set_socks(proxy: String, username: String, password: String) { - ipc::set_socks(config::Socks5Server { + let socks = config::Socks5Server { proxy, username, password, - }) - .ok(); + }; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + ipc::set_socks(socks).ok(); + #[cfg(target_os = "android")] + { + if socks.proxy.is_empty() { + Config::set_socks(None); + } else { + Config::set_socks(Some(socks)); + } + crate::common::test_nat_type(); + crate::RendezvousMediator::restart(); + log::info!("socks updated"); + } } #[inline] @@ -454,9 +465,6 @@ pub fn get_proxy_status() -> bool { return false; } -#[cfg(any(target_os = "android", target_os = "ios"))] -pub fn set_socks(_: String, _: String, _: String) {} - #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_installed() -> bool { From 0143eaf60176f420b7dbc376acda6ebebff940df Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 16 Jul 2024 15:10:24 +0800 Subject: [PATCH 275/335] install ffmpeg lib with vcpkg (#8724) * use vcpkg to install ffmpeg and build sdk from source, so no prebuild lib in hwcodec. * link ffmpeg in rustdesk directly, ffmpeg can be used as basic library. * for windows developers, `VCPKG_DEFAULT_HOST_TRIPLET` env need to be set to `x64-windows-static` during installation. Signed-off-by: 21pages --- .github/workflows/ci.yml | 6 +- .github/workflows/flutter-build.yml | 6 +- Cargo.lock | 4 +- libs/scrap/build.rs | 45 ++ res/vcpkg/ffmpeg/0002-fix-msvc-link.patch | 11 + .../ffmpeg/0003-fix-windowsinclude.patch | 13 + res/vcpkg/ffmpeg/0005-fix-nasm.patch | 55 ++ .../ffmpeg/0012-Fix-ssl-110-detection.patch | 14 + res/vcpkg/ffmpeg/0013-define-WINVER.patch | 15 + ...dd-query_timeout-option-for-h264-hev.patch | 71 ++ ...-amfenc-reconfig-when-bitrate-change.patch | 71 ++ ...-release-7.0-s-qsvenc-update_bitrate.patch | 95 +++ .../ffmpeg/5.1/0004-amf-colorspace.patch | 172 +++++ ...1-android-mediacodec-encode-align-64.patch | 40 + res/vcpkg/ffmpeg/FindFFMPEG.cmake.in | 161 +++++ res/vcpkg/ffmpeg/build.sh.in | 152 ++++ res/vcpkg/ffmpeg/portfile.cmake | 682 ++++++++++++++++++ res/vcpkg/ffmpeg/usage | 6 + res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake | 47 ++ res/vcpkg/ffmpeg/vcpkg.json | 47 ++ vcpkg.json | 39 +- 21 files changed, 1743 insertions(+), 9 deletions(-) create mode 100644 res/vcpkg/ffmpeg/0002-fix-msvc-link.patch create mode 100644 res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch create mode 100644 res/vcpkg/ffmpeg/0005-fix-nasm.patch create mode 100644 res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch create mode 100644 res/vcpkg/ffmpeg/0013-define-WINVER.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch create mode 100644 res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch create mode 100644 res/vcpkg/ffmpeg/FindFFMPEG.cmake.in create mode 100644 res/vcpkg/ffmpeg/build.sh.in create mode 100644 res/vcpkg/ffmpeg/portfile.cmake create mode 100644 res/vcpkg/ffmpeg/usage create mode 100644 res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake create mode 100644 res/vcpkg/ffmpeg/vcpkg.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41c83370528..90f312968e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,9 @@ env: # MIN_SUPPORTED_RUST_VERSION: "1.46.0" # CICD_INTERMEDIATES_DIR: "_cicd-intermediates" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - # vcpkg version: 2023.10.19 + # vcpkg version: 2024.06.15 # for multiarch gcc compatibility - VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50" + VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" on: workflow_dispatch: @@ -112,6 +112,8 @@ jobs: libgstreamer-plugins-base1.0-dev \ libgtk-3-dev \ libpulse-dev \ + libva-dev \ + libvdpau-dev \ libxcb-randr0-dev \ libxcb-shape0-dev \ libxcb-xfixes0-dev \ diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index e7a438997cd..ff406766bcc 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -115,6 +115,8 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + env: + VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }} run: | $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash @@ -255,6 +257,8 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + env: + VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }} run: | $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash @@ -427,7 +431,7 @@ jobs: - name: Install dependencies run: | - brew install nasm + brew install nasm yasm - name: Checkout source code uses: actions/checkout@v3 - name: Install flutter diff --git a/Cargo.lock b/Cargo.lock index 7455d04dd27..27706797eab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3087,8 +3087,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.4.19" -source = "git+https://github.com/21pages/hwcodec#852de98224605db7a2495cf276776dabaf8aa139" +version = "0.5.0" +source = "git+https://github.com/21pages/hwcodec#470d18826dc334b964c51acf0c6ac3bcf56f56ad" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index 1612a6b5bfe..d84d24a58d6 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -188,6 +188,50 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex); } +// If you have problems installing ffmpeg, you can disable hwcodec feature and disable this function. +fn ffmpeg() { + // ffmpeg + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let static_libs = vec!["avcodec", "avutil", "avformat"]; + static_libs.iter().for_each(|lib| { + find_package(lib); + }); + if target_os == "windows" { + println!("cargo:rustc-link-lib=static=libmfx"); + } + + // os + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let dyn_libs: Vec<&str> = if target_os == "windows" { + ["User32", "bcrypt", "ole32", "advapi32"].to_vec() + } else if target_os == "linux" { + let mut v = ["va", "va-drm", "va-x11", "vdpau", "X11", "stdc++"].to_vec(); + if target_arch == "x86_64" { + v.push("z"); + } + v + } else if target_os == "macos" || target_os == "ios" { + ["c++", "m"].to_vec() + } else if target_os == "android" { + ["z", "m", "android", "atomic"].to_vec() + } else { + panic!("unsupported os"); + }; + dyn_libs + .iter() + .map(|lib| println!("cargo:rustc-link-lib={}", lib)) + .count(); + + if target_os == "macos" || target_os == "ios" { + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=CoreVideo"); + println!("cargo:rustc-link-lib=framework=CoreMedia"); + println!("cargo:rustc-link-lib=framework=VideoToolbox"); + println!("cargo:rustc-link-lib=framework=AVFoundation"); + } +} + fn main() { // note: all link symbol names in x86 (32-bit) are prefixed wth "_". // run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc, @@ -204,6 +248,7 @@ fn main() { gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*"); gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*"); gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*"); + ffmpeg(); // there is problem with cfg(target_os) in build.rs, so use our workaround let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); diff --git a/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch b/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch new file mode 100644 index 00000000000..c9aa7e75237 --- /dev/null +++ b/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch @@ -0,0 +1,11 @@ +diff --git a/configure b/configure +--- a/configure ++++ b/configure +@@ -6162,6 +6162,7 @@ EOF + test -n "$extern_prefix" && append X86ASMFLAGS "-DPREFIX" + case "$objformat" in + elf*) enabled debug && append X86ASMFLAGS $x86asm_debug ;; ++ win*) enabled debug && append X86ASMFLAGS "-g" ;; + esac + + enabled avx512 && check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0" diff --git a/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch b/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch new file mode 100644 index 00000000000..8b2e22b476f --- /dev/null +++ b/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch @@ -0,0 +1,13 @@ +diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c +--- a/fftools/cmdutils.c ++++ b/fftools/cmdutils.c +@@ -51,6 +51,8 @@ + #include "fopen_utf8.h" + #include "opt_common.h" + #ifdef _WIN32 ++#define _WIN32_WINNT 0x0502 ++#define WIN32_LEAN_AND_MEAN + #include + #include "compat/w32dlfcn.h" + #endif + diff --git a/res/vcpkg/ffmpeg/0005-fix-nasm.patch b/res/vcpkg/ffmpeg/0005-fix-nasm.patch new file mode 100644 index 00000000000..9308e714a6b --- /dev/null +++ b/res/vcpkg/ffmpeg/0005-fix-nasm.patch @@ -0,0 +1,55 @@ +diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile +--- a/libavcodec/x86/Makefile ++++ b/libavcodec/x86/Makefile +@@ -158,6 +158,8 @@ X86ASM-OBJS-$(CONFIG_ALAC_DECODER) += x86/alacdsp.o + X86ASM-OBJS-$(CONFIG_APNG_DECODER) += x86/pngdsp.o + X86ASM-OBJS-$(CONFIG_CAVS_DECODER) += x86/cavsidct.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_CFHD_ENCODER) += x86/cfhdencdsp.o ++endif + X86ASM-OBJS-$(CONFIG_CFHD_DECODER) += x86/cfhddsp.o + X86ASM-OBJS-$(CONFIG_DCA_DECODER) += x86/dcadsp.o x86/synth_filter.o + X86ASM-OBJS-$(CONFIG_DIRAC_DECODER) += x86/diracdsp.o \ +@@ -175,15 +177,21 @@ x86/hevc_sao_10bit.o + X86ASM-OBJS-$(CONFIG_JPEG2000_DECODER) += x86/jpeg2000dsp.o + X86ASM-OBJS-$(CONFIG_LSCR_DECODER) += x86/pngdsp.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp.o ++endif + X86ASM-OBJS-$(CONFIG_MPEG4_DECODER) += x86/xvididct.o + X86ASM-OBJS-$(CONFIG_PNG_DECODER) += x86/pngdsp.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_PRORES_DECODER) += x86/proresdsp.o + X86ASM-OBJS-$(CONFIG_PRORES_LGPL_DECODER) += x86/proresdsp.o ++endif + X86ASM-OBJS-$(CONFIG_RV40_DECODER) += x86/rv40dsp.o + X86ASM-OBJS-$(CONFIG_SBC_ENCODER) += x86/sbcdsp.o + X86ASM-OBJS-$(CONFIG_SVQ1_ENCODER) += x86/svq1enc.o + X86ASM-OBJS-$(CONFIG_TAK_DECODER) += x86/takdsp.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_TRUEHD_DECODER) += x86/mlpdsp.o ++endif + X86ASM-OBJS-$(CONFIG_TTA_DECODER) += x86/ttadsp.o + X86ASM-OBJS-$(CONFIG_TTA_ENCODER) += x86/ttaencdsp.o + X86ASM-OBJS-$(CONFIG_UTVIDEO_DECODER) += x86/utvideodsp.o +diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile +--- a/libavfilter/x86/Makefile ++++ b/libavfilter/x86/Makefile +@@ -44,6 +44,8 @@ + X86ASM-OBJS-$(CONFIG_AFIR_FILTER) += x86/af_afir.o + X86ASM-OBJS-$(CONFIG_ANLMDN_FILTER) += x86/af_anlmdn.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_ATADENOISE_FILTER) += x86/vf_atadenoise.o ++endif + X86ASM-OBJS-$(CONFIG_BLEND_FILTER) += x86/vf_blend.o + X86ASM-OBJS-$(CONFIG_BWDIF_FILTER) += x86/vf_bwdif.o + X86ASM-OBJS-$(CONFIG_COLORSPACE_FILTER) += x86/colorspacedsp.o +@@ -62,6 +62,8 @@ X86ASM-OBJS-$(CONFIG_LUT3D_FILTER) += x86/vf_lut3d.o + X86ASM-OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += x86/vf_maskedclamp.o + X86ASM-OBJS-$(CONFIG_MASKEDMERGE_FILTER) += x86/vf_maskedmerge.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_NLMEANS_FILTER) += x86/vf_nlmeans.o ++endif + X86ASM-OBJS-$(CONFIG_OVERLAY_FILTER) += x86/vf_overlay.o + X86ASM-OBJS-$(CONFIG_PP7_FILTER) += x86/vf_pp7.o + X86ASM-OBJS-$(CONFIG_PSNR_FILTER) += x86/vf_psnr.o diff --git a/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch b/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch new file mode 100644 index 00000000000..b2e5501a13a --- /dev/null +++ b/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch @@ -0,0 +1,14 @@ +diff --git a/configure b/configure +index 2be953f7e7..e075949ffc 100755 +--- a/configure ++++ b/configure +@@ -6497,6 +6497,7 @@ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0 + { enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } || + { enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } || + check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl || + check_pkg_config openssl openssl openssl/ssl.h SSL_library_init || ++ check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto $pthreads_extralibs -ldl || + check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto || + check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto || + check_lib openssl openssl/ssl.h SSL_library_init -lssl32 -leay32 || + diff --git a/res/vcpkg/ffmpeg/0013-define-WINVER.patch b/res/vcpkg/ffmpeg/0013-define-WINVER.patch new file mode 100644 index 00000000000..295a738e74f --- /dev/null +++ b/res/vcpkg/ffmpeg/0013-define-WINVER.patch @@ -0,0 +1,15 @@ +diff --color -Naur src_old/libavcodec/mf_utils.c src/libavcodec/mf_utils.c +--- src_old/libavcodec/mf_utils.c 2020-07-11 05:26:17.000000000 +0700 ++++ src/libavcodec/mf_utils.c 2020-11-13 12:55:57.226976400 +0700 +@@ -22,6 +22,11 @@ + #define _WIN32_WINNT 0x0602 + #endif + ++#if !defined(WINVER) || WINVER < 0x0602 ++#undef WINVER ++#define WINVER 0x0602 ++#endif ++ + #include "mf_utils.h" + #include "libavutil/pixdesc.h" + diff --git a/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch b/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch new file mode 100644 index 00000000000..245a470d39f --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch @@ -0,0 +1,71 @@ +From f0b694749b38b2cfd94df4eed10e667342c234e5 Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sat, 24 Feb 2024 15:33:24 +0800 +Subject: [PATCH 1/2] avcodec/amfenc: add query_timeout option for h264/hevc + +Signed-off-by: 21pages +--- + libavcodec/amfenc.h | 1 + + libavcodec/amfenc_h264.c | 4 ++++ + libavcodec/amfenc_hevc.c | 4 ++++ + 3 files changed, 9 insertions(+) + +diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h +index 1ab98d2f78..e92120ea39 100644 +--- a/libavcodec/amfenc.h ++++ b/libavcodec/amfenc.h +@@ -87,6 +87,7 @@ typedef struct AmfContext { + int quality; + int b_frame_delta_qp; + int ref_b_frame_delta_qp; ++ int64_t query_timeout; + + // Dynamic options, can be set after Init() call + +diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c +index efb04589f6..f55dbc80f0 100644 +--- a/libavcodec/amfenc_h264.c ++++ b/libavcodec/amfenc_h264.c +@@ -121,6 +121,7 @@ static const AVOption options[] = { + { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, ++ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE }, + + { NULL } + }; +@@ -155,6 +156,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) + + AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMERATE, framerate); + ++ if (ctx->query_timeout >= 0) ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, ctx->query_timeout); ++ + switch (avctx->profile) { + case FF_PROFILE_H264_BASELINE: + profile = AMF_VIDEO_ENCODER_PROFILE_BASELINE; +diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c +index 8ab9330730..7a40bcad31 100644 +--- a/libavcodec/amfenc_hevc.c ++++ b/libavcodec/amfenc_hevc.c +@@ -89,6 +89,7 @@ static const AVOption options[] = { + { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, + + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, ++ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE }, + { NULL } + }; + +@@ -122,6 +123,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + + AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate); + ++ if (ctx->query_timeout >= 0) ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, ctx->query_timeout); ++ + switch (avctx->profile) { + case FF_PROFILE_HEVC_MAIN: + profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch b/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch new file mode 100644 index 00000000000..13b055ef289 --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch @@ -0,0 +1,71 @@ +From 4d0d20d96ad458cfec0444b9be0182ca6085ee0c Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sat, 24 Feb 2024 16:02:44 +0800 +Subject: [PATCH 2/2] libavcodec/amfenc: reconfig when bitrate change + +Signed-off-by: 21pages +--- + libavcodec/amfenc.c | 20 ++++++++++++++++++++ + libavcodec/amfenc.h | 1 + + 2 files changed, 21 insertions(+) + +diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c +index a033e1220e..3eab01a903 100644 +--- a/libavcodec/amfenc.c ++++ b/libavcodec/amfenc.c +@@ -222,6 +222,7 @@ static int amf_init_context(AVCodecContext *avctx) + + ctx->hwsurfaces_in_queue = 0; + ctx->hwsurfaces_in_queue_max = 16; ++ ctx->av_bitrate = avctx->bit_rate; + + // configure AMF logger + // the return of these functions indicates old state and do not affect behaviour +@@ -575,6 +576,23 @@ static void amf_release_buffer_with_frame_ref(AMFBuffer *frame_ref_storage_buffe + frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer); + } + ++static int reconfig_encoder(AVCodecContext *avctx) ++{ ++ AmfContext *ctx = avctx->priv_data; ++ AMF_RESULT res = AMF_OK; ++ ++ if (ctx->av_bitrate != avctx->bit_rate) { ++ av_log(ctx, AV_LOG_INFO, "change bitrate from %d to %d\n", ctx->av_bitrate, avctx->bit_rate); ++ ctx->av_bitrate = avctx->bit_rate; ++ if (avctx->codec->id == AV_CODEC_ID_H264) { ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_TARGET_BITRATE, avctx->bit_rate); ++ } else if (avctx->codec->id == AV_CODEC_ID_HEVC) { ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, avctx->bit_rate); ++ } ++ } ++ return 0; ++} ++ + int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) + { + AmfContext *ctx = avctx->priv_data; +@@ -586,6 +604,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) + AVFrame *frame = ctx->delayed_frame; + int block_and_wait; + ++ reconfig_encoder(avctx); ++ + if (!ctx->encoder) + return AVERROR(EINVAL); + +diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h +index e92120ea39..31172645f2 100644 +--- a/libavcodec/amfenc.h ++++ b/libavcodec/amfenc.h +@@ -107,6 +107,7 @@ typedef struct AmfContext { + int me_half_pel; + int me_quarter_pel; + int aud; ++ int64_t av_bitrate; + + // HEVC - specific options + +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch b/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch new file mode 100644 index 00000000000..475fb627f3e --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch @@ -0,0 +1,95 @@ +From afe89a70f6bc7ebd0a6a0a31101801b88cbd60ee Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sun, 5 May 2024 12:45:23 +0800 +Subject: [PATCH] use release/7.0's update_bitrate + +Signed-off-by: 21pages +--- + libavcodec/qsvenc.c | 39 +++++++++++++++++++++++++++++++++++++++ + libavcodec/qsvenc.h | 6 ++++++ + 2 files changed, 45 insertions(+) + +diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c +index 2382c2f5f7..9b34f37eb3 100644 +--- a/libavcodec/qsvenc.c ++++ b/libavcodec/qsvenc.c +@@ -714,6 +714,11 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) + brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes), + initial_delay_in_kilobytes) + 0x10000) / 0x10000; + ++ q->old_rc_buffer_size = avctx->rc_buffer_size; ++ q->old_rc_initial_buffer_occupancy = avctx->rc_initial_buffer_occupancy; ++ q->old_bit_rate = avctx->bit_rate; ++ q->old_rc_max_rate = avctx->rc_max_rate; ++ + switch (q->param.mfx.RateControlMethod) { + case MFX_RATECONTROL_CBR: + case MFX_RATECONTROL_VBR: +@@ -1657,6 +1662,39 @@ static int update_qp(AVCodecContext *avctx, QSVEncContext *q, + return updated; + } + ++static int update_bitrate(AVCodecContext *avctx, QSVEncContext *q) ++{ ++ int updated = 0; ++ int target_bitrate_kbps, max_bitrate_kbps, brc_param_multiplier; ++ int buffer_size_in_kilobytes, initial_delay_in_kilobytes; ++ ++ UPDATE_PARAM(q->old_rc_buffer_size, avctx->rc_buffer_size); ++ UPDATE_PARAM(q->old_rc_initial_buffer_occupancy, avctx->rc_initial_buffer_occupancy); ++ UPDATE_PARAM(q->old_bit_rate, avctx->bit_rate); ++ UPDATE_PARAM(q->old_rc_max_rate, avctx->rc_max_rate); ++ if (!updated) ++ return 0; ++ ++ buffer_size_in_kilobytes = avctx->rc_buffer_size / 8000; ++ initial_delay_in_kilobytes = avctx->rc_initial_buffer_occupancy / 8000; ++ target_bitrate_kbps = avctx->bit_rate / 1000; ++ max_bitrate_kbps = avctx->rc_max_rate / 1000; ++ brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes), ++ initial_delay_in_kilobytes) + 0x10000) / 0x10000; ++ ++ q->param.mfx.BufferSizeInKB = buffer_size_in_kilobytes / brc_param_multiplier; ++ q->param.mfx.InitialDelayInKB = initial_delay_in_kilobytes / brc_param_multiplier; ++ q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier; ++ q->param.mfx.MaxKbps = max_bitrate_kbps / brc_param_multiplier; ++ q->param.mfx.BRCParamMultiplier = brc_param_multiplier; ++ av_log(avctx, AV_LOG_VERBOSE, ++ "Reset BufferSizeInKB: %d; InitialDelayInKB: %d; " ++ "TargetKbps: %d; MaxKbps: %d; BRCParamMultiplier: %d\n", ++ q->param.mfx.BufferSizeInKB, q->param.mfx.InitialDelayInKB, ++ q->param.mfx.TargetKbps, q->param.mfx.MaxKbps, q->param.mfx.BRCParamMultiplier); ++ return updated; ++} ++ + static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, + const AVFrame *frame) + { +@@ -1666,6 +1704,7 @@ static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, + return 0; + + needReset = update_qp(avctx, q, frame); ++ needReset |= update_bitrate(avctx, q); + if (!needReset) + return 0; + +diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h +index b754ac4b56..5745533165 100644 +--- a/libavcodec/qsvenc.h ++++ b/libavcodec/qsvenc.h +@@ -224,6 +224,12 @@ typedef struct QSVEncContext { + int min_qp_p; + int max_qp_b; + int min_qp_b; ++ ++ // These are used for bitrate control reset ++ int old_bit_rate; ++ int old_rc_buffer_size; ++ int old_rc_initial_buffer_occupancy; ++ int old_rc_max_rate; + } QSVEncContext; + + int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q); +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch b/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch new file mode 100644 index 00000000000..d21d3eb1f4a --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch @@ -0,0 +1,172 @@ +From 7c29a6936e7b7a3a3a0bcc88894f2b739bdae9cf Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Thu, 11 Jul 2024 16:24:27 +0800 +Subject: [PATCH] amf colorspace + +Signed-off-by: 21pages +--- + libavcodec/amfenc.h | 1 + + libavcodec/amfenc_h264.c | 45 ++++++++++++++++++++++++++++++++++ + libavcodec/amfenc_hevc.c | 52 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 98 insertions(+) + +diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h +index 31172645f2..493e01603d 100644 +--- a/libavcodec/amfenc.h ++++ b/libavcodec/amfenc.h +@@ -23,6 +23,7 @@ + + #include + #include ++#include + + #include "libavutil/fifo.h" + +diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c +index f55dbc80f0..a916b35f9c 100644 +--- a/libavcodec/amfenc_h264.c ++++ b/libavcodec/amfenc_h264.c +@@ -139,6 +139,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) + AMFRate framerate; + AMFSize framesize = AMFConstructSize(avctx->width, avctx->height); + int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0; ++ amf_int64 color_depth; ++ amf_int64 color_profile; ++ enum AVPixelFormat pix_fmt; + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); +@@ -199,11 +202,53 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) + AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_ASPECT_RATIO, ratio); + } + ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; + /// Color Range (Partial/TV/MPEG or Full/PC/JPEG) + if (avctx->color_range == AVCOL_RANGE_JPEG) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 1); ++ /// Color Space for Full (JPEG) Range ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; ++ break; ++ } ++ } else { ++ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 0); ++ /// Color Space for Limited (MPEG) range ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; ++ break; ++ } ++ } ++ /// Color Depth ++ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format ++ : avctx->pix_fmt; ++ color_depth = AMF_COLOR_BIT_DEPTH_8; ++ if (pix_fmt == AV_PIX_FMT_P010) { ++ color_depth = AMF_COLOR_BIT_DEPTH_10; + } + ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH, color_depth); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, color_profile); ++ /// Color Transfer Characteristics (AMF matches ISO/IEC) ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); ++ /// Color Primaries (AMF matches ISO/IEC) ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); + // autodetect rate control method + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN) { + if (ctx->qp_i != -1 || ctx->qp_p != -1 || ctx->qp_b != -1) { +diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c +index 7a40bcad31..e7979d9aeb 100644 +--- a/libavcodec/amfenc_hevc.c ++++ b/libavcodec/amfenc_hevc.c +@@ -106,6 +106,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + AMFRate framerate; + AMFSize framesize = AMFConstructSize(avctx->width, avctx->height); + int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0; ++ amf_int64 color_depth; ++ amf_int64 color_profile; ++ enum AVPixelFormat pix_fmt; + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); +@@ -130,6 +133,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + case FF_PROFILE_HEVC_MAIN: + profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; + break; ++ case FF_PROFILE_HEVC_MAIN_10: ++ profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10; ++ break; + default: + break; + } +@@ -158,6 +164,52 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO, ratio); + } + ++ // Color Metadata ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; ++ if (avctx->color_range == AVCOL_RANGE_JPEG) { ++ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, 1); ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; ++ break; ++ } ++ } else { ++ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, 0); ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; ++ break; ++ } ++ } ++ /// Color Depth ++ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format ++ : avctx->pix_fmt; ++ color_depth = AMF_COLOR_BIT_DEPTH_8; ++ if (pix_fmt == AV_PIX_FMT_P010) { ++ color_depth = AMF_COLOR_BIT_DEPTH_10; ++ } ++ ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, color_depth); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE, color_profile); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); ++ ++ ++ + // Picture control properties + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, ctx->gops_per_idr); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, avctx->gop_size); +-- +2.39.3 (Apple Git-145) + diff --git a/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch b/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch new file mode 100644 index 00000000000..d46c54af6da --- /dev/null +++ b/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch @@ -0,0 +1,40 @@ +From be3d9d8092720bbe4239212648d2e9c4ffd7f40c Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Wed, 22 May 2024 17:09:28 +0800 +Subject: [PATCH] android mediacodec encode align 64 + +Signed-off-by: 21pages +--- + libavcodec/mediacodecenc.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c +index 984014f1b1..8dcd3dcd64 100644 +--- a/libavcodec/mediacodecenc.c ++++ b/libavcodec/mediacodecenc.c +@@ -200,16 +200,17 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) + ff_AMediaFormat_setString(format, "mime", codec_mime); + // Workaround the alignment requirement of mediacodec. We can't do it + // silently for AV_PIX_FMT_MEDIACODEC. ++ const int align = 64; + if (avctx->pix_fmt != AV_PIX_FMT_MEDIACODEC) { +- s->width = FFALIGN(avctx->width, 16); +- s->height = FFALIGN(avctx->height, 16); ++ s->width = FFALIGN(avctx->width, align); ++ s->height = FFALIGN(avctx->height, align); + } else { + s->width = avctx->width; + s->height = avctx->height; +- if (s->width % 16 || s->height % 16) ++ if (s->width % align || s->height % align) + av_log(avctx, AV_LOG_WARNING, +- "Video size %dx%d isn't align to 16, it may have device compatibility issue\n", +- s->width, s->height); ++ "Video size %dx%d isn't align to %d, it may have device compatibility issue\n", ++ s->width, s->height, align); + } + ff_AMediaFormat_setInt32(format, "width", s->width); + ff_AMediaFormat_setInt32(format, "height", s->height); +-- +2.34.1 + diff --git a/res/vcpkg/ffmpeg/FindFFMPEG.cmake.in b/res/vcpkg/ffmpeg/FindFFMPEG.cmake.in new file mode 100644 index 00000000000..8a7237fc434 --- /dev/null +++ b/res/vcpkg/ffmpeg/FindFFMPEG.cmake.in @@ -0,0 +1,161 @@ +# Distributed under the OSI-approved BSD 3-Clause License. +# +#.rst: +# FindFFMPEG +# -------- +# +# Find the FFPMEG libraries +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# The following variables will be defined: +# +# ``FFMPEG_FOUND`` +# True if FFMPEG found on the local system +# +# ``FFMPEG_INCLUDE_DIRS`` +# Location of FFMPEG header files +# +# ``FFMPEG_LIBRARY_DIRS`` +# Location of FFMPEG libraries +# +# ``FFMPEG_LIBRARIES`` +# List of the FFMPEG libraries found +# +# + +include(FindPackageHandleStandardArgs) +include(SelectLibraryConfigurations) +include(CMakeFindDependencyMacro) + +if(NOT FFMPEG_FOUND) + +# Compute the installation path relative to this file. +get_filename_component(SEARCH_PATH "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(SEARCH_PATH "${SEARCH_PATH}" PATH) +get_filename_component(SEARCH_PATH "${SEARCH_PATH}" PATH) +if(SEARCH_PATH STREQUAL "/") + set(SEARCH_PATH "") +endif() + +set(FFMPEG_VERSION "@FFMPEG_VERSION@") + +function(append_dependencies out) + cmake_parse_arguments(PARSE_ARGV 1 "arg" "DEBUG" "NAMES" "") + if(${arg_DEBUG}) + set(config DEBUG) + set(path "${CURRENT_INSTALLED_DIR}/debug/lib/") + else() + set(config RELEASE) + set(path "${CURRENT_INSTALLED_DIR}/lib/") + endif() + foreach(lib_name ${arg_NAMES}) + if("${lib_name}" STREQUAL "-pthread") + list(APPEND ${out} "-pthread") + elseif("${lib_name}" STREQUAL "-pthreads") + list(APPEND ${out} "-pthreads") + elseif("${lib_name}" STREQUAL "gcc") + list(APPEND ${out} "-lgcc") + elseif("${lib_name}" STREQUAL "gcc_s") + list(APPEND ${out} "-lgcc_s") + elseif("${lib_name}" STREQUAL "stdc++") + list(APPEND ${out} "-lstdc++") + elseif("${lib_name}" STREQUAL "atomic") + list(APPEND ${out} "-latomic") + else() + # first look in ${path} specifically to ensure we find the right release/debug variant + find_library(FFMPEG_DEPENDENCY_${lib_name}_${config} NAMES "${lib_name}" PATHS "${path}" NO_DEFAULT_PATH) + # if not found there, must be a system dependency, so look elsewhere + find_library(FFMPEG_DEPENDENCY_${lib_name}_${config} NAMES "${lib_name}" REQUIRED) + list(APPEND ${out} "${FFMPEG_DEPENDENCY_${lib_name}_${config}}") + endif() + endforeach() + set("${out}" "${${out}}" PARENT_SCOPE) +endfunction() + +macro(FFMPEG_FIND varname shortname headername) + if(NOT FFMPEG_${varname}_INCLUDE_DIRS) + find_path(FFMPEG_${varname}_INCLUDE_DIRS NAMES lib${shortname}/${headername} ${headername} PATHS ${SEARCH_PATH}/include NO_DEFAULT_PATH) + endif() + if(NOT FFMPEG_${varname}_LIBRARY) + find_library(FFMPEG_${varname}_LIBRARY_RELEASE NAMES ${shortname} PATHS ${SEARCH_PATH}/lib/ NO_DEFAULT_PATH) + find_library(FFMPEG_${varname}_LIBRARY_DEBUG NAMES ${shortname}d ${shortname} PATHS ${SEARCH_PATH}/debug/lib/ NO_DEFAULT_PATH) + get_filename_component(FFMPEG_${varname}_LIBRARY_RELEASE_DIR ${FFMPEG_${varname}_LIBRARY_RELEASE} DIRECTORY) + get_filename_component(FFMPEG_${varname}_LIBRARY_DEBUG_DIR ${FFMPEG_${varname}_LIBRARY_DEBUG} DIRECTORY) + select_library_configurations(FFMPEG_${varname}) + set(FFMPEG_${varname}_LIBRARY ${FFMPEG_${varname}_LIBRARY} CACHE STRING "") + endif() + if (FFMPEG_${varname}_LIBRARY AND FFMPEG_${varname}_INCLUDE_DIRS) + set(FFMPEG_${varname}_FOUND TRUE BOOL) + list(APPEND FFMPEG_INCLUDE_DIRS ${FFMPEG_${varname}_INCLUDE_DIRS}) + list(APPEND FFMPEG_LIBRARIES ${FFMPEG_${varname}_LIBRARY}) + list(APPEND FFMPEG_LIBRARY_DIRS ${FFMPEG_${varname}_LIBRARY_RELEASE_DIR} ${FFMPEG_${varname}_LIBRARY_DEBUG_DIR}) + endif() +endmacro(FFMPEG_FIND) + +if(@ENABLE_AVDEVICE@) + FFMPEG_FIND(libavdevice avdevice avdevice.h) +endif() +if(@ENABLE_AVFILTER@) + FFMPEG_FIND(libavfilter avfilter avfilter.h) +endif() +if(@ENABLE_AVFORMAT@) + FFMPEG_FIND(libavformat avformat avformat.h) +endif() +if(@ENABLE_AVCODEC@) + FFMPEG_FIND(libavcodec avcodec avcodec.h) +endif() +if(@ENABLE_POSTPROC@) + FFMPEG_FIND(libpostproc postproc postprocess.h) +endif() +if(@ENABLE_SWRESAMPLE@) + FFMPEG_FIND(libswresample swresample swresample.h) +endif() +if(@ENABLE_SWSCALE@) + FFMPEG_FIND(libswscale swscale swscale.h) +endif() +FFMPEG_FIND(libavutil avutil avutil.h) + +if (FFMPEG_libavutil_FOUND) + list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES FFMPEG_LIBRARY_DIRS) + set(FFMPEG_libavutil_VERSION "@LIBAVUTIL_VERSION@" CACHE STRING "") + + if(FFMPEG_libavcodec_FOUND) + set(FFMPEG_libavcodec_VERSION "@LIBAVCODEC_VERSION@" CACHE STRING "") + endif() + if(FFMPEG_libavdevice_FOUND) + set(FFMPEG_libavdevice_VERSION "@LIBAVDEVICE_VERSION@" CACHE STRING "") + endif() + if(FFMPEG_libavfilter_FOUND) + set(FFMPEG_libavfilter_VERSION "@LIBAVFILTER_VERSION@" CACHE STRING "") + endif() + if(FFMPEG_libavformat_FOUND) + set(FFMPEG_libavformat_VERSION "@LIBAVFORMAT_VERSION@" CACHE STRING "") + endif() + if(FFMPEG_libswresample_FOUND) + set(FFMPEG_libswresample_VERSION "@LIBSWRESAMPLE_VERSION@" CACHE STRING "") + endif() + if(FFMPEG_libswscale_FOUND) + set(FFMPEG_libswscale_VERSION "@LIBSWSCALE_VERSION@" CACHE STRING "") + endif() + + append_dependencies(FFMPEG_DEPS_LIBRARY_RELEASE NAMES "@FFMPEG_DEPENDENCIES_RELEASE@") + append_dependencies(FFMPEG_DEPS_LIBRARY_DEBUG NAMES "@FFMPEG_DEPENDENCIES_DEBUG@" DEBUG) + if(FFMPEG_DEPS_LIBRARY_RELEASE OR FFMPEG_DEPS_LIBRARY_DEBUG) + select_library_configurations(FFMPEG_DEPS) + list(APPEND FFMPEG_LIBRARIES ${FFMPEG_DEPS_LIBRARY}) + endif() + + set(FFMPEG_LIBRARY ${FFMPEG_LIBRARIES}) + + set(FFMPEG_FOUND TRUE CACHE BOOL "") + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "") + set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "") + set(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBRARY_DIRS} CACHE STRING "") +endif() + +find_package_handle_standard_args(FFMPEG REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_LIBRARY_DIRS FFMPEG_INCLUDE_DIRS) + +endif() diff --git a/res/vcpkg/ffmpeg/build.sh.in b/res/vcpkg/ffmpeg/build.sh.in new file mode 100644 index 00000000000..462737587b7 --- /dev/null +++ b/res/vcpkg/ffmpeg/build.sh.in @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +set -e + +export PATH="$PATH:/usr/bin" + +command -v cygpath >/dev/null && have_cygpath=1 + +cygpath() { + if [ -n "$have_cygpath" ]; then + command cygpath "$@" + else + eval _p='$'$# + printf '%s\n' "$_p" + fi +} + +move_binary() { + SOURCE=$1 + TARGET=$2 + BINARY=$3 + + # run lipo over the command to check whether it really + # is a binary that we need to merge architectures + lipo $SOURCE/$BINARY -info &> /dev/null || return 0 + + # get the directory name the file is in + DIRNAME=$(dirname $BINARY) + + # ensure the directory to move the binary to exists + mkdir -p $TARGET/$DIRNAME + + # now finally move the binary + mv $SOURCE/$BINARY $TARGET/$BINARY +} + +move_binaries() { + SOURCE=$1 + TARGET=$2 + + [ ! -d $SOURCE ] && return 0 + pushd $SOURCE + + for BINARY in $(find . -type f); do + move_binary $SOURCE $TARGET $BINARY + done + + popd +} + +merge_binaries() { + TARGET=$1 + SOURCE=$2 + + shift + shift + + pushd $SOURCE/$1 + BINARIES=$(find . -type f) + popd + + for BINARY in $BINARIES; do + COMMAND="lipo -create -output $TARGET/$BINARY" + + for ARCH in $@; do + COMMAND="$COMMAND -arch $ARCH $SOURCE/$ARCH/$BINARY" + done + + $($COMMAND) + done +} + +export PKG_CONFIG_PATH="$(cygpath -p "${PKG_CONFIG_PATH}")" + +# Export HTTP(S)_PROXY as http(s)_proxy: +[ -n "$HTTP_PROXY" ] && export http_proxy="$HTTP_PROXY" +[ -n "$HTTPS_PROXY" ] && export https_proxy="$HTTPS_PROXY" + +PATH_TO_BUILD_DIR=$( cygpath "@BUILD_DIR@") +PATH_TO_SRC_DIR=$( cygpath "@SOURCE_PATH@") +PATH_TO_PACKAGE_DIR=$(cygpath "@INST_PREFIX@") + +JOBS=@VCPKG_CONCURRENCY@ + +OSX_ARCHS="@OSX_ARCHS@" +OSX_ARCH_COUNT=0@OSX_ARCH_COUNT@ + +# Default to hardware concurrency if unset. +: ${JOBS:=$(nproc)} + +# Disable asm and x86asm on all android targets because they trigger build failures: +# arm64 Android build fails with 'relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol ff_cos_32; recompile with -fPIC' +# x86 Android build fails with 'error: inline assembly requires more registers than available'. +# x64 Android build fails with 'relocation R_X86_64_PC32 cannot be used against symbol ff_h264_cabac_tables; recompile with -fPIC' +if [ "@VCPKG_CMAKE_SYSTEM_NAME@" = "Android" ]; then + OPTIONS_arm=" --disable-asm --disable-x86asm" + OPTIONS_arm64=" --disable-asm --disable-x86asm" + OPTIONS_x86=" --disable-asm --disable-x86asm" + OPTIONS_x86_64="${OPTIONS_x86}" +else + OPTIONS_arm=" --disable-asm --disable-x86asm" + OPTIONS_arm64=" --enable-asm --disable-x86asm" + OPTIONS_x86=" --enable-asm --enable-x86asm" + OPTIONS_x86_64="${OPTIONS_x86}" +fi + +build_ffmpeg() { + # extract build architecture + BUILD_ARCH=$1 + shift + + echo "BUILD_ARCH=${BUILD_ARCH}" + + # get architecture-specific options + OPTION_VARIABLE="OPTIONS_${BUILD_ARCH}" + echo "OPTION_VARIABLE=${OPTION_VARIABLE}" + + echo "=== CONFIGURING ===" + + sh "$PATH_TO_SRC_DIR/configure" "--prefix=$PATH_TO_PACKAGE_DIR" @CONFIGURE_OPTIONS@ --arch=${BUILD_ARCH} ${!OPTION_VARIABLE} $@ + + echo "=== BUILDING ===" + + make -j${JOBS} V=1 + + echo "=== INSTALLING ===" + + make install +} + +cd "$PATH_TO_BUILD_DIR" + +if [ $OSX_ARCH_COUNT -gt 0 ]; then + for ARCH in $OSX_ARCHS; do + echo "=== CLEANING FOR $ARCH ===" + + make clean && make distclean + + build_ffmpeg $ARCH --extra-cflags=-arch --extra-cflags=$ARCH --extra-ldflags=-arch --extra-ldflags=$ARCH + + echo "=== COLLECTING BINARIES FOR $ARCH ===" + + move_binaries $PATH_TO_PACKAGE_DIR/lib $PATH_TO_BUILD_DIR/stage/$ARCH/lib + move_binaries $PATH_TO_PACKAGE_DIR/bin $PATH_TO_BUILD_DIR/stage/$ARCH/bin + done + + echo "=== MERGING ARCHITECTURES ===" + + merge_binaries $PATH_TO_PACKAGE_DIR $PATH_TO_BUILD_DIR/stage $OSX_ARCHS +else + build_ffmpeg @BUILD_ARCH@ +fi diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake new file mode 100644 index 00000000000..54b1aca7923 --- /dev/null +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -0,0 +1,682 @@ +if(VCPKG_TARGET_IS_WINDOWS OR VCPKG_TARGET_IS_LINUX) + set(FF_VERSION "n5.1.5") + set(FF_SHA512 "a933f18e53207ccc277b42c9a68db00f31cefec555e6d5d7c57db3409023b2c38fd93ebe2ccfcd17ba2397adb912e93f2388241ca970b7d8bd005ccfe86d5679") +else() + set(FF_VERSION "n7.0.1") + set(FF_SHA512 "1212ebcb78fdaa103b0304373d374e41bf1fe680e1fa4ce0f60624857491c26b4dda004c490c3ef32d4a0e10f42ae6b54546f9f318e2dcfbaa116117f687bc88") +endif() + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO ffmpeg/ffmpeg + REF "${FF_VERSION}" + SHA512 "${FF_SHA512}" + HEAD_REF master + PATCHES + 0002-fix-msvc-link.patch # upstreamed in future version + 0003-fix-windowsinclude.patch + 0005-fix-nasm.patch # upstreamed in future version + 0012-Fix-ssl-110-detection.patch + 0013-define-WINVER.patch +) + +if(VCPKG_TARGET_IS_WINDOWS OR VCPKG_TARGET_IS_LINUX) + vcpkg_apply_patches( + SOURCE_PATH ${SOURCE_PATH} + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch + ${CMAKE_CURRENT_LIST_DIR}/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch + ${CMAKE_CURRENT_LIST_DIR}/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch + ${CMAKE_CURRENT_LIST_DIR}/5.1/0004-amf-colorspace.patch + ) +elseif(VCPKG_TARGET_IS_ANDROID) + vcpkg_apply_patches( + SOURCE_PATH ${SOURCE_PATH} + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/7.0/0001-android-mediacodec-encode-align-64.patch + ) +endif() + +if(SOURCE_PATH MATCHES " ") + message(FATAL_ERROR "Error: ffmpeg will not build with spaces in the path. Please use a directory with no spaces") +endif() + +if(NOT VCPKG_TARGET_ARCHITECTURE STREQUAL "wasm32") + vcpkg_find_acquire_program(NASM) + get_filename_component(NASM_EXE_PATH "${NASM}" DIRECTORY) + vcpkg_add_to_path("${NASM_EXE_PATH}") +endif() + +set(OPTIONS "\ +--disable-shared \ +--enable-static \ +--enable-pic \ +--disable-everything \ +--disable-programs \ +--disable-doc \ +--disable-htmlpages \ +--disable-manpages \ +--disable-podpages \ +--disable-txtpages \ +--disable-network \ +--disable-appkit \ +--disable-coreimage \ +--disable-metal \ +--disable-sdl2 \ +--disable-securetransport \ +--disable-vulkan \ +--disable-audiotoolbox \ +--disable-v4l2-m2m \ +--disable-debug \ +--disable-valgrind-backtrace \ +--disable-large-tests \ +--disable-avdevice \ +--enable-avcodec \ +--enable-avformat \ +--disable-avfilter \ +--disable-swresample \ +--disable-swscale \ +--disable-postproc \ +--enable-decoder=h264 \ +--enable-decoder=hevc \ +--enable-parser=h264 \ +--enable-parser=hevc \ +--enable-bsf=h264_mp4toannexb \ +--enable-bsf=hevc_mp4toannexb \ +--enable-bsf=h264_metadata \ +--enable-bsf=hevc_metadata \ +--enable-muxer=mp4 \ +--enable-protocol=file \ +") + +if(VCPKG_HOST_IS_WINDOWS) + vcpkg_acquire_msys(MSYS_ROOT PACKAGES automake1.16) + set(SHELL "${MSYS_ROOT}/usr/bin/bash.exe") + vcpkg_add_to_path("${MSYS_ROOT}/usr/share/automake-1.16") + string(APPEND OPTIONS " --pkg-config=${CURRENT_HOST_INSTALLED_DIR}/tools/pkgconf/pkgconf${VCPKG_HOST_EXECUTABLE_SUFFIX}") +else() + find_program(SHELL bash) +endif() + +if(VCPKG_TARGET_IS_LINUX) + string(APPEND OPTIONS "\ +--target-os=linux \ +--enable-pthreads \ +--enable-cuda \ +--enable-cuda_llvm \ +--enable-ffnvcodec \ +--enable-encoder=h264_nvenc \ +--enable-encoder=hevc_nvenc \ +--enable-hwaccel=h264_nvdec \ +--enable-hwaccel=hevc_nvdec \ +--enable-amf \ +--enable-encoder=h264_amf \ +--enable-encoder=hevc_amf \ +--enable-hwaccel=h264_vaapi \ +--enable-hwaccel=hevc_vaapi \ +--enable-encoder=h264_vaapi \ +--enable-encoder=hevc_vaapi \ +") +elseif(VCPKG_TARGET_IS_WINDOWS) + string(APPEND OPTIONS "\ +--target-os=win32 \ +--toolchain=msvc \ +--enable-gpl \ +--enable-d3d11va \ +--enable-cuda \ +--enable-ffnvcodec \ +--enable-hwaccel=h264_nvdec \ +--enable-hwaccel=hevc_nvdec \ +--enable-hwaccel=h264_d3d11va \ +--enable-hwaccel=hevc_d3d11va \ +--enable-hwaccel=h264_d3d11va2 \ +--enable-hwaccel=hevc_d3d11va2 \ +--enable-amf \ +--enable-encoder=h264_amf \ +--enable-encoder=hevc_amf \ +--enable-encoder=h264_nvenc \ +--enable-encoder=hevc_nvenc \ +--enable-libmfx \ +--enable-encoder=h264_qsv \ +--enable-encoder=hevc_qsv \ +") + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") + set(LIB_MACHINE_ARG /machine:x86) + string(APPEND OPTIONS " --arch=i686 --enable-cross-compile") + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(LIB_MACHINE_ARG /machine:x64) + string(APPEND OPTIONS " --arch=x86_64") + else() + message(FATAL_ERROR "Unsupported target architecture") + endif() +elseif(VCPKG_TARGET_IS_OSX) + string(APPEND OPTIONS "\ +--disable-autodetect \ +--enable-videotoolbox \ +--enable-encoder=h264_videotoolbox,hevc_videotoolbox \ +--enable-hwaccel=h264_videotoolbox,hevc_videotoolbox \ +") +elseif(VCPKG_TARGET_IS_IOS) + string(APPEND OPTIONS "\ +--arch=arm64 \ +--disable-autodetect \ +--disable-hwaccels \ +--disable-encoders \ +--disable-videotoolbox \ +--extra-cflags=\"-arch arm64 -mios-version-min=8.0 -fembed-bitcode\" \ +--extra-ldflags=\"-arch arm64 -mios-version-min=8.0 -fembed-bitcode\" \ +") +elseif(VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Android") + string(APPEND OPTIONS "\ +--target-os=android \ +--disable-asm \ +--enable-jni \ +--enable-mediacodec \ +--disable-hwaccels \ +--enable-encoder=h264_mediacodec \ +--enable-encoder=hevc_mediacodec \ +--enable-decoder=h264_mediacodec \ +--enable-decoder=hevc_mediacodec \ +") +endif() + +if(VCPKG_TARGET_IS_OSX) + list(JOIN VCPKG_OSX_ARCHITECTURES " " OSX_ARCHS) + list(LENGTH VCPKG_OSX_ARCHITECTURES OSX_ARCH_COUNT) +endif() + +vcpkg_cmake_get_vars(cmake_vars_file) +include("${cmake_vars_file}") + +if(VCPKG_DETECTED_MSVC) + string(APPEND OPTIONS " --disable-inline-asm") # clang-cl has inline assembly but this leads to undefined symbols. + set(OPTIONS "--toolchain=msvc ${OPTIONS}") + + # This is required because ffmpeg depends upon optimizations to link correctly + string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -O2") + string(REGEX REPLACE "(^| )-RTC1( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") + string(REGEX REPLACE "(^| )-Od( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") + string(REGEX REPLACE "(^| )-Ob0( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") +endif() + +string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -I \"${CURRENT_INSTALLED_DIR}/include\"") +string(APPEND VCPKG_COMBINED_C_FLAGS_RELEASE " -I \"${CURRENT_INSTALLED_DIR}/include\"") + +# # Setup vcpkg toolchain +set(prog_env "") + +if(VCPKG_DETECTED_CMAKE_C_COMPILER) + get_filename_component(CC_path "${VCPKG_DETECTED_CMAKE_C_COMPILER}" DIRECTORY) + get_filename_component(CC_filename "${VCPKG_DETECTED_CMAKE_C_COMPILER}" NAME) + set(ENV{CC} "${CC_filename}") + string(APPEND OPTIONS " --cc=${CC_filename}") + + # string(APPEND OPTIONS " --host_cc=${CC_filename}") ffmpeg not yet setup for cross builds? + list(APPEND prog_env "${CC_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_CXX_COMPILER) + get_filename_component(CXX_path "${VCPKG_DETECTED_CMAKE_CXX_COMPILER}" DIRECTORY) + get_filename_component(CXX_filename "${VCPKG_DETECTED_CMAKE_CXX_COMPILER}" NAME) + set(ENV{CXX} "${CXX_filename}") + string(APPEND OPTIONS " --cxx=${CXX_filename}") + + # string(APPEND OPTIONS " --host_cxx=${CC_filename}") + list(APPEND prog_env "${CXX_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_RC_COMPILER) + get_filename_component(RC_path "${VCPKG_DETECTED_CMAKE_RC_COMPILER}" DIRECTORY) + get_filename_component(RC_filename "${VCPKG_DETECTED_CMAKE_RC_COMPILER}" NAME) + set(ENV{WINDRES} "${RC_filename}") + string(APPEND OPTIONS " --windres=${RC_filename}") + list(APPEND prog_env "${RC_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_LINKER AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW) + get_filename_component(LD_path "${VCPKG_DETECTED_CMAKE_LINKER}" DIRECTORY) + get_filename_component(LD_filename "${VCPKG_DETECTED_CMAKE_LINKER}" NAME) + set(ENV{LD} "${LD_filename}") + string(APPEND OPTIONS " --ld=${LD_filename}") + + # string(APPEND OPTIONS " --host_ld=${LD_filename}") + list(APPEND prog_env "${LD_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_NM) + get_filename_component(NM_path "${VCPKG_DETECTED_CMAKE_NM}" DIRECTORY) + get_filename_component(NM_filename "${VCPKG_DETECTED_CMAKE_NM}" NAME) + set(ENV{NM} "${NM_filename}") + string(APPEND OPTIONS " --nm=${NM_filename}") + list(APPEND prog_env "${NM_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_AR) + get_filename_component(AR_path "${VCPKG_DETECTED_CMAKE_AR}" DIRECTORY) + get_filename_component(AR_filename "${VCPKG_DETECTED_CMAKE_AR}" NAME) + + if(AR_filename MATCHES [[^(llvm-)?lib\.exe$]]) + set(ENV{AR} "ar-lib ${AR_filename}") + string(APPEND OPTIONS " --ar='ar-lib ${AR_filename}'") + else() + set(ENV{AR} "${AR_filename}") + string(APPEND OPTIONS " --ar='${AR_filename}'") + endif() + + list(APPEND prog_env "${AR_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_RANLIB) + get_filename_component(RANLIB_path "${VCPKG_DETECTED_CMAKE_RANLIB}" DIRECTORY) + get_filename_component(RANLIB_filename "${VCPKG_DETECTED_CMAKE_RANLIB}" NAME) + set(ENV{RANLIB} "${RANLIB_filename}") + string(APPEND OPTIONS " --ranlib=${RANLIB_filename}") + list(APPEND prog_env "${RANLIB_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_STRIP) + get_filename_component(STRIP_path "${VCPKG_DETECTED_CMAKE_STRIP}" DIRECTORY) + get_filename_component(STRIP_filename "${VCPKG_DETECTED_CMAKE_STRIP}" NAME) + set(ENV{STRIP} "${STRIP_filename}") + string(APPEND OPTIONS " --strip=${STRIP_filename}") + list(APPEND prog_env "${STRIP_path}") +endif() + +list(REMOVE_DUPLICATES prog_env) +vcpkg_add_to_path(PREPEND ${prog_env}) + +# More? OBJCC BIN2C +file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg" "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") + +set(FFMPEG_PKGCONFIG_MODULES libavutil) + +set(OPTIONS_CROSS "--enable-cross-compile") + +# ffmpeg needs --cross-prefix option to use appropriate tools for cross-compiling. +if(VCPKG_DETECTED_CMAKE_C_COMPILER MATCHES "([^\/]*-)gcc$") + string(APPEND OPTIONS_CROSS " --cross-prefix=${CMAKE_MATCH_1}") +endif() + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(BUILD_ARCH "x86_64") +else() + set(BUILD_ARCH ${VCPKG_TARGET_ARCHITECTURE}) +endif() + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm" OR VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") + if(VCPKG_TARGET_IS_WINDOWS) + vcpkg_find_acquire_program(GASPREPROCESSOR) + + foreach(GAS_PATH ${GASPREPROCESSOR}) + get_filename_component(GAS_ITEM_PATH ${GAS_PATH} DIRECTORY) + vcpkg_add_to_path("${GAS_ITEM_PATH}") + endforeach(GAS_PATH) + endif() +endif() + +set(OPTIONS_DEBUG "--disable-optimizations") +set(OPTIONS_RELEASE "--enable-optimizations") + +set(OPTIONS "${OPTIONS} ${OPTIONS_CROSS}") + +if(VCPKG_TARGET_IS_MINGW) + set(OPTIONS "${OPTIONS} --extra_cflags=-D_WIN32_WINNT=0x0601") +elseif(VCPKG_TARGET_IS_WINDOWS) + set(OPTIONS "${OPTIONS} --extra-cflags=-DHAVE_UNISTD_H=0") +endif() + +vcpkg_find_acquire_program(PKGCONFIG) +set(OPTIONS "${OPTIONS} --pkg-config=${PKGCONFIG}") + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + set(OPTIONS "${OPTIONS} --pkg-config-flags=--static") +endif() + +message(STATUS "Building Options: ${OPTIONS}") + +# Release build +if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + if(VCPKG_DETECTED_MSVC) + set(OPTIONS_RELEASE "${OPTIONS_RELEASE} --extra-ldflags=-libpath:\"${CURRENT_INSTALLED_DIR}/lib\"") + else() + set(OPTIONS_RELEASE "${OPTIONS_RELEASE} --extra-ldflags=-L\"${CURRENT_INSTALLED_DIR}/lib\"") + endif() + + message(STATUS "Building Release Options: ${OPTIONS_RELEASE}") + set(ENV{PKG_CONFIG_PATH} "${CURRENT_INSTALLED_DIR}/lib/pkgconfig") + message(STATUS "Building ${PORT} for Release") + file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") + + # We use response files here as the only known way to handle spaces in paths + set(crsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/cflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_C_FLAGS_RELEASE_SANITIZED "${VCPKG_COMBINED_C_FLAGS_RELEASE}") + file(WRITE "${crsp}" "${VCPKG_COMBINED_C_FLAGS_RELEASE_SANITIZED}") + set(ldrsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/ldflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE_SANITIZED "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE}") + file(WRITE "${ldrsp}" "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE_SANITIZED}") + set(ENV{CFLAGS} "@${crsp}") + + # All tools except the msvc arm{,64} assembler accept @... as response file syntax. + # For that assembler, there is no known way to pass in flags. We must hope that not passing flags will work acceptably. + if(NOT VCPKG_DETECTED_MSVC OR NOT VCPKG_TARGET_ARCHITECTURE MATCHES "^arm") + set(ENV{ASFLAGS} "@${crsp}") + endif() + + set(ENV{LDFLAGS} "@${ldrsp}") + set(ENV{ARFLAGS} "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_RELEASE}") + + set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") + set(CONFIGURE_OPTIONS "${OPTIONS} ${OPTIONS_RELEASE}") + set(INST_PREFIX "${CURRENT_PACKAGES_DIR}") + + configure_file("${CMAKE_CURRENT_LIST_DIR}/build.sh.in" "${BUILD_DIR}/build.sh" @ONLY) + + z_vcpkg_setup_pkgconfig_path(CONFIG RELEASE) + + vcpkg_execute_required_process( + COMMAND "${SHELL}" ./build.sh + WORKING_DIRECTORY "${BUILD_DIR}" + LOGNAME "build-${TARGET_TRIPLET}-rel" + SAVE_LOG_FILES ffbuild/config.log + ) + + z_vcpkg_restore_pkgconfig_path() +endif() + +# Debug build +if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + if(VCPKG_DETECTED_MSVC) + set(OPTIONS_DEBUG "${OPTIONS_DEBUG} --extra-ldflags=-libpath:\"${CURRENT_INSTALLED_DIR}/debug/lib\"") + else() + set(OPTIONS_DEBUG "${OPTIONS_DEBUG} --extra-ldflags=-L\"${CURRENT_INSTALLED_DIR}/debug/lib\"") + endif() + + message(STATUS "Building Debug Options: ${OPTIONS_DEBUG}") + set(ENV{LDFLAGS} "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG}") + set(ENV{PKG_CONFIG_PATH} "${CURRENT_INSTALLED_DIR}/debug/lib/pkgconfig") + message(STATUS "Building ${PORT} for Debug") + file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg") + set(crsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/cflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_C_FLAGS_DEBUG_SANITIZED "${VCPKG_COMBINED_C_FLAGS_DEBUG}") + file(WRITE "${crsp}" "${VCPKG_COMBINED_C_FLAGS_DEBUG_SANITIZED}") + set(ldrsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/ldflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG_SANITIZED "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG}") + file(WRITE "${ldrsp}" "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG_SANITIZED}") + set(ENV{CFLAGS} "@${crsp}") + + if(NOT VCPKG_DETECTED_MSVC OR NOT VCPKG_TARGET_ARCHITECTURE MATCHES "^arm") + set(ENV{ASFLAGS} "@${crsp}") + endif() + + set(ENV{LDFLAGS} "@${ldrsp}") + set(ENV{ARFLAGS} "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_DEBUG}") + + set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg") + set(CONFIGURE_OPTIONS "${OPTIONS} ${OPTIONS_DEBUG}") + set(INST_PREFIX "${CURRENT_PACKAGES_DIR}/debug") + + configure_file("${CMAKE_CURRENT_LIST_DIR}/build.sh.in" "${BUILD_DIR}/build.sh" @ONLY) + + z_vcpkg_setup_pkgconfig_path(CONFIG DEBUG) + + vcpkg_execute_required_process( + COMMAND "${SHELL}" ./build.sh + WORKING_DIRECTORY "${BUILD_DIR}" + LOGNAME "build-${TARGET_TRIPLET}-dbg" + SAVE_LOG_FILES ffbuild/config.log + ) + + z_vcpkg_restore_pkgconfig_path() +endif() + +if(VCPKG_TARGET_IS_WINDOWS) + file(GLOB DEF_FILES "${CURRENT_PACKAGES_DIR}/lib/*.def" "${CURRENT_PACKAGES_DIR}/debug/lib/*.def") + + if(NOT VCPKG_TARGET_IS_MINGW) + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") + set(LIB_MACHINE_ARG /machine:ARM) + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") + set(LIB_MACHINE_ARG /machine:ARM64) + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") + set(LIB_MACHINE_ARG /machine:x86) + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(LIB_MACHINE_ARG /machine:x64) + else() + message(FATAL_ERROR "Unsupported target architecture") + endif() + + foreach(DEF_FILE ${DEF_FILES}) + get_filename_component(DEF_FILE_DIR "${DEF_FILE}" DIRECTORY) + get_filename_component(DEF_FILE_NAME "${DEF_FILE}" NAME) + string(REGEX REPLACE "-[0-9]*\\.def" "${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}" OUT_FILE_NAME "${DEF_FILE_NAME}") + file(TO_NATIVE_PATH "${DEF_FILE}" DEF_FILE_NATIVE) + file(TO_NATIVE_PATH "${DEF_FILE_DIR}/${OUT_FILE_NAME}" OUT_FILE_NATIVE) + message(STATUS "Generating ${OUT_FILE_NATIVE}") + vcpkg_execute_required_process( + COMMAND lib.exe "/def:${DEF_FILE_NATIVE}" "/out:${OUT_FILE_NATIVE}" ${LIB_MACHINE_ARG} + WORKING_DIRECTORY "${CURRENT_PACKAGES_DIR}" + LOGNAME "libconvert-${TARGET_TRIPLET}" + ) + endforeach() + endif() + + file(GLOB EXP_FILES "${CURRENT_PACKAGES_DIR}/lib/*.exp" "${CURRENT_PACKAGES_DIR}/debug/lib/*.exp") + file(GLOB LIB_FILES "${CURRENT_PACKAGES_DIR}/bin/*${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}" "${CURRENT_PACKAGES_DIR}/debug/bin/*${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}") + + if(VCPKG_TARGET_IS_MINGW) + file(GLOB LIB_FILES_2 "${CURRENT_PACKAGES_DIR}/bin/*.lib" "${CURRENT_PACKAGES_DIR}/debug/bin/*.lib") + endif() + + set(files_to_remove ${EXP_FILES} ${LIB_FILES} ${LIB_FILES_2} ${DEF_FILES}) + + if(files_to_remove) + file(REMOVE ${files_to_remove}) + endif() +endif() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include" "${CURRENT_PACKAGES_DIR}/debug/share") + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin") +endif() + +vcpkg_copy_pdbs() + +if(VCPKG_TARGET_IS_WINDOWS) + set(_dirs "/") + + if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + list(APPEND _dirs "/debug/") + endif() + + foreach(_debug IN LISTS _dirs) + foreach(PKGCONFIG_MODULE IN LISTS FFMPEG_PKGCONFIG_MODULES) + set(PKGCONFIG_FILE "${CURRENT_PACKAGES_DIR}${_debug}lib/pkgconfig/${PKGCONFIG_MODULE}.pc") + + # remove redundant cygwin style -libpath entries + execute_process( + COMMAND "${MSYS_ROOT}/usr/bin/cygpath.exe" -u "${CURRENT_INSTALLED_DIR}" + OUTPUT_VARIABLE CYG_INSTALLED_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + vcpkg_replace_string("${PKGCONFIG_FILE}" "-libpath:${CYG_INSTALLED_DIR}${_debug}lib/pkgconfig/../../lib " "") + + # transform libdir, includedir, and prefix paths from cygwin style to windows style + file(READ "${PKGCONFIG_FILE}" PKGCONFIG_CONTENT) + + foreach(PATH_NAME prefix libdir includedir) + string(REGEX MATCH "${PATH_NAME}=[^\n]*" PATH_VALUE "${PKGCONFIG_CONTENT}") + string(REPLACE "${PATH_NAME}=" "" PATH_VALUE "${PATH_VALUE}") + + if(NOT PATH_VALUE) + message(FATAL_ERROR "failed to find pkgconfig variable ${PATH_NAME}") + endif() + + execute_process( + COMMAND "${MSYS_ROOT}/usr/bin/cygpath.exe" -w "${PATH_VALUE}" + OUTPUT_VARIABLE FIXED_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + file(TO_CMAKE_PATH "${FIXED_PATH}" FIXED_PATH) + vcpkg_replace_string("${PKGCONFIG_FILE}" "${PATH_NAME}=${PATH_VALUE}" "${PATH_NAME}=${FIXED_PATH}") + endforeach() + + # list libraries with -l flag (so pkgconf knows they are libraries and not just linker flags) + foreach(LIBS_ENTRY Libs Libs.private) + string(REGEX MATCH "${LIBS_ENTRY}: [^\n]*" LIBS_VALUE "${PKGCONFIG_CONTENT}") + + if(NOT LIBS_VALUE) + message(FATAL_ERROR "failed to find pkgconfig entry ${LIBS_ENTRY}") + endif() + + string(REPLACE "${LIBS_ENTRY}: " "" LIBS_VALUE "${LIBS_VALUE}") + + if(LIBS_VALUE) + set(LIBS_VALUE_OLD "${LIBS_VALUE}") + string(REGEX REPLACE "([^ ]+)[.]lib" "-l\\1" LIBS_VALUE "${LIBS_VALUE}") + set(LIBS_VALUE_NEW "${LIBS_VALUE}") + vcpkg_replace_string("${PKGCONFIG_FILE}" "${LIBS_ENTRY}: ${LIBS_VALUE_OLD}" "${LIBS_ENTRY}: ${LIBS_VALUE_NEW}") + endif() + endforeach() + endforeach() + endforeach() +endif() + +vcpkg_fixup_pkgconfig() + +# Handle dependencies +x_vcpkg_pkgconfig_get_modules(PREFIX FFMPEG_PKGCONFIG MODULES ${FFMPEG_PKGCONFIG_MODULES} LIBS) + +function(append_dependencies_from_libs out) + cmake_parse_arguments(PARSE_ARGV 1 "arg" "" "LIBS" "") + string(REGEX REPLACE "[ ]+" ";" contents "${arg_LIBS}") + list(FILTER contents EXCLUDE REGEX "^-F.+") + list(FILTER contents EXCLUDE REGEX "^-framework$") + list(FILTER contents EXCLUDE REGEX "^-L.+") + list(FILTER contents EXCLUDE REGEX "^-libpath:.+") + list(TRANSFORM contents REPLACE "^-Wl,-framework," "-l") + list(FILTER contents EXCLUDE REGEX "^-Wl,.+") + list(TRANSFORM contents REPLACE "^-l" "") + list(FILTER contents EXCLUDE REGEX "^avutil$") + list(FILTER contents EXCLUDE REGEX "^avcodec$") + list(FILTER contents EXCLUDE REGEX "^avdevice$") + list(FILTER contents EXCLUDE REGEX "^avfilter$") + list(FILTER contents EXCLUDE REGEX "^avformat$") + list(FILTER contents EXCLUDE REGEX "^postproc$") + list(FILTER contents EXCLUDE REGEX "^swresample$") + list(FILTER contents EXCLUDE REGEX "^swscale$") + + if(VCPKG_TARGET_IS_WINDOWS) + list(TRANSFORM contents TOLOWER) + endif() + + if(contents) + list(APPEND "${out}" "${contents}") + set("${out}" "${${out}}" PARENT_SCOPE) + endif() +endfunction() + +append_dependencies_from_libs(FFMPEG_DEPENDENCIES_RELEASE LIBS "${FFMPEG_PKGCONFIG_LIBS_RELEASE}") +append_dependencies_from_libs(FFMPEG_DEPENDENCIES_DEBUG LIBS "${FFMPEG_PKGCONFIG_LIBS_DEBUG}") + +# must remove duplicates from the front to respect link order so reverse first +list(REVERSE FFMPEG_DEPENDENCIES_RELEASE) +list(REVERSE FFMPEG_DEPENDENCIES_DEBUG) +list(REMOVE_DUPLICATES FFMPEG_DEPENDENCIES_RELEASE) +list(REMOVE_DUPLICATES FFMPEG_DEPENDENCIES_DEBUG) +list(REVERSE FFMPEG_DEPENDENCIES_RELEASE) +list(REVERSE FFMPEG_DEPENDENCIES_DEBUG) + +message(STATUS "Dependencies (release): ${FFMPEG_DEPENDENCIES_RELEASE}") +message(STATUS "Dependencies (debug): ${FFMPEG_DEPENDENCIES_DEBUG}") + +# Handle version strings +function(extract_regex_from_file out) + cmake_parse_arguments(PARSE_ARGV 1 "arg" "MAJOR" "FILE_WITHOUT_EXTENSION;REGEX" "") + file(READ "${arg_FILE_WITHOUT_EXTENSION}.h" contents) + + if(contents MATCHES "${arg_REGEX}") + if(NOT CMAKE_MATCH_COUNT EQUAL 1) + message(FATAL_ERROR "Could not identify match group in regular expression \"${arg_REGEX}\"") + endif() + else() + if(arg_MAJOR) + file(READ "${arg_FILE_WITHOUT_EXTENSION}_major.h" contents) + + if(contents MATCHES "${arg_REGEX}") + if(NOT CMAKE_MATCH_COUNT EQUAL 1) + message(FATAL_ERROR "Could not identify match group in regular expression \"${arg_REGEX}\"") + endif() + else() + message(WARNING "Could not find line matching \"${arg_REGEX}\" in file \"${arg_FILE_WITHOUT_EXTENSION}_major.h\"") + endif() + else() + message(WARNING "Could not find line matching \"${arg_REGEX}\" in file \"${arg_FILE_WITHOUT_EXTENSION}.h\"") + endif() + endif() + + set("${out}" "${CMAKE_MATCH_1}" PARENT_SCOPE) +endfunction() + +function(extract_version_from_component out) + cmake_parse_arguments(PARSE_ARGV 1 "arg" "" "COMPONENT" "") + string(TOLOWER "${arg_COMPONENT}" component_lower) + string(TOUPPER "${arg_COMPONENT}" component_upper) + extract_regex_from_file(major_version + FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" + MAJOR + REGEX "#define ${component_upper}_VERSION_MAJOR[ ]+([0-9]+)" + ) + extract_regex_from_file(minor_version + FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" + REGEX "#define ${component_upper}_VERSION_MINOR[ ]+([0-9]+)" + ) + extract_regex_from_file(micro_version + FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" + REGEX "#define ${component_upper}_VERSION_MICRO[ ]+([0-9]+)" + ) + set("${out}" "${major_version}.${minor_version}.${micro_version}" PARENT_SCOPE) +endfunction() + +extract_regex_from_file(FFMPEG_VERSION + FILE_WITHOUT_EXTENSION "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/libavutil/ffversion" + REGEX "#define FFMPEG_VERSION[ ]+\"(.+)\"" +) + +extract_version_from_component(LIBAVUTIL_VERSION + COMPONENT libavutil) +extract_version_from_component(LIBAVCODEC_VERSION + COMPONENT libavcodec) +extract_version_from_component(LIBAVDEVICE_VERSION + COMPONENT libavdevice) +extract_version_from_component(LIBAVFILTER_VERSION + COMPONENT libavfilter) +extract_version_from_component(LIBAVFORMAT_VERSION + COMPONENT libavformat) +extract_version_from_component(LIBSWRESAMPLE_VERSION + COMPONENT libswresample) +extract_version_from_component(LIBSWSCALE_VERSION + COMPONENT libswscale) + +# Handle copyright +file(STRINGS "${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-out.log" LICENSE_STRING REGEX "License: .*" LIMIT_COUNT 1) + +if(LICENSE_STRING STREQUAL "License: LGPL version 2.1 or later") + set(LICENSE_FILE "COPYING.LGPLv2.1") +elseif(LICENSE_STRING STREQUAL "License: LGPL version 3 or later") + set(LICENSE_FILE "COPYING.LGPLv3") +elseif(LICENSE_STRING STREQUAL "License: GPL version 2 or later") + set(LICENSE_FILE "COPYING.GPLv2") +elseif(LICENSE_STRING STREQUAL "License: GPL version 3 or later") + set(LICENSE_FILE "COPYING.GPLv3") +elseif(LICENSE_STRING STREQUAL "License: nonfree and unredistributable") + set(LICENSE_FILE "COPYING.NONFREE") + file(WRITE "${SOURCE_PATH}/${LICENSE_FILE}" "${LICENSE_STRING}") +else() + message(FATAL_ERROR "Failed to identify license (${LICENSE_STRING})") +endif() + +configure_file("${CMAKE_CURRENT_LIST_DIR}/FindFFMPEG.cmake.in" "${CURRENT_PACKAGES_DIR}/share/${PORT}/FindFFMPEG.cmake" @ONLY) +configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-cmake-wrapper.cmake" @ONLY) +file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/${LICENSE_FILE}") diff --git a/res/vcpkg/ffmpeg/usage b/res/vcpkg/ffmpeg/usage new file mode 100644 index 00000000000..6ef6c3bd07a --- /dev/null +++ b/res/vcpkg/ffmpeg/usage @@ -0,0 +1,6 @@ +To use ffmpeg add the following to your CMake project: + + find_package(FFMPEG REQUIRED) + target_include_directories(main PRIVATE ${FFMPEG_INCLUDE_DIRS}) + target_link_directories(main PRIVATE ${FFMPEG_LIBRARY_DIRS}) + target_link_libraries(main PRIVATE ${FFMPEG_LIBRARIES}) diff --git a/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake b/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake new file mode 100644 index 00000000000..233d61343a3 --- /dev/null +++ b/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake @@ -0,0 +1,47 @@ +set(FFMPEG_PREV_MODULE_PATH ${CMAKE_MODULE_PATH}) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +include(SelectLibraryConfigurations) + +cmake_policy(SET CMP0012 NEW) + +set(vcpkg_no_avcodec_target ON) +set(vcpkg_no_avformat_target ON) +set(vcpkg_no_avutil_target ON) +if(TARGET FFmpeg::avcodec) + set(vcpkg_no_avcodec_target OFF) +endif() +if(TARGET FFmpeg::avformat) + set(vcpkg_no_avformat_target OFF) +endif() +if(TARGET FFmpeg::avutil) + set(vcpkg_no_avutil_target OFF) +endif() + +_find_package(${ARGS}) + +if(WIN32) + set(PKG_CONFIG_EXECUTABLE "${CMAKE_CURRENT_LIST_DIR}/../../../@_HOST_TRIPLET@/tools/pkgconf/pkgconf.exe" CACHE STRING "" FORCE) +endif() + +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) # Required for CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.1 which otherwise ignores CMAKE_PREFIX_PATH + +if(@WITH_MFX@) + find_package(PkgConfig ) + pkg_check_modules(libmfx IMPORTED_TARGET libmfx) + list(APPEND FFMPEG_LIBRARIES PkgConfig::libmfx) + if(vcpkg_no_avcodec_target AND TARGET FFmpeg::avcodec) + target_link_libraries(FFmpeg::avcodec INTERFACE PkgConfig::libmfx) + endif() + if(vcpkg_no_avutil_target AND TARGET FFmpeg::avutil) + target_link_libraries(FFmpeg::avutil INTERFACE PkgConfig::libmfx) + endif() +endif() + +set(FFMPEG_LIBRARY ${FFMPEG_LIBRARIES}) + +set(CMAKE_MODULE_PATH ${FFMPEG_PREV_MODULE_PATH}) + +unset(vcpkg_no_avformat_target) +unset(vcpkg_no_avcodec_target) +unset(vcpkg_no_avutil_target) diff --git a/res/vcpkg/ffmpeg/vcpkg.json b/res/vcpkg/ffmpeg/vcpkg.json new file mode 100644 index 00000000000..60114113338 --- /dev/null +++ b/res/vcpkg/ffmpeg/vcpkg.json @@ -0,0 +1,47 @@ +{ + "name": "ffmpeg", + "version": "7.0.1", + "port-version": 0, + "description": [ + "a library to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created.", + "FFmpeg is the leading multimedia framework, able to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created. It supports the most obscure ancient formats up to the cutting edge. No matter if they were designed by some standards committee, the community or a corporation. It is also highly portable: FFmpeg compiles, runs, and passes our testing infrastructure FATE across Linux, Mac OS X, Microsoft Windows, the BSDs, Solaris, etc. under a wide variety of build environments, machine architectures, and configurations." + ], + "homepage": "https://ffmpeg.org", + "license": null, + "dependencies": [ + { + "name": "vcpkg-cmake-get-vars", + "host": true + }, + { + "name": "vcpkg-pkgconfig-get-modules", + "host": true + } + ], + "default-features": [ + "avcodec", + "avdevice", + "avformat" + ], + "features": { + "amf": { + "description": "AMD AMF codec support", + "dependencies": [ + "amd-amf" + ] + }, + "nvcodec": { + "description": "Nvidia video decoding/encoding acceleration", + "supports": "linux | (!osx & !uwp & !(arm64 & windows))", + "dependencies": [ + "ffnvcodec" + ] + }, + "qsv": { + "description": "Intel QSV Codec", + "dependencies": [ + "mfx-dispatch" + ] + } + } +} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index 940783f2d8b..6d4cbb7b213 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -51,13 +51,44 @@ { "name": "libyuv", "host": false + }, + { + "name": "ffmpeg", + "host": true, + "features": [ + { + "name": "amf", + "platform": "((windows | linux) & static)" + }, + { + "name": "nvcodec", + "platform": "((windows | linux) & static)" + }, + { + "name": "qsv", + "platform": "(windows & static)" + } + ], + "platform": "((windows | linux | osx) & static)" + }, + { + "name": "ffmpeg", + "host": false, + "platform": "((android | ios) & static)" } ], "vcpkg-configuration": { "default-registry": { "kind": "builtin", - "baseline": "" + "baseline": "f7423ee180c4b7f40d43402c2feb3859161ef625" }, - "overlay-ports": [ "./res/vcpkg" ] - } -} + "overlay-ports": [ + "./res/vcpkg" + ] + }, + "overrides": [ + { "name": "ffnvcodec", "version": "11.1.5.2" }, + { "name": "amd-amf", "version": "1.4.29" }, + { "name": "mfx-dispatch", "version": "1.35.1" } + ] +} \ No newline at end of file From 72c96f22b626bbb06bf603068737c2f359d290b1 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 16 Jul 2024 15:46:20 +0800 Subject: [PATCH 276/335] remove unused vcpkg ffmpeg code (#8725) Signed-off-by: 21pages --- .../ffmpeg/5.1/0004-amf-colorspace.patch | 35 ++-- res/vcpkg/ffmpeg/FindFFMPEG.cmake.in | 161 ------------------ res/vcpkg/ffmpeg/portfile.cmake | 2 - res/vcpkg/ffmpeg/usage | 6 - res/vcpkg/ffmpeg/vcpkg.json | 3 - 5 files changed, 12 insertions(+), 195 deletions(-) delete mode 100644 res/vcpkg/ffmpeg/FindFFMPEG.cmake.in delete mode 100644 res/vcpkg/ffmpeg/usage diff --git a/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch b/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch index d21d3eb1f4a..49aef694795 100644 --- a/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch +++ b/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch @@ -1,14 +1,14 @@ -From 7c29a6936e7b7a3a3a0bcc88894f2b739bdae9cf Mon Sep 17 00:00:00 2001 +From 8fd62e4ecd058b09abf8847be5fbbf0eef44a90f Mon Sep 17 00:00:00 2001 From: 21pages -Date: Thu, 11 Jul 2024 16:24:27 +0800 +Date: Tue, 16 Jul 2024 14:58:33 +0800 Subject: [PATCH] amf colorspace Signed-off-by: 21pages --- libavcodec/amfenc.h | 1 + - libavcodec/amfenc_h264.c | 45 ++++++++++++++++++++++++++++++++++ - libavcodec/amfenc_hevc.c | 52 ++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 98 insertions(+) + libavcodec/amfenc_h264.c | 39 +++++++++++++++++++++++++++++++++ + libavcodec/amfenc_hevc.c | 47 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 87 insertions(+) diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h index 31172645f2..493e01603d 100644 @@ -23,7 +23,7 @@ index 31172645f2..493e01603d 100644 #include "libavutil/fifo.h" diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c -index f55dbc80f0..a916b35f9c 100644 +index f55dbc80f0..5a6b6e164f 100644 --- a/libavcodec/amfenc_h264.c +++ b/libavcodec/amfenc_h264.c @@ -139,6 +139,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) @@ -36,7 +36,7 @@ index f55dbc80f0..a916b35f9c 100644 if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); -@@ -199,11 +202,53 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) +@@ -199,11 +202,47 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_ASPECT_RATIO, ratio); } @@ -44,7 +44,6 @@ index f55dbc80f0..a916b35f9c 100644 /// Color Range (Partial/TV/MPEG or Full/PC/JPEG) if (avctx->color_range == AVCOL_RANGE_JPEG) { AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 1); -+ /// Color Space for Full (JPEG) Range + switch (avctx->colorspace) { + case AVCOL_SPC_SMPTE170M: + color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; @@ -59,7 +58,6 @@ index f55dbc80f0..a916b35f9c 100644 + } + } else { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 0); -+ /// Color Space for Limited (MPEG) range + switch (avctx->colorspace) { + case AVCOL_SPC_SMPTE170M: + color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; @@ -73,9 +71,7 @@ index f55dbc80f0..a916b35f9c 100644 + break; + } + } -+ /// Color Depth -+ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format -+ : avctx->pix_fmt; ++ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format : avctx->pix_fmt; + color_depth = AMF_COLOR_BIT_DEPTH_8; + if (pix_fmt == AV_PIX_FMT_P010) { + color_depth = AMF_COLOR_BIT_DEPTH_10; @@ -83,15 +79,13 @@ index f55dbc80f0..a916b35f9c 100644 + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH, color_depth); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, color_profile); -+ /// Color Transfer Characteristics (AMF matches ISO/IEC) + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); -+ /// Color Primaries (AMF matches ISO/IEC) + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); // autodetect rate control method if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN) { if (ctx->qp_i != -1 || ctx->qp_p != -1 || ctx->qp_b != -1) { diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c -index 7a40bcad31..e7979d9aeb 100644 +index 7a40bcad31..0260f43c81 100644 --- a/libavcodec/amfenc_hevc.c +++ b/libavcodec/amfenc_hevc.c @@ -106,6 +106,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) @@ -114,11 +108,10 @@ index 7a40bcad31..e7979d9aeb 100644 default: break; } -@@ -158,6 +164,52 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) +@@ -158,6 +164,47 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO, ratio); } -+ // Color Metadata + color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; + if (avctx->color_range == AVCOL_RANGE_JPEG) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, 1); @@ -149,9 +142,7 @@ index 7a40bcad31..e7979d9aeb 100644 + break; + } + } -+ /// Color Depth -+ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format -+ : avctx->pix_fmt; ++ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format : avctx->pix_fmt; + color_depth = AMF_COLOR_BIT_DEPTH_8; + if (pix_fmt == AV_PIX_FMT_P010) { + color_depth = AMF_COLOR_BIT_DEPTH_10; @@ -161,12 +152,10 @@ index 7a40bcad31..e7979d9aeb 100644 + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE, color_profile); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); -+ -+ + // Picture control properties AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, ctx->gops_per_idr); AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, avctx->gop_size); -- -2.39.3 (Apple Git-145) +2.43.0.windows.1 diff --git a/res/vcpkg/ffmpeg/FindFFMPEG.cmake.in b/res/vcpkg/ffmpeg/FindFFMPEG.cmake.in deleted file mode 100644 index 8a7237fc434..00000000000 --- a/res/vcpkg/ffmpeg/FindFFMPEG.cmake.in +++ /dev/null @@ -1,161 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. -# -#.rst: -# FindFFMPEG -# -------- -# -# Find the FFPMEG libraries -# -# Result Variables -# ^^^^^^^^^^^^^^^^ -# -# The following variables will be defined: -# -# ``FFMPEG_FOUND`` -# True if FFMPEG found on the local system -# -# ``FFMPEG_INCLUDE_DIRS`` -# Location of FFMPEG header files -# -# ``FFMPEG_LIBRARY_DIRS`` -# Location of FFMPEG libraries -# -# ``FFMPEG_LIBRARIES`` -# List of the FFMPEG libraries found -# -# - -include(FindPackageHandleStandardArgs) -include(SelectLibraryConfigurations) -include(CMakeFindDependencyMacro) - -if(NOT FFMPEG_FOUND) - -# Compute the installation path relative to this file. -get_filename_component(SEARCH_PATH "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(SEARCH_PATH "${SEARCH_PATH}" PATH) -get_filename_component(SEARCH_PATH "${SEARCH_PATH}" PATH) -if(SEARCH_PATH STREQUAL "/") - set(SEARCH_PATH "") -endif() - -set(FFMPEG_VERSION "@FFMPEG_VERSION@") - -function(append_dependencies out) - cmake_parse_arguments(PARSE_ARGV 1 "arg" "DEBUG" "NAMES" "") - if(${arg_DEBUG}) - set(config DEBUG) - set(path "${CURRENT_INSTALLED_DIR}/debug/lib/") - else() - set(config RELEASE) - set(path "${CURRENT_INSTALLED_DIR}/lib/") - endif() - foreach(lib_name ${arg_NAMES}) - if("${lib_name}" STREQUAL "-pthread") - list(APPEND ${out} "-pthread") - elseif("${lib_name}" STREQUAL "-pthreads") - list(APPEND ${out} "-pthreads") - elseif("${lib_name}" STREQUAL "gcc") - list(APPEND ${out} "-lgcc") - elseif("${lib_name}" STREQUAL "gcc_s") - list(APPEND ${out} "-lgcc_s") - elseif("${lib_name}" STREQUAL "stdc++") - list(APPEND ${out} "-lstdc++") - elseif("${lib_name}" STREQUAL "atomic") - list(APPEND ${out} "-latomic") - else() - # first look in ${path} specifically to ensure we find the right release/debug variant - find_library(FFMPEG_DEPENDENCY_${lib_name}_${config} NAMES "${lib_name}" PATHS "${path}" NO_DEFAULT_PATH) - # if not found there, must be a system dependency, so look elsewhere - find_library(FFMPEG_DEPENDENCY_${lib_name}_${config} NAMES "${lib_name}" REQUIRED) - list(APPEND ${out} "${FFMPEG_DEPENDENCY_${lib_name}_${config}}") - endif() - endforeach() - set("${out}" "${${out}}" PARENT_SCOPE) -endfunction() - -macro(FFMPEG_FIND varname shortname headername) - if(NOT FFMPEG_${varname}_INCLUDE_DIRS) - find_path(FFMPEG_${varname}_INCLUDE_DIRS NAMES lib${shortname}/${headername} ${headername} PATHS ${SEARCH_PATH}/include NO_DEFAULT_PATH) - endif() - if(NOT FFMPEG_${varname}_LIBRARY) - find_library(FFMPEG_${varname}_LIBRARY_RELEASE NAMES ${shortname} PATHS ${SEARCH_PATH}/lib/ NO_DEFAULT_PATH) - find_library(FFMPEG_${varname}_LIBRARY_DEBUG NAMES ${shortname}d ${shortname} PATHS ${SEARCH_PATH}/debug/lib/ NO_DEFAULT_PATH) - get_filename_component(FFMPEG_${varname}_LIBRARY_RELEASE_DIR ${FFMPEG_${varname}_LIBRARY_RELEASE} DIRECTORY) - get_filename_component(FFMPEG_${varname}_LIBRARY_DEBUG_DIR ${FFMPEG_${varname}_LIBRARY_DEBUG} DIRECTORY) - select_library_configurations(FFMPEG_${varname}) - set(FFMPEG_${varname}_LIBRARY ${FFMPEG_${varname}_LIBRARY} CACHE STRING "") - endif() - if (FFMPEG_${varname}_LIBRARY AND FFMPEG_${varname}_INCLUDE_DIRS) - set(FFMPEG_${varname}_FOUND TRUE BOOL) - list(APPEND FFMPEG_INCLUDE_DIRS ${FFMPEG_${varname}_INCLUDE_DIRS}) - list(APPEND FFMPEG_LIBRARIES ${FFMPEG_${varname}_LIBRARY}) - list(APPEND FFMPEG_LIBRARY_DIRS ${FFMPEG_${varname}_LIBRARY_RELEASE_DIR} ${FFMPEG_${varname}_LIBRARY_DEBUG_DIR}) - endif() -endmacro(FFMPEG_FIND) - -if(@ENABLE_AVDEVICE@) - FFMPEG_FIND(libavdevice avdevice avdevice.h) -endif() -if(@ENABLE_AVFILTER@) - FFMPEG_FIND(libavfilter avfilter avfilter.h) -endif() -if(@ENABLE_AVFORMAT@) - FFMPEG_FIND(libavformat avformat avformat.h) -endif() -if(@ENABLE_AVCODEC@) - FFMPEG_FIND(libavcodec avcodec avcodec.h) -endif() -if(@ENABLE_POSTPROC@) - FFMPEG_FIND(libpostproc postproc postprocess.h) -endif() -if(@ENABLE_SWRESAMPLE@) - FFMPEG_FIND(libswresample swresample swresample.h) -endif() -if(@ENABLE_SWSCALE@) - FFMPEG_FIND(libswscale swscale swscale.h) -endif() -FFMPEG_FIND(libavutil avutil avutil.h) - -if (FFMPEG_libavutil_FOUND) - list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) - list(REMOVE_DUPLICATES FFMPEG_LIBRARY_DIRS) - set(FFMPEG_libavutil_VERSION "@LIBAVUTIL_VERSION@" CACHE STRING "") - - if(FFMPEG_libavcodec_FOUND) - set(FFMPEG_libavcodec_VERSION "@LIBAVCODEC_VERSION@" CACHE STRING "") - endif() - if(FFMPEG_libavdevice_FOUND) - set(FFMPEG_libavdevice_VERSION "@LIBAVDEVICE_VERSION@" CACHE STRING "") - endif() - if(FFMPEG_libavfilter_FOUND) - set(FFMPEG_libavfilter_VERSION "@LIBAVFILTER_VERSION@" CACHE STRING "") - endif() - if(FFMPEG_libavformat_FOUND) - set(FFMPEG_libavformat_VERSION "@LIBAVFORMAT_VERSION@" CACHE STRING "") - endif() - if(FFMPEG_libswresample_FOUND) - set(FFMPEG_libswresample_VERSION "@LIBSWRESAMPLE_VERSION@" CACHE STRING "") - endif() - if(FFMPEG_libswscale_FOUND) - set(FFMPEG_libswscale_VERSION "@LIBSWSCALE_VERSION@" CACHE STRING "") - endif() - - append_dependencies(FFMPEG_DEPS_LIBRARY_RELEASE NAMES "@FFMPEG_DEPENDENCIES_RELEASE@") - append_dependencies(FFMPEG_DEPS_LIBRARY_DEBUG NAMES "@FFMPEG_DEPENDENCIES_DEBUG@" DEBUG) - if(FFMPEG_DEPS_LIBRARY_RELEASE OR FFMPEG_DEPS_LIBRARY_DEBUG) - select_library_configurations(FFMPEG_DEPS) - list(APPEND FFMPEG_LIBRARIES ${FFMPEG_DEPS_LIBRARY}) - endif() - - set(FFMPEG_LIBRARY ${FFMPEG_LIBRARIES}) - - set(FFMPEG_FOUND TRUE CACHE BOOL "") - set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "") - set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "") - set(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBRARY_DIRS} CACHE STRING "") -endif() - -find_package_handle_standard_args(FFMPEG REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_LIBRARY_DIRS FFMPEG_INCLUDE_DIRS) - -endif() diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake index 54b1aca7923..b56debb3ece 100644 --- a/res/vcpkg/ffmpeg/portfile.cmake +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -676,7 +676,5 @@ else() message(FATAL_ERROR "Failed to identify license (${LICENSE_STRING})") endif() -configure_file("${CMAKE_CURRENT_LIST_DIR}/FindFFMPEG.cmake.in" "${CURRENT_PACKAGES_DIR}/share/${PORT}/FindFFMPEG.cmake" @ONLY) configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-cmake-wrapper.cmake" @ONLY) -file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/${LICENSE_FILE}") diff --git a/res/vcpkg/ffmpeg/usage b/res/vcpkg/ffmpeg/usage deleted file mode 100644 index 6ef6c3bd07a..00000000000 --- a/res/vcpkg/ffmpeg/usage +++ /dev/null @@ -1,6 +0,0 @@ -To use ffmpeg add the following to your CMake project: - - find_package(FFMPEG REQUIRED) - target_include_directories(main PRIVATE ${FFMPEG_INCLUDE_DIRS}) - target_link_directories(main PRIVATE ${FFMPEG_LIBRARY_DIRS}) - target_link_libraries(main PRIVATE ${FFMPEG_LIBRARIES}) diff --git a/res/vcpkg/ffmpeg/vcpkg.json b/res/vcpkg/ffmpeg/vcpkg.json index 60114113338..61ff2c8b549 100644 --- a/res/vcpkg/ffmpeg/vcpkg.json +++ b/res/vcpkg/ffmpeg/vcpkg.json @@ -19,9 +19,6 @@ } ], "default-features": [ - "avcodec", - "avdevice", - "avformat" ], "features": { "amf": { From 188f85b042595d045043d9c17211cba2b5e2dc4d Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Tue, 16 Jul 2024 08:35:10 +0000 Subject: [PATCH 277/335] F-Droid: enable hwcodec for future builds (#8726) Signed-off-by: Vasyl Gello --- flutter/build_fdroid.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh index abf311b2f9e..2e0a20b6db6 100755 --- a/flutter/build_fdroid.sh +++ b/flutter/build_fdroid.sh @@ -43,15 +43,13 @@ arm64-v8a) FLUTTER_TARGET=android-arm64 NDK_TARGET=aarch64-linux-android RUST_TARGET=aarch64-linux-android -# RUSTDESK_FEATURES='flutter,hwcodec' - RUSTDESK_FEATURES='flutter' + RUSTDESK_FEATURES='flutter,hwcodec' ;; armeabi-v7a) FLUTTER_TARGET=android-arm NDK_TARGET=arm-linux-androideabi RUST_TARGET=armv7-linux-androideabi -# RUSTDESK_FEATURES='flutter,hwcodec' - RUSTDESK_FEATURES='flutter' + RUSTDESK_FEATURES='flutter,hwcodec' ;; x86_64) FLUTTER_TARGET=android-x64 From 092e4089c7935d54d5d45f425d17843b0d4cd160 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:55:46 +0800 Subject: [PATCH 278/335] fix: try workaround, macos, subwindow, frozen (#8729) Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 5 ++++- flutter/lib/utils/multi_window_manager.dart | 12 +++++++++++- flutter/pubspec.lock | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 802ef7c4585..050d37d393d 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/main.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @@ -417,7 +418,9 @@ class _RemotePageState extends State void leaveView(PointerExitEvent evt) { if (isMacOS) { - DesktopMultiWindow.hideShow(); + if (kWindowId != null) { + DesktopMultiWindow.hideShow(kWindowId!); + } } if (_ffi.ffiModel.keyboard) { diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 191152c8625..1a14ac77e17 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -141,7 +141,17 @@ class RustDeskMultiWindowManager { )); } if (isMacOS) { - Future.microtask(() => windowController.show()); + Future.microtask(() { + windowController.show(); + // Manually simulate the hide/show event to fix the issue + // https://github.com/rustdesk/rustdesk/issues/8548 + // https://github.com/flutter/flutter/issues/133533 + // https://github.com/MixinNetwork/flutter-plugins/issues/289#issuecomment-1817665239 + // https://github.com/rustdesk/rustdesk/pull/8712#issuecomment-2229912473 + Future.delayed(const Duration(milliseconds: 300), () { + DesktopMultiWindow.hideShow(-1); + }); + }); } registerActiveWindow(windowId); windows.add(windowId); diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 1c743423edc..2fb3d0b62ef 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "53fee59855c44f35381428c9fb7c7678f700d11d" + resolved-ref: "c9ac8e78f8e8f0a554062c2c13cdeb644af2c25b" url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From a4565bf0da99b21d8ba3e38cad5e75118b79921d Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 17 Jul 2024 11:40:50 +0800 Subject: [PATCH 279/335] try fix arm64 linux ci and publish error log (#8730) FFmepg can be built on arm64 ubuntu vm, possible reason is that the condition of nvcodec can't be satisfied. If this still can't work, nvcodec can be removed for arm linux. Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index ff406766bcc..59273667e08 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1056,10 +1056,24 @@ jobs: - name: Install vcpkg dependencies if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' + continue-on-error: true run: | + if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then + sudo apt-get install -y clang + fi $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash + - name: Publish vcpkg log files + if: matrix.job.arch == 'aarch64' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm64-linux-rel-out.log + /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm64-linux-rel-err.log + - name: Restore bridge files if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' uses: actions/download-artifact@master @@ -1351,11 +1365,25 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + continue-on-error: true run: | cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake + if [[ "${{ matrix.job.arch }}" == "armv7" ]]; then + sudo apt-get install -y clang + fi $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash + - name: Publish vcpkg log files + if: matrix.job.arch == 'armv7' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm-linux-rel-out.log + /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm-linux-rel-err.log + - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk sciter binary for ${{ matrix.job.arch }} id: vcpkg From 901505e8be5ecef1e4dcd5812cefa897639c0d5c Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:49:11 +0800 Subject: [PATCH 280/335] fix: macos, load multi dylib instances (#8731) Multiple dylib instances will cause some global instances to be invalid. eg. lazy_static objects in rust side, will be created more than once. Signed-off-by: fufesou --- flutter/lib/models/native_model.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index 0b70e30c8c0..b99cf2e7fb8 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -117,9 +117,13 @@ class PlatformFFI { ? DynamicLibrary.open('librustdesk.so') : isWindows ? DynamicLibrary.open('librustdesk.dll') - : isMacOS - ? DynamicLibrary.open("liblibrustdesk.dylib") - : DynamicLibrary.process(); + : + // Use executable itself as the dynamic library for MacOS. + // Multiple dylib instances will cause some global instances to be invalid. + // eg. `lazy_static` objects in rust side, will be created more than once, which is not expected. + // + // isMacOS? DynamicLibrary.open("liblibrustdesk.dylib") : + DynamicLibrary.process(); debugPrint('initializing FFI $_appType'); try { _session_get_rgba = dylib.lookupFunction("session_get_rgba"); From e283d33f288742a2a4de119d3e7e322371ec90b7 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 17 Jul 2024 13:35:32 +0800 Subject: [PATCH 281/335] Revert "try fix arm64 linux ci and publish error log (#8730)" (#8732) setup a selfhost and will fix it later This reverts commit a4565bf0da99b21d8ba3e38cad5e75118b79921d. --- .github/workflows/flutter-build.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 59273667e08..ff406766bcc 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1056,24 +1056,10 @@ jobs: - name: Install vcpkg dependencies if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' - continue-on-error: true run: | - if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then - sudo apt-get install -y clang - fi $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - - name: Publish vcpkg log files - if: matrix.job.arch == 'aarch64' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm64-linux-rel-out.log - /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm64-linux-rel-err.log - - name: Restore bridge files if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' uses: actions/download-artifact@master @@ -1365,25 +1351,11 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies - continue-on-error: true run: | cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake - if [[ "${{ matrix.job.arch }}" == "armv7" ]]; then - sudo apt-get install -y clang - fi $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - - name: Publish vcpkg log files - if: matrix.job.arch == 'armv7' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm-linux-rel-out.log - /opt/artifacts/vcpkg/buildtrees/ffmpeg/build-arm-linux-rel-err.log - - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk sciter binary for ${{ matrix.job.arch }} id: vcpkg From bc672b336705290ac9dafd35b44fb6378acda63c Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 17 Jul 2024 17:19:21 +0800 Subject: [PATCH 282/335] arm linux remove cuda-llvm option (#8735) Signed-off-by: 21pages --- libs/scrap/build.rs | 2 +- res/vcpkg/ffmpeg/portfile.cmake | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index d84d24a58d6..84b8631f332 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -188,7 +188,7 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex); } -// If you have problems installing ffmpeg, you can disable hwcodec feature and disable this function. +// If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci fn ffmpeg() { // ffmpeg let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake index b56debb3ece..d8ea95bd258 100644 --- a/res/vcpkg/ffmpeg/portfile.cmake +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -103,7 +103,6 @@ if(VCPKG_TARGET_IS_LINUX) --target-os=linux \ --enable-pthreads \ --enable-cuda \ ---enable-cuda_llvm \ --enable-ffnvcodec \ --enable-encoder=h264_nvenc \ --enable-encoder=hevc_nvenc \ @@ -117,6 +116,11 @@ if(VCPKG_TARGET_IS_LINUX) --enable-encoder=h264_vaapi \ --enable-encoder=hevc_vaapi \ ") + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + string(APPEND OPTIONS "\ +--enable-cuda_llvm \ +") + endif() elseif(VCPKG_TARGET_IS_WINDOWS) string(APPEND OPTIONS "\ --target-os=win32 \ From 930561f4318f93970f8a257b884d1c1dce3494d7 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 17 Jul 2024 18:17:30 +0800 Subject: [PATCH 283/335] remove unused protobuf --- libs/hbb_common/protos/rendezvous.proto | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/libs/hbb_common/protos/rendezvous.proto b/libs/hbb_common/protos/rendezvous.proto index 49d737c69e1..2fc0d9040e6 100644 --- a/libs/hbb_common/protos/rendezvous.proto +++ b/libs/hbb_common/protos/rendezvous.proto @@ -28,7 +28,6 @@ message PunchHole { bytes socket_addr = 1; string relay_server = 2; NatType nat_type = 3; - string request_region = 4; } message TestNatRequest { @@ -53,7 +52,6 @@ message PunchHoleSent { string relay_server = 3; NatType nat_type = 4; string version = 5; - string request_region = 6; } message RegisterPk { @@ -63,10 +61,6 @@ message RegisterPk { string old_id = 4; } -message HealthCheck { - string token = 1; -} - message RegisterPkResponse { enum Result { OK = 0; @@ -114,7 +108,6 @@ message RequestRelay { string licence_key = 6; ConnType conn_type = 7; string token = 8; - string request_region = 9; } message RelayResponse { @@ -127,7 +120,6 @@ message RelayResponse { } string refuse_reason = 6; string version = 7; - string request_region = 8; int32 feedback = 9; } @@ -140,7 +132,6 @@ message SoftwareUpdate { string url = 1; } message FetchLocalAddr { bytes socket_addr = 1; string relay_server = 2; - string request_region = 3; } message LocalAddr { @@ -149,7 +140,6 @@ message LocalAddr { string relay_server = 3; string id = 4; string version = 5; - string request_region = 6; } message PeerDiscovery { @@ -175,6 +165,10 @@ message KeyExchange { repeated bytes keys = 1; } +message HealthCheck { + string token = 1; +} + message RendezvousMessage { oneof union { RegisterPeer register_peer = 6; From 6821bef5e5db8f6e1ec9ba59f186e697b228e3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E9=A4=85=E3=81=AECreeeper?= <56744841+creeper-0910@users.noreply.github.com> Date: Wed, 17 Jul 2024 20:11:04 +0900 Subject: [PATCH 284/335] Update README-JP.md and ja.rs (#8737) * Update README-JP.md Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> * Update ja.rs Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> * Fix README-JP.md Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> --------- Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> --- docs/README-JP.md | 101 +++++++++------ src/lang/ja.rs | 316 +++++++++++++++++++++++----------------------- 2 files changed, 218 insertions(+), 199 deletions(-) diff --git a/docs/README-JP.md b/docs/README-JP.md index 01fe8d28f46..d530399a137 100644 --- a/docs/README-JP.md +++ b/docs/README-JP.md @@ -1,60 +1,73 @@

- RustDesk - Your remote desktop
+ RustDesk - あなたのためのリモートデスクトップ
ServersBuildDockerStructureSnapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
- このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。 + [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Dansk] | [Ελληνικά] | [Türkçe]
+ READMEやRustDesk UIRustDesk Docの翻訳者を歓迎します!

-Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) - +私たちと話す: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) -Rustで書かれた、設定不要ですぐに使えるリモートデスクトップソフトウェアです。自分のデータを完全にコントロールでき、セキュリティの心配もありません。私たちのランデブー/リレーサーバを使うことも、[自分で設定する](https://rustdesk.com/server) ことも、 [自分でランデブー/リレーサーバを書くこともできます](https://github.com/rustdesk/rustdesk-server-demo)。 +Rustで書かれた、設定不要ですぐに使えるリモートデスクトップソフトウェアです。自分のデータを完全にコントロールでき、セキュリティの心配もありません。私たちのランデブー/リレーサーバを使うことも、[自分でサーバーをセットアップする](https://rustdesk.com/server) ことも、 [自分でランデブー/リレーサーバを作成する](https://github.com/rustdesk/rustdesk-server-demo)こともできます。 ![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) -RustDeskは誰からの貢献も歓迎します。 貢献するには [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) を参照してください。 +RustDeskは皆さんの貢献を歓迎します。 +貢献の方法については[CONTRIBUTING.md](docs/CONTRIBUTING.md)をご確認ください。 + +[**よくある質問**](https://github.com/rustdesk/rustdesk/wiki/FAQ) + +[**パッケージのダウンロード**](https://github.com/rustdesk/rustdesk/releases) -[**RustDeskはどの様に動くのか?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F) +[**ナイトリービルド**](https://github.com/rustdesk/rustdesk/releases/tag/nightly) -[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) +[F-Droidで入手する](https://f-droid.org/en/packages/com.carriez.flutter_hbb) ## 依存関係 -デスクトップ版ではGUIに [sciter](https://sciter.com/) が使われています。 sciter dynamic library をダウンロードしてください。 +デスクトップ版ではGUIにFlutterまたはSciter(非推奨)を使用しますが、チュートリアルでは分かりやすく、簡単なSciterのみを対象に解説しています。Flutterでのビルド方法については[CI](https://github.com/rustdesk/rustdesk/blob/master/.github/workflows/flutter-build.yml)をご覧ください。 + +Sciter dynamic libraryを事前にダウンロードしてください。 [Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) | [Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | -[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib) - -モバイル版はFlutterを利用します。デスクトップ版もSciterからFlutterへマイグレーション予定です。 +[macOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib) ## ビルド手順 -- Rust開発環境とC ++ビルド環境を準備します - -- [vcpkg](https://github.com/microsoft/vcpkg), をインストールし、 `VCPKG_ROOT` 環境変数を正しく設定します。 +- Rust開発環境とC++ビルド環境を準備します。 - - Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static - - Linux/MacOS: vcpkg install libvpx libyuv opus aom - -- run `cargo run` +- [vcpkg](https://github.com/microsoft/vcpkg)をインストールし、環境変数に`VCPKG_ROOT`を設定します。 +その後、以下のコマンドを実行します。 + - Windowsの場合: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static + - Linux/macOSの場合: vcpkg install libvpx libyuv opus aom +- `cargo run`を実行します。 ## [ビルド](https://rustdesk.com/docs/en/dev/build/) -## Linuxでのビルド手順 +## Linuxでのビルド方法 ### Ubuntu 18 (Debian 10) ```sh -sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake +sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \ + libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \ + libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev +``` + +### openSUSE Tumbleweed + +```sh +sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel ``` ### Fedora 28 (CentOS 8) @@ -69,7 +82,7 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb- sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire ``` -### Install vcpkg +### vcpkgのインストール ```sh git clone https://github.com/microsoft/vcpkg @@ -81,7 +94,7 @@ export VCPKG_ROOT=$HOME/vcpkg vcpkg/vcpkg install libvpx libyuv opus aom ``` -### Fix libvpx (For Fedora) +### libvpxの修正 (Fedoraのみ) ```sh cd vcpkg/buildtrees/libvpx/src @@ -107,9 +120,9 @@ mv libsciter-gtk.so target/debug VCPKG_ROOT=$HOME/vcpkg cargo run ``` -## Dockerでビルドする方法 +## Dockerでのビルド方法 -リポジトリのクローンを作成し、Dockerコンテナを構築することから始めます。 +リポジトリをクローンし、Dockerコンテナを構築します: ```sh git clone https://github.com/rustdesk/rustdesk @@ -117,44 +130,50 @@ cd rustdesk docker build -t "rustdesk-builder" . ``` -その後、アプリケーションをビルドする必要があるたびに、以下のコマンドを実行します。 +以下のコマンドを実行します: ```sh docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder ``` +このコマンドはRustDeskをビルドする度に実行する必要があります。 -なお、最初のビルドでは、依存関係がキャッシュされるまで時間がかかることがありますが、その後のビルドではより速くなります。さらに、ビルドコマンドに別の引数を指定する必要がある場合は、コマンドの最後にある `` の位置で指定することができます。例えば、最適化されたリリースバージョンをビルドしたい場合は、上記のコマンドの後に -`--release` を実行します。できあがった実行ファイルは、システムのターゲット・フォルダに格納され、次のコマンドで実行できます。 +初回ビルドは時間がかかるかもしれませんが、2回目以降は依存関係がキャッシュされるため、ビルドにかかる時間が短くなります。 +ビルドコマンドに追加の引数を指定する必要がある場合は、コマンドの最後(``の位置)で指定することができます。例えば、最適化されたリリースバージョンをビルドしたい場合は、上記のコマンドの後に `--release` を追記し実行します。ビルドされた実行ファイルはあなたのシステムのターゲットフォルダに保存され、下記のコマンドで実行することができます。 +デバッグビルドを起動する場合: ```sh target/debug/rustdesk ``` -あるいは、リリース用の実行ファイルを実行している場合: +リリースビルドを起動する場合: ```sh target/release/rustdesk ``` -これらのコマンドをRustDeskリポジトリのルートから実行していることを確認してください。そうしないと、アプリケーションが必要なリソースを見つけられない可能性があります。また、 `install` や `run` などの他の cargo サブコマンドは、ホストではなくコンテナ内にプログラムをインストールまたは実行するため、現在この方法ではサポートされていないことに注意してください。 +コマンドをRustDeskリポジトリのルートから実行していることを確認してください。また、`install` や `run` などの他のcargoサブコマンドは、ホストではなくコンテナ内でプログラムをインストール、実行するため、現在の方法ではサポートされていません。 ## ファイル構造 -- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: ビデオコーデック、コンフィグ、tcp/udpラッパー、protobuf、ファイル転送用のfs関数、その他のユーティリティ関数 +- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: ビデオコーデック、設定、tcp/udpラッパー、protobuf、ファイル転送に利用されるfs関数やその他のユーティリティ関数 - **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: スクリーンキャプチャ -- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: プラットフォーム固有のキーボード/マウスコントロール -- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI -- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: オーディオ/クリップボード/入力/ビデオサービス、ネットワーク接続 +- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: プラットフォーム固有のキーボード/マウス操作 +- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: Windows、Linux、macOS向けのファイルのコピーと貼り付けの実装 +- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: 廃止された Sciter UI (非推奨) +- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: +オーディオ/クリップボード/入力/ビデオ サービスとネットワーク接続 - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: ピア接続の開始 -- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: [rustdesk-server](https://github.com/rustdesk/rustdesk-server), と通信し、リモートダイレクト (TCP hole punching) または中継接続を待つ。 +- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: [rustdesk-server](https://github.com/rustdesk/rustdesk-server)と通信し、リモートの直接接続(TCPホールパンチング)や中継接続を担う。 - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: プラットフォーム固有のコード +- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: デスクトップとモバイル向けのFlutterコード +- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutterウェブクライアント向けのJavaScript -## スナップショット +## Screenshots -![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png) +![Connection Manager](https://github.com/rustdesk/rustdesk/assets/28412477/db82d4e7-c4bc-4823-8e6f-6af7eadf7651) -![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png) +![Connected to a Windows PC](https://github.com/rustdesk/rustdesk/assets/28412477/9baa91e9-3362-4d06-aa1a-7518edcbd7ea) -![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png) +![File Transfer](https://github.com/rustdesk/rustdesk/assets/28412477/39511ad3-aa9a-4f8c-8947-1cce286a46ad) -![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png) +![TCP Tunneling](https://github.com/rustdesk/rustdesk/assets/28412477/78e8708f-e87e-4570-8373-1360033ea6c5) diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 33619dce1cf..acbf1850b3d 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -37,19 +37,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Clipboard is empty", "クリップボードは空です"), ("Stop service", "サービスを停止"), ("Change ID", "IDを変更"), - ("Your new ID", ""), - ("length %min% to %max%", ""), - ("starts with a letter", ""), - ("allowed characters", ""), + ("Your new ID", "新しいID"), + ("length %min% to %max%", "文字長が%min%~%max%文字以内"), + ("starts with a letter", "始まりがアルファベット"), + ("allowed characters", "使用可能な文字のみ"), ("id_change_tip", "使用できるのは大文字・小文字のアルファベット、数字、アンダースコア(_)のみです。初めの文字はアルファベットにする必要があります。6文字から16文字までです。"), ("Website", "公式サイト"), ("About", "情報"), ("Slogan_tip", ""), ("Privacy Statement", ""), ("Mute", "ミュート"), - ("Build Date", ""), - ("Version", ""), - ("Home", ""), + ("Build Date", "ビルド日時"), + ("Version", "バージョン"), + ("Home", "ホーム"), ("Audio Input", "音声入力デバイス"), ("Enhancements", "追加機能"), ("Hardware Codec", "ハードウェア コーデック"), @@ -62,26 +62,26 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Invalid format", "無効な形式"), ("server_not_support", "サーバー側でまだサポートされていません"), ("Not available", "利用不可"), - ("Too frequent", "使用量が多すぎです"), + ("Too frequent", "接続頻度が高すぎます"), ("Cancel", "キャンセル"), ("Skip", "スキップ"), ("Close", "閉じる"), ("Retry", "再試行"), ("OK", "OK"), - ("Password Required", "パスワードが必要"), + ("Password Required", "パスワードが必要です"), ("Please enter your password", "パスワードを入力してください"), ("Remember password", "パスワードを記憶する"), ("Wrong Password", "パスワードが間違っています"), ("Do you want to enter again?", "もう一度入力しますか?"), ("Connection Error", "接続エラー"), ("Error", "エラー"), - ("Reset by the peer", "相手がリセットしました"), + ("Reset by the peer", "接続先によって接続がリセットされました"), ("Connecting...", "接続中..."), ("Connection in progress. Please wait.", "接続中です。しばらくお待ちください。"), ("Please try 1 minute later", "1分後にもう一度お試しください"), ("Login Error", "ログインエラー"), ("Successful", "成功"), - ("Connected, waiting for image...", "接続完了、画像を取得中..."), + ("Connected, waiting for image...", "接続完了、映像を待機しています..."), ("Name", "名前"), ("Type", "種類"), ("Modified", "最終更新"), @@ -120,7 +120,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Original", "オリジナル"), ("Shrink", "縮小"), ("Stretch", "伸縮"), - ("Scrollbar", ""), + ("Scrollbar", "スクロールバー"), ("ScrollAuto", ""), ("Good image quality", "画質優先"), ("Balanced", "バランス"), @@ -136,7 +136,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("ID does not exist", "IDが存在しません"), ("Failed to connect to rendezvous server", "ランデブーサーバーに接続できませんでした"), ("Please try later", "後でもう一度お試しください"), - ("Remote desktop is offline", "リモート側デスクトップがオフラインです"), + ("Remote desktop is offline", "リモート側のデスクトップがオフラインです"), ("Key mismatch", "キーが一致しません"), ("Timeout", "タイムアウト"), ("Failed to connect to relay server", "中継サーバーに接続できませんでした"), @@ -162,7 +162,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept and Install", "同意してインストール"), ("End-user license agreement", "エンドユーザー ライセンス条項"), ("Generating ...", "生成中 ..."), - ("Your installation is lower version.", "インストール済みのバージョンが古いです。"), + ("Your installation is lower version.", "インストールされているバージョン古くなっています。"), ("not_close_tcp_tip", "トンネルを使用中はこのウィンドウを閉じないでください"), ("Listening ...", "リッスン中 ..."), ("Remote Host", "リモートのホスト"), @@ -172,19 +172,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Local Port", "ローカルのポート"), ("Local Address", ""), ("Change Local Port", ""), - ("setup_server_tip", "接続をより速くするには、自分のサーバーをセットアップしてください"), - ("Too short, at least 6 characters.", "短すぎます。最低6文字です。"), - ("The confirmation is not identical.", "確認用と一致しません。"), + ("setup_server_tip", "より高速に接続したい場合は、自分のサーバーをセットアップすることをおすすめします"), + ("Too short, at least 6 characters.", "文字列が短すぎます。最低文字数は6文字です。"), + ("The confirmation is not identical.", "確認用の文字列と一致しません。"), ("Permissions", "権限"), ("Accept", "承諾"), ("Dismiss", "無視"), ("Disconnect", "切断"), ("Enable file copy and paste", "ファイルのコピーアンドペーストを許可"), ("Connected", "接続済み"), - ("Direct and encrypted connection", "接続は暗号化され、直接つながっている"), - ("Relayed and encrypted connection", "接続は暗号化され、中継されている"), - ("Direct and unencrypted connection", "接続は暗号化されてなく、直接つながっている"), - ("Relayed and unencrypted connection", "接続は暗号化されてなく、中継されている"), + ("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...", "ログイン中..."), @@ -214,10 +214,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always connect via relay", "常に中継サーバー経由で接続"), ("whitelist_tip", "ホワイトリストに登録されたIPからのみ接続を許可します"), ("Login", "ログイン"), - ("Verify", ""), - ("Remember me", ""), - ("Trust this device", ""), - ("Verification code", ""), + ("Verify", "認証"), + ("Remember me", "入力内容を記憶する"), + ("Trust this device", "このデバイスを信頼する"), + ("Verification code", "認証コード"), ("verification_tip", ""), ("Logout", "ログアウト"), ("Tags", "タグ"), @@ -230,7 +230,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Username missed", "ユーザー名がありません"), ("Password missed", "パスワードがありません"), ("Wrong credentials", "資格情報が間違っています"), - ("The verification code is incorrect or has expired", ""), + ("The verification code is incorrect or has expired", "認証コードが間違っているか、有効期限が切れています"), ("Edit Tag", "タグを編集"), ("Forget Password", "パスワードの記憶を解除"), ("Favorites", "お気に入り"), @@ -244,7 +244,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("install_daemon_tip", "起動時に開始するには、システムサービスをインストールする必要があります。"), ("Remote ID", "リモートのID"), ("Paste", "ペースト"), - ("Paste here?", "ここにペースト?"), + ("Paste here?", "ここに貼り付けますか?"), ("Are you sure to close the connection?", "本当に切断しますか?"), ("Download new version", "新しいバージョンをダウンロード"), ("Touch mode", "タッチモード"), @@ -281,14 +281,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Open System Setting", "端末設定を開く"), ("How to get Android input permission?", "Androidの入力権限を取得するには?"), ("android_input_permission_tip1", "このAndroid端末をリモートの端末からマウスやタッチで操作するには、RustDeskに「アクセシビリティ」サービスの使用を許可する必要があります。"), - ("android_input_permission_tip2", "次の端末設定ページに進み、「インストール済みアプリ」から「RestDesk Input」をオンにしてください。"), + ("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", ""), + ("Account", "アカウント"), ("Overwrite", "上書き"), ("This file exists, skip or overwrite this file?", "このファイルは存在しています。スキップするか上書きしますか?"), ("Quit", "終了"), @@ -305,13 +305,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Language", "言語"), ("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"), ("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"), - ("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"), + ("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」のチェックを外してください"), ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "接続が許可されていません"), - ("Legacy mode", ""), + ("Legacy mode", "レガシーモード"), ("Map mode", ""), - ("Translate mode", ""), + ("Translate mode", "翻訳モード"), ("Use permanent password", "固定のパスワードを使用"), ("Use both passwords", "どちらのパスワードも使用"), ("Set permanent password", "固定のパスワードを設定"), @@ -320,7 +320,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Are you sure you want to restart", "本当に再起動しますか"), ("Restarting remote device", "リモート端末を再起動中"), ("remote_restarting_tip", "リモート端末は再起動中です。このメッセージボックスを閉じて、しばらくした後に固定のパスワードを使用して再接続してください。"), - ("Copied", ""), + ("Copied", "コピーしました"), ("Exit Fullscreen", "全画面表示を終了"), ("Fullscreen", "全画面表示"), ("Mobile Actions", "モバイル アクション"), @@ -330,82 +330,82 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ratio", "比率"), ("Image Quality", "画質"), ("Scroll Style", "スクロール スタイル"), - ("Show Toolbar", ""), - ("Hide Toolbar", ""), + ("Show Toolbar", "ツールバーを表示"), + ("Hide Toolbar", "ツールバーを隠す"), ("Direct Connection", "直接接続"), - ("Relay 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", ""), + ("General", "一般"), + ("Security", "セキュリティ"), + ("Theme", "テーマ"), + ("Dark Theme", "ダークテーマ"), + ("Light Theme", "ライトテーマ"), + ("Dark", "ダーク"), + ("Light", "ライト"), + ("Follow System", "システム設定に従う"), + ("Enable hardware codec", "ハードウェアコーデックを有効化"), ("Unlock Security Settings", ""), - ("Enable audio", ""), + ("Enable audio", "オーディオを有効化"), ("Unlock Network Settings", ""), - ("Server", ""), - ("Direct IP Access", ""), - ("Proxy", ""), - ("Apply", ""), - ("Disconnect all devices?", ""), - ("Clear", ""), - ("Audio Input Device", ""), - ("Use IP Whitelisting", ""), - ("Network", ""), - ("Pin Toolbar", ""), - ("Unpin Toolbar", ""), - ("Recording", ""), - ("Directory", ""), - ("Automatically record incoming sessions", ""), - ("Change", ""), - ("Start session recording", ""), - ("Stop session recording", ""), - ("Enable recording session", ""), + ("Server", "サーバー"), + ("Direct IP Access", "IPへ直接接続"), + ("Proxy", "プロキシ"), + ("Apply", "適用"), + ("Disconnect all devices?", "すべてのデバイスから切断しますか?"), + ("Clear", "クリア"), + ("Audio Input Device", "オーディオ入力デバイス"), + ("Use IP Whitelisting", "IPホワイトリストを使用する"), + ("Network", "ネットワーク"), + ("Pin Toolbar", "ツールバーをピン留め"), + ("Unpin Toolbar", "ツールバーのピン留めを外す"), + ("Recording", "録画中"), + ("Directory", "ディレクトリ"), + ("Automatically record incoming sessions", "受信したセッションの自動録画"), + ("Change", "変更"), + ("Start session recording", "セッションの録画を開始"), + ("Stop session recording", "セッションの録画を停止"), + ("Enable recording session", "セッションの録画を有効化"), ("Enable LAN discovery", ""), ("Deny LAN discovery", ""), ("Write a message", ""), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), ("elevated_foreground_window_tip", ""), - ("Disconnected", ""), - ("Other", "他の"), + ("Disconnected", "切断済み"), + ("Other", "その他"), ("Confirm before closing multiple tabs", "同時に複数のタブを閉じる前に確認する"), - ("Keyboard Settings", ""), - ("Full Access", ""), + ("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 デスクトップを試すか、OS を変更してください。"), ("JumpLink", "View"), - ("Please Select the screen to be shared(Operate on the peer side).", "共有する画面を選択してください(ピア側で操作)。"), - ("Show RustDesk", ""), - ("This PC", ""), - ("or", ""), - ("Continue with", ""), + ("Please Select the screen to be shared(Operate on the peer side).", "共有する画面を選択してください(接続先が操作します)"), + ("Show RustDesk", "RustDeskを表示"), + ("This PC", "この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", ""), + ("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", ""), ("Right click to select tabs", ""), ("Skipped", ""), ("Add to address book", ""), - ("Group", ""), - ("Search", ""), + ("Group", "グループ"), + ("Search", "検索"), ("Closed manually by web console", ""), ("Local keyboard type", ""), ("Select local keyboard type", ""), @@ -423,57 +423,57 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Request Elevation", ""), ("wait_accept_uac_tip", ""), ("Elevate successfully", ""), - ("uppercase", ""), - ("lowercase", ""), + ("uppercase", "大文字"), + ("lowercase", "小文字"), ("digit", ""), - ("special character", ""), - ("length>=8", ""), - ("Weak", ""), - ("Medium", ""), - ("Strong", ""), + ("special character", "特殊文字"), + ("length>=8", "8文字以上"), + ("Weak", "脆弱"), + ("Medium", "普通"), + ("Strong", "強い"), ("Switch Sides", ""), - ("Please confirm if you want to share your desktop?", ""), - ("Display", ""), + ("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", ""), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), + ("Default Image Quality", "デフォルトの画質"), + ("Default Codec", "デフォルトのコーデック"), + ("Bitrate", "ビットレート"), + ("FPS", "FPS"), + ("Auto", "自動"), + ("Other Default Options", "その他のデフォルト設定"), + ("Voice call", "音声通話"), + ("Text chat", "テキストチャット"), + ("Stop voice call", "音声通話を終了"), + ("relay_hint_tip", "直接接続が行えない場合は、リレー経由での接続をお試しください。初回接続で中継接続を行いたい場合は末尾に「/r」を付けるか、最近のセッション履歴に「常に中継サーバー経由で接続」という設定がある場合はそちらを選択してください。"), + ("Reconnect", "再接続"), + ("Codec", "コーデック"), + ("Resolution", "解像度"), ("No transfers in progress", ""), ("Set one-time password length", ""), - ("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", ""), - ("Me", ""), + ("Empty Username", "空のユーザー名"), + ("Empty Password", "空のパスワード"), + ("Me", "あなた"), ("identical_file_tip", ""), ("show_monitors_tip", ""), - ("View Mode", ""), + ("View Mode", "閲覧モード"), ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), ("os_account_desk_tip", ""), - ("OS Account", ""), + ("OS Account", "OSアカウント"), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), @@ -481,20 +481,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), - ("System Sound", ""), - ("Default", ""), + ("System Sound", "システム音声"), + ("Default", "デフォルト"), ("New RDP", ""), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), - ("Select a peer", ""), - ("Select peers", ""), - ("Plugins", ""), - ("Uninstall", ""), - ("Update", ""), - ("Enable", ""), - ("Disable", ""), - ("Options", ""), + ("Fingerprint", "フィンガープリント"), + ("Copy Fingerprint", "フィンガープリントをコピー"), + ("no fingerprints", "フィンガープリントがありません"), + ("Select a peer", "接続先を選択"), + ("Select peers", "複数の接続先を選択"), + ("Plugins", "プラグイン"), + ("Uninstall", "アンインストール"), + ("Update", "更新"), + ("Enable", "有効"), + ("Disable", "無効"), + ("Options", "設定"), ("resolution_original_tip", ""), ("resolution_fit_local_tip", ""), ("resolution_custom_tip", ""), @@ -504,25 +504,25 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("clipboard_wait_response_timeout_tip", ""), ("Incoming connection", ""), ("Outgoing connection", ""), - ("Exit", ""), - ("Open", ""), + ("Exit", "終了"), + ("Open", "開く"), ("logout_tip", ""), - ("Service", ""), - ("Start", ""), - ("Stop", ""), + ("Service", "サービス"), + ("Start", "開始"), + ("Stop", "停止"), ("exceed_max_devices", ""), ("Sync with recent sessions", ""), - ("Sort tags", ""), + ("Sort tags", "タグで並べ替え"), ("Open connection in new tab", ""), ("Move tab to new window", ""), - ("Can not be empty", ""), - ("Already exists", ""), - ("Change Password", ""), - ("Refresh Password", ""), + ("Can not be empty", "空にすることはできません"), + ("Already exists", "すでに存在します"), + ("Change Password", "パスワードを変更"), + ("Refresh Password", "パスワードをリフレッシュ"), ("ID", ""), ("Grid View", ""), ("List View", ""), - ("Select", ""), + ("Select", "選択"), ("Toggle Tags", ""), ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), @@ -537,9 +537,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("scam_title", ""), ("scam_text1", ""), ("scam_text2", ""), - ("Don't show again", ""), - ("I Agree", ""), - ("Decline", ""), + ("Don't show again", "今後表示しない"), + ("I Agree", "同意します"), + ("Decline", "同意しません"), ("Timeout in minutes", ""), ("auto_disconnect_option_tip", ""), ("Connection failed due to inactivity", ""), @@ -550,7 +550,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Remove wallpaper during incoming sessions", ""), ("Test", ""), ("display_is_plugged_out_msg", ""), - ("No displays", ""), + ("No displays", "ディスプレイがありません"), ("Open in new window", ""), ("Show displays as individual windows", ""), ("Use all my displays for the remote session", ""), @@ -574,7 +574,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Swap control-command key", ""), ("swap-left-right-mouse", ""), ("2FA code", ""), - ("More", ""), + ("More", "詳細"), ("enable-2fa-title", ""), ("enable-2fa-desc", ""), ("wrong-2fa-code", ""), @@ -588,13 +588,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("preset_password_warning", ""), ("Security Alert", ""), ("My address book", ""), - ("Personal", ""), + ("Personal", "個人"), ("Owner", ""), ("Set shared password", ""), ("Exist in", ""), - ("Read-only", ""), - ("Read/Write", ""), - ("Full Control", ""), + ("Read-only", "読み取り専用"), + ("Read/Write", "読み取り/書き込み"), + ("Full Control", "完全コントロール"), ("share_warning_tip", ""), ("Everyone", ""), ("ab_web_console_tip", ""), @@ -604,8 +604,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow remote window focus", ""), ("default_proxy_tip", ""), ("no_audio_input_device_tip", ""), - ("Incoming", ""), - ("Outgoing", ""), + ("Incoming", "受信"), + ("Outgoing", "発信"), ("Clear Wayland screen selection", ""), ("clear_Wayland_screen_selection_tip", ""), ("confirm_clear_Wayland_screen_selection_tip", ""), @@ -619,11 +619,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("During controlled", ""), ("During service is on", ""), ("Capture screen using DirectX", ""), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), + ("Back", "戻る"), + ("Apps", "アプリ"), + ("Volume up", "音量アップ"), + ("Volume down", "音量ダウン"), + ("Power", "電源"), ("Telegram bot", ""), ("enable-bot-tip", ""), ("enable-bot-desc", ""), From 875ac28ab5b169f22f88bde8d076ad17bb44d5fd Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Wed, 17 Jul 2024 19:13:03 +0800 Subject: [PATCH 285/335] fix: macos, remove useless workaround (#8738) Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 8 ++++++++ flutter/lib/desktop/pages/remote_page.dart | 7 ------- flutter/pubspec.lock | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index ff406766bcc..d623f13322d 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -628,6 +628,14 @@ jobs: channel: "stable" flutter-version: ${{ env.FLUTTER_VERSION }} + - name: Workaround for flutter issue + shell: bash + run: | + cd "$(dirname "$(which flutter)")" + # https://github.com/flutter/flutter/issues/133533 + sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart + grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 with: diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 050d37d393d..f108f74445c 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_hbb/main.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @@ -417,12 +416,6 @@ class _RemotePageState extends State } void leaveView(PointerExitEvent evt) { - if (isMacOS) { - if (kWindowId != null) { - DesktopMultiWindow.hideShow(kWindowId!); - } - } - if (_ffi.ffiModel.keyboard) { _ffi.inputModel.tryMoveEdgeOnExit(evt.position); } diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 2fb3d0b62ef..94f0671afc3 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: "c9ac8e78f8e8f0a554062c2c13cdeb644af2c25b" + resolved-ref: c9ac8e78f8e8f0a554062c2c13cdeb644af2c25b url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From f0587796e2f5c4ae0ba8dc58db9b615250639056 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 17 Jul 2024 20:10:56 +0800 Subject: [PATCH 286/335] OPTION_PRESET_STRATEGY_NAME, OPTION_PRESET_USERNAME --- libs/hbb_common/src/config.rs | 4 ++++ src/hbbs_http/sync.rs | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 07e6fee1b7b..0f31c9c09f5 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2096,6 +2096,8 @@ pub mod keys { pub const OPTION_KEY: &str = "key"; pub const OPTION_PRESET_ADDRESS_BOOK_NAME: &str = "preset-address-book-name"; pub const OPTION_PRESET_ADDRESS_BOOK_TAG: &str = "preset-address-book-tag"; + pub const OPTION_PRESET_USERNAME: &str = "preset-username"; + pub const OPTION_PRESET_STRATEGY_NAME: &str = "preset-strategy-name"; pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture"; pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str = "enable-android-software-encoding-half-scale"; @@ -2236,6 +2238,8 @@ pub mod keys { OPTION_ENABLE_DIRECTX_CAPTURE, OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE, OPTION_DISABLE_UDP, + OPTION_PRESET_USERNAME, + OPTION_PRESET_STRATEGY_NAME, ]; } diff --git a/src/hbbs_http/sync.rs b/src/hbbs_http/sync.rs index f92d3b3872c..a1e1a25681e 100644 --- a/src/hbbs_http/sync.rs +++ b/src/hbbs_http/sync.rs @@ -7,7 +7,7 @@ use std::{ #[cfg(not(any(target_os = "ios")))] use crate::Connection; use hbb_common::{ - config::{Config, LocalConfig}, + config::{keys, Config, LocalConfig}, tokio::{self, sync::broadcast, time::Instant}, }; use serde::{Deserialize, Serialize}; @@ -83,13 +83,21 @@ async fn start_hbbs_sync_async() { v["version"] = json!(crate::VERSION); v["id"] = json!(id); v["uuid"] = json!(crate::encode64(hbb_common::get_uuid())); - let ab_name = Config::get_option("preset-address-book-name"); + let ab_name = Config::get_option(keys::OPTION_PRESET_ADDRESS_BOOK_NAME); if !ab_name.is_empty() { - v["preset-address-book-name"] = json!(ab_name); + v[keys::OPTION_PRESET_ADDRESS_BOOK_NAME] = json!(ab_name); } - let ab_tag = Config::get_option("preset-address-book-tag"); + let ab_tag = Config::get_option(keys::OPTION_PRESET_ADDRESS_BOOK_TAG); if !ab_tag.is_empty() { - v["preset-address-book-tag"] = json!(ab_tag); + v[keys::OPTION_PRESET_ADDRESS_BOOK_TAG] = json!(ab_tag); + } + let username = Config::get_option(keys::OPTION_PRESET_USERNAME); + if !username.is_empty() { + v[keys::OPTION_PRESET_USERNAME] = json!(username); + } + let strategy_name = Config::get_option(keys::OPTION_PRESET_STRATEGY_NAME); + if !strategy_name.is_empty() { + v[keys::OPTION_PRESET_STRATEGY_NAME] = json!(strategy_name); } match crate::post_request(url.replace("heartbeat", "sysinfo"), v.to_string(), "").await { Ok(x) => { From 9e931a6f04a1f00982a2df5b8f63d9e827feb0d0 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 17 Jul 2024 21:17:54 +0800 Subject: [PATCH 287/335] change remove-preset-password-warning default value to Y --- flutter/lib/common.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 0c46836e4e7..759a306395f 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3307,8 +3307,8 @@ Widget buildPresetPasswordWarning() { return Text( 'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error } else if (snapshot.hasData && snapshot.data == true) { - if (bind.mainGetLocalOption(key: "remove-preset-password-warning") == - 'Y') { + if (bind.mainGetLocalOption(key: "remove-preset-password-warning") != + 'N') { return SizedBox.shrink(); } return Container( From b68d7a3054a45a608ddc188f455d0846452abc39 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 17 Jul 2024 22:37:05 +0800 Subject: [PATCH 288/335] fix linux armv7 ffmpeg arch, linux x64 sciter add hwcodec feature (#8744) Signed-off-by: 21pages --- .github/workflows/flutter-build.yml | 4 +++- libs/scrap/build.rs | 3 +-- res/vcpkg/ffmpeg/portfile.cmake | 11 ++++++++--- vcpkg.json | 4 ++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index d623f13322d..75e21a6c096 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1297,6 +1297,7 @@ jobs: deb_arch: amd64, sciter_arch: x64, vcpkg-triplet: x64-linux, + extra_features: ",hwcodec", } - { arch: armv7, @@ -1306,6 +1307,7 @@ jobs: deb_arch: armhf, sciter_arch: arm32, vcpkg-triplet: arm-linux, + extra_features: "", } steps: - name: Export GitHub Actions cache environment variables @@ -1436,7 +1438,7 @@ jobs: python3 ./res/inline-sciter.py export VCPKG_ROOT=/opt/artifacts/vcpkg export CARGO_INCREMENTAL=0 - cargo build --features inline --release --bins --jobs 1 + cargo build --features inline${{ matrix.job.extra_features }} --release --bins --jobs 1 # package mkdir -p ./Release mv ./target/release/rustdesk ./Release/rustdesk diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index 84b8631f332..ec54705460a 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -192,6 +192,7 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s fn ffmpeg() { // ffmpeg let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let static_libs = vec!["avcodec", "avutil", "avformat"]; static_libs.iter().for_each(|lib| { find_package(lib); @@ -201,8 +202,6 @@ fn ffmpeg() { } // os - let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); - let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); let dyn_libs: Vec<&str> = if target_os == "windows" { ["User32", "bcrypt", "ole32", "advapi32"].to_vec() } else if target_os == "linux" { diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake index d8ea95bd258..dc35752ff8b 100644 --- a/res/vcpkg/ffmpeg/portfile.cmake +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -102,6 +102,10 @@ if(VCPKG_TARGET_IS_LINUX) string(APPEND OPTIONS "\ --target-os=linux \ --enable-pthreads \ +") + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") + else() + string(APPEND OPTIONS "\ --enable-cuda \ --enable-ffnvcodec \ --enable-encoder=h264_nvenc \ @@ -116,10 +120,11 @@ if(VCPKG_TARGET_IS_LINUX) --enable-encoder=h264_vaapi \ --enable-encoder=hevc_vaapi \ ") - if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") - string(APPEND OPTIONS "\ ---enable-cuda_llvm \ + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + string(APPEND OPTIONS "\ + --enable-cuda_llvm \ ") + endif() endif() elseif(VCPKG_TARGET_IS_WINDOWS) string(APPEND OPTIONS "\ diff --git a/vcpkg.json b/vcpkg.json index 6d4cbb7b213..f1d7036eb5f 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -69,12 +69,12 @@ "platform": "(windows & static)" } ], - "platform": "((windows | linux | osx) & static)" + "platform": "((windows | (linux & !arm32) | osx) & static)" }, { "name": "ffmpeg", "host": false, - "platform": "((android | ios) & static)" + "platform": "((android | ios | (linux & arm32)) & static)" } ], "vcpkg-configuration": { From 5a8c8cbf7cde7ae578f2b9593289d0ed43695a92 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 18 Jul 2024 09:46:44 +0800 Subject: [PATCH 289/335] hide-help-cards --- flutter/lib/desktop/pages/desktop_home_page.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 1f53172a530..4a38a080b08 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -410,6 +410,9 @@ class _DesktopHomePageState extends State } Future buildHelpCards() async { + if (bind.mainGetLocalOption(key: 'hide-help-cards') == 'Y') { + return const SizedBox(); + } if (!bind.isCustomClient() && updateUrl.isNotEmpty && !isCardClosed && From a72bc0fb28ce358172077ef6b0b66c5211189444 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 18 Jul 2024 09:46:56 +0800 Subject: [PATCH 290/335] hide-help-cards --- libs/hbb_common/src/config.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 0f31c9c09f5..6f99c554261 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2199,6 +2199,7 @@ pub mod keys { "hide-proxy-settings", "hide-username-on-card", OPTION_ALLOW_REMOTE_CM_MODIFICATION, + "hide-help-cards", ]; // DEFAULT_SETTINGS, OVERWRITE_SETTINGS pub const KEYS_SETTINGS: &[&str] = &[ From e9c8ba5393dbd3595618b9294764be4fe018b7e2 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:38:15 +0800 Subject: [PATCH 291/335] fix: macos, remove unused workaround (#8746) Signed-off-by: fufesou --- flutter/lib/utils/multi_window_manager.dart | 12 +----------- flutter/pubspec.lock | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 1a14ac77e17..191152c8625 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -141,17 +141,7 @@ class RustDeskMultiWindowManager { )); } if (isMacOS) { - Future.microtask(() { - windowController.show(); - // Manually simulate the hide/show event to fix the issue - // https://github.com/rustdesk/rustdesk/issues/8548 - // https://github.com/flutter/flutter/issues/133533 - // https://github.com/MixinNetwork/flutter-plugins/issues/289#issuecomment-1817665239 - // https://github.com/rustdesk/rustdesk/pull/8712#issuecomment-2229912473 - Future.delayed(const Duration(milliseconds: 300), () { - DesktopMultiWindow.hideShow(-1); - }); - }); + Future.microtask(() => windowController.show()); } registerActiveWindow(windowId); windows.add(windowId); diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 94f0671afc3..060606181bb 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -335,7 +335,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: c9ac8e78f8e8f0a554062c2c13cdeb644af2c25b + resolved-ref: "336308d86ec8b9640504a371b50ba500eb779363" url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window" source: git version: "0.1.0" From edc5d86ee7fb9a8c48c34873398e681a5b0588d6 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 18 Jul 2024 11:24:43 +0800 Subject: [PATCH 292/335] fix hide-help-cards and fix https://github.com/rustdesk/rustdesk/issues/8687 --- flutter/lib/desktop/pages/desktop_home_page.dart | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 4a38a080b08..addfacec3fe 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -410,9 +410,6 @@ class _DesktopHomePageState extends State } Future buildHelpCards() async { - if (bind.mainGetLocalOption(key: 'hide-help-cards') == 'Y') { - return const SizedBox(); - } if (!bind.isCustomClient() && updateUrl.isNotEmpty && !isCardClosed && @@ -446,14 +443,14 @@ class _DesktopHomePageState extends State }); } } else if (isMacOS) { - if (!(bind.isOutgoingOnly() || - bind.mainIsCanScreenRecording(prompt: false))) { + final isOutgoingOnly = bind.isOutgoingOnly(); + if (!(isOutgoingOnly || bind.mainIsCanScreenRecording(prompt: false))) { return buildInstallCard("Permissions", "config_screen", "Configure", () async { bind.mainIsCanScreenRecording(prompt: true); watchIsCanScreenRecording = true; }, help: 'Help', link: translate("doc_mac_permission")); - } else if (!bind.mainIsProcessTrusted(prompt: false)) { + } else if (!isOutgoingOnly && !bind.mainIsProcessTrusted(prompt: false)) { return buildInstallCard("Permissions", "config_acc", "Configure", () async { bind.mainIsProcessTrusted(prompt: true); @@ -465,7 +462,8 @@ class _DesktopHomePageState extends State bind.mainIsCanInputMonitoring(prompt: true); watchIsInputMonitoring = true; }, help: 'Help', link: translate("doc_mac_permission")); - } else if (!svcStopped.value && + } else if (!isOutgoingOnly && + !svcStopped.value && bind.mainIsInstalled() && !bind.mainIsInstalledDaemon(prompt: false)) { return buildInstallCard("", "install_daemon_tip", "Install", () async { @@ -548,6 +546,10 @@ class _DesktopHomePageState extends State String? link, bool? closeButton, String? closeOption}) { + if (bind.mainGetLocalOption(key: 'hide-help-cards') == 'Y' && + content != 'install_daemon_tip') { + return const SizedBox(); + } void closeCard() async { if (closeOption != null) { await bind.mainSetLocalOption(key: closeOption, value: 'N'); From 48464835f5e7cceca736fa1825aeb07a5883f972 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 18 Jul 2024 13:18:21 +0800 Subject: [PATCH 293/335] fix linux ffmpeg link, still link ffmpeg in hwcodec (#8747) Signed-off-by: 21pages --- Cargo.lock | 4 ++-- libs/scrap/build.rs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27706797eab..db61225d5ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3087,8 +3087,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.5.0" -source = "git+https://github.com/21pages/hwcodec#470d18826dc334b964c51acf0c6ac3bcf56f56ad" +version = "0.5.1" +source = "git+https://github.com/21pages/hwcodec#74e8288f776a9d43861f16aa62e86b57c7209868" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index ec54705460a..55a68863381 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -189,6 +189,8 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s } // If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci +// Linux require link in hwcodec +/* fn ffmpeg() { // ffmpeg let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); @@ -230,6 +232,7 @@ fn ffmpeg() { println!("cargo:rustc-link-lib=framework=AVFoundation"); } } +*/ fn main() { // note: all link symbol names in x86 (32-bit) are prefixed wth "_". @@ -247,7 +250,7 @@ fn main() { gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*"); gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*"); gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*"); - ffmpeg(); + // ffmpeg(); // there is problem with cfg(target_os) in build.rs, so use our workaround let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); From a81d6468ccd22fa26497db5c0a09220829900bf8 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:57:36 +0800 Subject: [PATCH 294/335] fix: desktop settings, disconnect, timeout (#8749) Signed-off-by: fufesou --- flutter/lib/desktop/pages/desktop_setting_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index a8ed3f082f3..c0a2f476268 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1220,7 +1220,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { width: 95, child: TextField( controller: controller, - enabled: enabled && !locked && isOptFixed, + enabled: enabled && !locked && !isOptFixed, onChanged: (_) => applyEnabled.value = true, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp( From c3c99ba10725158eaf37fb7fbf7665526138bb88 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 18 Jul 2024 17:16:25 +0800 Subject: [PATCH 295/335] fix missing retry (#8750) Signed-off-by: 21pages --- flutter/lib/common.dart | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 759a306395f..3e5689d3530 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1096,21 +1096,33 @@ void msgBox(SessionID sessionId, String type, String title, String text, dialogManager.dismissAll(); })); } - if (reconnect != null && - title == "Connection Error" && - reconnectTimeout != null) { + if (reconnect != null && title == "Connection Error") { // `enabled` is used to disable the dialog button once the button is clicked. final enabled = true.obs; - final button = Obx(() => _ReconnectCountDownButton( - second: reconnectTimeout, - onPressed: enabled.isTrue - ? () { - // Disable the button - enabled.value = false; - reconnect(dialogManager, sessionId, false); - } - : null, - )); + final button = reconnectTimeout != null + ? Obx(() => _ReconnectCountDownButton( + second: reconnectTimeout, + onPressed: enabled.isTrue + ? () { + // Disable the button + enabled.value = false; + reconnect(dialogManager, sessionId, false); + } + : null, + )) + : Obx( + () => dialogButton( + 'Reconnect', + isOutline: true, + onPressed: enabled.isTrue + ? () { + // Disable the button + enabled.value = false; + reconnect(dialogManager, sessionId, false); + } + : null, + ), + ); buttons.insert(0, button); } if (link.isNotEmpty) { From 2be05608d8f46c2f77d91d7955ad92e82242e4bb Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 18 Jul 2024 22:23:45 +0800 Subject: [PATCH 296/335] fix: windows, headless, not work when exiting RDP (#8753) Signed-off-by: fufesou --- src/server/display_service.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/server/display_service.rs b/src/server/display_service.rs index 71772468c64..b4abdecfa4a 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -414,10 +414,23 @@ pub fn try_get_displays_(add_amyuni_headless: bool) -> ResultType> return Ok(displays); } - // If is switching session, no displays may be detected. But it is not a real case. - if displays.is_empty() && crate::platform::desktop_changed() { - return Ok(displays); - } + // The following code causes a bug. + // The virtual display cannot be added when there's no session(eg. when exiting from RDP). + // Because `crate::platform::desktop_changed()` always returns true at that time. + // + // The code only solves a rare case: + // 1. The control side is connecting. + // 2. The windows session is switching, no displays are detected, but they're there. + // Then the controlled side plugs in a virtual display for "headless". + // + // No need to do the following check. But the code is kept here for marking the issue. + // If there're someones reporting the issue, we may add a better check by waiting for a while. (switching session). + // But I don't think it's good to add the timeout check without any issue. + // + // If is switching session, no displays may be detected. + // if displays.is_empty() && crate::platform::desktop_changed() { + // return Ok(displays); + // } let no_displays_v = no_displays(&displays); virtual_display_manager::set_can_plug_out_all(!no_displays_v); From 04c175c62e7ec9b0c07eaad18474c6e3a8182381 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 18 Jul 2024 22:27:31 +0800 Subject: [PATCH 297/335] fix custom client settings tab visibility (#8752) * fix custom client settings tab visibility only control with tabKeys Signed-off-by: 21pages * fix preset-user-name typo Signed-off-by: 21pages --------- Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 49 ++++++++++++------- libs/hbb_common/src/config.rs | 2 +- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index c0a2f476268..c69ba186ab0 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -61,9 +61,13 @@ class DesktopSettingPage extends StatefulWidget { final SettingsTabKey initialTabkey; static final List tabKeys = [ SettingsTabKey.general, - if (!bind.isOutgoingOnly() && !bind.isDisableSettings()) + if (!bind.isOutgoingOnly() && + !bind.isDisableSettings() && + bind.mainGetLocalOption(key: "hide-security-settings") != 'Y') SettingsTabKey.safety, - if (!bind.isDisableSettings()) SettingsTabKey.network, + if (!bind.isDisableSettings() && + bind.mainGetLocalOption(key: "hide-network-settings") != 'Y') + SettingsTabKey.network, if (!bind.isIncomingOnly()) SettingsTabKey.display, if (!isWeb && !bind.isIncomingOnly() && bind.pluginFeatureIsEnabled()) SettingsTabKey.plugin, @@ -173,21 +177,32 @@ class _DesktopSettingPageState extends State } List _children() { - final hideSecurity = - bind.mainGetLocalOption(key: "hide-security-settings") == 'Y'; - final hideNetwork = - bind.mainGetLocalOption(key: "hide-network-settings") == 'Y'; - final children = [ - _General(), - if (!bind.isOutgoingOnly() && !bind.isDisableSettings() && !hideSecurity) - _Safety(), - if (!bind.isDisableSettings() && !hideNetwork) _Network(), - if (!bind.isIncomingOnly()) _Display(), - if (!isWeb && !bind.isIncomingOnly() && bind.pluginFeatureIsEnabled()) - _Plugin(), - if (!bind.isDisableAccount()) _Account(), - _About(), - ]; + final children = List.empty(growable: true); + for (final tab in DesktopSettingPage.tabKeys) { + switch (tab) { + case SettingsTabKey.general: + children.add(const _General()); + break; + case SettingsTabKey.safety: + children.add(const _Safety()); + break; + case SettingsTabKey.network: + children.add(const _Network()); + break; + case SettingsTabKey.display: + children.add(const _Display()); + break; + case SettingsTabKey.plugin: + children.add(const _Plugin()); + break; + case SettingsTabKey.account: + children.add(const _Account()); + break; + case SettingsTabKey.about: + children.add(const _About()); + break; + } + } return children; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6f99c554261..51a33a00ba9 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2096,7 +2096,7 @@ pub mod keys { pub const OPTION_KEY: &str = "key"; pub const OPTION_PRESET_ADDRESS_BOOK_NAME: &str = "preset-address-book-name"; pub const OPTION_PRESET_ADDRESS_BOOK_TAG: &str = "preset-address-book-tag"; - pub const OPTION_PRESET_USERNAME: &str = "preset-username"; + pub const OPTION_PRESET_USERNAME: &str = "preset-user-name"; pub const OPTION_PRESET_STRATEGY_NAME: &str = "preset-strategy-name"; pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture"; pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str = From 5c16a8302e6c9c1381873aff45dba1487ee51d4f Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 18 Jul 2024 22:28:35 +0800 Subject: [PATCH 298/335] Revert vcpkg ffmpeg (#8751) * Revert "fix linux ffmpeg link, still link ffmpeg in hwcodec (#8747)" This reverts commit 48464835f5e7cceca736fa1825aeb07a5883f972. * Revert "fix linux armv7 ffmpeg arch, linux x64 sciter add hwcodec feature (#8744)" This reverts commit b68d7a3054a45a608ddc188f455d0846452abc39. * Revert "arm linux remove cuda-llvm option (#8735)" This reverts commit bc672b336705290ac9dafd35b44fb6378acda63c. * Reapply "try fix arm64 linux ci and publish error log (#8730)" (#8732) This reverts commit e283d33f288742a2a4de119d3e7e322371ec90b7. * Revert "try fix arm64 linux ci and publish error log (#8730)" This reverts commit a4565bf0da99b21d8ba3e38cad5e75118b79921d. * Revert "F-Droid: enable hwcodec for future builds (#8726)" This reverts commit 188f85b042595d045043d9c17211cba2b5e2dc4d. * Revert "remove unused vcpkg ffmpeg code (#8725)" This reverts commit 72c96f22b626bbb06bf603068737c2f359d290b1. * Revert "install ffmpeg lib with vcpkg (#8724)" This reverts commit 0143eaf60176f420b7dbc376acda6ebebff940df. * update hwcodec to reverted Signed-off-by: 21pages --------- Signed-off-by: 21pages --- .github/workflows/ci.yml | 6 +- .github/workflows/flutter-build.yml | 10 +- Cargo.lock | 6 +- flutter/build_fdroid.sh | 6 +- libs/scrap/build.rs | 47 -- res/vcpkg/ffmpeg/0002-fix-msvc-link.patch | 11 - .../ffmpeg/0003-fix-windowsinclude.patch | 13 - res/vcpkg/ffmpeg/0005-fix-nasm.patch | 55 -- .../ffmpeg/0012-Fix-ssl-110-detection.patch | 14 - res/vcpkg/ffmpeg/0013-define-WINVER.patch | 15 - ...dd-query_timeout-option-for-h264-hev.patch | 71 -- ...-amfenc-reconfig-when-bitrate-change.patch | 71 -- ...-release-7.0-s-qsvenc-update_bitrate.patch | 95 --- .../ffmpeg/5.1/0004-amf-colorspace.patch | 161 ---- ...1-android-mediacodec-encode-align-64.patch | 40 - res/vcpkg/ffmpeg/build.sh.in | 152 ---- res/vcpkg/ffmpeg/portfile.cmake | 689 ------------------ res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake | 47 -- res/vcpkg/ffmpeg/vcpkg.json | 44 -- vcpkg.json | 39 +- 20 files changed, 15 insertions(+), 1577 deletions(-) delete mode 100644 res/vcpkg/ffmpeg/0002-fix-msvc-link.patch delete mode 100644 res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch delete mode 100644 res/vcpkg/ffmpeg/0005-fix-nasm.patch delete mode 100644 res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch delete mode 100644 res/vcpkg/ffmpeg/0013-define-WINVER.patch delete mode 100644 res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch delete mode 100644 res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch delete mode 100644 res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch delete mode 100644 res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch delete mode 100644 res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch delete mode 100644 res/vcpkg/ffmpeg/build.sh.in delete mode 100644 res/vcpkg/ffmpeg/portfile.cmake delete mode 100644 res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake delete mode 100644 res/vcpkg/ffmpeg/vcpkg.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90f312968e0..41c83370528 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,9 @@ env: # MIN_SUPPORTED_RUST_VERSION: "1.46.0" # CICD_INTERMEDIATES_DIR: "_cicd-intermediates" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - # vcpkg version: 2024.06.15 + # vcpkg version: 2023.10.19 # for multiarch gcc compatibility - VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" + VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50" on: workflow_dispatch: @@ -112,8 +112,6 @@ jobs: libgstreamer-plugins-base1.0-dev \ libgtk-3-dev \ libpulse-dev \ - libva-dev \ - libvdpau-dev \ libxcb-randr0-dev \ libxcb-shape0-dev \ libxcb-xfixes0-dev \ diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 75e21a6c096..2cf992892ce 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -115,8 +115,6 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies - env: - VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }} run: | $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash @@ -257,8 +255,6 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies - env: - VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }} run: | $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash @@ -431,7 +427,7 @@ jobs: - name: Install dependencies run: | - brew install nasm yasm + brew install nasm - name: Checkout source code uses: actions/checkout@v3 - name: Install flutter @@ -1297,7 +1293,6 @@ jobs: deb_arch: amd64, sciter_arch: x64, vcpkg-triplet: x64-linux, - extra_features: ",hwcodec", } - { arch: armv7, @@ -1307,7 +1302,6 @@ jobs: deb_arch: armhf, sciter_arch: arm32, vcpkg-triplet: arm-linux, - extra_features: "", } steps: - name: Export GitHub Actions cache environment variables @@ -1438,7 +1432,7 @@ jobs: python3 ./res/inline-sciter.py export VCPKG_ROOT=/opt/artifacts/vcpkg export CARGO_INCREMENTAL=0 - cargo build --features inline${{ matrix.job.extra_features }} --release --bins --jobs 1 + cargo build --features inline --release --bins --jobs 1 # package mkdir -p ./Release mv ./target/release/rustdesk ./Release/rustdesk diff --git a/Cargo.lock b/Cargo.lock index db61225d5ac..e6a35c493c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3087,8 +3087,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.5.1" -source = "git+https://github.com/21pages/hwcodec#74e8288f776a9d43861f16aa62e86b57c7209868" +version = "0.6.0" +source = "git+https://github.com/21pages/hwcodec#89879f2f02c6f74e88a4a43744a1153aec5b7e7f" dependencies = [ "bindgen 0.59.2", "cc", @@ -3558,7 +3558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.52.5", + "windows-targets 0.48.5", ] [[package]] diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh index 2e0a20b6db6..abf311b2f9e 100755 --- a/flutter/build_fdroid.sh +++ b/flutter/build_fdroid.sh @@ -43,13 +43,15 @@ arm64-v8a) FLUTTER_TARGET=android-arm64 NDK_TARGET=aarch64-linux-android RUST_TARGET=aarch64-linux-android - RUSTDESK_FEATURES='flutter,hwcodec' +# RUSTDESK_FEATURES='flutter,hwcodec' + RUSTDESK_FEATURES='flutter' ;; armeabi-v7a) FLUTTER_TARGET=android-arm NDK_TARGET=arm-linux-androideabi RUST_TARGET=armv7-linux-androideabi - RUSTDESK_FEATURES='flutter,hwcodec' +# RUSTDESK_FEATURES='flutter,hwcodec' + RUSTDESK_FEATURES='flutter' ;; x86_64) FLUTTER_TARGET=android-x64 diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index 55a68863381..1612a6b5bfe 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -188,52 +188,6 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex); } -// If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci -// Linux require link in hwcodec -/* -fn ffmpeg() { - // ffmpeg - let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); - let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - let static_libs = vec!["avcodec", "avutil", "avformat"]; - static_libs.iter().for_each(|lib| { - find_package(lib); - }); - if target_os == "windows" { - println!("cargo:rustc-link-lib=static=libmfx"); - } - - // os - let dyn_libs: Vec<&str> = if target_os == "windows" { - ["User32", "bcrypt", "ole32", "advapi32"].to_vec() - } else if target_os == "linux" { - let mut v = ["va", "va-drm", "va-x11", "vdpau", "X11", "stdc++"].to_vec(); - if target_arch == "x86_64" { - v.push("z"); - } - v - } else if target_os == "macos" || target_os == "ios" { - ["c++", "m"].to_vec() - } else if target_os == "android" { - ["z", "m", "android", "atomic"].to_vec() - } else { - panic!("unsupported os"); - }; - dyn_libs - .iter() - .map(|lib| println!("cargo:rustc-link-lib={}", lib)) - .count(); - - if target_os == "macos" || target_os == "ios" { - println!("cargo:rustc-link-lib=framework=CoreFoundation"); - println!("cargo:rustc-link-lib=framework=CoreVideo"); - println!("cargo:rustc-link-lib=framework=CoreMedia"); - println!("cargo:rustc-link-lib=framework=VideoToolbox"); - println!("cargo:rustc-link-lib=framework=AVFoundation"); - } -} -*/ - fn main() { // note: all link symbol names in x86 (32-bit) are prefixed wth "_". // run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc, @@ -250,7 +204,6 @@ fn main() { gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*"); gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*"); gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*"); - // ffmpeg(); // there is problem with cfg(target_os) in build.rs, so use our workaround let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); diff --git a/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch b/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch deleted file mode 100644 index c9aa7e75237..00000000000 --- a/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff --git a/configure b/configure ---- a/configure -+++ b/configure -@@ -6162,6 +6162,7 @@ EOF - test -n "$extern_prefix" && append X86ASMFLAGS "-DPREFIX" - case "$objformat" in - elf*) enabled debug && append X86ASMFLAGS $x86asm_debug ;; -+ win*) enabled debug && append X86ASMFLAGS "-g" ;; - esac - - enabled avx512 && check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0" diff --git a/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch b/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch deleted file mode 100644 index 8b2e22b476f..00000000000 --- a/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c ---- a/fftools/cmdutils.c -+++ b/fftools/cmdutils.c -@@ -51,6 +51,8 @@ - #include "fopen_utf8.h" - #include "opt_common.h" - #ifdef _WIN32 -+#define _WIN32_WINNT 0x0502 -+#define WIN32_LEAN_AND_MEAN - #include - #include "compat/w32dlfcn.h" - #endif - diff --git a/res/vcpkg/ffmpeg/0005-fix-nasm.patch b/res/vcpkg/ffmpeg/0005-fix-nasm.patch deleted file mode 100644 index 9308e714a6b..00000000000 --- a/res/vcpkg/ffmpeg/0005-fix-nasm.patch +++ /dev/null @@ -1,55 +0,0 @@ -diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile ---- a/libavcodec/x86/Makefile -+++ b/libavcodec/x86/Makefile -@@ -158,6 +158,8 @@ X86ASM-OBJS-$(CONFIG_ALAC_DECODER) += x86/alacdsp.o - X86ASM-OBJS-$(CONFIG_APNG_DECODER) += x86/pngdsp.o - X86ASM-OBJS-$(CONFIG_CAVS_DECODER) += x86/cavsidct.o -+ifdef ARCH_X86_64 - X86ASM-OBJS-$(CONFIG_CFHD_ENCODER) += x86/cfhdencdsp.o -+endif - X86ASM-OBJS-$(CONFIG_CFHD_DECODER) += x86/cfhddsp.o - X86ASM-OBJS-$(CONFIG_DCA_DECODER) += x86/dcadsp.o x86/synth_filter.o - X86ASM-OBJS-$(CONFIG_DIRAC_DECODER) += x86/diracdsp.o \ -@@ -175,15 +177,21 @@ x86/hevc_sao_10bit.o - X86ASM-OBJS-$(CONFIG_JPEG2000_DECODER) += x86/jpeg2000dsp.o - X86ASM-OBJS-$(CONFIG_LSCR_DECODER) += x86/pngdsp.o -+ifdef ARCH_X86_64 - X86ASM-OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp.o -+endif - X86ASM-OBJS-$(CONFIG_MPEG4_DECODER) += x86/xvididct.o - X86ASM-OBJS-$(CONFIG_PNG_DECODER) += x86/pngdsp.o -+ifdef ARCH_X86_64 - X86ASM-OBJS-$(CONFIG_PRORES_DECODER) += x86/proresdsp.o - X86ASM-OBJS-$(CONFIG_PRORES_LGPL_DECODER) += x86/proresdsp.o -+endif - X86ASM-OBJS-$(CONFIG_RV40_DECODER) += x86/rv40dsp.o - X86ASM-OBJS-$(CONFIG_SBC_ENCODER) += x86/sbcdsp.o - X86ASM-OBJS-$(CONFIG_SVQ1_ENCODER) += x86/svq1enc.o - X86ASM-OBJS-$(CONFIG_TAK_DECODER) += x86/takdsp.o -+ifdef ARCH_X86_64 - X86ASM-OBJS-$(CONFIG_TRUEHD_DECODER) += x86/mlpdsp.o -+endif - X86ASM-OBJS-$(CONFIG_TTA_DECODER) += x86/ttadsp.o - X86ASM-OBJS-$(CONFIG_TTA_ENCODER) += x86/ttaencdsp.o - X86ASM-OBJS-$(CONFIG_UTVIDEO_DECODER) += x86/utvideodsp.o -diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile ---- a/libavfilter/x86/Makefile -+++ b/libavfilter/x86/Makefile -@@ -44,6 +44,8 @@ - X86ASM-OBJS-$(CONFIG_AFIR_FILTER) += x86/af_afir.o - X86ASM-OBJS-$(CONFIG_ANLMDN_FILTER) += x86/af_anlmdn.o -+ifdef ARCH_X86_64 - X86ASM-OBJS-$(CONFIG_ATADENOISE_FILTER) += x86/vf_atadenoise.o -+endif - X86ASM-OBJS-$(CONFIG_BLEND_FILTER) += x86/vf_blend.o - X86ASM-OBJS-$(CONFIG_BWDIF_FILTER) += x86/vf_bwdif.o - X86ASM-OBJS-$(CONFIG_COLORSPACE_FILTER) += x86/colorspacedsp.o -@@ -62,6 +62,8 @@ X86ASM-OBJS-$(CONFIG_LUT3D_FILTER) += x86/vf_lut3d.o - X86ASM-OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += x86/vf_maskedclamp.o - X86ASM-OBJS-$(CONFIG_MASKEDMERGE_FILTER) += x86/vf_maskedmerge.o -+ifdef ARCH_X86_64 - X86ASM-OBJS-$(CONFIG_NLMEANS_FILTER) += x86/vf_nlmeans.o -+endif - X86ASM-OBJS-$(CONFIG_OVERLAY_FILTER) += x86/vf_overlay.o - X86ASM-OBJS-$(CONFIG_PP7_FILTER) += x86/vf_pp7.o - X86ASM-OBJS-$(CONFIG_PSNR_FILTER) += x86/vf_psnr.o diff --git a/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch b/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch deleted file mode 100644 index b2e5501a13a..00000000000 --- a/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/configure b/configure -index 2be953f7e7..e075949ffc 100755 ---- a/configure -+++ b/configure -@@ -6497,6 +6497,7 @@ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0 - { enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } || - { enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } || - check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl || - check_pkg_config openssl openssl openssl/ssl.h SSL_library_init || -+ check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto $pthreads_extralibs -ldl || - check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto || - check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto || - check_lib openssl openssl/ssl.h SSL_library_init -lssl32 -leay32 || - diff --git a/res/vcpkg/ffmpeg/0013-define-WINVER.patch b/res/vcpkg/ffmpeg/0013-define-WINVER.patch deleted file mode 100644 index 295a738e74f..00000000000 --- a/res/vcpkg/ffmpeg/0013-define-WINVER.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --color -Naur src_old/libavcodec/mf_utils.c src/libavcodec/mf_utils.c ---- src_old/libavcodec/mf_utils.c 2020-07-11 05:26:17.000000000 +0700 -+++ src/libavcodec/mf_utils.c 2020-11-13 12:55:57.226976400 +0700 -@@ -22,6 +22,11 @@ - #define _WIN32_WINNT 0x0602 - #endif - -+#if !defined(WINVER) || WINVER < 0x0602 -+#undef WINVER -+#define WINVER 0x0602 -+#endif -+ - #include "mf_utils.h" - #include "libavutil/pixdesc.h" - diff --git a/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch b/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch deleted file mode 100644 index 245a470d39f..00000000000 --- a/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch +++ /dev/null @@ -1,71 +0,0 @@ -From f0b694749b38b2cfd94df4eed10e667342c234e5 Mon Sep 17 00:00:00 2001 -From: 21pages -Date: Sat, 24 Feb 2024 15:33:24 +0800 -Subject: [PATCH 1/2] avcodec/amfenc: add query_timeout option for h264/hevc - -Signed-off-by: 21pages ---- - libavcodec/amfenc.h | 1 + - libavcodec/amfenc_h264.c | 4 ++++ - libavcodec/amfenc_hevc.c | 4 ++++ - 3 files changed, 9 insertions(+) - -diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h -index 1ab98d2f78..e92120ea39 100644 ---- a/libavcodec/amfenc.h -+++ b/libavcodec/amfenc.h -@@ -87,6 +87,7 @@ typedef struct AmfContext { - int quality; - int b_frame_delta_qp; - int ref_b_frame_delta_qp; -+ int64_t query_timeout; - - // Dynamic options, can be set after Init() call - -diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c -index efb04589f6..f55dbc80f0 100644 ---- a/libavcodec/amfenc_h264.c -+++ b/libavcodec/amfenc_h264.c -@@ -121,6 +121,7 @@ static const AVOption options[] = { - { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - - { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, -+ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE }, - - { NULL } - }; -@@ -155,6 +156,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) - - AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMERATE, framerate); - -+ if (ctx->query_timeout >= 0) -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, ctx->query_timeout); -+ - switch (avctx->profile) { - case FF_PROFILE_H264_BASELINE: - profile = AMF_VIDEO_ENCODER_PROFILE_BASELINE; -diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c -index 8ab9330730..7a40bcad31 100644 ---- a/libavcodec/amfenc_hevc.c -+++ b/libavcodec/amfenc_hevc.c -@@ -89,6 +89,7 @@ static const AVOption options[] = { - { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, - - { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, -+ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE }, - { NULL } - }; - -@@ -122,6 +123,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) - - AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate); - -+ if (ctx->query_timeout >= 0) -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, ctx->query_timeout); -+ - switch (avctx->profile) { - case FF_PROFILE_HEVC_MAIN: - profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; --- -2.43.0.windows.1 - diff --git a/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch b/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch deleted file mode 100644 index 13b055ef289..00000000000 --- a/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 4d0d20d96ad458cfec0444b9be0182ca6085ee0c Mon Sep 17 00:00:00 2001 -From: 21pages -Date: Sat, 24 Feb 2024 16:02:44 +0800 -Subject: [PATCH 2/2] libavcodec/amfenc: reconfig when bitrate change - -Signed-off-by: 21pages ---- - libavcodec/amfenc.c | 20 ++++++++++++++++++++ - libavcodec/amfenc.h | 1 + - 2 files changed, 21 insertions(+) - -diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c -index a033e1220e..3eab01a903 100644 ---- a/libavcodec/amfenc.c -+++ b/libavcodec/amfenc.c -@@ -222,6 +222,7 @@ static int amf_init_context(AVCodecContext *avctx) - - ctx->hwsurfaces_in_queue = 0; - ctx->hwsurfaces_in_queue_max = 16; -+ ctx->av_bitrate = avctx->bit_rate; - - // configure AMF logger - // the return of these functions indicates old state and do not affect behaviour -@@ -575,6 +576,23 @@ static void amf_release_buffer_with_frame_ref(AMFBuffer *frame_ref_storage_buffe - frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer); - } - -+static int reconfig_encoder(AVCodecContext *avctx) -+{ -+ AmfContext *ctx = avctx->priv_data; -+ AMF_RESULT res = AMF_OK; -+ -+ if (ctx->av_bitrate != avctx->bit_rate) { -+ av_log(ctx, AV_LOG_INFO, "change bitrate from %d to %d\n", ctx->av_bitrate, avctx->bit_rate); -+ ctx->av_bitrate = avctx->bit_rate; -+ if (avctx->codec->id == AV_CODEC_ID_H264) { -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_TARGET_BITRATE, avctx->bit_rate); -+ } else if (avctx->codec->id == AV_CODEC_ID_HEVC) { -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, avctx->bit_rate); -+ } -+ } -+ return 0; -+} -+ - int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) - { - AmfContext *ctx = avctx->priv_data; -@@ -586,6 +604,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) - AVFrame *frame = ctx->delayed_frame; - int block_and_wait; - -+ reconfig_encoder(avctx); -+ - if (!ctx->encoder) - return AVERROR(EINVAL); - -diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h -index e92120ea39..31172645f2 100644 ---- a/libavcodec/amfenc.h -+++ b/libavcodec/amfenc.h -@@ -107,6 +107,7 @@ typedef struct AmfContext { - int me_half_pel; - int me_quarter_pel; - int aud; -+ int64_t av_bitrate; - - // HEVC - specific options - --- -2.43.0.windows.1 - diff --git a/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch b/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch deleted file mode 100644 index 475fb627f3e..00000000000 --- a/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch +++ /dev/null @@ -1,95 +0,0 @@ -From afe89a70f6bc7ebd0a6a0a31101801b88cbd60ee Mon Sep 17 00:00:00 2001 -From: 21pages -Date: Sun, 5 May 2024 12:45:23 +0800 -Subject: [PATCH] use release/7.0's update_bitrate - -Signed-off-by: 21pages ---- - libavcodec/qsvenc.c | 39 +++++++++++++++++++++++++++++++++++++++ - libavcodec/qsvenc.h | 6 ++++++ - 2 files changed, 45 insertions(+) - -diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c -index 2382c2f5f7..9b34f37eb3 100644 ---- a/libavcodec/qsvenc.c -+++ b/libavcodec/qsvenc.c -@@ -714,6 +714,11 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) - brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes), - initial_delay_in_kilobytes) + 0x10000) / 0x10000; - -+ q->old_rc_buffer_size = avctx->rc_buffer_size; -+ q->old_rc_initial_buffer_occupancy = avctx->rc_initial_buffer_occupancy; -+ q->old_bit_rate = avctx->bit_rate; -+ q->old_rc_max_rate = avctx->rc_max_rate; -+ - switch (q->param.mfx.RateControlMethod) { - case MFX_RATECONTROL_CBR: - case MFX_RATECONTROL_VBR: -@@ -1657,6 +1662,39 @@ static int update_qp(AVCodecContext *avctx, QSVEncContext *q, - return updated; - } - -+static int update_bitrate(AVCodecContext *avctx, QSVEncContext *q) -+{ -+ int updated = 0; -+ int target_bitrate_kbps, max_bitrate_kbps, brc_param_multiplier; -+ int buffer_size_in_kilobytes, initial_delay_in_kilobytes; -+ -+ UPDATE_PARAM(q->old_rc_buffer_size, avctx->rc_buffer_size); -+ UPDATE_PARAM(q->old_rc_initial_buffer_occupancy, avctx->rc_initial_buffer_occupancy); -+ UPDATE_PARAM(q->old_bit_rate, avctx->bit_rate); -+ UPDATE_PARAM(q->old_rc_max_rate, avctx->rc_max_rate); -+ if (!updated) -+ return 0; -+ -+ buffer_size_in_kilobytes = avctx->rc_buffer_size / 8000; -+ initial_delay_in_kilobytes = avctx->rc_initial_buffer_occupancy / 8000; -+ target_bitrate_kbps = avctx->bit_rate / 1000; -+ max_bitrate_kbps = avctx->rc_max_rate / 1000; -+ brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes), -+ initial_delay_in_kilobytes) + 0x10000) / 0x10000; -+ -+ q->param.mfx.BufferSizeInKB = buffer_size_in_kilobytes / brc_param_multiplier; -+ q->param.mfx.InitialDelayInKB = initial_delay_in_kilobytes / brc_param_multiplier; -+ q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier; -+ q->param.mfx.MaxKbps = max_bitrate_kbps / brc_param_multiplier; -+ q->param.mfx.BRCParamMultiplier = brc_param_multiplier; -+ av_log(avctx, AV_LOG_VERBOSE, -+ "Reset BufferSizeInKB: %d; InitialDelayInKB: %d; " -+ "TargetKbps: %d; MaxKbps: %d; BRCParamMultiplier: %d\n", -+ q->param.mfx.BufferSizeInKB, q->param.mfx.InitialDelayInKB, -+ q->param.mfx.TargetKbps, q->param.mfx.MaxKbps, q->param.mfx.BRCParamMultiplier); -+ return updated; -+} -+ - static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, - const AVFrame *frame) - { -@@ -1666,6 +1704,7 @@ static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, - return 0; - - needReset = update_qp(avctx, q, frame); -+ needReset |= update_bitrate(avctx, q); - if (!needReset) - return 0; - -diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h -index b754ac4b56..5745533165 100644 ---- a/libavcodec/qsvenc.h -+++ b/libavcodec/qsvenc.h -@@ -224,6 +224,12 @@ typedef struct QSVEncContext { - int min_qp_p; - int max_qp_b; - int min_qp_b; -+ -+ // These are used for bitrate control reset -+ int old_bit_rate; -+ int old_rc_buffer_size; -+ int old_rc_initial_buffer_occupancy; -+ int old_rc_max_rate; - } QSVEncContext; - - int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q); --- -2.43.0.windows.1 - diff --git a/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch b/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch deleted file mode 100644 index 49aef694795..00000000000 --- a/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch +++ /dev/null @@ -1,161 +0,0 @@ -From 8fd62e4ecd058b09abf8847be5fbbf0eef44a90f Mon Sep 17 00:00:00 2001 -From: 21pages -Date: Tue, 16 Jul 2024 14:58:33 +0800 -Subject: [PATCH] amf colorspace - -Signed-off-by: 21pages ---- - libavcodec/amfenc.h | 1 + - libavcodec/amfenc_h264.c | 39 +++++++++++++++++++++++++++++++++ - libavcodec/amfenc_hevc.c | 47 ++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 87 insertions(+) - -diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h -index 31172645f2..493e01603d 100644 ---- a/libavcodec/amfenc.h -+++ b/libavcodec/amfenc.h -@@ -23,6 +23,7 @@ - - #include - #include -+#include - - #include "libavutil/fifo.h" - -diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c -index f55dbc80f0..5a6b6e164f 100644 ---- a/libavcodec/amfenc_h264.c -+++ b/libavcodec/amfenc_h264.c -@@ -139,6 +139,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) - AMFRate framerate; - AMFSize framesize = AMFConstructSize(avctx->width, avctx->height); - int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0; -+ amf_int64 color_depth; -+ amf_int64 color_profile; -+ enum AVPixelFormat pix_fmt; - - if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { - framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); -@@ -199,11 +202,47 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) - AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_ASPECT_RATIO, ratio); - } - -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; - /// Color Range (Partial/TV/MPEG or Full/PC/JPEG) - if (avctx->color_range == AVCOL_RANGE_JPEG) { - AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 1); -+ switch (avctx->colorspace) { -+ case AVCOL_SPC_SMPTE170M: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; -+ break; -+ case AVCOL_SPC_BT709: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; -+ break; -+ case AVCOL_SPC_BT2020_NCL: -+ case AVCOL_SPC_BT2020_CL: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; -+ break; -+ } -+ } else { -+ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 0); -+ switch (avctx->colorspace) { -+ case AVCOL_SPC_SMPTE170M: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; -+ break; -+ case AVCOL_SPC_BT709: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; -+ break; -+ case AVCOL_SPC_BT2020_NCL: -+ case AVCOL_SPC_BT2020_CL: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; -+ break; -+ } -+ } -+ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format : avctx->pix_fmt; -+ color_depth = AMF_COLOR_BIT_DEPTH_8; -+ if (pix_fmt == AV_PIX_FMT_P010) { -+ color_depth = AMF_COLOR_BIT_DEPTH_10; - } - -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH, color_depth); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, color_profile); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); - // autodetect rate control method - if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN) { - if (ctx->qp_i != -1 || ctx->qp_p != -1 || ctx->qp_b != -1) { -diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c -index 7a40bcad31..0260f43c81 100644 ---- a/libavcodec/amfenc_hevc.c -+++ b/libavcodec/amfenc_hevc.c -@@ -106,6 +106,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) - AMFRate framerate; - AMFSize framesize = AMFConstructSize(avctx->width, avctx->height); - int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0; -+ amf_int64 color_depth; -+ amf_int64 color_profile; -+ enum AVPixelFormat pix_fmt; - - if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { - framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); -@@ -130,6 +133,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) - case FF_PROFILE_HEVC_MAIN: - profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; - break; -+ case FF_PROFILE_HEVC_MAIN_10: -+ profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10; -+ break; - default: - break; - } -@@ -158,6 +164,47 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) - AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO, ratio); - } - -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; -+ if (avctx->color_range == AVCOL_RANGE_JPEG) { -+ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, 1); -+ switch (avctx->colorspace) { -+ case AVCOL_SPC_SMPTE170M: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; -+ break; -+ case AVCOL_SPC_BT709: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; -+ break; -+ case AVCOL_SPC_BT2020_NCL: -+ case AVCOL_SPC_BT2020_CL: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; -+ break; -+ } -+ } else { -+ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, 0); -+ switch (avctx->colorspace) { -+ case AVCOL_SPC_SMPTE170M: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; -+ break; -+ case AVCOL_SPC_BT709: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; -+ break; -+ case AVCOL_SPC_BT2020_NCL: -+ case AVCOL_SPC_BT2020_CL: -+ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; -+ break; -+ } -+ } -+ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format : avctx->pix_fmt; -+ color_depth = AMF_COLOR_BIT_DEPTH_8; -+ if (pix_fmt == AV_PIX_FMT_P010) { -+ color_depth = AMF_COLOR_BIT_DEPTH_10; -+ } -+ -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, color_depth); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE, color_profile); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); -+ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); -+ - // Picture control properties - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, ctx->gops_per_idr); - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, avctx->gop_size); --- -2.43.0.windows.1 - diff --git a/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch b/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch deleted file mode 100644 index d46c54af6da..00000000000 --- a/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch +++ /dev/null @@ -1,40 +0,0 @@ -From be3d9d8092720bbe4239212648d2e9c4ffd7f40c Mon Sep 17 00:00:00 2001 -From: 21pages -Date: Wed, 22 May 2024 17:09:28 +0800 -Subject: [PATCH] android mediacodec encode align 64 - -Signed-off-by: 21pages ---- - libavcodec/mediacodecenc.c | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c -index 984014f1b1..8dcd3dcd64 100644 ---- a/libavcodec/mediacodecenc.c -+++ b/libavcodec/mediacodecenc.c -@@ -200,16 +200,17 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) - ff_AMediaFormat_setString(format, "mime", codec_mime); - // Workaround the alignment requirement of mediacodec. We can't do it - // silently for AV_PIX_FMT_MEDIACODEC. -+ const int align = 64; - if (avctx->pix_fmt != AV_PIX_FMT_MEDIACODEC) { -- s->width = FFALIGN(avctx->width, 16); -- s->height = FFALIGN(avctx->height, 16); -+ s->width = FFALIGN(avctx->width, align); -+ s->height = FFALIGN(avctx->height, align); - } else { - s->width = avctx->width; - s->height = avctx->height; -- if (s->width % 16 || s->height % 16) -+ if (s->width % align || s->height % align) - av_log(avctx, AV_LOG_WARNING, -- "Video size %dx%d isn't align to 16, it may have device compatibility issue\n", -- s->width, s->height); -+ "Video size %dx%d isn't align to %d, it may have device compatibility issue\n", -+ s->width, s->height, align); - } - ff_AMediaFormat_setInt32(format, "width", s->width); - ff_AMediaFormat_setInt32(format, "height", s->height); --- -2.34.1 - diff --git a/res/vcpkg/ffmpeg/build.sh.in b/res/vcpkg/ffmpeg/build.sh.in deleted file mode 100644 index 462737587b7..00000000000 --- a/res/vcpkg/ffmpeg/build.sh.in +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env bash - -set -e - -export PATH="$PATH:/usr/bin" - -command -v cygpath >/dev/null && have_cygpath=1 - -cygpath() { - if [ -n "$have_cygpath" ]; then - command cygpath "$@" - else - eval _p='$'$# - printf '%s\n' "$_p" - fi -} - -move_binary() { - SOURCE=$1 - TARGET=$2 - BINARY=$3 - - # run lipo over the command to check whether it really - # is a binary that we need to merge architectures - lipo $SOURCE/$BINARY -info &> /dev/null || return 0 - - # get the directory name the file is in - DIRNAME=$(dirname $BINARY) - - # ensure the directory to move the binary to exists - mkdir -p $TARGET/$DIRNAME - - # now finally move the binary - mv $SOURCE/$BINARY $TARGET/$BINARY -} - -move_binaries() { - SOURCE=$1 - TARGET=$2 - - [ ! -d $SOURCE ] && return 0 - pushd $SOURCE - - for BINARY in $(find . -type f); do - move_binary $SOURCE $TARGET $BINARY - done - - popd -} - -merge_binaries() { - TARGET=$1 - SOURCE=$2 - - shift - shift - - pushd $SOURCE/$1 - BINARIES=$(find . -type f) - popd - - for BINARY in $BINARIES; do - COMMAND="lipo -create -output $TARGET/$BINARY" - - for ARCH in $@; do - COMMAND="$COMMAND -arch $ARCH $SOURCE/$ARCH/$BINARY" - done - - $($COMMAND) - done -} - -export PKG_CONFIG_PATH="$(cygpath -p "${PKG_CONFIG_PATH}")" - -# Export HTTP(S)_PROXY as http(s)_proxy: -[ -n "$HTTP_PROXY" ] && export http_proxy="$HTTP_PROXY" -[ -n "$HTTPS_PROXY" ] && export https_proxy="$HTTPS_PROXY" - -PATH_TO_BUILD_DIR=$( cygpath "@BUILD_DIR@") -PATH_TO_SRC_DIR=$( cygpath "@SOURCE_PATH@") -PATH_TO_PACKAGE_DIR=$(cygpath "@INST_PREFIX@") - -JOBS=@VCPKG_CONCURRENCY@ - -OSX_ARCHS="@OSX_ARCHS@" -OSX_ARCH_COUNT=0@OSX_ARCH_COUNT@ - -# Default to hardware concurrency if unset. -: ${JOBS:=$(nproc)} - -# Disable asm and x86asm on all android targets because they trigger build failures: -# arm64 Android build fails with 'relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol ff_cos_32; recompile with -fPIC' -# x86 Android build fails with 'error: inline assembly requires more registers than available'. -# x64 Android build fails with 'relocation R_X86_64_PC32 cannot be used against symbol ff_h264_cabac_tables; recompile with -fPIC' -if [ "@VCPKG_CMAKE_SYSTEM_NAME@" = "Android" ]; then - OPTIONS_arm=" --disable-asm --disable-x86asm" - OPTIONS_arm64=" --disable-asm --disable-x86asm" - OPTIONS_x86=" --disable-asm --disable-x86asm" - OPTIONS_x86_64="${OPTIONS_x86}" -else - OPTIONS_arm=" --disable-asm --disable-x86asm" - OPTIONS_arm64=" --enable-asm --disable-x86asm" - OPTIONS_x86=" --enable-asm --enable-x86asm" - OPTIONS_x86_64="${OPTIONS_x86}" -fi - -build_ffmpeg() { - # extract build architecture - BUILD_ARCH=$1 - shift - - echo "BUILD_ARCH=${BUILD_ARCH}" - - # get architecture-specific options - OPTION_VARIABLE="OPTIONS_${BUILD_ARCH}" - echo "OPTION_VARIABLE=${OPTION_VARIABLE}" - - echo "=== CONFIGURING ===" - - sh "$PATH_TO_SRC_DIR/configure" "--prefix=$PATH_TO_PACKAGE_DIR" @CONFIGURE_OPTIONS@ --arch=${BUILD_ARCH} ${!OPTION_VARIABLE} $@ - - echo "=== BUILDING ===" - - make -j${JOBS} V=1 - - echo "=== INSTALLING ===" - - make install -} - -cd "$PATH_TO_BUILD_DIR" - -if [ $OSX_ARCH_COUNT -gt 0 ]; then - for ARCH in $OSX_ARCHS; do - echo "=== CLEANING FOR $ARCH ===" - - make clean && make distclean - - build_ffmpeg $ARCH --extra-cflags=-arch --extra-cflags=$ARCH --extra-ldflags=-arch --extra-ldflags=$ARCH - - echo "=== COLLECTING BINARIES FOR $ARCH ===" - - move_binaries $PATH_TO_PACKAGE_DIR/lib $PATH_TO_BUILD_DIR/stage/$ARCH/lib - move_binaries $PATH_TO_PACKAGE_DIR/bin $PATH_TO_BUILD_DIR/stage/$ARCH/bin - done - - echo "=== MERGING ARCHITECTURES ===" - - merge_binaries $PATH_TO_PACKAGE_DIR $PATH_TO_BUILD_DIR/stage $OSX_ARCHS -else - build_ffmpeg @BUILD_ARCH@ -fi diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake deleted file mode 100644 index dc35752ff8b..00000000000 --- a/res/vcpkg/ffmpeg/portfile.cmake +++ /dev/null @@ -1,689 +0,0 @@ -if(VCPKG_TARGET_IS_WINDOWS OR VCPKG_TARGET_IS_LINUX) - set(FF_VERSION "n5.1.5") - set(FF_SHA512 "a933f18e53207ccc277b42c9a68db00f31cefec555e6d5d7c57db3409023b2c38fd93ebe2ccfcd17ba2397adb912e93f2388241ca970b7d8bd005ccfe86d5679") -else() - set(FF_VERSION "n7.0.1") - set(FF_SHA512 "1212ebcb78fdaa103b0304373d374e41bf1fe680e1fa4ce0f60624857491c26b4dda004c490c3ef32d4a0e10f42ae6b54546f9f318e2dcfbaa116117f687bc88") -endif() - -vcpkg_from_github( - OUT_SOURCE_PATH SOURCE_PATH - REPO ffmpeg/ffmpeg - REF "${FF_VERSION}" - SHA512 "${FF_SHA512}" - HEAD_REF master - PATCHES - 0002-fix-msvc-link.patch # upstreamed in future version - 0003-fix-windowsinclude.patch - 0005-fix-nasm.patch # upstreamed in future version - 0012-Fix-ssl-110-detection.patch - 0013-define-WINVER.patch -) - -if(VCPKG_TARGET_IS_WINDOWS OR VCPKG_TARGET_IS_LINUX) - vcpkg_apply_patches( - SOURCE_PATH ${SOURCE_PATH} - PATCHES - ${CMAKE_CURRENT_LIST_DIR}/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch - ${CMAKE_CURRENT_LIST_DIR}/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch - ${CMAKE_CURRENT_LIST_DIR}/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch - ${CMAKE_CURRENT_LIST_DIR}/5.1/0004-amf-colorspace.patch - ) -elseif(VCPKG_TARGET_IS_ANDROID) - vcpkg_apply_patches( - SOURCE_PATH ${SOURCE_PATH} - PATCHES - ${CMAKE_CURRENT_LIST_DIR}/7.0/0001-android-mediacodec-encode-align-64.patch - ) -endif() - -if(SOURCE_PATH MATCHES " ") - message(FATAL_ERROR "Error: ffmpeg will not build with spaces in the path. Please use a directory with no spaces") -endif() - -if(NOT VCPKG_TARGET_ARCHITECTURE STREQUAL "wasm32") - vcpkg_find_acquire_program(NASM) - get_filename_component(NASM_EXE_PATH "${NASM}" DIRECTORY) - vcpkg_add_to_path("${NASM_EXE_PATH}") -endif() - -set(OPTIONS "\ ---disable-shared \ ---enable-static \ ---enable-pic \ ---disable-everything \ ---disable-programs \ ---disable-doc \ ---disable-htmlpages \ ---disable-manpages \ ---disable-podpages \ ---disable-txtpages \ ---disable-network \ ---disable-appkit \ ---disable-coreimage \ ---disable-metal \ ---disable-sdl2 \ ---disable-securetransport \ ---disable-vulkan \ ---disable-audiotoolbox \ ---disable-v4l2-m2m \ ---disable-debug \ ---disable-valgrind-backtrace \ ---disable-large-tests \ ---disable-avdevice \ ---enable-avcodec \ ---enable-avformat \ ---disable-avfilter \ ---disable-swresample \ ---disable-swscale \ ---disable-postproc \ ---enable-decoder=h264 \ ---enable-decoder=hevc \ ---enable-parser=h264 \ ---enable-parser=hevc \ ---enable-bsf=h264_mp4toannexb \ ---enable-bsf=hevc_mp4toannexb \ ---enable-bsf=h264_metadata \ ---enable-bsf=hevc_metadata \ ---enable-muxer=mp4 \ ---enable-protocol=file \ -") - -if(VCPKG_HOST_IS_WINDOWS) - vcpkg_acquire_msys(MSYS_ROOT PACKAGES automake1.16) - set(SHELL "${MSYS_ROOT}/usr/bin/bash.exe") - vcpkg_add_to_path("${MSYS_ROOT}/usr/share/automake-1.16") - string(APPEND OPTIONS " --pkg-config=${CURRENT_HOST_INSTALLED_DIR}/tools/pkgconf/pkgconf${VCPKG_HOST_EXECUTABLE_SUFFIX}") -else() - find_program(SHELL bash) -endif() - -if(VCPKG_TARGET_IS_LINUX) - string(APPEND OPTIONS "\ ---target-os=linux \ ---enable-pthreads \ -") - if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") - else() - string(APPEND OPTIONS "\ ---enable-cuda \ ---enable-ffnvcodec \ ---enable-encoder=h264_nvenc \ ---enable-encoder=hevc_nvenc \ ---enable-hwaccel=h264_nvdec \ ---enable-hwaccel=hevc_nvdec \ ---enable-amf \ ---enable-encoder=h264_amf \ ---enable-encoder=hevc_amf \ ---enable-hwaccel=h264_vaapi \ ---enable-hwaccel=hevc_vaapi \ ---enable-encoder=h264_vaapi \ ---enable-encoder=hevc_vaapi \ -") - if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") - string(APPEND OPTIONS "\ - --enable-cuda_llvm \ -") - endif() - endif() -elseif(VCPKG_TARGET_IS_WINDOWS) - string(APPEND OPTIONS "\ ---target-os=win32 \ ---toolchain=msvc \ ---enable-gpl \ ---enable-d3d11va \ ---enable-cuda \ ---enable-ffnvcodec \ ---enable-hwaccel=h264_nvdec \ ---enable-hwaccel=hevc_nvdec \ ---enable-hwaccel=h264_d3d11va \ ---enable-hwaccel=hevc_d3d11va \ ---enable-hwaccel=h264_d3d11va2 \ ---enable-hwaccel=hevc_d3d11va2 \ ---enable-amf \ ---enable-encoder=h264_amf \ ---enable-encoder=hevc_amf \ ---enable-encoder=h264_nvenc \ ---enable-encoder=hevc_nvenc \ ---enable-libmfx \ ---enable-encoder=h264_qsv \ ---enable-encoder=hevc_qsv \ -") - if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") - set(LIB_MACHINE_ARG /machine:x86) - string(APPEND OPTIONS " --arch=i686 --enable-cross-compile") - elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") - set(LIB_MACHINE_ARG /machine:x64) - string(APPEND OPTIONS " --arch=x86_64") - else() - message(FATAL_ERROR "Unsupported target architecture") - endif() -elseif(VCPKG_TARGET_IS_OSX) - string(APPEND OPTIONS "\ ---disable-autodetect \ ---enable-videotoolbox \ ---enable-encoder=h264_videotoolbox,hevc_videotoolbox \ ---enable-hwaccel=h264_videotoolbox,hevc_videotoolbox \ -") -elseif(VCPKG_TARGET_IS_IOS) - string(APPEND OPTIONS "\ ---arch=arm64 \ ---disable-autodetect \ ---disable-hwaccels \ ---disable-encoders \ ---disable-videotoolbox \ ---extra-cflags=\"-arch arm64 -mios-version-min=8.0 -fembed-bitcode\" \ ---extra-ldflags=\"-arch arm64 -mios-version-min=8.0 -fembed-bitcode\" \ -") -elseif(VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Android") - string(APPEND OPTIONS "\ ---target-os=android \ ---disable-asm \ ---enable-jni \ ---enable-mediacodec \ ---disable-hwaccels \ ---enable-encoder=h264_mediacodec \ ---enable-encoder=hevc_mediacodec \ ---enable-decoder=h264_mediacodec \ ---enable-decoder=hevc_mediacodec \ -") -endif() - -if(VCPKG_TARGET_IS_OSX) - list(JOIN VCPKG_OSX_ARCHITECTURES " " OSX_ARCHS) - list(LENGTH VCPKG_OSX_ARCHITECTURES OSX_ARCH_COUNT) -endif() - -vcpkg_cmake_get_vars(cmake_vars_file) -include("${cmake_vars_file}") - -if(VCPKG_DETECTED_MSVC) - string(APPEND OPTIONS " --disable-inline-asm") # clang-cl has inline assembly but this leads to undefined symbols. - set(OPTIONS "--toolchain=msvc ${OPTIONS}") - - # This is required because ffmpeg depends upon optimizations to link correctly - string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -O2") - string(REGEX REPLACE "(^| )-RTC1( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") - string(REGEX REPLACE "(^| )-Od( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") - string(REGEX REPLACE "(^| )-Ob0( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") -endif() - -string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -I \"${CURRENT_INSTALLED_DIR}/include\"") -string(APPEND VCPKG_COMBINED_C_FLAGS_RELEASE " -I \"${CURRENT_INSTALLED_DIR}/include\"") - -# # Setup vcpkg toolchain -set(prog_env "") - -if(VCPKG_DETECTED_CMAKE_C_COMPILER) - get_filename_component(CC_path "${VCPKG_DETECTED_CMAKE_C_COMPILER}" DIRECTORY) - get_filename_component(CC_filename "${VCPKG_DETECTED_CMAKE_C_COMPILER}" NAME) - set(ENV{CC} "${CC_filename}") - string(APPEND OPTIONS " --cc=${CC_filename}") - - # string(APPEND OPTIONS " --host_cc=${CC_filename}") ffmpeg not yet setup for cross builds? - list(APPEND prog_env "${CC_path}") -endif() - -if(VCPKG_DETECTED_CMAKE_CXX_COMPILER) - get_filename_component(CXX_path "${VCPKG_DETECTED_CMAKE_CXX_COMPILER}" DIRECTORY) - get_filename_component(CXX_filename "${VCPKG_DETECTED_CMAKE_CXX_COMPILER}" NAME) - set(ENV{CXX} "${CXX_filename}") - string(APPEND OPTIONS " --cxx=${CXX_filename}") - - # string(APPEND OPTIONS " --host_cxx=${CC_filename}") - list(APPEND prog_env "${CXX_path}") -endif() - -if(VCPKG_DETECTED_CMAKE_RC_COMPILER) - get_filename_component(RC_path "${VCPKG_DETECTED_CMAKE_RC_COMPILER}" DIRECTORY) - get_filename_component(RC_filename "${VCPKG_DETECTED_CMAKE_RC_COMPILER}" NAME) - set(ENV{WINDRES} "${RC_filename}") - string(APPEND OPTIONS " --windres=${RC_filename}") - list(APPEND prog_env "${RC_path}") -endif() - -if(VCPKG_DETECTED_CMAKE_LINKER AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW) - get_filename_component(LD_path "${VCPKG_DETECTED_CMAKE_LINKER}" DIRECTORY) - get_filename_component(LD_filename "${VCPKG_DETECTED_CMAKE_LINKER}" NAME) - set(ENV{LD} "${LD_filename}") - string(APPEND OPTIONS " --ld=${LD_filename}") - - # string(APPEND OPTIONS " --host_ld=${LD_filename}") - list(APPEND prog_env "${LD_path}") -endif() - -if(VCPKG_DETECTED_CMAKE_NM) - get_filename_component(NM_path "${VCPKG_DETECTED_CMAKE_NM}" DIRECTORY) - get_filename_component(NM_filename "${VCPKG_DETECTED_CMAKE_NM}" NAME) - set(ENV{NM} "${NM_filename}") - string(APPEND OPTIONS " --nm=${NM_filename}") - list(APPEND prog_env "${NM_path}") -endif() - -if(VCPKG_DETECTED_CMAKE_AR) - get_filename_component(AR_path "${VCPKG_DETECTED_CMAKE_AR}" DIRECTORY) - get_filename_component(AR_filename "${VCPKG_DETECTED_CMAKE_AR}" NAME) - - if(AR_filename MATCHES [[^(llvm-)?lib\.exe$]]) - set(ENV{AR} "ar-lib ${AR_filename}") - string(APPEND OPTIONS " --ar='ar-lib ${AR_filename}'") - else() - set(ENV{AR} "${AR_filename}") - string(APPEND OPTIONS " --ar='${AR_filename}'") - endif() - - list(APPEND prog_env "${AR_path}") -endif() - -if(VCPKG_DETECTED_CMAKE_RANLIB) - get_filename_component(RANLIB_path "${VCPKG_DETECTED_CMAKE_RANLIB}" DIRECTORY) - get_filename_component(RANLIB_filename "${VCPKG_DETECTED_CMAKE_RANLIB}" NAME) - set(ENV{RANLIB} "${RANLIB_filename}") - string(APPEND OPTIONS " --ranlib=${RANLIB_filename}") - list(APPEND prog_env "${RANLIB_path}") -endif() - -if(VCPKG_DETECTED_CMAKE_STRIP) - get_filename_component(STRIP_path "${VCPKG_DETECTED_CMAKE_STRIP}" DIRECTORY) - get_filename_component(STRIP_filename "${VCPKG_DETECTED_CMAKE_STRIP}" NAME) - set(ENV{STRIP} "${STRIP_filename}") - string(APPEND OPTIONS " --strip=${STRIP_filename}") - list(APPEND prog_env "${STRIP_path}") -endif() - -list(REMOVE_DUPLICATES prog_env) -vcpkg_add_to_path(PREPEND ${prog_env}) - -# More? OBJCC BIN2C -file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg" "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") - -set(FFMPEG_PKGCONFIG_MODULES libavutil) - -set(OPTIONS_CROSS "--enable-cross-compile") - -# ffmpeg needs --cross-prefix option to use appropriate tools for cross-compiling. -if(VCPKG_DETECTED_CMAKE_C_COMPILER MATCHES "([^\/]*-)gcc$") - string(APPEND OPTIONS_CROSS " --cross-prefix=${CMAKE_MATCH_1}") -endif() - -if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") - set(BUILD_ARCH "x86_64") -else() - set(BUILD_ARCH ${VCPKG_TARGET_ARCHITECTURE}) -endif() - -if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm" OR VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") - if(VCPKG_TARGET_IS_WINDOWS) - vcpkg_find_acquire_program(GASPREPROCESSOR) - - foreach(GAS_PATH ${GASPREPROCESSOR}) - get_filename_component(GAS_ITEM_PATH ${GAS_PATH} DIRECTORY) - vcpkg_add_to_path("${GAS_ITEM_PATH}") - endforeach(GAS_PATH) - endif() -endif() - -set(OPTIONS_DEBUG "--disable-optimizations") -set(OPTIONS_RELEASE "--enable-optimizations") - -set(OPTIONS "${OPTIONS} ${OPTIONS_CROSS}") - -if(VCPKG_TARGET_IS_MINGW) - set(OPTIONS "${OPTIONS} --extra_cflags=-D_WIN32_WINNT=0x0601") -elseif(VCPKG_TARGET_IS_WINDOWS) - set(OPTIONS "${OPTIONS} --extra-cflags=-DHAVE_UNISTD_H=0") -endif() - -vcpkg_find_acquire_program(PKGCONFIG) -set(OPTIONS "${OPTIONS} --pkg-config=${PKGCONFIG}") - -if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") - set(OPTIONS "${OPTIONS} --pkg-config-flags=--static") -endif() - -message(STATUS "Building Options: ${OPTIONS}") - -# Release build -if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") - if(VCPKG_DETECTED_MSVC) - set(OPTIONS_RELEASE "${OPTIONS_RELEASE} --extra-ldflags=-libpath:\"${CURRENT_INSTALLED_DIR}/lib\"") - else() - set(OPTIONS_RELEASE "${OPTIONS_RELEASE} --extra-ldflags=-L\"${CURRENT_INSTALLED_DIR}/lib\"") - endif() - - message(STATUS "Building Release Options: ${OPTIONS_RELEASE}") - set(ENV{PKG_CONFIG_PATH} "${CURRENT_INSTALLED_DIR}/lib/pkgconfig") - message(STATUS "Building ${PORT} for Release") - file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") - - # We use response files here as the only known way to handle spaces in paths - set(crsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/cflags.rsp") - string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_C_FLAGS_RELEASE_SANITIZED "${VCPKG_COMBINED_C_FLAGS_RELEASE}") - file(WRITE "${crsp}" "${VCPKG_COMBINED_C_FLAGS_RELEASE_SANITIZED}") - set(ldrsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/ldflags.rsp") - string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE_SANITIZED "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE}") - file(WRITE "${ldrsp}" "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE_SANITIZED}") - set(ENV{CFLAGS} "@${crsp}") - - # All tools except the msvc arm{,64} assembler accept @... as response file syntax. - # For that assembler, there is no known way to pass in flags. We must hope that not passing flags will work acceptably. - if(NOT VCPKG_DETECTED_MSVC OR NOT VCPKG_TARGET_ARCHITECTURE MATCHES "^arm") - set(ENV{ASFLAGS} "@${crsp}") - endif() - - set(ENV{LDFLAGS} "@${ldrsp}") - set(ENV{ARFLAGS} "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_RELEASE}") - - set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") - set(CONFIGURE_OPTIONS "${OPTIONS} ${OPTIONS_RELEASE}") - set(INST_PREFIX "${CURRENT_PACKAGES_DIR}") - - configure_file("${CMAKE_CURRENT_LIST_DIR}/build.sh.in" "${BUILD_DIR}/build.sh" @ONLY) - - z_vcpkg_setup_pkgconfig_path(CONFIG RELEASE) - - vcpkg_execute_required_process( - COMMAND "${SHELL}" ./build.sh - WORKING_DIRECTORY "${BUILD_DIR}" - LOGNAME "build-${TARGET_TRIPLET}-rel" - SAVE_LOG_FILES ffbuild/config.log - ) - - z_vcpkg_restore_pkgconfig_path() -endif() - -# Debug build -if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - if(VCPKG_DETECTED_MSVC) - set(OPTIONS_DEBUG "${OPTIONS_DEBUG} --extra-ldflags=-libpath:\"${CURRENT_INSTALLED_DIR}/debug/lib\"") - else() - set(OPTIONS_DEBUG "${OPTIONS_DEBUG} --extra-ldflags=-L\"${CURRENT_INSTALLED_DIR}/debug/lib\"") - endif() - - message(STATUS "Building Debug Options: ${OPTIONS_DEBUG}") - set(ENV{LDFLAGS} "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG}") - set(ENV{PKG_CONFIG_PATH} "${CURRENT_INSTALLED_DIR}/debug/lib/pkgconfig") - message(STATUS "Building ${PORT} for Debug") - file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg") - set(crsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/cflags.rsp") - string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_C_FLAGS_DEBUG_SANITIZED "${VCPKG_COMBINED_C_FLAGS_DEBUG}") - file(WRITE "${crsp}" "${VCPKG_COMBINED_C_FLAGS_DEBUG_SANITIZED}") - set(ldrsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/ldflags.rsp") - string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG_SANITIZED "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG}") - file(WRITE "${ldrsp}" "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG_SANITIZED}") - set(ENV{CFLAGS} "@${crsp}") - - if(NOT VCPKG_DETECTED_MSVC OR NOT VCPKG_TARGET_ARCHITECTURE MATCHES "^arm") - set(ENV{ASFLAGS} "@${crsp}") - endif() - - set(ENV{LDFLAGS} "@${ldrsp}") - set(ENV{ARFLAGS} "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_DEBUG}") - - set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg") - set(CONFIGURE_OPTIONS "${OPTIONS} ${OPTIONS_DEBUG}") - set(INST_PREFIX "${CURRENT_PACKAGES_DIR}/debug") - - configure_file("${CMAKE_CURRENT_LIST_DIR}/build.sh.in" "${BUILD_DIR}/build.sh" @ONLY) - - z_vcpkg_setup_pkgconfig_path(CONFIG DEBUG) - - vcpkg_execute_required_process( - COMMAND "${SHELL}" ./build.sh - WORKING_DIRECTORY "${BUILD_DIR}" - LOGNAME "build-${TARGET_TRIPLET}-dbg" - SAVE_LOG_FILES ffbuild/config.log - ) - - z_vcpkg_restore_pkgconfig_path() -endif() - -if(VCPKG_TARGET_IS_WINDOWS) - file(GLOB DEF_FILES "${CURRENT_PACKAGES_DIR}/lib/*.def" "${CURRENT_PACKAGES_DIR}/debug/lib/*.def") - - if(NOT VCPKG_TARGET_IS_MINGW) - if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") - set(LIB_MACHINE_ARG /machine:ARM) - elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") - set(LIB_MACHINE_ARG /machine:ARM64) - elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") - set(LIB_MACHINE_ARG /machine:x86) - elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") - set(LIB_MACHINE_ARG /machine:x64) - else() - message(FATAL_ERROR "Unsupported target architecture") - endif() - - foreach(DEF_FILE ${DEF_FILES}) - get_filename_component(DEF_FILE_DIR "${DEF_FILE}" DIRECTORY) - get_filename_component(DEF_FILE_NAME "${DEF_FILE}" NAME) - string(REGEX REPLACE "-[0-9]*\\.def" "${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}" OUT_FILE_NAME "${DEF_FILE_NAME}") - file(TO_NATIVE_PATH "${DEF_FILE}" DEF_FILE_NATIVE) - file(TO_NATIVE_PATH "${DEF_FILE_DIR}/${OUT_FILE_NAME}" OUT_FILE_NATIVE) - message(STATUS "Generating ${OUT_FILE_NATIVE}") - vcpkg_execute_required_process( - COMMAND lib.exe "/def:${DEF_FILE_NATIVE}" "/out:${OUT_FILE_NATIVE}" ${LIB_MACHINE_ARG} - WORKING_DIRECTORY "${CURRENT_PACKAGES_DIR}" - LOGNAME "libconvert-${TARGET_TRIPLET}" - ) - endforeach() - endif() - - file(GLOB EXP_FILES "${CURRENT_PACKAGES_DIR}/lib/*.exp" "${CURRENT_PACKAGES_DIR}/debug/lib/*.exp") - file(GLOB LIB_FILES "${CURRENT_PACKAGES_DIR}/bin/*${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}" "${CURRENT_PACKAGES_DIR}/debug/bin/*${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}") - - if(VCPKG_TARGET_IS_MINGW) - file(GLOB LIB_FILES_2 "${CURRENT_PACKAGES_DIR}/bin/*.lib" "${CURRENT_PACKAGES_DIR}/debug/bin/*.lib") - endif() - - set(files_to_remove ${EXP_FILES} ${LIB_FILES} ${LIB_FILES_2} ${DEF_FILES}) - - if(files_to_remove) - file(REMOVE ${files_to_remove}) - endif() -endif() - -file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include" "${CURRENT_PACKAGES_DIR}/debug/share") - -if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") - file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin") -endif() - -vcpkg_copy_pdbs() - -if(VCPKG_TARGET_IS_WINDOWS) - set(_dirs "/") - - if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") - list(APPEND _dirs "/debug/") - endif() - - foreach(_debug IN LISTS _dirs) - foreach(PKGCONFIG_MODULE IN LISTS FFMPEG_PKGCONFIG_MODULES) - set(PKGCONFIG_FILE "${CURRENT_PACKAGES_DIR}${_debug}lib/pkgconfig/${PKGCONFIG_MODULE}.pc") - - # remove redundant cygwin style -libpath entries - execute_process( - COMMAND "${MSYS_ROOT}/usr/bin/cygpath.exe" -u "${CURRENT_INSTALLED_DIR}" - OUTPUT_VARIABLE CYG_INSTALLED_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - vcpkg_replace_string("${PKGCONFIG_FILE}" "-libpath:${CYG_INSTALLED_DIR}${_debug}lib/pkgconfig/../../lib " "") - - # transform libdir, includedir, and prefix paths from cygwin style to windows style - file(READ "${PKGCONFIG_FILE}" PKGCONFIG_CONTENT) - - foreach(PATH_NAME prefix libdir includedir) - string(REGEX MATCH "${PATH_NAME}=[^\n]*" PATH_VALUE "${PKGCONFIG_CONTENT}") - string(REPLACE "${PATH_NAME}=" "" PATH_VALUE "${PATH_VALUE}") - - if(NOT PATH_VALUE) - message(FATAL_ERROR "failed to find pkgconfig variable ${PATH_NAME}") - endif() - - execute_process( - COMMAND "${MSYS_ROOT}/usr/bin/cygpath.exe" -w "${PATH_VALUE}" - OUTPUT_VARIABLE FIXED_PATH - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - file(TO_CMAKE_PATH "${FIXED_PATH}" FIXED_PATH) - vcpkg_replace_string("${PKGCONFIG_FILE}" "${PATH_NAME}=${PATH_VALUE}" "${PATH_NAME}=${FIXED_PATH}") - endforeach() - - # list libraries with -l flag (so pkgconf knows they are libraries and not just linker flags) - foreach(LIBS_ENTRY Libs Libs.private) - string(REGEX MATCH "${LIBS_ENTRY}: [^\n]*" LIBS_VALUE "${PKGCONFIG_CONTENT}") - - if(NOT LIBS_VALUE) - message(FATAL_ERROR "failed to find pkgconfig entry ${LIBS_ENTRY}") - endif() - - string(REPLACE "${LIBS_ENTRY}: " "" LIBS_VALUE "${LIBS_VALUE}") - - if(LIBS_VALUE) - set(LIBS_VALUE_OLD "${LIBS_VALUE}") - string(REGEX REPLACE "([^ ]+)[.]lib" "-l\\1" LIBS_VALUE "${LIBS_VALUE}") - set(LIBS_VALUE_NEW "${LIBS_VALUE}") - vcpkg_replace_string("${PKGCONFIG_FILE}" "${LIBS_ENTRY}: ${LIBS_VALUE_OLD}" "${LIBS_ENTRY}: ${LIBS_VALUE_NEW}") - endif() - endforeach() - endforeach() - endforeach() -endif() - -vcpkg_fixup_pkgconfig() - -# Handle dependencies -x_vcpkg_pkgconfig_get_modules(PREFIX FFMPEG_PKGCONFIG MODULES ${FFMPEG_PKGCONFIG_MODULES} LIBS) - -function(append_dependencies_from_libs out) - cmake_parse_arguments(PARSE_ARGV 1 "arg" "" "LIBS" "") - string(REGEX REPLACE "[ ]+" ";" contents "${arg_LIBS}") - list(FILTER contents EXCLUDE REGEX "^-F.+") - list(FILTER contents EXCLUDE REGEX "^-framework$") - list(FILTER contents EXCLUDE REGEX "^-L.+") - list(FILTER contents EXCLUDE REGEX "^-libpath:.+") - list(TRANSFORM contents REPLACE "^-Wl,-framework," "-l") - list(FILTER contents EXCLUDE REGEX "^-Wl,.+") - list(TRANSFORM contents REPLACE "^-l" "") - list(FILTER contents EXCLUDE REGEX "^avutil$") - list(FILTER contents EXCLUDE REGEX "^avcodec$") - list(FILTER contents EXCLUDE REGEX "^avdevice$") - list(FILTER contents EXCLUDE REGEX "^avfilter$") - list(FILTER contents EXCLUDE REGEX "^avformat$") - list(FILTER contents EXCLUDE REGEX "^postproc$") - list(FILTER contents EXCLUDE REGEX "^swresample$") - list(FILTER contents EXCLUDE REGEX "^swscale$") - - if(VCPKG_TARGET_IS_WINDOWS) - list(TRANSFORM contents TOLOWER) - endif() - - if(contents) - list(APPEND "${out}" "${contents}") - set("${out}" "${${out}}" PARENT_SCOPE) - endif() -endfunction() - -append_dependencies_from_libs(FFMPEG_DEPENDENCIES_RELEASE LIBS "${FFMPEG_PKGCONFIG_LIBS_RELEASE}") -append_dependencies_from_libs(FFMPEG_DEPENDENCIES_DEBUG LIBS "${FFMPEG_PKGCONFIG_LIBS_DEBUG}") - -# must remove duplicates from the front to respect link order so reverse first -list(REVERSE FFMPEG_DEPENDENCIES_RELEASE) -list(REVERSE FFMPEG_DEPENDENCIES_DEBUG) -list(REMOVE_DUPLICATES FFMPEG_DEPENDENCIES_RELEASE) -list(REMOVE_DUPLICATES FFMPEG_DEPENDENCIES_DEBUG) -list(REVERSE FFMPEG_DEPENDENCIES_RELEASE) -list(REVERSE FFMPEG_DEPENDENCIES_DEBUG) - -message(STATUS "Dependencies (release): ${FFMPEG_DEPENDENCIES_RELEASE}") -message(STATUS "Dependencies (debug): ${FFMPEG_DEPENDENCIES_DEBUG}") - -# Handle version strings -function(extract_regex_from_file out) - cmake_parse_arguments(PARSE_ARGV 1 "arg" "MAJOR" "FILE_WITHOUT_EXTENSION;REGEX" "") - file(READ "${arg_FILE_WITHOUT_EXTENSION}.h" contents) - - if(contents MATCHES "${arg_REGEX}") - if(NOT CMAKE_MATCH_COUNT EQUAL 1) - message(FATAL_ERROR "Could not identify match group in regular expression \"${arg_REGEX}\"") - endif() - else() - if(arg_MAJOR) - file(READ "${arg_FILE_WITHOUT_EXTENSION}_major.h" contents) - - if(contents MATCHES "${arg_REGEX}") - if(NOT CMAKE_MATCH_COUNT EQUAL 1) - message(FATAL_ERROR "Could not identify match group in regular expression \"${arg_REGEX}\"") - endif() - else() - message(WARNING "Could not find line matching \"${arg_REGEX}\" in file \"${arg_FILE_WITHOUT_EXTENSION}_major.h\"") - endif() - else() - message(WARNING "Could not find line matching \"${arg_REGEX}\" in file \"${arg_FILE_WITHOUT_EXTENSION}.h\"") - endif() - endif() - - set("${out}" "${CMAKE_MATCH_1}" PARENT_SCOPE) -endfunction() - -function(extract_version_from_component out) - cmake_parse_arguments(PARSE_ARGV 1 "arg" "" "COMPONENT" "") - string(TOLOWER "${arg_COMPONENT}" component_lower) - string(TOUPPER "${arg_COMPONENT}" component_upper) - extract_regex_from_file(major_version - FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" - MAJOR - REGEX "#define ${component_upper}_VERSION_MAJOR[ ]+([0-9]+)" - ) - extract_regex_from_file(minor_version - FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" - REGEX "#define ${component_upper}_VERSION_MINOR[ ]+([0-9]+)" - ) - extract_regex_from_file(micro_version - FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" - REGEX "#define ${component_upper}_VERSION_MICRO[ ]+([0-9]+)" - ) - set("${out}" "${major_version}.${minor_version}.${micro_version}" PARENT_SCOPE) -endfunction() - -extract_regex_from_file(FFMPEG_VERSION - FILE_WITHOUT_EXTENSION "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/libavutil/ffversion" - REGEX "#define FFMPEG_VERSION[ ]+\"(.+)\"" -) - -extract_version_from_component(LIBAVUTIL_VERSION - COMPONENT libavutil) -extract_version_from_component(LIBAVCODEC_VERSION - COMPONENT libavcodec) -extract_version_from_component(LIBAVDEVICE_VERSION - COMPONENT libavdevice) -extract_version_from_component(LIBAVFILTER_VERSION - COMPONENT libavfilter) -extract_version_from_component(LIBAVFORMAT_VERSION - COMPONENT libavformat) -extract_version_from_component(LIBSWRESAMPLE_VERSION - COMPONENT libswresample) -extract_version_from_component(LIBSWSCALE_VERSION - COMPONENT libswscale) - -# Handle copyright -file(STRINGS "${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-out.log" LICENSE_STRING REGEX "License: .*" LIMIT_COUNT 1) - -if(LICENSE_STRING STREQUAL "License: LGPL version 2.1 or later") - set(LICENSE_FILE "COPYING.LGPLv2.1") -elseif(LICENSE_STRING STREQUAL "License: LGPL version 3 or later") - set(LICENSE_FILE "COPYING.LGPLv3") -elseif(LICENSE_STRING STREQUAL "License: GPL version 2 or later") - set(LICENSE_FILE "COPYING.GPLv2") -elseif(LICENSE_STRING STREQUAL "License: GPL version 3 or later") - set(LICENSE_FILE "COPYING.GPLv3") -elseif(LICENSE_STRING STREQUAL "License: nonfree and unredistributable") - set(LICENSE_FILE "COPYING.NONFREE") - file(WRITE "${SOURCE_PATH}/${LICENSE_FILE}" "${LICENSE_STRING}") -else() - message(FATAL_ERROR "Failed to identify license (${LICENSE_STRING})") -endif() - -configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-cmake-wrapper.cmake" @ONLY) -vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/${LICENSE_FILE}") diff --git a/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake b/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake deleted file mode 100644 index 233d61343a3..00000000000 --- a/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake +++ /dev/null @@ -1,47 +0,0 @@ -set(FFMPEG_PREV_MODULE_PATH ${CMAKE_MODULE_PATH}) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) - -include(SelectLibraryConfigurations) - -cmake_policy(SET CMP0012 NEW) - -set(vcpkg_no_avcodec_target ON) -set(vcpkg_no_avformat_target ON) -set(vcpkg_no_avutil_target ON) -if(TARGET FFmpeg::avcodec) - set(vcpkg_no_avcodec_target OFF) -endif() -if(TARGET FFmpeg::avformat) - set(vcpkg_no_avformat_target OFF) -endif() -if(TARGET FFmpeg::avutil) - set(vcpkg_no_avutil_target OFF) -endif() - -_find_package(${ARGS}) - -if(WIN32) - set(PKG_CONFIG_EXECUTABLE "${CMAKE_CURRENT_LIST_DIR}/../../../@_HOST_TRIPLET@/tools/pkgconf/pkgconf.exe" CACHE STRING "" FORCE) -endif() - -set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) # Required for CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.1 which otherwise ignores CMAKE_PREFIX_PATH - -if(@WITH_MFX@) - find_package(PkgConfig ) - pkg_check_modules(libmfx IMPORTED_TARGET libmfx) - list(APPEND FFMPEG_LIBRARIES PkgConfig::libmfx) - if(vcpkg_no_avcodec_target AND TARGET FFmpeg::avcodec) - target_link_libraries(FFmpeg::avcodec INTERFACE PkgConfig::libmfx) - endif() - if(vcpkg_no_avutil_target AND TARGET FFmpeg::avutil) - target_link_libraries(FFmpeg::avutil INTERFACE PkgConfig::libmfx) - endif() -endif() - -set(FFMPEG_LIBRARY ${FFMPEG_LIBRARIES}) - -set(CMAKE_MODULE_PATH ${FFMPEG_PREV_MODULE_PATH}) - -unset(vcpkg_no_avformat_target) -unset(vcpkg_no_avcodec_target) -unset(vcpkg_no_avutil_target) diff --git a/res/vcpkg/ffmpeg/vcpkg.json b/res/vcpkg/ffmpeg/vcpkg.json deleted file mode 100644 index 61ff2c8b549..00000000000 --- a/res/vcpkg/ffmpeg/vcpkg.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "ffmpeg", - "version": "7.0.1", - "port-version": 0, - "description": [ - "a library to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created.", - "FFmpeg is the leading multimedia framework, able to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created. It supports the most obscure ancient formats up to the cutting edge. No matter if they were designed by some standards committee, the community or a corporation. It is also highly portable: FFmpeg compiles, runs, and passes our testing infrastructure FATE across Linux, Mac OS X, Microsoft Windows, the BSDs, Solaris, etc. under a wide variety of build environments, machine architectures, and configurations." - ], - "homepage": "https://ffmpeg.org", - "license": null, - "dependencies": [ - { - "name": "vcpkg-cmake-get-vars", - "host": true - }, - { - "name": "vcpkg-pkgconfig-get-modules", - "host": true - } - ], - "default-features": [ - ], - "features": { - "amf": { - "description": "AMD AMF codec support", - "dependencies": [ - "amd-amf" - ] - }, - "nvcodec": { - "description": "Nvidia video decoding/encoding acceleration", - "supports": "linux | (!osx & !uwp & !(arm64 & windows))", - "dependencies": [ - "ffnvcodec" - ] - }, - "qsv": { - "description": "Intel QSV Codec", - "dependencies": [ - "mfx-dispatch" - ] - } - } -} \ No newline at end of file diff --git a/vcpkg.json b/vcpkg.json index f1d7036eb5f..940783f2d8b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -51,44 +51,13 @@ { "name": "libyuv", "host": false - }, - { - "name": "ffmpeg", - "host": true, - "features": [ - { - "name": "amf", - "platform": "((windows | linux) & static)" - }, - { - "name": "nvcodec", - "platform": "((windows | linux) & static)" - }, - { - "name": "qsv", - "platform": "(windows & static)" - } - ], - "platform": "((windows | (linux & !arm32) | osx) & static)" - }, - { - "name": "ffmpeg", - "host": false, - "platform": "((android | ios | (linux & arm32)) & static)" } ], "vcpkg-configuration": { "default-registry": { "kind": "builtin", - "baseline": "f7423ee180c4b7f40d43402c2feb3859161ef625" + "baseline": "" }, - "overlay-ports": [ - "./res/vcpkg" - ] - }, - "overrides": [ - { "name": "ffnvcodec", "version": "11.1.5.2" }, - { "name": "amd-amf", "version": "1.4.29" }, - { "name": "mfx-dispatch", "version": "1.35.1" } - ] -} \ No newline at end of file + "overlay-ports": [ "./res/vcpkg" ] + } +} From 85ded0a3e5185ff6d7d7931a584b71efc443cb98 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Fri, 19 Jul 2024 00:32:19 +0800 Subject: [PATCH 299/335] no idea why publish sciter windows tar.gz, remove it --- .github/workflows/flutter-build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 2cf992892ce..91d8cc18979 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -312,8 +312,6 @@ jobs: popd mkdir -p ./SignOutput mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.exe - mv ./Release ./rustdesk - tar czf rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.tar.gz rustdesk - name: Sign rustdesk self-extracted file if: env.UPLOAD_ARTIFACT == 'true' && env.SIGN_BASE_URL != '' @@ -329,7 +327,6 @@ jobs: tag_name: ${{ env.TAG_NAME }} files: | ./SignOutput/rustdesk-*.exe - ./rustdesk-*.tar.gz build-for-macOS-arm64-selfhost: # use build-for-macOS instead From 2b54a553c72b6f79720de1bf8a24f7a6cfee5494 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 19 Jul 2024 23:55:52 +0800 Subject: [PATCH 300/335] buildin options and add to mobile (#8759) Signed-off-by: 21pages --- flutter/lib/common.dart | 3 +- flutter/lib/common/widgets/peer_card.dart | 4 +- flutter/lib/consts.dart | 10 ++++- .../lib/desktop/pages/desktop_home_page.dart | 2 +- .../desktop/pages/desktop_setting_page.dart | 8 ++-- flutter/lib/mobile/pages/server_page.dart | 23 ++++++++++-- flutter/lib/mobile/pages/settings_page.dart | 24 +++++++++--- libs/hbb_common/src/config.rs | 37 +++++++++++++------ src/client.rs | 4 +- src/common.rs | 14 +++++++ src/flutter_ffi.rs | 4 ++ src/hbbs_http/sync.rs | 6 +-- src/rendezvous_mediator.rs | 3 +- src/ui_interface.rs | 10 +++++ 14 files changed, 114 insertions(+), 38 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 3e5689d3530..0ce9433f40e 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3319,7 +3319,8 @@ Widget buildPresetPasswordWarning() { return Text( 'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error } else if (snapshot.hasData && snapshot.data == true) { - if (bind.mainGetLocalOption(key: "remove-preset-password-warning") != + if (bind.mainGetBuildinOption( + key: kOptionRemovePresetPasswordWarning) != 'N') { return SizedBox.shrink(); } diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 9f828c1906c..1b099cbeba6 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -133,7 +133,7 @@ class _PeerCardState extends State<_PeerCard> Widget _buildPeerTile( BuildContext context, Peer peer, Rx? deco) { hideUsernameOnCard ??= - bind.mainGetLocalOption(key: kHideUsernameOnCard) == 'Y'; + bind.mainGetBuildinOption(key: kHideUsernameOnCard) == 'Y'; final name = hideUsernameOnCard == true ? peer.hostname : '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; @@ -245,7 +245,7 @@ class _PeerCardState extends State<_PeerCard> Widget _buildPeerCard( BuildContext context, Peer peer, Rx deco) { hideUsernameOnCard ??= - bind.mainGetLocalOption(key: kHideUsernameOnCard) == 'Y'; + bind.mainGetBuildinOption(key: kHideUsernameOnCard) == 'Y'; final name = hideUsernameOnCard == true ? peer.hostname : '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index b54ff15dd05..5af3f6d251f 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -136,8 +136,16 @@ const String kOptionAllowRemoveWallpaper = "allow-remove-wallpaper"; const String kOptionStopService = "stop-service"; const String kOptionDirectxCapture = "enable-directx-capture"; const String kOptionAllowRemoteCmModification = "allow-remote-cm-modification"; + +// buildin opitons const String kOptionHideServerSetting = "hide-server-settings"; const String kOptionHideProxySetting = "hide-proxy-settings"; +const String kOptionHideSecuritySetting = "hide-security-settings"; +const String kOptionHideNetworkSetting = "hide-network-settings"; +const String kOptionRemovePresetPasswordWarning = + "remove-preset-password-warning"; +const kHideUsernameOnCard = "hide-username-on-card"; +const String kOptionHideHelpCards = "hide-help-cards"; const String kOptionToggleViewOnly = "view-only"; @@ -306,8 +314,6 @@ const kRequestIgnoreBatteryOptimizations = const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW"; const kAndroid13Notification = "android.permission.POST_NOTIFICATIONS"; -const kHideUsernameOnCard = "hide-username-on-card"; - /// Android channel invoke type key class AndroidChannel { static final kStartAction = "start_action"; diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index addfacec3fe..31a8e1374ff 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -546,7 +546,7 @@ class _DesktopHomePageState extends State String? link, bool? closeButton, String? closeOption}) { - if (bind.mainGetLocalOption(key: 'hide-help-cards') == 'Y' && + if (bind.mainGetBuildinOption(key: kOptionHideHelpCards) == 'Y' && content != 'install_daemon_tip') { return const SizedBox(); } diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index c69ba186ab0..70aa6c54e06 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -63,10 +63,10 @@ class DesktopSettingPage extends StatefulWidget { SettingsTabKey.general, if (!bind.isOutgoingOnly() && !bind.isDisableSettings() && - bind.mainGetLocalOption(key: "hide-security-settings") != 'Y') + bind.mainGetBuildinOption(key: kOptionHideSecuritySetting) != 'Y') SettingsTabKey.safety, if (!bind.isDisableSettings() && - bind.mainGetLocalOption(key: "hide-network-settings") != 'Y') + bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) != 'Y') SettingsTabKey.network, if (!bind.isIncomingOnly()) SettingsTabKey.display, if (!isWeb && !bind.isIncomingOnly() && bind.pluginFeatureIsEnabled()) @@ -1289,9 +1289,9 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin { bool enabled = !locked; final scrollController = ScrollController(); final hideServer = - bind.mainGetLocalOption(key: kOptionHideServerSetting) == 'Y'; + bind.mainGetBuildinOption(key: kOptionHideServerSetting) == 'Y'; final hideProxy = - bind.mainGetLocalOption(key: kOptionHideProxySetting) == 'Y'; + bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y'; return DesktopScrollWrapper( scrollController: scrollController, child: ListView( diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 3609564ec17..66449617680 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -23,7 +23,22 @@ class ServerPage extends StatefulWidget implements PageShape { final icon = const Icon(Icons.mobile_screen_share); @override - final appBarActions = [ + final appBarActions = (!bind.isDisableSettings() && + bind.mainGetBuildinOption(key: kOptionHideSecuritySetting) != 'Y') + ? [_DropDownAction()] + : []; + + ServerPage({Key? key}) : super(key: key); + + @override + State createState() => _ServerPageState(); +} + +class _DropDownAction extends StatelessWidget { + _DropDownAction(); + + // should only have one action + final actions = [ PopupMenuButton( tooltip: "", icon: const Icon(Icons.more_vert), @@ -136,10 +151,10 @@ class ServerPage extends StatefulWidget implements PageShape { }) ]; - ServerPage({Key? key}) : super(key: key); - @override - State createState() => _ServerPageState(); + Widget build(BuildContext context) { + return actions[0]; + } } class _ServerPageState extends State { diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 2fc77634848..63972d0ea62 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -86,6 +86,7 @@ class _SettingsState extends State with WidgetsBindingObserver { var _autoDisconnectTimeout = ""; var _hideServer = false; var _hideProxy = false; + var _hideNetwork = false; @override void initState() { @@ -112,8 +113,11 @@ class _SettingsState extends State with WidgetsBindingObserver { bind.mainGetOptionSync(key: kOptionAllowAutoDisconnect)); _autoDisconnectTimeout = bind.mainGetOptionSync(key: kOptionAutoDisconnectTimeout); - _hideServer = bind.mainGetLocalOption(key: kOptionHideServerSetting) == 'Y'; - _hideProxy = bind.mainGetLocalOption(key: kOptionHideProxySetting) == 'Y'; + _hideServer = + bind.mainGetBuildinOption(key: kOptionHideServerSetting) == 'Y'; + _hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y'; + _hideNetwork = + bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y'; () async { var update = false; @@ -535,6 +539,8 @@ class _SettingsState extends State with WidgetsBindingObserver { )); final disabledSettings = bind.isDisableSettings(); + final hideSecuritySettings = + bind.mainGetBuildinOption(key: kOptionHideSecuritySetting) == 'Y'; final settings = SettingsList( sections: [ customClientSection, @@ -558,14 +564,14 @@ class _SettingsState extends State with WidgetsBindingObserver { ], ), SettingsSection(title: Text(translate("Settings")), tiles: [ - if (!disabledSettings && !_hideServer) + if (!disabledSettings && !_hideNetwork && !_hideServer) SettingsTile( title: Text(translate('ID/Relay Server')), leading: Icon(Icons.cloud), onPressed: (context) { showServerSettings(gFFI.dialogManager); }), - if (!isIOS && !_hideProxy) + if (!isIOS && !_hideNetwork && !_hideProxy) SettingsTile( title: Text(translate('Socks5/Http(s) Proxy')), leading: Icon(Icons.network_ping), @@ -637,13 +643,19 @@ class _SettingsState extends State with WidgetsBindingObserver { ), ], ), - if (isAndroid && !disabledSettings && !outgoingOnly) + if (isAndroid && + !disabledSettings && + !outgoingOnly && + !hideSecuritySettings) SettingsSection( title: Text(translate("Share Screen")), tiles: shareScreenTiles, ), if (!bind.isIncomingOnly()) defaultDisplaySection(), - if (isAndroid && !disabledSettings && !outgoingOnly) + if (isAndroid && + !disabledSettings && + !outgoingOnly && + !hideSecuritySettings) SettingsSection( title: Text(translate("Enhancements")), tiles: enhancementsTiles, diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 51a33a00ba9..e33cf8ebbcf 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -69,6 +69,7 @@ lazy_static::lazy_static! { pub static ref DEFAULT_LOCAL_SETTINGS: RwLock> = Default::default(); pub static ref OVERWRITE_LOCAL_SETTINGS: RwLock> = Default::default(); pub static ref HARD_SETTINGS: RwLock> = Default::default(); + pub static ref BUILDIN_SETTINGS: RwLock> = Default::default(); } lazy_static::lazy_static! { @@ -2096,12 +2097,22 @@ pub mod keys { pub const OPTION_KEY: &str = "key"; pub const OPTION_PRESET_ADDRESS_BOOK_NAME: &str = "preset-address-book-name"; pub const OPTION_PRESET_ADDRESS_BOOK_TAG: &str = "preset-address-book-tag"; - pub const OPTION_PRESET_USERNAME: &str = "preset-user-name"; - pub const OPTION_PRESET_STRATEGY_NAME: &str = "preset-strategy-name"; pub const OPTION_ENABLE_DIRECTX_CAPTURE: &str = "enable-directx-capture"; pub const OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE: &str = "enable-android-software-encoding-half-scale"; + + // buildin options + pub const OPTION_DISPLAY_NAME: &str = "display-name"; pub const OPTION_DISABLE_UDP: &str = "disable-udp"; + pub const OPTION_PRESET_USERNAME: &str = "preset-user-name"; + pub const OPTION_PRESET_STRATEGY_NAME: &str = "preset-strategy-name"; + pub const OPTION_REMOVE_PRESET_PASSWORD_WARNING: &str = "remove-preset-password-warning"; + pub const OPTION_HIDE_SECURITY_SETTINGS: &str = "hide-security-settings"; + pub const OPTION_HIDE_NETWORK_SETTINGS: &str = "hide-network-settings"; + pub const OPTION_HIDE_SERVER_SETTINGS: &str = "hide-server-settings"; + pub const OPTION_HIDE_PROXY_SETTINGS: &str = "hide-proxy-settings"; + pub const OPTION_HIDE_USERNAME_ON_CARD: &str = "hide-username-on-card"; + pub const OPTION_HIDE_HELP_CARDS: &str = "hide-help-cards"; // flutter local options pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState"; @@ -2126,8 +2137,6 @@ pub mod keys { pub const OPTION_DISABLE_GROUP_PANEL: &str = "disable-group-panel"; pub const OPTION_PRE_ELEVATE_SERVICE: &str = "pre-elevate-service"; - pub const OPTION_DISPLAY_NAME: &str = "display-name"; - // proxy settings // The following options are not real keys, they are just used for custom client advanced settings. // The real keys are in Config2::socks. @@ -2191,15 +2200,7 @@ pub mod keys { OPTION_KEEP_SCREEN_ON, OPTION_DISABLE_GROUP_PANEL, OPTION_PRE_ELEVATE_SERVICE, - OPTION_DISPLAY_NAME, - "remove-preset-password-warning", - "hide-security-settings", - "hide-network-settings", - "hide-server-settings", - "hide-proxy-settings", - "hide-username-on-card", OPTION_ALLOW_REMOTE_CM_MODIFICATION, - "hide-help-cards", ]; // DEFAULT_SETTINGS, OVERWRITE_SETTINGS pub const KEYS_SETTINGS: &[&str] = &[ @@ -2238,9 +2239,21 @@ pub mod keys { OPTION_PRESET_ADDRESS_BOOK_TAG, OPTION_ENABLE_DIRECTX_CAPTURE, OPTION_ENABLE_ANDROID_SOFTWARE_ENCODING_HALF_SCALE, + ]; + + // BUILDIN_SETTINGS + pub const KEYS_BUILDIN_SETTINGS: &[&str] = &[ + OPTION_DISPLAY_NAME, OPTION_DISABLE_UDP, OPTION_PRESET_USERNAME, OPTION_PRESET_STRATEGY_NAME, + OPTION_REMOVE_PRESET_PASSWORD_WARNING, + OPTION_HIDE_SECURITY_SETTINGS, + OPTION_HIDE_NETWORK_SETTINGS, + OPTION_HIDE_SERVER_SETTINGS, + OPTION_HIDE_PROXY_SETTINGS, + OPTION_HIDE_USERNAME_ON_CARD, + OPTION_HIDE_HELP_CARDS, ]; } diff --git a/src/client.rs b/src/client.rs index d0c0c6635da..163aae1a1a3 100644 --- a/src/client.rs +++ b/src/client.rs @@ -60,7 +60,7 @@ use crate::{ check_port, common::input::{MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT, MOUSE_TYPE_DOWN, MOUSE_TYPE_UP}, create_symmetric_key_msg, decode_id_pk, get_rs_pk, is_keyboard_mode_supported, secure_tcp, - ui_interface::use_texture_render, + ui_interface::{get_buildin_option, use_texture_render}, ui_session_interface::{InvokeUiSession, Session}, }; @@ -2027,7 +2027,7 @@ impl LoginConfigHandler { } else { (my_id, self.id.clone()) }; - let mut display_name = LocalConfig::get_option(&config::keys::OPTION_DISPLAY_NAME); + let mut display_name = get_buildin_option(config::keys::OPTION_DISPLAY_NAME); if display_name.is_empty() { display_name = crate::username(); } diff --git a/src/common.rs b/src/common.rs index 27135b56826..740b5d9e375 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1341,6 +1341,7 @@ fn read_custom_client_advanced_settings( map_display_settings: &HashMap, map_local_settings: &HashMap, map_settings: &HashMap, + map_buildin_settings: &HashMap, is_override: bool, ) { let mut display_settings = if is_override { @@ -1358,6 +1359,8 @@ fn read_custom_client_advanced_settings( } else { config::DEFAULT_SETTINGS.write().unwrap() }; + let mut buildin_settings = config::BUILDIN_SETTINGS.write().unwrap(); + if let Some(settings) = settings.as_object() { for (k, v) in settings { let Some(v) = v.as_str() else { @@ -1369,6 +1372,8 @@ fn read_custom_client_advanced_settings( local_settings.insert(k2.to_string(), v.to_owned()); } else if let Some(k2) = map_settings.get(k) { server_settings.insert(k2.to_string(), v.to_owned()); + } else if let Some(k2) = map_buildin_settings.get(k) { + buildin_settings.insert(k2.to_string(), v.to_owned()); } else { let k2 = k.replace("_", "-"); let k = k2.replace("-", "_"); @@ -1381,6 +1386,9 @@ fn read_custom_client_advanced_settings( // server server_settings.insert(k.clone(), v.to_owned()); server_settings.insert(k2.clone(), v.to_owned()); + // buildin + buildin_settings.insert(k.clone(), v.to_owned()); + buildin_settings.insert(k2.clone(), v.to_owned()); } } } @@ -1443,12 +1451,17 @@ pub fn read_custom_client(config: &str) { for s in config::keys::KEYS_SETTINGS { map_settings.insert(s.replace("_", "-"), s); } + let mut buildin_settings = HashMap::new(); + for s in config::keys::KEYS_BUILDIN_SETTINGS { + buildin_settings.insert(s.replace("_", "-"), s); + } if let Some(default_settings) = data.remove("default-settings") { read_custom_client_advanced_settings( default_settings, &map_display_settings, &map_local_settings, &map_settings, + &buildin_settings, false, ); } @@ -1458,6 +1471,7 @@ pub fn read_custom_client(config: &str) { &map_display_settings, &map_local_settings, &map_settings, + &buildin_settings, true, ); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index a7eda9d9ba7..700c08704a3 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -2213,6 +2213,10 @@ pub fn main_get_hard_option(key: String) -> SyncReturn { SyncReturn(get_hard_option(key)) } +pub fn main_get_buildin_option(key: String) -> SyncReturn { + SyncReturn(get_buildin_option(&key)) +} + pub fn main_check_hwcodec() { check_hwcodec() } diff --git a/src/hbbs_http/sync.rs b/src/hbbs_http/sync.rs index a1e1a25681e..f2717a42ca1 100644 --- a/src/hbbs_http/sync.rs +++ b/src/hbbs_http/sync.rs @@ -5,7 +5,7 @@ use std::{ }; #[cfg(not(any(target_os = "ios")))] -use crate::Connection; +use crate::{ui_interface::get_buildin_option, Connection}; use hbb_common::{ config::{keys, Config, LocalConfig}, tokio::{self, sync::broadcast, time::Instant}, @@ -91,11 +91,11 @@ async fn start_hbbs_sync_async() { if !ab_tag.is_empty() { v[keys::OPTION_PRESET_ADDRESS_BOOK_TAG] = json!(ab_tag); } - let username = Config::get_option(keys::OPTION_PRESET_USERNAME); + let username = get_buildin_option(keys::OPTION_PRESET_USERNAME); if !username.is_empty() { v[keys::OPTION_PRESET_USERNAME] = json!(username); } - let strategy_name = Config::get_option(keys::OPTION_PRESET_STRATEGY_NAME); + let strategy_name = get_buildin_option(keys::OPTION_PRESET_STRATEGY_NAME); if !strategy_name.is_empty() { v[keys::OPTION_PRESET_STRATEGY_NAME] = json!(strategy_name); } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 94fe128be04..dcb538179f3 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -29,6 +29,7 @@ use hbb_common::{ use crate::{ check_port, server::{check_zombie, new as new_server, ServerPtr}, + ui_interface::get_buildin_option, }; type Message = RendezvousMessage; @@ -387,7 +388,7 @@ impl RendezvousMediator { }; if (cfg!(debug_assertions) && option_env!("TEST_TCP").is_some()) || is_http_proxy - || Config::get_option(config::keys::OPTION_DISABLE_UDP) == "Y" + || get_buildin_option(config::keys::OPTION_DISABLE_UDP) == "Y" { Self::start_tcp(server, host).await } else { diff --git a/src/ui_interface.rs b/src/ui_interface.rs index ead6b4168e8..fec0380b534 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -202,6 +202,16 @@ pub fn get_hard_option(key: String) -> String { .unwrap_or_default() } +#[inline] +pub fn get_buildin_option(key: &str) -> String { + config::BUILDIN_SETTINGS + .read() + .unwrap() + .get(key) + .cloned() + .unwrap_or_default() +} + #[inline] pub fn set_local_option(key: String, value: String) { LocalConfig::set_option(key, value); From 6ad662260e68ee7168f66bf60903c3cba8e93bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E9=A4=85=E3=81=AECreeeper?= <56744841+creeper-0910@users.noreply.github.com> Date: Sat, 20 Jul 2024 00:56:39 +0900 Subject: [PATCH 301/335] Update ja.rs (#8758) * Update ja.rs Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> * Update README-JP.md Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> --------- Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> --- docs/README-JP.md | 2 +- src/lang/ja.rs | 1265 +++++++++++++++++++++++---------------------- 2 files changed, 634 insertions(+), 633 deletions(-) diff --git a/docs/README-JP.md b/docs/README-JP.md index d530399a137..f1245fa05af 100644 --- a/docs/README-JP.md +++ b/docs/README-JP.md @@ -168,7 +168,7 @@ target/release/rustdesk - **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: デスクトップとモバイル向けのFlutterコード - **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutterウェブクライアント向けのJavaScript -## Screenshots +## スクリーンショット ![Connection Manager](https://github.com/rustdesk/rustdesk/assets/28412477/db82d4e7-c4bc-4823-8e6f-6af7eadf7651) diff --git a/src/lang/ja.rs b/src/lang/ja.rs index acbf1850b3d..059f048c5f7 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -1,633 +1,634 @@ 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", "RuskDeskネットワークに接続中..."), - ("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", "認証・中継サーバー"), - ("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", "使用できるのは大文字・小文字のアルファベット、数字、アンダースコア(_)のみです。初めの文字はアルファベットにする必要があります。6文字から16文字までです。"), - ("Website", "公式サイト"), - ("About", "情報"), - ("Slogan_tip", ""), - ("Privacy Statement", ""), - ("Mute", "ミュート"), - ("Build Date", "ビルド日時"), - ("Version", "バージョン"), - ("Home", "ホーム"), - ("Audio Input", "音声入力デバイス"), - ("Enhancements", "追加機能"), - ("Hardware Codec", "ハードウェア コーデック"), - ("Adaptive bitrate", "アダプティブビットレート"), - ("ID Server", "認証サーバー"), - ("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", "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", "ランデブーサーバーに接続できませんでした"), - ("Please try later", "後でもう一度お試しください"), - ("Remote desktop is offline", "リモート側のデスクトップがオフラインです"), - ("Key mismatch", "キーが一致しません"), - ("Timeout", "タイムアウト"), - ("Failed to connect to relay server", "中継サーバーに接続できませんでした"), - ("Failed to connect via rendezvous server", "ランデブーサーバー経由で接続できませんでした"), - ("Failed to connect via relay server", "中継サーバー経由で接続できませんでした"), - ("Failed to make direct connection to remote desktop", "リモート側デスクトップと直接接続できませんでした"), - ("Set Password", "パスワードを設定"), - ("OS Password", "OSのパスワード"), - ("install_tip", "RustDeskがUACの影響によりリモート側で正常に動作しない場合があります。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", "切断"), - ("Enable 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", "資格情報が間違っています"), - ("The verification code is incorrect or has expired", "認証コードが間違っているか、有効期限が切れています"), - ("Edit Tag", "タグを編集"), - ("Forget Password", "パスワードの記憶を解除"), - ("Favorites", "お気に入り"), - ("Add to Favorites", "お気に入りに追加"), - ("Remove from Favorites", "お気に入りから削除"), - ("Empty", "空"), - ("Invalid folder name", "無効なフォルダ名"), - ("Socks5 Proxy", "SOCKS5プロキシ"), - ("Socks5/Http(s) Proxy", "Socks5/Http(s)プロキシ"), - ("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", "1本指でタップ"), - ("Left Mouse", "マウス左クリック"), - ("One-Long Tap", "1本指でロングタップ"), - ("Two-Finger Tap", "2本指でタップ"), - ("Right Mouse", "マウス右クリック"), - ("One-Finger Move", "1本指でドラッグ"), - ("Double Tap & Move", "2本指でタップ&ドラッグ"), - ("Mouse Drag", "マウスドラッグ"), - ("Three-Finger vertically", "3本指で縦方向"), - ("Mouse Wheel", "マウスホイール"), - ("Two-Finger Move", "2本指でドラッグ"), - ("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端末をリモートの端末からマウスやタッチで操作するには、RustDeskに「アクセシビリティ」サービスの使用を許可する必要があります。"), - ("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", "終了"), - ("Help", "ヘルプ"), - ("Failed", "失敗"), - ("Succeeded", "成功"), - ("Someone turns on privacy mode, exit", "プライバシーモードがオンになりました。終了します。"), - ("Unsupported", "サポートされていません"), - ("Peer denied", "相手が拒否しました"), - ("Please install plugins", "プラグインをインストールしてください"), - ("Peer exit", "相手が終了しました"), - ("Failed to turn off", "オフにできませんでした"), - ("Turned off", "オフになりました"), - ("Language", "言語"), - ("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"), - ("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"), - ("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」のチェックを外してください"), - ("Start on boot", ""), - ("Start the screen sharing service on boot, requires special permissions", ""), - ("Connection not allowed", "接続が許可されていません"), - ("Legacy mode", "レガシーモード"), - ("Map mode", ""), - ("Translate mode", "翻訳モード"), - ("Use permanent password", "固定のパスワードを使用"), - ("Use both passwords", "どちらのパスワードも使用"), - ("Set permanent password", "固定のパスワードを設定"), - ("Enable 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 Toolbar", "ツールバーを表示"), - ("Hide Toolbar", "ツールバーを隠す"), - ("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", "オーディオ入力デバイス"), - ("Use IP Whitelisting", "IPホワイトリストを使用する"), - ("Network", "ネットワーク"), - ("Pin Toolbar", "ツールバーをピン留め"), - ("Unpin Toolbar", "ツールバーのピン留めを外す"), - ("Recording", "録画中"), - ("Directory", "ディレクトリ"), - ("Automatically record incoming sessions", "受信したセッションの自動録画"), - ("Change", "変更"), - ("Start session recording", "セッションの録画を開始"), - ("Stop session recording", "セッションの録画を停止"), - ("Enable recording session", "セッションの録画を有効化"), - ("Enable LAN discovery", ""), - ("Deny LAN discovery", ""), - ("Write a message", ""), - ("Prompt", ""), - ("Please wait for confirmation of 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 デスクトップを試すか、OS を変更してください。"), - ("JumpLink", "View"), - ("Please Select the screen to be shared(Operate on the peer side).", "共有する画面を選択してください(接続先が操作します)"), - ("Show RustDesk", "RustDeskを表示"), - ("This PC", "この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", ""), - ("Right click to select tabs", ""), - ("Skipped", ""), - ("Add to address book", ""), - ("Group", "グループ"), - ("Search", "検索"), - ("Closed manually by web console", ""), - ("Local keyboard type", ""), - ("Select local keyboard type", ""), - ("software_render_tip", ""), - ("Always use software rendering", ""), - ("config_input", ""), - ("config_microphone", ""), - ("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", ""), - ("Request Elevation", ""), - ("wait_accept_uac_tip", ""), - ("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", "FPS"), - ("Auto", "自動"), - ("Other Default Options", "その他のデフォルト設定"), - ("Voice call", "音声通話"), - ("Text chat", "テキストチャット"), - ("Stop voice call", "音声通話を終了"), - ("relay_hint_tip", "直接接続が行えない場合は、リレー経由での接続をお試しください。初回接続で中継接続を行いたい場合は末尾に「/r」を付けるか、最近のセッション履歴に「常に中継サーバー経由で接続」という設定がある場合はそちらを選択してください。"), - ("Reconnect", "再接続"), - ("Codec", "コーデック"), - ("Resolution", "解像度"), - ("No transfers in progress", ""), - ("Set one-time password length", ""), - ("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", "空のパスワード"), - ("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", "OSアカウント"), - ("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", ""), - ("No need to elevate", ""), - ("System Sound", "システム音声"), - ("Default", "デフォルト"), - ("New RDP", ""), - ("Fingerprint", "フィンガープリント"), - ("Copy Fingerprint", "フィンガープリントをコピー"), - ("no fingerprints", "フィンガープリントがありません"), - ("Select a peer", "接続先を選択"), - ("Select peers", "複数の接続先を選択"), - ("Plugins", "プラグイン"), - ("Uninstall", "アンインストール"), - ("Update", "更新"), - ("Enable", "有効"), - ("Disable", "無効"), - ("Options", "設定"), - ("resolution_original_tip", ""), - ("resolution_fit_local_tip", ""), - ("resolution_custom_tip", ""), - ("Collapse toolbar", ""), - ("Accept and Elevate", ""), - ("accept_and_elevate_btn_tooltip", ""), - ("clipboard_wait_response_timeout_tip", ""), - ("Incoming connection", ""), - ("Outgoing connection", ""), - ("Exit", "終了"), - ("Open", "開く"), - ("logout_tip", ""), - ("Service", "サービス"), - ("Start", "開始"), - ("Stop", "停止"), - ("exceed_max_devices", ""), - ("Sync with recent sessions", ""), - ("Sort tags", "タグで並べ替え"), - ("Open connection in new tab", ""), - ("Move tab to new window", ""), - ("Can not be empty", "空にすることはできません"), - ("Already exists", "すでに存在します"), - ("Change Password", "パスワードを変更"), - ("Refresh Password", "パスワードをリフレッシュ"), - ("ID", ""), - ("Grid View", ""), - ("List View", ""), - ("Select", "選択"), - ("Toggle Tags", ""), - ("pull_ab_failed_tip", ""), - ("push_ab_failed_tip", ""), - ("synced_peer_readded_tip", ""), - ("Change Color", ""), - ("Primary Color", ""), - ("HSV Color", ""), - ("Installation Successful!", ""), - ("Installation failed!", ""), - ("Reverse mouse wheel", ""), - ("{} sessions", ""), - ("scam_title", ""), - ("scam_text1", ""), - ("scam_text2", ""), - ("Don't show again", "今後表示しない"), - ("I Agree", "同意します"), - ("Decline", "同意しません"), - ("Timeout in minutes", ""), - ("auto_disconnect_option_tip", ""), - ("Connection failed due to inactivity", ""), - ("Check for software update on startup", ""), - ("upgrade_rustdesk_server_pro_to_{}_tip", ""), - ("pull_group_failed_tip", ""), - ("Filter by intersection", ""), - ("Remove wallpaper during incoming sessions", ""), - ("Test", ""), - ("display_is_plugged_out_msg", ""), - ("No displays", "ディスプレイがありません"), - ("Open in new window", ""), - ("Show displays as individual windows", ""), - ("Use all my displays for the remote session", ""), - ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("Virtual display", ""), - ("Plug out all", ""), - ("True color (4:4:4)", ""), - ("Enable blocking user input", ""), - ("id_input_tip", ""), - ("privacy_mode_impl_mag_tip", ""), - ("privacy_mode_impl_virtual_display_tip", ""), - ("Enter privacy mode", ""), - ("Exit privacy mode", ""), - ("idd_not_support_under_win10_2004_tip", ""), - ("input_source_1_tip", ""), - ("input_source_2_tip", ""), - ("Swap control-command key", ""), - ("swap-left-right-mouse", ""), - ("2FA code", ""), - ("More", "詳細"), - ("enable-2fa-title", ""), - ("enable-2fa-desc", ""), - ("wrong-2fa-code", ""), - ("enter-2fa-title", ""), - ("Email verification code must be 6 characters.", ""), - ("2FA code must be 6 digits.", ""), - ("Multiple Windows sessions found", ""), - ("Please select the session you want to connect to", ""), - ("powered_by_me", ""), - ("outgoing_only_desk_tip", ""), - ("preset_password_warning", ""), - ("Security Alert", ""), - ("My address book", ""), - ("Personal", "個人"), - ("Owner", ""), - ("Set shared password", ""), - ("Exist in", ""), - ("Read-only", "読み取り専用"), - ("Read/Write", "読み取り/書き込み"), - ("Full Control", "完全コントロール"), - ("share_warning_tip", ""), - ("Everyone", ""), - ("ab_web_console_tip", ""), - ("allow-only-conn-window-open-tip", ""), - ("no_need_privacy_mode_no_physical_displays_tip", ""), - ("Follow remote cursor", ""), - ("Follow remote window focus", ""), - ("default_proxy_tip", ""), - ("no_audio_input_device_tip", ""), - ("Incoming", "受信"), - ("Outgoing", "発信"), - ("Clear Wayland screen selection", ""), - ("clear_Wayland_screen_selection_tip", ""), - ("confirm_clear_Wayland_screen_selection_tip", ""), - ("android_new_voice_call_tip", ""), - ("texture_render_tip", ""), - ("Use texture rendering", ""), - ("Floating window", ""), - ("floating_window_tip", ""), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), - ("Capture screen using DirectX", ""), - ("Back", "戻る"), - ("Apps", "アプリ"), - ("Volume up", "音量アップ"), - ("Volume down", "音量ダウン"), - ("Power", "電源"), - ("Telegram bot", ""), - ("enable-bot-tip", ""), - ("enable-bot-desc", ""), - ("cancel-2fa-confirm-tip", ""), - ("cancel-bot-confirm-tip", ""), - ].iter().cloned().collect(); -} + pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "状態"), + ("Your Desktop", "あなたのコンピューター"), + ("desk_tip", "下記のIDとパスワードであなたのコンピューターにアクセスできます。"), + ("Password", "パスワード"), + ("Ready", "準備完了"), + ("Established", "接続完了"), + ("connecting_status", "RuskDeskネットワークに接続中..."), + ("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", "TXPトンネリング"), + ("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", "認証/中継サーバー"), + ("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", "使用できるのは大文字・小文字のアルファベット、数字、アンダースコア(_)のみです。先頭の文字はアルファベット、長さは6文字から16文字である必要があります。"), + ("Website", "公式サイト"), + ("About", "RustDeskについて"), + ("Slogan_tip", "この混沌とした世界から、愛をこめて!"), + ("Privacy Statement", "プライバシーポリシー"), + ("Mute", "ミュート"), + ("Build Date", "ビルド日時"), + ("Version", "バージョン"), + ("Home", "ホーム"), + ("Audio Input", "音声入力"), + ("Enhancements", "拡張機能"), + ("Hardware Codec", "ハードウェアコーデック"), + ("Adaptive bitrate", "可変ビットレート"), + ("ID Server", "認証サーバー"), + ("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", "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", "ランデブーサーバーに接続できませんでした"), + ("Please try later", "後でもう一度お試しください"), + ("Remote desktop is offline", "リモートコンピューターがオフラインです"), + ("Key mismatch", "キーが一致しません"), + ("Timeout", "タイムアウト"), + ("Failed to connect to relay server", "中継サーバーに接続できませんでした"), + ("Failed to connect via rendezvous server", "ランデブーサーバー経由で接続できませんでした"), + ("Failed to connect via relay server", "中継サーバー経由で接続できませんでした"), + ("Failed to make direct connection to remote desktop", "リモートコンピューターと直接接続できませんでした"), + ("Set Password", "パスワードを設定"), + ("OS Password", "OSのパスワード"), + ("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", "切断"), + ("Enable 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", "資格情報が間違っています"), + ("The verification code is incorrect or has expired", "認証コードが間違っているか、有効期限が切れています"), + ("Edit Tag", "タグを編集"), + ("Forget Password", "パスワードを忘れる"), + ("Favorites", "お気に入り"), + ("Add to Favorites", "お気に入りに追加"), + ("Remove from Favorites", "お気に入りから削除"), + ("Empty", "空"), + ("Invalid folder name", "無効なフォルダ名"), + ("Socks5 Proxy", "SOCKS5プロキシ"), + ("Socks5/Http(s) Proxy", "Socks5/Http(s)プロキシ"), + ("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", "1本指でタップ"), + ("Left Mouse", "マウス左クリック"), + ("One-Long Tap", "1本指でロングタップ"), + ("Two-Finger Tap", "2本指でタップ"), + ("Right Mouse", "マウス右クリック"), + ("One-Finger Move", "1本指でドラッグ"), + ("Double Tap & Move", "2本指でタップ&ドラッグ"), + ("Mouse Drag", "マウスドラッグ"), + ("Three-Finger vertically", "3本指で縦方向"), + ("Mouse Wheel", "マウスホイール"), + ("Two-Finger Move", "2本指でドラッグ"), + ("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端末をリモートコンピューターからマウスやタッチで操作するには、RustDeskに「アクセシビリティ」サービスの使用を許可する必要があります。"), + ("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", "終了"), + ("Help", "ヘルプ"), + ("Failed", "失敗"), + ("Succeeded", "成功"), + ("Someone turns on privacy mode, exit", "プライバシーモードがオンになりました。終了します。"), + ("Unsupported", "サポートされていません"), + ("Peer denied", "リモートホストに拒否されました"), + ("Please install plugins", "プラグインをインストールしてください"), + ("Peer exit", "リモートホストが退出しました"), + ("Failed to turn off", "オフにできませんでした"), + ("Turned off", "オフになりました"), + ("Language", "言語"), + ("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"), + ("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"), + ("android_open_battery_optimizations_tip", "この機能を使わない場合は、RestDeskアプリの設定ページから「バッテリー」に進み、「制限なし」のチェックを外してください"), + ("Start on boot", "起動時に自動実行する"), + ("Start the screen sharing service on boot, requires special permissions", "起動時に画面共有サービスを開始します。これには特別な権限が必要です。"), + ("Connection not allowed", "接続が許可されていません"), + ("Legacy mode", "レガシーモード"), + ("Map mode", "マップモード"), + ("Translate mode", "変換モード"), + ("Use permanent password", "固定パスワードを使用"), + ("Use both passwords", "どちらのパスワードも使用"), + ("Set permanent password", "固定パスワードを設定"), + ("Enable 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 Toolbar", "ツールバーを表示"), + ("Hide Toolbar", "ツールバーを隠す"), + ("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", "音声入力デバイス"), + ("Use IP Whitelisting", "IPホワイトリストを使用する"), + ("Network", "ネットワーク"), + ("Pin Toolbar", "ツールバーをピン止め"), + ("Unpin Toolbar", "ツールバーのピン止めを解除"), + ("Recording", "録画"), + ("Directory", "ディレクトリ"), + ("Automatically record incoming sessions", "受信したセッションを自動で記録する"), + ("Change", "変更"), + ("Start session recording", "セッションの録画を開始"), + ("Stop session recording", "セッションの録画を停止"), + ("Enable recording session", "セッションの録画を有効化"), + ("Enable LAN discovery", "LAN探索を有効化"), + ("Deny LAN discovery", "LAN探索を拒否"), + ("Write a message", "メッセージを書き込む"), + ("Prompt", "必須"), + ("Please wait for confirmation of UAC...", "UACの承認を待機しています..."), + ("elevated_foreground_window_tip", "リモートデスクトップでフォーカスされているウィンドウの操作にはより高い権限が必要なため、マウスとキーボードが一時的に使用できなくなっています。リモートユーザーにウィンドウを最小化、または接続管理画面から権限を昇格するよう要求してください。この問題を回避するには、リモートコンピューターにRustDeskをインストールしてください。"), + ("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デスクトップを試すか、OSを変更してください。"), + ("JumpLink", "View"), + ("Please Select the screen to be shared(Operate on the peer side).", "共有する画面を選択してください(リモートコンピューターが操作します)"), + ("Show RustDesk", "RustDeskを表示"), + ("This PC", "この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", "LinuxでNvidia製のグラフィックカードを使用していると、接続後すぐにリモートウィンドウが閉じてしまう場合があります。オープンソースのNouveauドライバに切り替え、ソフトウェアレンダリングを使用するよう設定すると解決するかもしれません。(RustDeskの再起動が必要です)"), + ("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", "リモートデスクトップ ユーザーがRustDeskを実行する際に、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", "FPS"), + ("Auto", "自動"), + ("Other Default Options", "その他のデフォルト設定"), + ("Voice call", "音声通話"), + ("Text chat", "テキストチャット"), + ("Stop voice call", "音声通話を終了"), + ("relay_hint_tip", "直接接続が行えない場合は、リレー経由での接続をお試しください。初回接続で中継接続を行いたい場合は末尾に「/r」を付けるか、最近のセッション履歴に「常に中継サーバー経由で接続」という設定がある場合はそちらを選択してください。"), + ("Reconnect", "再接続"), + ("Codec", "コーデック"), + ("Resolution", "解像度"), + ("No transfers in progress", "進行中の転送はありません"), + ("Set one-time password length", "ワンタイムパスワードの長さを設定する"), + ("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", "空のパスワード"), + ("Me", "あなた"), + ("identical_file_tip", "このファイルはリモートコンピューターと同一です。"), + ("show_monitors_tip", "ツールバーにモニターを表示します"), + ("View Mode", "表示モード"), + ("login_linux_tip", "Xデスクトップのセッションにログインするには、リモートコンピューターのLinuxアカウントにログインする必要があります。"), + ("verify_rustdesk_password_tip", "RustDeskのパスワードを確認する"), + ("remember_account_tip", "このアカウントを記憶する"), + ("os_account_desk_tip", "このアカウントは、リモートコンピューターのOSにログインし、ヘッドレスでセッションを有効化するために使用されます。"), + ("OS Account", "OSのアカウント"), + ("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デスクトップ環境をインストールしてください"), + ("No need to elevate", "権限昇格の必要はありません"), + ("System Sound", "システム音声"), + ("Default", "デフォルト"), + ("New RDP", "新しいRDP"), + ("Fingerprint", "フィンガープリント"), + ("Copy Fingerprint", "フィンガープリントをコピー"), + ("no fingerprints", "フィンガープリントがありません"), + ("Select a peer", "リモートコンピューターを選択"), + ("Select peers", "複数のリモートコンピューターを選択"), + ("Plugins", "プラグイン"), + ("Uninstall", "アンインストール"), + ("Update", "更新"), + ("Enable", "有効"), + ("Disable", "無効"), + ("Options", "設定"), + ("resolution_original_tip", "オリジナルの解像度"), + ("resolution_fit_local_tip", "ローカル解像度に合わせる"), + ("resolution_custom_tip", "カスタム解像度"), + ("Collapse toolbar", "ツールバーを折りたたむ"), + ("Accept and Elevate", "承認して権限を昇格する"), + ("accept_and_elevate_btn_tooltip", "接続を受け入れた上で、UAC権限を昇格します。"), + ("clipboard_wait_response_timeout_tip", "クリップボードのコピーがタイムアウトしました。"), + ("Incoming connection", "接続の受信"), + ("Outgoing connection", "接続の送信"), + ("Exit", "終了"), + ("Open", "開く"), + ("logout_tip", "本当にログアウトしますか?"), + ("Service", "サービス"), + ("Start", "開始"), + ("Stop", "停止"), + ("exceed_max_devices", "管理対象のデバイスが最大数に達しました。"), + ("Sync with recent sessions", "最近のセッションと同期"), + ("Sort tags", "タグで並べ替え"), + ("Open connection in new tab", "新しいタブで接続を開く"), + ("Move tab to new window", "新しいウィンドウにタブを移動する"), + ("Can not be empty", "空にすることはできません"), + ("Already exists", "すでに存在します"), + ("Change Password", "パスワードを変更"), + ("Refresh Password", "パスワードをリフレッシュ"), + ("ID", "ID"), + ("Grid View", "グリッドビュー"), + ("List View", "リストビュー"), + ("Select", "選択"), + ("Toggle Tags", "タグの切り替え"), + ("pull_ab_failed_tip", "アドレス帳の更新に失敗しました"), + ("push_ab_failed_tip", "サーバーへのアドレス帳の同期に失敗しました"), + ("synced_peer_readded_tip", "最近セッションを行ったデバイスはアドレス帳に同期されます。"), + ("Change Color", "色の変更"), + ("Primary Color", "プライマリ カラー"), + ("HSV Color", "HSVカラー"), + ("Installation Successful!", "インストールに成功しました!"), + ("Installation failed!", "インストールに失敗しました。"), + ("Reverse mouse wheel", "マウスホイールを反転する"), + ("{} sessions", "{}件のセッション"), + ("scam_title", "あなたは詐欺にあっているかもしれません!"), + ("scam_text1", "もし、知らない相手から電話でRustDeskのインストールやサービスの開始を依頼された場合、作業を進めずに、すぐに電話を切ってください。"), + ("scam_text2", "相手はあなたからお金や個人情報を盗もうとする詐欺師である可能性があります。"), + ("Don't show again", "今後表示しない"), + ("I Agree", "同意する"), + ("Decline", "同意しない"), + ("Timeout in minutes", "タイムアウトまでの時間(分)"), + ("auto_disconnect_option_tip", "ユーザーが非アクティブの場合、自動的に受信したセッションを閉じる"), + ("Connection failed due to inactivity", "リモートデスクトップ ユーザーが非アクティブなため、接続に失敗しました"), + ("Check for software update on startup", "起動時にソフトウェアの更新をチェック"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "RustDesk Server Proをバージョン{}以上にアップグレードしてください!"), + ("pull_group_failed_tip", "グループの更新に失敗しました"), + ("Filter by intersection", "交差位置でフィルター"), + ("Remove wallpaper during incoming sessions", "セッションの受信中、デスクトップ背景を削除する"), + ("Test", "テスト"), + ("display_is_plugged_out_msg", "モニターが接続されていません。最初のモニターを選択してください。"), + ("No displays", "モニターがありません"), + ("Open in new window", "新しいウィンドウで開く"), + ("Show displays as individual windows", "モニターを別々のウィンドウとして表示"), + ("Use all my displays for the remote session", "すべてのディスプレイをセッションで使用する"), + ("selinux_tip", "SELinuxが有効になっているため、RustDeskが正常に動作しない可能性があります。"), + ("Change view", "表示変更"), + ("Big tiles", "大きなタイル"), + ("Small tiles", "小さなタイル"), + ("List", "リスト"), + ("Virtual display", "仮想モニター"), + ("Plug out all", "すべて切断する"), + ("True color (4:4:4)", "True color (4:4:4)"), + ("Enable blocking user input", "ユーザー入力のブロックを有効化"), + ("id_input_tip", "ID、IPアドレス、またはドメインとポート番号(<ドメイン>:<ポート>)を使用できます。\n他のサーバーのデバイスにアクセスしたい場合は、サーバーアドレス(@<サーバーアドレス>?key=<キーの値>)を追加してください。 \n(例:9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=)\nパブリックサーバーのデバイスに接続したい場合は、「@public」のように入力してください。パブリックサーバーの場合、キーは不要です。\n\n初回接続で中継接続を行いたい場合は、「9123456234/r」のように末尾に「/r」を付けてください。"), + ("privacy_mode_impl_mag_tip", "モード 1"), + ("privacy_mode_impl_virtual_display_tip", "モード 2"), + ("Enter privacy mode", "プライバシーモードを起動"), + ("Exit privacy mode", "プライバシーモードを終了"), + ("idd_not_support_under_win10_2004_tip", "Indirect display driverには対応していません。Windows 10 バージョン2004以降が必要です。"), + ("input_source_1_tip", "入力ソース 1"), + ("input_source_2_tip", "入力ソース 2"), + ("Swap control-command key", "ctrlとcommandキーを入れ替える"), + ("swap-left-right-mouse", "マウスのクリックを入れ替える"), + ("2FA code", "二要素認証コード"), + ("More", "詳細"), + ("enable-2fa-title", "二要素認証を有効化"), + ("enable-2fa-desc", "認証アプリをセットアップします。Authy、MicrosoftまたはGoogle AuthenticatorなどがPCまたはスマートフォンで利用できます。\n\nQRコードをスキャンし、アプリが表示するコードを入力することで二要素認証が有効になります。"), + ("wrong-2fa-code", "コードが違います。コードと端末の時刻設定が正しいかをご確認ください。"), + ("enter-2fa-title", "二要素認証"), + ("Email verification code must be 6 characters.", "電子メール認証コードは6文字である必要があります。"), + ("2FA code must be 6 digits.", "二要素認証コードは6文字である必要があります。"), + ("Multiple Windows sessions found", "複数のWindowsセッションが見つかりました"), + ("Please select the session you want to connect to", "接続したいセッションを選択してください"), + ("powered_by_me", "Powered by RustDesk"), + ("outgoing_only_desk_tip", "カスタマイズされたエディションを使用しています。\n他のコンピューターに接続できますが、他のコンピューターからのリクエストは受信できません。"), + ("preset_password_warning", "このエディションには、デフォルトで固定パスワードが設定されています。このパスワードを知っているユーザーはあなたのデバイスを完全にコントロールできるため、そのような危険がある場合は直ちにRustDeskをアンインストールして下さい!"), + ("Security Alert", "セキュリティ警告"), + ("My address book", "あなたのアドレス帳"), + ("Personal", "個人"), + ("Owner", "所有者"), + ("Set shared password", "共有パスワードの設定"), + ("Exist in", "既に存在します"), + ("Read-only", "読み取り専用"), + ("Read/Write", "読み取り/書き込み"), + ("Full Control", "フルアクセス"), + ("share_warning_tip", "フィールドは共有され、他の人からも閲覧できます。"), + ("Everyone", "全員"), + ("ab_web_console_tip", "webコンソールの詳細"), + ("allow-only-conn-window-open-tip", "RustDeskのウィンドウが開いている場合のみ接続を許可する"), + ("no_need_privacy_mode_no_physical_displays_tip", "物理モニターが存在しないため、プライバシーモードは不要です。"), + ("Follow remote cursor", "リモートカーソルに追従"), + ("Follow remote window focus", "リモートウィンドウのフォーカスに追従"), + ("default_proxy_tip", "デフォルトのプロトコルとポートはSocks5と1080です。"), + ("no_audio_input_device_tip", "オーディオ入力デバイスが見つかりません。"), + ("Incoming", "受信"), + ("Outgoing", "発信"), + ("Clear Wayland screen selection", "Waylandの画面選択をクリア"), + ("clear_Wayland_screen_selection_tip", "画面選択をクリア後、共有画面を再び選択できます。"), + ("confirm_clear_Wayland_screen_selection_tip", "本当にWaylandの画面選択をクリアしますか?"), + ("android_new_voice_call_tip", "新しい音声通話リクエストを受信しました。承認すると音声通話に切り替わります。"), + ("texture_render_tip", "テクスチャレンダリングを使用し、画像をより滑らかに描画します。レンダリングの問題が発生した場合は無効にしてみてください。"), + ("Use texture rendering", "テクスチャレンダリングを使用"), + ("Floating window", "フローティングウィンドウ"), + ("floating_window_tip", "RustDeskのバックグラウンドサービスを維持するために使用されます。"), + ("Keep screen on", "常に画面をオン"), + ("Never", "画面をオンにしない"), + ("During controlled", "操作中"), + ("During service is on", "サービスの動作中"), + ("Capture screen using DirectX", "DirectXを使用した画面キャプチャ"), + ("Back", "戻る"), + ("Apps", "アプリ"), + ("Volume up", "音量アップ"), + ("Volume down", "音量ダウン"), + ("Power", "電源"), + ("Telegram bot", "Telegram Bot"), + ("enable-bot-tip", "この機能を有効にすると、ボットから二要素認証コードを受け取ることができます。また、接続時の通知としても機能します。"), + ("enable-bot-desc", "1. @BotFatherのチャットを開きます。\n2. 「/newbot」コマンドを送信します。送信後、トークンを取得できます。\n3. 新しく作成したbotとチャットを開始します。「/hello」のようにスラッシュで始まるメッセージを送信して起動します。\n"), + ("cancel-2fa-confirm-tip", "本当に二要素認証をキャンセルしますか?"), + ("cancel-bot-confirm-tip", "本当にTelegram Botをキャンセルしますか?"), + ].iter().cloned().collect(); + } + \ No newline at end of file From cf8ef2533a8e9159d897436522d011dea70993ae Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sat, 20 Jul 2024 00:42:49 +0800 Subject: [PATCH 302/335] fix: keyboard, linux, repeated keys, #6793 (#8757) * fix: keyboard, linux, repeat keys, #6793 Signed-off-by: fufesou * fix: keyboard, linux->linux, may also repeat keys with bad network Signed-off-by: fufesou --------- Signed-off-by: fufesou --- src/keyboard.rs | 47 ++++++++++++++++++++++++---------- src/virtual_display_manager.rs | 1 - 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 21da622c4b6..ced80b38021 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -580,10 +580,7 @@ pub fn event_to_key_events( #[cfg(any(target_os = "android", target_os = "ios"))] let key_events; key_events = match keyboard_mode { - KeyboardMode::Map => match map_keyboard_mode(peer.as_str(), event, key_event) { - Some(event) => [event].to_vec(), - None => Vec::new(), - }, + KeyboardMode::Map => map_keyboard_mode(peer.as_str(), event, key_event), KeyboardMode::Translate => translate_keyboard_mode(peer.as_str(), event, key_event), _ => { #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -865,7 +862,27 @@ pub fn legacy_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Vec Option { +pub fn map_keyboard_mode(_peer: &str, event: &Event, key_event: KeyEvent) -> Vec { + match _map_keyboard_mode(_peer, event, key_event) { + Some(key_event) => { + if _peer == OS_LOWER_LINUX { + if let EventType::KeyPress(k) = &event.event_type { + #[cfg(target_os = "ios")] + let try_workaround = true; + #[cfg(not(target_os = "ios"))] + let try_workaround = !is_modifier(k); + if try_workaround { + return try_workaround_linux_long_press(key_event); + } + } + } + vec![key_event] + } + None => Vec::new(), + } +} + +fn _map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) -> Option { match event.event_type { EventType::KeyPress(..) => { key_event.down = true; @@ -923,6 +940,14 @@ pub fn map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) -> Some(key_event) } +// https://github.com/rustdesk/rustdesk/issues/6793 +#[inline] +fn try_workaround_linux_long_press(key_event: KeyEvent) -> Vec { + let mut key_event_up = key_event.clone(); + key_event_up.down = false; + vec![key_event, key_event_up] +} + #[cfg(not(any(target_os = "ios")))] fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &mut Vec) { match &event.unicode { @@ -954,7 +979,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m } #[cfg(target_os = "windows")] -fn try_file_win2win_hotkey( +fn try_fill_win2win_hotkey( peer: &str, event: &Event, key_event: &KeyEvent, @@ -1041,9 +1066,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) = map_keyboard_mode(peer, event, key_event) { - events.push(evt); - } + events.append(&mut map_keyboard_mode(peer, event, key_event)); return events; } @@ -1064,7 +1087,7 @@ 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); + try_fill_win2win_hotkey(peer, event, &key_event, &mut events); #[cfg(any(target_os = "linux", target_os = "windows"))] if events.is_empty() && is_press(event) { @@ -1085,9 +1108,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } if events.is_empty() { - if let Some(evt) = map_keyboard_mode(peer, event, key_event) { - events.push(evt); - } + events.append(&mut map_keyboard_mode(peer, event, key_event)); } events } diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index 37bfeb33427..ac87199726b 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -405,7 +405,6 @@ pub mod amyuni_idd { use crate::platform::win_device; use hbb_common::{bail, lazy_static, log, tokio::time::Instant, ResultType}; use std::{ - ops::Sub, ptr::null_mut, sync::{Arc, Mutex}, time::Duration, From d3454f07d354468090a9923f60a4e21d06e579ff Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Sat, 20 Jul 2024 23:49:40 +0800 Subject: [PATCH 303/335] fix: move some crates to rustdesk-org (#8772) Signed-off-by: fufesou Co-authored-by: fufesou --- Cargo.lock | 12 ++++++------ Cargo.toml | 8 ++++---- build.py | 13 ++----------- flutter/lib/common.dart | 2 +- libs/enigo/Cargo.toml | 4 ++-- libs/virtual_display/dylib/README.md | 2 +- res/msi/preprocess.py | 2 +- src/keyboard.rs | 2 +- src/platform/windows.rs | 2 +- src/plugin/manager.rs | 11 ++++++----- src/privacy_mode/win_virtual_display.rs | 2 +- src/virtual_display_manager.rs | 2 +- 12 files changed, 27 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6a35c493c6..3df52e1cc95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1940,7 +1940,7 @@ checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" [[package]] name = "evdev" version = "0.11.5" -source = "git+https://github.com/fufesou/evdev#cec616e37790293d2cd2aa54a96601ed6b1b35a9" +source = "git+https://github.com/rustdesk-org/evdev#cec616e37790293d2cd2aa54a96601ed6b1b35a9" dependencies = [ "bitvec", "libc", @@ -3850,7 +3850,7 @@ dependencies = [ [[package]] name = "mouce" version = "0.2.1" -source = "git+https://github.com/fufesou/mouce.git#ed83800d532b95d70e39915314f6052aa433e9b9" +source = "git+https://github.com/rustdesk-org/mouce.git#ed83800d532b95d70e39915314f6052aa433e9b9" dependencies = [ "glob", ] @@ -4551,7 +4551,7 @@ dependencies = [ [[package]] name = "pam" version = "0.7.0" -source = "git+https://github.com/fufesou/pam#3a2aaa6e07b176d8e2d66a5eec38d2ddb45f009f" +source = "git+https://github.com/rustdesk-org/pam#7bfd25510202cd269292cbdd7c71f3977a6fd762" dependencies = [ "libc", "pam-macros", @@ -4573,7 +4573,7 @@ dependencies = [ [[package]] name = "pam-sys" version = "1.0.0-alpha4" -source = "git+https://github.com/fufesou/pam-sys?branch=fix/v1.0.0-alpha4_gnuc_va_list#3337c9bb9a9c68d7497ec8c93cad2368c26091b7" +source = "git+https://github.com/rustdesk-org/pam-sys?branch=fix/v1.0.0-alpha4_gnuc_va_list#3337c9bb9a9c68d7497ec8c93cad2368c26091b7" dependencies = [ "bindgen 0.59.2", "libc", @@ -5260,7 +5260,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#b3434caee84c92412b45a2f655a15ac5dad33488" +source = "git+https://github.com/rustdesk-org/rdev#b3434caee84c92412b45a2f655a15ac5dad33488" dependencies = [ "cocoa 0.24.1", "core-foundation 0.9.4", @@ -6594,7 +6594,7 @@ dependencies = [ [[package]] name = "tfc" version = "0.6.1" -source = "git+https://github.com/fufesou/The-Fat-Controller#9dd86151525fd010dc93f6bc9b6aedd1a75cc342" +source = "git+https://github.com/rustdesk-org/The-Fat-Controller#9dd86151525fd010dc93f6bc9b6aedd1a75cc342" dependencies = [ "anyhow", "core-graphics 0.22.3", diff --git a/Cargo.toml b/Cargo.toml index 0c54897e1c8..85b2b540121 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,7 @@ default-net = "0.14" wol-rs = "1.0" flutter_rust_bridge = { version = "=1.80", features = ["uuid"], optional = true} errno = "0.3" -rdev = { git = "https://github.com/fufesou/rdev" } +rdev = { git = "https://github.com/rustdesk-org/rdev" } url = { version = "2.3", features = ["serde"] } crossbeam-queue = "0.3" hex = "0.4" @@ -152,11 +152,11 @@ psimple = { package = "libpulse-simple-binding", version = "2.27" } pulse = { package = "libpulse-binding", version = "2.27" } rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" } async-process = "1.7" -mouce = { git="https://github.com/fufesou/mouce.git" } -evdev = { git="https://github.com/fufesou/evdev" } +mouce = { git="https://github.com/rustdesk-org/mouce.git" } +evdev = { git="https://github.com/rustdesk-org/evdev" } dbus = "0.9" dbus-crossroads = "0.5" -pam = { git="https://github.com/fufesou/pam" } +pam = { git="https://github.com/rustdesk-org/pam" } users = { version = "0.11" } x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true} x11rb = {version = "0.12", features = ["all-extensions"], optional = true} diff --git a/build.py b/build.py index 38a8687be1b..97ec9dfe9f9 100755 --- a/build.py +++ b/build.py @@ -48,15 +48,7 @@ def get_version(): def parse_rc_features(feature): - available_features = { - 'PrivacyMode': { - 'platform': ['windows'], - 'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3' - '/TempTopMostWindow_x64.zip', - 'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3/checksum_md5', - 'include': ['WindowInjection.dll'], - } - } + available_features = {} apply_features = {} if not feature: feature = [] @@ -81,7 +73,6 @@ def get_all_features(): elif isinstance(feature, list): if windows: # download third party is deprecated, we use github ci instead. - # force add PrivacyMode # feature.append('PrivacyMode') pass for feat in feature: @@ -108,7 +99,7 @@ def make_parser(): nargs='+', default='', help='Integrate features, windows only.' - 'Available: PrivacyMode. Special value is "ALL" and empty "". Default is empty.') + 'Available: [Not used for now]. Special value is "ALL" and empty "". Default is empty.') parser.add_argument('--flutter', action='store_true', help='Build flutter package', default=False) parser.add_argument( diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 0ce9433f40e..d0d753fbbca 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1062,7 +1062,7 @@ void msgBox(SessionID sessionId, String type, String title, String text, bool hasOk = false; submit() { dialogManager.dismissAll(); - // https://github.com/fufesou/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263 + // https://github.com/rustdesk/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263 if (!type.contains("custom") && desktopType != DesktopType.portForward) { closeConnection(); } diff --git a/libs/enigo/Cargo.toml b/libs/enigo/Cargo.toml index 1f67ae36b86..1549209e13c 100644 --- a/libs/enigo/Cargo.toml +++ b/libs/enigo/Cargo.toml @@ -22,8 +22,8 @@ appveyor = { repository = "pythoneer/enigo-85xiy" } serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } log = "0.4" -rdev = { git = "https://github.com/fufesou/rdev" } -tfc = { git = "https://github.com/fufesou/The-Fat-Controller" } +rdev = { git = "https://github.com/rustdesk-org/rdev" } +tfc = { git = "https://github.com/rustdesk-org/The-Fat-Controller" } hbb_common = { path = "../hbb_common" } [features] diff --git a/libs/virtual_display/dylib/README.md b/libs/virtual_display/dylib/README.md index d5a1a086288..30fa588f1ee 100644 --- a/libs/virtual_display/dylib/README.md +++ b/libs/virtual_display/dylib/README.md @@ -10,7 +10,7 @@ Virtual display may be used on computers that do not have a monitor. Win10 provides [Indirect Display Driver Model](https://msdn.microsoft.com/en-us/library/windows/hardware/mt761968(v=vs.85).aspx). -This lib uses [this project](https://github.com/fufesou/RustDeskIddDriver) as the driver. +This lib uses [this project](https://github.com/rustdesk-org/RustDeskIddDriver) as the driver. **NOTE**: Versions before Win10 1607. Try follow [this method](https://github.com/fanxiushu/xdisp_virt/tree/master/indirect_display). diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index 74a7fb68eb0..e9ac0a887bd 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -33,7 +33,7 @@ }, "ReadMe": { "msi": "ARPREADME", - "v": "https://github.com/fufesou/rustdesk", + "v": "https://github.com/rustdesk/rustdesk", }, } diff --git a/src/keyboard.rs b/src/keyboard.rs index ced80b38021..6b4b0988fb2 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1046,7 +1046,7 @@ fn is_press(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(_)) } -// https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode +// https://github.com/rustdesk/rustdesk/wiki/FAQ#keyboard-translation-modes pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 93b209839f5..09ff347a295 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -2028,7 +2028,7 @@ pub fn get_unicode_from_vk(vk: u32) -> Option { let current_window_thread_id = GetWindowThreadProcessId(GetForegroundWindow(), null_mut()); let layout = GetKeyboardLayout(current_window_thread_id); - // refs: https://github.com/fufesou/rdev/blob/25a99ce71ab42843ad253dd51e6a35e83e87a8a4/src/windows/keyboard.rs#L115 + // refs: https://github.com/rustdesk-org/rdev/blob/25a99ce71ab42843ad253dd51e6a35e83e87a8a4/src/windows/keyboard.rs#L115 let press_state = 129; let mut state: [BYTE; 256] = [0; 256]; let shift_left = rdev::get_modifier(rdev::Key::ShiftLeft); diff --git a/src/plugin/manager.rs b/src/plugin/manager.rs index 25e73541eb1..74a7f736f24 100644 --- a/src/plugin/manager.rs +++ b/src/plugin/manager.rs @@ -57,11 +57,12 @@ static PLUGIN_SOURCE_LOCAL: &str = "local"; fn get_plugin_source_list() -> Vec { // Only one source for now. - vec![PluginSource { - name: "rustdesk".to_string(), - url: "https://raw.githubusercontent.com/fufesou/rustdesk-plugins/main".to_string(), - description: "".to_string(), - }] + // vec![PluginSource { + // name: "rustdesk".to_string(), + // url: "https://raw.githubusercontent.com/fufesou/rustdesk-plugins/main".to_string(), + // description: "".to_string(), + // }] + vec![] } fn get_source_plugins() -> HashMap { diff --git a/src/privacy_mode/win_virtual_display.rs b/src/privacy_mode/win_virtual_display.rs index fd562c8e7a7..0bf4b721f48 100644 --- a/src/privacy_mode/win_virtual_display.rs +++ b/src/privacy_mode/win_virtual_display.rs @@ -85,7 +85,7 @@ impl PrivacyModeImpl { } } - // mainly from https://github.com/fufesou/rustdesk/blob/44c3a52ca8502cf53b58b59db130611778d34dbe/libs/scrap/src/dxgi/mod.rs#L365 + // mainly from https://github.com/rustdesk-org/rustdesk/blob/44c3a52ca8502cf53b58b59db130611778d34dbe/libs/scrap/src/dxgi/mod.rs#L365 fn set_displays(&mut self) { self.displays.clear(); self.virtual_displays.clear(); diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index ac87199726b..af5eda9732d 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -2,7 +2,7 @@ use hbb_common::{bail, platform::windows::is_windows_version_or_greater, ResultT use std::sync::atomic; // This string is defined here. -// https://github.com/fufesou/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 +// https://github.com/rustdesk/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 pub const RUSTDESK_IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0"; pub const AMYUNI_IDD_DEVICE_STRING: &'static str = "USB Mobile Monitor Virtual Display\0"; From de375c91bbaa5d0c8c5840557d6285f656eabc13 Mon Sep 17 00:00:00 2001 From: Bot_wxt1221 <3264117476@qq.com> Date: Sun, 21 Jul 2024 17:05:32 +0800 Subject: [PATCH 304/335] fix: nixos's headless mode. (#8774) --- 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 0810bb2460b..f936bfb8cc1 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -730,7 +730,7 @@ pub fn block_input(_v: bool) -> (bool, String) { pub fn is_installed() -> bool { if let Ok(p) = std::env::current_exe() { - p.to_str().unwrap_or_default().starts_with("/usr") + p.to_str().unwrap_or_default().starts_with("/usr") || p.to_str().unwrap_or_default().starts_with("/nix/store") } else { false } From c4f3c0f133d44e9409ef6d3e3c8d6bdb4f0b4a3e Mon Sep 17 00:00:00 2001 From: DiegoGZ Date: Mon, 22 Jul 2024 03:58:04 +0200 Subject: [PATCH 305/335] Update ca.rs - Added new translation into Catalan and a few corrections (#8777) Added new translation into Catalan and a few corrections --- src/lang/ca.rs | 396 ++++++++++++++++++++++++------------------------- 1 file changed, 198 insertions(+), 198 deletions(-) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index adf0975a24f..25b983d6e81 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -2,7 +2,7 @@ lazy_static::lazy_static! { pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ("Status", "Estat"), - ("Your Desktop", "EL teu escriptori"), + ("Your Desktop", "El teu escriptori"), ("desk_tip", "Pots accedir al teu escriptori amb aquest ID i contrasenya."), ("Password", "Contrasenya"), ("Ready", "Llest"), @@ -27,7 +27,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable clipboard", "Habilitar portapapers"), ("Enable file transfer", "Habilitar transferència d'arxius"), ("Enable TCP tunneling", "Habilitar túnel TCP"), - ("IP Whitelisting", "Direccions IP admeses"), + ("IP Whitelisting", "Adreces IP admeses"), ("ID/Relay Server", "Servidor ID/Relay"), ("Import server config", "Importar configuració de servidor"), ("Export Server Config", "Exportar configuració del servidor"), @@ -37,19 +37,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Clipboard is empty", "El portapapers està buit"), ("Stop service", "Aturar servei"), ("Change ID", "Canviar ID"), - ("Your new ID", ""), - ("length %min% to %max%", ""), - ("starts with a letter", ""), - ("allowed characters", ""), + ("Your new ID", "La teva nova ID"), + ("length %min% to %max%", "Ha de tenir entre %min% i %max% caràcters"), + ("starts with a letter", "començar amb una lletra"), + ("allowed characters", "caràcters permesos"), ("id_change_tip", "Només pots utilitzar caràcters a-z, A-Z, 0-9 e _ (guionet baix). El primer caràcter ha de ser a-z o A-Z. La longitut ha d'estar entre 6 i 16 caràcters."), ("Website", "Lloc web"), ("About", "Sobre"), ("Slogan_tip", ""), - ("Privacy Statement", ""), + ("Privacy Statement", "Declaració de privacitat"), ("Mute", "Silenciar"), - ("Build Date", ""), - ("Version", ""), - ("Home", ""), + ("Build Date", "Data de creació"), + ("Version", "Versió"), + ("Home", "Inici"), ("Audio Input", "Entrada d'àudio"), ("Enhancements", "Millores"), ("Hardware Codec", "Còdec de hardware"), @@ -67,14 +67,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Skip", "Saltar"), ("Close", "Tancar"), ("Retry", "Reintentar"), - ("OK", ""), + ("OK", "D'acord"), ("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"), ("Do you want to enter again?", "Vol tornar a entrar?"), ("Connection Error", "Error de connexió"), - ("Error", ""), + ("Error", "Error"), ("Reset by the peer", "Reestablert pel peer"), ("Connecting...", "Connectant..."), ("Connection in progress. Please wait.", "Connexió en procés. Esperi."), @@ -90,7 +90,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Receive", "Rebre"), ("Send", "Enviar"), ("Refresh File", "Actualitzar arxiu"), - ("Local", ""), + ("Local", "Local"), ("Remote", "Remot"), ("Remote Computer", "Ordinador remot"), ("Local Computer", "Ordinador local"), @@ -151,7 +151,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Click to update", "Clicar per refrescar"), ("Configure", "Configurar"), ("config_acc", ""), - ("config_screen", ""), + ("config_screen", "Configurar pantalla"), ("Installing ...", "Instal·lant ..."), ("Install", "Instal·lar"), ("Installation", "Instal·lació"), @@ -203,24 +203,24 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Reboot required", "Cal reiniciar"), ("Unsupported display server", "Servidor de visualització no compatible"), ("x11 expected", "x11 necessari"), - ("Port", ""), + ("Port", "Port"), ("Settings", "Ajustaments"), ("Username", " Nom d'usuari"), ("Invalid port", "Port incorrecte"), ("Closed manually by the peer", "Tancat manualment pel peer"), ("Enable remote configuration modification", "Habilitar modificació remota de configuració"), ("Run without install", "Executar sense instal·lar"), - ("Connect via relay", ""), + ("Connect via relay", "Connecta per relay"), ("Always connect via relay", "Connecta sempre a través de relay"), ("whitelist_tip", ""), ("Login", "Inicia sessió"), - ("Verify", ""), - ("Remember me", ""), - ("Trust this device", ""), - ("Verification code", ""), + ("Verify", "Verificar"), + ("Remember me", "Recorda'm"), + ("Trust this device", "Confia en aquest dispositiu"), + ("Verification code", "Codi de verificació"), ("verification_tip", ""), ("Logout", "Sortir"), - ("Tags", ""), + ("Tags", "Etiquetes"), ("Search ID", "Cerca ID"), ("whitelist_sep", ""), ("Add ID", "Afegir ID"), @@ -230,7 +230,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Username missed", "Nom d'usuari oblidat"), ("Password missed", "Contrasenya oblidada"), ("Wrong credentials", "Credencials incorrectes"), - ("The verification code is incorrect or has expired", ""), + ("The verification code is incorrect or has expired", "El codi de verificació es incorrecte o ha expirat"), ("Edit Tag", "Editar tag"), ("Forget Password", "Contrasenya oblidada"), ("Favorites", "Preferits"), @@ -306,8 +306,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Mantenir RustDesk com a servei en segon pla"), ("Ignore Battery Optimizations", "Ignorar optimizacions de la bateria"), ("android_open_battery_optimizations_tip", ""), - ("Start on boot", ""), - ("Start the screen sharing service on boot, requires special permissions", ""), + ("Start on boot", "Engegar en l'arrencada"), + ("Start the screen sharing service on boot, requires special permissions", "Engegar el servei de captura de pantalla en l'arrencada, requereix permisos especials"), ("Connection not allowed", "Connexió no disponible"), ("Legacy mode", "Mode heretat"), ("Map mode", "Mode mapa"), @@ -330,19 +330,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ratio", "Relació"), ("Image Quality", "Qualitat d'imatge"), ("Scroll Style", "Estil de desplaçament"), - ("Show Toolbar", ""), - ("Hide Toolbar", ""), + ("Show Toolbar", "Mostrar la barra d'eines"), + ("Hide Toolbar", "Amancar la barra d'eines"), ("Direct Connection", "Connexió directa"), ("Relay Connection", "Connexió Relay"), ("Secure Connection", "Connexió segura"), ("Insecure Connection", "Connexió insegura"), ("Scale original", "Escala original"), ("Scale adaptive", "Escala adaptativa"), - ("General", ""), + ("General", "General"), ("Security", "Seguretat"), ("Theme", "Tema"), ("Dark Theme", "Tema Fosc"), - ("Light Theme", ""), + ("Light Theme", "Tema clar"), ("Dark", "Fosc"), ("Light", "Clar"), ("Follow System", "Tema del sistema"), @@ -352,15 +352,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Unlock Network Settings", "Desbloquejar Ajustaments de Xarxa"), ("Server", "Servidor"), ("Direct IP Access", "Accés IP Directe"), - ("Proxy", ""), + ("Proxy", "Proxy"), ("Apply", "Aplicar"), ("Disconnect all devices?", "Desconnectar tots els dispositius?"), ("Clear", "Netejar"), ("Audio Input Device", "Dispositiu d'entrada d'àudio"), ("Use IP Whitelisting", "Utilitza llista de IPs admeses"), ("Network", "Xarxa"), - ("Pin Toolbar", ""), - ("Unpin Toolbar", ""), + ("Pin Toolbar", "Fixa la barra d'eines"), + ("Unpin Toolbar", "Soltar la barra d'eines"), ("Recording", "Gravant"), ("Directory", "Directori"), ("Automatically record incoming sessions", "Gravació automàtica de sessions entrants"), @@ -371,8 +371,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable LAN discovery", "Habilitar descobriment de LAN"), ("Deny LAN discovery", "Denegar descobriment de LAN"), ("Write a message", "Escriure un missatge"), - ("Prompt", ""), - ("Please wait for confirmation of UAC...", ""), + ("Prompt", "Consultar"), + ("Please wait for confirmation of UAC...", "Sisplau, espereu per confirmar l'UAC..."), ("elevated_foreground_window_tip", ""), ("Disconnected", "Desconnectat"), ("Other", "Altre"), @@ -388,87 +388,87 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "Aquest PC"), ("or", "o"), ("Continue with", "Continuar amb"), - ("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", ""), + ("Elevate", "Elevar"), + ("Zoom cursor", "Zoom del ratolí"), + ("Accept sessions via password", "Acceptar sessions via contrasenya"), + ("Accept sessions via click", "Acceptar sessions via clic"), + ("Accept sessions via both", "Acceptar sessions via les dues opcions"), + ("Please wait for the remote side to accept your session request...", "Sisplau, espereu que la part remota accepti la teva sol·licitud de sessió..."), + ("One-time Password", "Contrasenya d'un sol ús"), + ("Use one-time password", "Fer ser una contrasenya d'un sol ús"), + ("One-time password length", "Caracters de la contrasenya d'un sol ús"), + ("Request access to your device", "Sol·licitar l'acces al vostre dispositiu"), + ("Hide connection management window", "Amagar la finestra de gestió de connexió"), ("hide_cm_tip", ""), ("wayland_experiment_tip", ""), - ("Right click to select tabs", ""), - ("Skipped", ""), - ("Add to address book", ""), - ("Group", ""), - ("Search", ""), - ("Closed manually by web console", ""), - ("Local keyboard type", ""), - ("Select local keyboard type", ""), + ("Right click to select tabs", "Clic dret per seleccionar pestanyes"), + ("Skipped", "Pasat"), + ("Add to address book", "Afegir a la llibreta d'adreces"), + ("Group", "Grup"), + ("Search", "Cercar"), + ("Closed manually by web console", "Tancat manualment amb la consola web"), + ("Local keyboard type", "Tipus de teclat local"), + ("Select local keyboard type", "Seleccionar el tipus de teclat local"), ("software_render_tip", ""), - ("Always use software rendering", ""), + ("Always use software rendering", "Sempre fer servir renderització per software"), ("config_input", ""), ("config_microphone", ""), ("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", ""), + ("Wait", "Espereu"), + ("Elevation Error", "Error de elevació"), + ("Ask the remote user for authentication", "Demana autenticació a l'usuari remot"), + ("Choose this if the remote account is administrator", "Seleccionar això si l'usuari remot es administrador"), + ("Transmit the username and password of administrator", "Transmet el nom d'usuari i la contrasenya de l'administrador"), ("still_click_uac_tip", ""), - ("Request Elevation", ""), + ("Request Elevation", "Demanar l'elevació"), ("wait_accept_uac_tip", ""), - ("Elevate successfully", ""), - ("uppercase", ""), - ("lowercase", ""), - ("digit", ""), - ("special character", ""), + ("Elevate successfully", "Elevació exitosa"), + ("uppercase", "majúscula"), + ("lowercase", "minúscula"), + ("digit", "dígit"), + ("special character", "caràcter especial"), ("length>=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", ""), + ("Weak", "Dèbil"), + ("Medium", "Media"), + ("Strong", "Forta"), + ("Switch Sides", "Canviar l'esquerra i la dreta"), + ("Please confirm if you want to share your desktop?", "Sisplau, confirmeu si voleu compartir el seu escriptori?"), + ("Display", "Pantalla"), + ("Default View Style", "Estil de visualització predeterminat"), + ("Default Scroll Style", "Estil de desplaçament predeterminat"), + ("Default Image Quality", "Qualitat d'imatge predeterminada"), + ("Default Codec", "Còdec predeterminat"), + ("Bitrate", "Ratio de bits"), ("FPS", ""), - ("Auto", ""), - ("Other Default Options", ""), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), + ("Auto", "Auto"), + ("Other Default Options", "Altres opcions predeterminades"), + ("Voice call", "Trucada de veu"), + ("Text chat", "Xat de text"), + ("Stop voice call", "Penjar la trucada de veu"), ("relay_hint_tip", ""), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), - ("No transfers in progress", ""), - ("Set one-time password length", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), + ("Reconnect", "Reconectar"), + ("Codec", "Còdec"), + ("Resolution", "Resolució"), + ("No transfers in progress", "Sense transferències en curs"), + ("Set one-time password length", "Seleccionar la longitud de la contrasenya d'un sol ús"), + ("RDP Settings", "Configuració RDP"), + ("Sort by", "Ordenar per"), + ("New Connection", "Nova connexió"), + ("Restore", "Restaurar"), + ("Minimize", "Minimizar"), + ("Maximize", "Maximizar"), + ("Your Device", "El teu dispositiu"), ("empty_recent_tip", ""), ("empty_favorite_tip", ""), ("empty_lan_tip", ""), ("empty_address_book_tip", ""), - ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), - ("Me", ""), + ("eg: admin", "p.ex.: admin"), + ("Empty Username", "Usuari buit"), + ("Empty Password", "Contrasenya buida"), + ("Me", "Jo"), ("identical_file_tip", ""), ("show_monitors_tip", ""), - ("View Mode", ""), + ("View Mode", "Tipus de visualització"), ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), @@ -481,150 +481,150 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), - ("System Sound", ""), - ("Default", ""), - ("New RDP", ""), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), - ("Select a peer", ""), - ("Select peers", ""), - ("Plugins", ""), - ("Uninstall", ""), - ("Update", ""), - ("Enable", ""), - ("Disable", ""), - ("Options", ""), + ("System Sound", "Sistema de so"), + ("Default", "Predeterminat"), + ("New RDP", "Nou RDP"), + ("Fingerprint", "Empremta digital"), + ("Copy Fingerprint", "Copiar l'emprenta digital"), + ("no fingerprints", "sense emprentes"), + ("Select a peer", "Seleccionar un peer"), + ("Select peers", "Seleccionar varios peers"), + ("Plugins", "Plugins"), + ("Uninstall", "Desinstal·lar"), + ("Update", "Actualitzar"), + ("Enable", "Activar"), + ("Disable", "Desactivar"), + ("Options", "Opcions"), ("resolution_original_tip", ""), ("resolution_fit_local_tip", ""), ("resolution_custom_tip", ""), - ("Collapse toolbar", ""), - ("Accept and Elevate", ""), + ("Collapse toolbar", "Col·lapsar la barra d'etiquetes"), + ("Accept and Elevate", "Aceptar i elevar"), ("accept_and_elevate_btn_tooltip", ""), ("clipboard_wait_response_timeout_tip", ""), - ("Incoming connection", ""), - ("Outgoing connection", ""), - ("Exit", ""), - ("Open", ""), + ("Incoming connection", "Connexió entrant"), + ("Outgoing connection", "Connexió sortint"), + ("Exit", "Tancar"), + ("Open", "Obrir"), ("logout_tip", ""), - ("Service", ""), - ("Start", ""), - ("Stop", ""), + ("Service", "Servici"), + ("Start", "Iniciar"), + ("Stop", "Aturar"), ("exceed_max_devices", ""), - ("Sync with recent sessions", ""), - ("Sort tags", ""), - ("Open connection in new tab", ""), - ("Move tab to new window", ""), - ("Can not be empty", ""), - ("Already exists", ""), - ("Change Password", ""), - ("Refresh Password", ""), - ("ID", ""), - ("Grid View", ""), - ("List View", ""), - ("Select", ""), - ("Toggle Tags", ""), + ("Sync with recent sessions", "Sincronitzar amb les sessions recents"), + ("Sort tags", "Ordenar per etiquetes"), + ("Open connection in new tab", "Obrir connexió en una nova pestanya"), + ("Move tab to new window", "Mou la pestanya a una nova finestra"), + ("Can not be empty", "No pot estar buit"), + ("Already exists", "Ja existeix"), + ("Change Password", "Canviar la contrasenya"), + ("Refresh Password", "Refrescar la contrasenya"), + ("ID", "ID"), + ("Grid View", "Visualització de grilla"), + ("List View", "Visualització de llista"), + ("Select", "Seleccionar"), + ("Toggle Tags", "Activar/desactivar etiquetes"), ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), - ("Change Color", ""), - ("Primary Color", ""), - ("HSV Color", ""), - ("Installation Successful!", ""), - ("Installation failed!", ""), - ("Reverse mouse wheel", ""), - ("{} sessions", ""), + ("Change Color", "Canviar el color"), + ("Primary Color", "Color primari"), + ("HSV Color", "Color HSV"), + ("Installation Successful!", "Instal·lació correcta!"), + ("Installation failed!", "Instal·lació fallada!"), + ("Reverse mouse wheel", "Canviar la orientació de la roda del ratolí"), + ("{} sessions", "{} sessions"), ("scam_title", ""), ("scam_text1", ""), ("scam_text2", ""), - ("Don't show again", ""), - ("I Agree", ""), - ("Decline", ""), - ("Timeout in minutes", ""), + ("Don't show again", "No mostrar més"), + ("I Agree", "D'acord"), + ("Decline", "Rebutjar"), + ("Timeout in minutes", "Desconexió en minuts"), ("auto_disconnect_option_tip", ""), - ("Connection failed due to inactivity", ""), - ("Check for software update on startup", ""), + ("Connection failed due to inactivity", "Connexió fallada per inactivitat"), + ("Check for software update on startup", "Revisar actualitzacions de software al iniciar"), ("upgrade_rustdesk_server_pro_to_{}_tip", ""), ("pull_group_failed_tip", ""), - ("Filter by intersection", ""), - ("Remove wallpaper during incoming sessions", ""), - ("Test", ""), + ("Filter by intersection", "Filtrar per intersecció"), + ("Remove wallpaper during incoming sessions", "Amagar el fons de pantalla en les connexions entrants"), + ("Test", "Prova"), ("display_is_plugged_out_msg", ""), - ("No displays", ""), - ("Open in new window", ""), - ("Show displays as individual windows", ""), - ("Use all my displays for the remote session", ""), + ("No displays", "Sense pantalles"), + ("Open in new window", "Obrir en una nova finestra"), + ("Show displays as individual windows", "Mostrar pantalles com finestres individuals"), + ("Use all my displays for the remote session", "Fer servir totes las meves pantalles per la sessió remota"), ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("Virtual display", ""), - ("Plug out all", ""), - ("True color (4:4:4)", ""), - ("Enable blocking user input", ""), + ("Change view", "Canviar la vista"), + ("Big tiles", "Títols grans"), + ("Small tiles", "Títols petits"), + ("List", "Llista"), + ("Virtual display", "Pantalla virtual"), + ("Plug out all", "Desconnectar tots"), + ("True color (4:4:4)", "Color real (4:4:4)"), + ("Enable blocking user input", "Activar bloqueig d'entrada d'usuari"), ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), - ("Enter privacy mode", ""), - ("Exit privacy mode", ""), + ("Enter privacy mode", "Activar el mode de privacitat"), + ("Exit privacy mode", "Sortir del mode de privacitat"), ("idd_not_support_under_win10_2004_tip", ""), ("input_source_1_tip", ""), ("input_source_2_tip", ""), - ("Swap control-command key", ""), + ("Swap control-command key", "Canviar la tecla de control"), ("swap-left-right-mouse", ""), - ("2FA code", ""), - ("More", ""), + ("2FA code", "Codi 2FA"), + ("More", "Més"), ("enable-2fa-title", ""), ("enable-2fa-desc", ""), ("wrong-2fa-code", ""), ("enter-2fa-title", ""), - ("Email verification code must be 6 characters.", ""), - ("2FA code must be 6 digits.", ""), - ("Multiple Windows sessions found", ""), - ("Please select the session you want to connect to", ""), + ("Email verification code must be 6 characters.", "El codi de verificació de l'email ha de tenir 6 caràcters."), + ("2FA code must be 6 digits.", "El codi 2FA ha de tenir 6 digits."), + ("Multiple Windows sessions found", "Multiples sessions de Windows trobades"), + ("Please select the session you want to connect to", "Sisplau, seleccioni la sessió que voleu connectar"), ("powered_by_me", ""), ("outgoing_only_desk_tip", ""), ("preset_password_warning", ""), - ("Security Alert", ""), - ("My address book", ""), - ("Personal", ""), - ("Owner", ""), - ("Set shared password", ""), - ("Exist in", ""), - ("Read-only", ""), - ("Read/Write", ""), - ("Full Control", ""), + ("Security Alert", "Alerta de seguretat"), + ("My address book", "La meva llibreta d'adreces"), + ("Personal", "Personal"), + ("Owner", "Propietari"), + ("Set shared password", "Establir la contrasenya compartida"), + ("Exist in", "Existeix en"), + ("Read-only", "Només lectura"), + ("Read/Write", "Lectura/Escriptura"), + ("Full Control", "Control total"), ("share_warning_tip", ""), - ("Everyone", ""), + ("Everyone", "Tots"), ("ab_web_console_tip", ""), ("allow-only-conn-window-open-tip", ""), ("no_need_privacy_mode_no_physical_displays_tip", ""), - ("Follow remote cursor", ""), - ("Follow remote window focus", ""), + ("Follow remote cursor", "Segueix el cursor remot"), + ("Follow remote window focus", "Segueix el focus de la finestra remota"), ("default_proxy_tip", ""), ("no_audio_input_device_tip", ""), - ("Incoming", ""), - ("Outgoing", ""), - ("Clear Wayland screen selection", ""), + ("Incoming", "Entrant"), + ("Outgoing", "Sortint"), + ("Clear Wayland screen selection", "Netejar la selecció de pantalla Wayland"), ("clear_Wayland_screen_selection_tip", ""), ("confirm_clear_Wayland_screen_selection_tip", ""), ("android_new_voice_call_tip", ""), ("texture_render_tip", ""), - ("Use texture rendering", ""), - ("Floating window", ""), + ("Use texture rendering", "Fer servir renderització de textures"), + ("Floating window", "Finestra flotant"), ("floating_window_tip", ""), - ("Keep screen on", ""), - ("Never", ""), - ("During controlled", ""), - ("During service is on", ""), - ("Capture screen using DirectX", ""), - ("Back", ""), - ("Apps", ""), - ("Volume up", ""), - ("Volume down", ""), - ("Power", ""), - ("Telegram bot", ""), + ("Keep screen on", "Mantenir la pantalla encesa"), + ("Never", "Mai"), + ("During controlled", "Mentre estigui controlat"), + ("During service is on", "Mentres el servei estigui encés"), + ("Capture screen using DirectX", "Captura de pantalla utilitzant DirectX"), + ("Back", "Enrere"), + ("Apps", "Aplicacions"), + ("Volume up", "Pujar el volum"), + ("Volume down", "Baixa el volum"), + ("Power", "Engegar"), + ("Telegram bot", "Bot de Telegram"), ("enable-bot-tip", ""), ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), From 4723d6a8302abff9e38797025ea0a1482750eca4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 22 Jul 2024 10:29:00 +0800 Subject: [PATCH 306/335] mouse wrong commit ref --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 3df52e1cc95..7096c8b4eed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3850,7 +3850,7 @@ dependencies = [ [[package]] name = "mouce" version = "0.2.1" -source = "git+https://github.com/rustdesk-org/mouce.git#ed83800d532b95d70e39915314f6052aa433e9b9" +source = "git+https://github.com/rustdesk-org/mouce.git#177625a395cd8fa73964714d0039535cb9b47893" dependencies = [ "glob", ] From 22f3425ace67cdda02b01951d5b0387ef3438505 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 22 Jul 2024 17:00:29 +0800 Subject: [PATCH 307/335] fix custom client show ip whiltelist warning (#8778) Signed-off-by: 21pages --- flutter/lib/common.dart | 6 ++++++ flutter/lib/desktop/pages/desktop_setting_page.dart | 7 ++----- flutter/lib/mobile/pages/settings_page.dart | 7 ++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index d0d753fbbca..2a5ed86fc12 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3425,6 +3425,12 @@ get defaultOptionWhitelist => isCustomClient ? ',' : ''; get defaultOptionAccessMode => isCustomClient ? 'custom' : ''; get defaultOptionApproveMode => isCustomClient ? 'password-click' : ''; +bool whitelistNotEmpty() { + // https://rustdesk.com/docs/en/self-host/client-configuration/advanced-settings/#whitelist + final v = bind.mainGetOptionSync(key: kOptionWhitelist); + return v != '' && v != ','; +} + // `setMovable()` is only supported on macOS. // // On macOS, the window can be dragged by the tab bar by default. diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 70aa6c54e06..76c6ab27016 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1119,12 +1119,9 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { bool enabled = !locked; // Simple temp wrapper for PR check tmpWrapper() { - RxBool hasWhitelist = (bind.mainGetOptionSync(key: kOptionWhitelist) != - defaultOptionWhitelist) - .obs; + RxBool hasWhitelist = whitelistNotEmpty().obs; update() async { - hasWhitelist.value = bind.mainGetOptionSync(key: kOptionWhitelist) != - defaultOptionWhitelist; + hasWhitelist.value = whitelistNotEmpty(); } onChanged(bool? checked) async { diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 63972d0ea62..cd14ca14fbc 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -97,8 +97,7 @@ class _SettingsState extends State with WidgetsBindingObserver { kOptionEnableAbr, bind.mainGetOptionSync(key: kOptionEnableAbr)); _denyLANDiscovery = !option2bool(kOptionEnableLanDiscovery, bind.mainGetOptionSync(key: kOptionEnableLanDiscovery)); - _onlyWhiteList = (bind.mainGetOptionSync(key: kOptionWhitelist)) != - defaultOptionWhitelist; + _onlyWhiteList = whitelistNotEmpty(); _enableDirectIPAccess = option2bool( kOptionDirectServer, bind.mainGetOptionSync(key: kOptionDirectServer)); _enableRecordSession = option2bool(kOptionEnableRecordSession, @@ -282,9 +281,7 @@ class _SettingsState extends State with WidgetsBindingObserver { initialValue: _onlyWhiteList, onToggle: (_) async { update() async { - final onlyWhiteList = - (await bind.mainGetOption(key: kOptionWhitelist)) != - defaultOptionWhitelist; + final onlyWhiteList = whitelistNotEmpty(); if (onlyWhiteList != _onlyWhiteList) { setState(() { _onlyWhiteList = onlyWhiteList; From 14b505130b36f97e7c422408acf6e52ccc94a306 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 22 Jul 2024 17:11:32 +0800 Subject: [PATCH 308/335] remove end slash when setting server config (#8779) Signed-off-by: 21pages --- flutter/lib/common.dart | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 2a5ed86fc12..e58c361f8b9 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3098,9 +3098,16 @@ Future setServerConfig( List? errMsgs, ServerConfig config, ) async { - config.idServer = config.idServer.trim(); - config.relayServer = config.relayServer.trim(); - config.apiServer = config.apiServer.trim(); + String removeEndSlash(String input) { + if (input.endsWith('/')) { + return input.substring(0, input.length - 1); + } + return input; + } + + config.idServer = removeEndSlash(config.idServer.trim()); + config.relayServer = removeEndSlash(config.relayServer.trim()); + config.apiServer = removeEndSlash(config.apiServer.trim()); config.key = config.key.trim(); if (controllers != null) { controllers[0].text = config.idServer; From 31e7b6acf1021a7b36eda3c69a4cf9a3457e757e Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Mon, 22 Jul 2024 23:37:25 +0800 Subject: [PATCH 309/335] refact: msi, revision version (#8782) * refact: msi, revision version Signed-off-by: dignow * check revision version Signed-off-by: dignow * refact: msi, default revision number Signed-off-by: dignow * Simple refact Signed-off-by: dignow --------- Signed-off-by: dignow --- res/msi/preprocess.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/res/msi/preprocess.py b/res/msi/preprocess.py index e9ac0a887bd..02db5bde43a 100644 --- a/res/msi/preprocess.py +++ b/res/msi/preprocess.py @@ -37,6 +37,8 @@ }, } +def default_revision_version(): + return int(datetime.datetime.now().timestamp() / 60) def make_parser(): parser = argparse.ArgumentParser(description="Msi preprocess script.") @@ -68,6 +70,9 @@ def make_parser(): parser.add_argument( "-v", "--version", type=str, default="", help="The app version." ) + parser.add_argument( + "--revision-version", type=int, default=default_revision_version(), help="The revision version." + ) parser.add_argument( "-m", "--manufacturer", @@ -430,6 +435,11 @@ def read_process_output(args): if not version_pattern.match(g_version): print(f"Error: version {g_version} not found in {dist_app}") return False + if g_version.count(".") == 2: + # https://github.com/dotnet/runtime/blob/5535e31a712343a63f5d7d796cd874e563e5ac14/src/libraries/System.Private.CoreLib/src/System/Version.cs + if args.revision_version < 0 or args.revision_version > 2147483647: + raise ValueError(f"Invalid revision version: {args.revision_version}") + g_version = f"{g_version}.{args.revision_version}" g_build_date = read_process_output("--build-date") build_date_pattern = re.compile(r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}") From b828768fa970f773b2e86256029313c428f9d111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8A=E9=A4=85=E3=81=AECreeeper?= <56744841+creeper-0910@users.noreply.github.com> Date: Tue, 23 Jul 2024 09:58:48 +0900 Subject: [PATCH 310/335] Update ja.rs and Add "About Rustdesk" (#8784) * Update ja.rs Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> * Update ja.rs Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> * Add 'About RustDesk' Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> --------- Signed-off-by: creeper-0910 <56744841+creeper-0910@users.noreply.github.com> --- .../desktop/pages/desktop_setting_page.dart | 2 +- flutter/lib/mobile/pages/connection_page.dart | 2 +- flutter/lib/mobile/pages/settings_page.dart | 2 +- src/lang/ar.rs | 1 + src/lang/be.rs | 1 + src/lang/bg.rs | 1 + src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/en.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/et.rs | 1 + src/lang/eu.rs | 3 +- src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/he.rs | 1 + src/lang/hr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1265 +++++++++-------- src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/lv.rs | 1 + src/lang/nb.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + 47 files changed, 680 insertions(+), 636 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 76c6ab27016..16b97848ce4 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1806,7 +1806,7 @@ class _AboutState extends State<_About> { child: SingleChildScrollView( controller: scrollController, physics: DraggableNeverScrollableScrollPhysics(), - child: _Card(title: '${translate('About')} RustDesk', children: [ + child: _Card(title: translate('About RustDesk'), children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index d8a95a601ad..4a2bb8b558a 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -395,7 +395,7 @@ class _WebMenuState extends State { [ PopupMenuItem( value: "about", - child: Text('${translate('About')} RustDesk'), + child: Text(translate('About RustDesk')), ) ]; }, diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index cd14ca14fbc..dd95c42759b 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -807,7 +807,7 @@ void showThemeSettings(OverlayDialogManager dialogManager) async { void showAbout(OverlayDialogManager dialogManager) { dialogManager.show((setState, close, context) { return CustomAlertDialog( - title: Text('${translate('About')} RustDesk'), + title: Text(translate('About RustDesk')), content: Wrap(direction: Axis.vertical, spacing: 12, children: [ Text('Version: $version'), InkWell( diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 808535f3edb..ff6b9d453ba 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index c76c2033245..d2a2589e099 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 7a40985b4f5..78eec7bc12f 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 25b983d6e81..cca04e65dfd 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 05ccf7fd1b6..6409e8ff9ff 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", "确定要取消双重认证吗?"), ("cancel-bot-confirm-tip", "确定要取消 Telegram 机器人吗?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 3b68bbcb7ed..3113db4471d 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1, Otevřete chat s @BotFather.\n2, Pošlete příkaz \"/newbot\". Po dokončení tohoto kroku obdržíte token.\n3, Spusťte chat s nově vytvořeným botem. Pro jeho aktivaci odešlete zprávu začínající lomítkem vpřed (\"/\"), například \"/hello\".\n"), ("cancel-2fa-confirm-tip", "Jste si jisti, že chcete zrušit 2FA?"), ("cancel-bot-confirm-tip", "Jste si jisti, že chcete zrušit bota Telegramu?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index a68258b2f5b..af57a0cc963 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index ff412e4c208..546eb704765 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1. Öffnen Sie einen Chat mit @BotFather.\n2. Senden Sie den Befehl \"/newbot\". Sie erhalten ein Token, nachdem Sie diesen Schritt abgeschlossen haben.\n3. Starten Sie einen Chat mit Ihrem neu erstellten Bot. Senden Sie eine Nachricht, die mit einem Schrägstrich (\"/\") beginnt, z. B. \"/hello\", um ihn zu aktivieren.\n"), ("cancel-2fa-confirm-tip", "Sind Sie sicher, dass Sie 2FA abbrechen möchten?"), ("cancel-bot-confirm-tip", "Sind Sie sicher, dass Sie Telegram-Bot abbrechen möchten?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 0618d031a82..7b6c6a713ed 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1, Ανοίξτε μια συνομιλία με τον @BotFather., Στείλτε την εντολή \"/newbot\". Θα λάβετε ένα διακριτικό αφού ολοκληρώσετε αυτό το βήμα.3, Ξεκινήστε μια συνομιλία με το bot που μόλις δημιουργήσατε. Στείλτε ένα μήνυμα που αρχίζει με κάθετο (\"/\") όπως \"/hello\" για να το ενεργοποιήσετε."), ("cancel-2fa-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το 2FA;"), ("cancel-bot-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το Telegram bot;"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 1e1d5708c35..c4045fd8a58 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -231,5 +231,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1. Open a chat with @BotFather.\n2. Send the command \"/newbot\". You will receive a token after completing this step.\n3. Start a chat with your newly created bot. Send a message beginning with a forward slash (\"/\") like \"/hello\" to activate it.\n"), ("cancel-2fa-confirm-tip", "Are you sure you want to cancel 2FA?"), ("cancel-bot-confirm-tip", "Are you sure you want to cancel Telegram bot?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 733bc80ba75..b9a764046a7 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index b938840f99d..4bf72e47556 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1, Abre un chat con @BotFather.\n2, Envía el comando \"/newbot\". Recibirás un token tras completar esta paso.\n3, Inicia un chat con tu bot recién creado. Envía un mensaje que comience con una barra (\"/\") como \"/hola\" para activarlo.\n"), ("cancel-2fa-confirm-tip", "¿Seguro que quieres cancelar 2FA?"), ("cancel-bot-confirm-tip", "¿Seguro que quieres cancelar el bot de Telegram?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 43afea356e5..ba65b4951a2 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index 47a209239a6..00be90b961f 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -630,6 +630,7 @@ lazy_static::lazy_static! { ("Telegram bot", "Telegrameko bot-a"), ("enable-bot-tip", "Ezaugarri hau gaitzen baduzu, zure bot-etik 2FA kodea jaso dezakezu. Konexio jakinarazpenetarako ere balio dezake."), ("enable-bot-desc", "1, Ireki txat bat @BotFather bot-arekin.\n2, Bidali \"/newbot\" agindua. Token bat jasoko duzu urrats honen ostean.\n3, Hasi txat bat zure bot berriarekin. Bidali mezu bat aurreko barra batekin (\"/\") \"/kaixo\" bezala gaitzeko.\n"), - ].iter().cloned().collect(); + ("About RustDesk", ""), + ].iter().cloned().collect(); } \ No newline at end of file diff --git a/src/lang/fa.rs b/src/lang/fa.rs index df6fb70668e..2436ba3f5a0 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 8ad0ca28ac0..feb75402744 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 91a1753bd64..7d41acc6e4b 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 2cf6edb94ea..8c73ff2e657 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 1009f24904c..db9a259e9ad 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index f051231b921..81b6e09c7ad 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 5c49e8840cc..b4510222215 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1. apri una chat con @BotFather.\n2. Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3. Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), ("cancel-2fa-confirm-tip", "Sei sicuro di voler annullare 2FA?"), ("cancel-bot-confirm-tip", "Sei sicuro di voler annulare Telegram?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 059f048c5f7..8fddd9eb671 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -1,634 +1,635 @@ 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", "RuskDeskネットワークに接続中..."), - ("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", "TXPトンネリング"), - ("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", "認証/中継サーバー"), - ("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", "使用できるのは大文字・小文字のアルファベット、数字、アンダースコア(_)のみです。先頭の文字はアルファベット、長さは6文字から16文字である必要があります。"), - ("Website", "公式サイト"), - ("About", "RustDeskについて"), - ("Slogan_tip", "この混沌とした世界から、愛をこめて!"), - ("Privacy Statement", "プライバシーポリシー"), - ("Mute", "ミュート"), - ("Build Date", "ビルド日時"), - ("Version", "バージョン"), - ("Home", "ホーム"), - ("Audio Input", "音声入力"), - ("Enhancements", "拡張機能"), - ("Hardware Codec", "ハードウェアコーデック"), - ("Adaptive bitrate", "可変ビットレート"), - ("ID Server", "認証サーバー"), - ("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", "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", "ランデブーサーバーに接続できませんでした"), - ("Please try later", "後でもう一度お試しください"), - ("Remote desktop is offline", "リモートコンピューターがオフラインです"), - ("Key mismatch", "キーが一致しません"), - ("Timeout", "タイムアウト"), - ("Failed to connect to relay server", "中継サーバーに接続できませんでした"), - ("Failed to connect via rendezvous server", "ランデブーサーバー経由で接続できませんでした"), - ("Failed to connect via relay server", "中継サーバー経由で接続できませんでした"), - ("Failed to make direct connection to remote desktop", "リモートコンピューターと直接接続できませんでした"), - ("Set Password", "パスワードを設定"), - ("OS Password", "OSのパスワード"), - ("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", "切断"), - ("Enable 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", "資格情報が間違っています"), - ("The verification code is incorrect or has expired", "認証コードが間違っているか、有効期限が切れています"), - ("Edit Tag", "タグを編集"), - ("Forget Password", "パスワードを忘れる"), - ("Favorites", "お気に入り"), - ("Add to Favorites", "お気に入りに追加"), - ("Remove from Favorites", "お気に入りから削除"), - ("Empty", "空"), - ("Invalid folder name", "無効なフォルダ名"), - ("Socks5 Proxy", "SOCKS5プロキシ"), - ("Socks5/Http(s) Proxy", "Socks5/Http(s)プロキシ"), - ("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", "1本指でタップ"), - ("Left Mouse", "マウス左クリック"), - ("One-Long Tap", "1本指でロングタップ"), - ("Two-Finger Tap", "2本指でタップ"), - ("Right Mouse", "マウス右クリック"), - ("One-Finger Move", "1本指でドラッグ"), - ("Double Tap & Move", "2本指でタップ&ドラッグ"), - ("Mouse Drag", "マウスドラッグ"), - ("Three-Finger vertically", "3本指で縦方向"), - ("Mouse Wheel", "マウスホイール"), - ("Two-Finger Move", "2本指でドラッグ"), - ("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端末をリモートコンピューターからマウスやタッチで操作するには、RustDeskに「アクセシビリティ」サービスの使用を許可する必要があります。"), - ("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", "終了"), - ("Help", "ヘルプ"), - ("Failed", "失敗"), - ("Succeeded", "成功"), - ("Someone turns on privacy mode, exit", "プライバシーモードがオンになりました。終了します。"), - ("Unsupported", "サポートされていません"), - ("Peer denied", "リモートホストに拒否されました"), - ("Please install plugins", "プラグインをインストールしてください"), - ("Peer exit", "リモートホストが退出しました"), - ("Failed to turn off", "オフにできませんでした"), - ("Turned off", "オフになりました"), - ("Language", "言語"), - ("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"), - ("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"), - ("android_open_battery_optimizations_tip", "この機能を使わない場合は、RestDeskアプリの設定ページから「バッテリー」に進み、「制限なし」のチェックを外してください"), - ("Start on boot", "起動時に自動実行する"), - ("Start the screen sharing service on boot, requires special permissions", "起動時に画面共有サービスを開始します。これには特別な権限が必要です。"), - ("Connection not allowed", "接続が許可されていません"), - ("Legacy mode", "レガシーモード"), - ("Map mode", "マップモード"), - ("Translate mode", "変換モード"), - ("Use permanent password", "固定パスワードを使用"), - ("Use both passwords", "どちらのパスワードも使用"), - ("Set permanent password", "固定パスワードを設定"), - ("Enable 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 Toolbar", "ツールバーを表示"), - ("Hide Toolbar", "ツールバーを隠す"), - ("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", "音声入力デバイス"), - ("Use IP Whitelisting", "IPホワイトリストを使用する"), - ("Network", "ネットワーク"), - ("Pin Toolbar", "ツールバーをピン止め"), - ("Unpin Toolbar", "ツールバーのピン止めを解除"), - ("Recording", "録画"), - ("Directory", "ディレクトリ"), - ("Automatically record incoming sessions", "受信したセッションを自動で記録する"), - ("Change", "変更"), - ("Start session recording", "セッションの録画を開始"), - ("Stop session recording", "セッションの録画を停止"), - ("Enable recording session", "セッションの録画を有効化"), - ("Enable LAN discovery", "LAN探索を有効化"), - ("Deny LAN discovery", "LAN探索を拒否"), - ("Write a message", "メッセージを書き込む"), - ("Prompt", "必須"), - ("Please wait for confirmation of UAC...", "UACの承認を待機しています..."), - ("elevated_foreground_window_tip", "リモートデスクトップでフォーカスされているウィンドウの操作にはより高い権限が必要なため、マウスとキーボードが一時的に使用できなくなっています。リモートユーザーにウィンドウを最小化、または接続管理画面から権限を昇格するよう要求してください。この問題を回避するには、リモートコンピューターにRustDeskをインストールしてください。"), - ("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デスクトップを試すか、OSを変更してください。"), - ("JumpLink", "View"), - ("Please Select the screen to be shared(Operate on the peer side).", "共有する画面を選択してください(リモートコンピューターが操作します)"), - ("Show RustDesk", "RustDeskを表示"), - ("This PC", "この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", "LinuxでNvidia製のグラフィックカードを使用していると、接続後すぐにリモートウィンドウが閉じてしまう場合があります。オープンソースのNouveauドライバに切り替え、ソフトウェアレンダリングを使用するよう設定すると解決するかもしれません。(RustDeskの再起動が必要です)"), - ("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", "リモートデスクトップ ユーザーがRustDeskを実行する際に、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", "FPS"), - ("Auto", "自動"), - ("Other Default Options", "その他のデフォルト設定"), - ("Voice call", "音声通話"), - ("Text chat", "テキストチャット"), - ("Stop voice call", "音声通話を終了"), - ("relay_hint_tip", "直接接続が行えない場合は、リレー経由での接続をお試しください。初回接続で中継接続を行いたい場合は末尾に「/r」を付けるか、最近のセッション履歴に「常に中継サーバー経由で接続」という設定がある場合はそちらを選択してください。"), - ("Reconnect", "再接続"), - ("Codec", "コーデック"), - ("Resolution", "解像度"), - ("No transfers in progress", "進行中の転送はありません"), - ("Set one-time password length", "ワンタイムパスワードの長さを設定する"), - ("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", "空のパスワード"), - ("Me", "あなた"), - ("identical_file_tip", "このファイルはリモートコンピューターと同一です。"), - ("show_monitors_tip", "ツールバーにモニターを表示します"), - ("View Mode", "表示モード"), - ("login_linux_tip", "Xデスクトップのセッションにログインするには、リモートコンピューターのLinuxアカウントにログインする必要があります。"), - ("verify_rustdesk_password_tip", "RustDeskのパスワードを確認する"), - ("remember_account_tip", "このアカウントを記憶する"), - ("os_account_desk_tip", "このアカウントは、リモートコンピューターのOSにログインし、ヘッドレスでセッションを有効化するために使用されます。"), - ("OS Account", "OSのアカウント"), - ("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デスクトップ環境をインストールしてください"), - ("No need to elevate", "権限昇格の必要はありません"), - ("System Sound", "システム音声"), - ("Default", "デフォルト"), - ("New RDP", "新しいRDP"), - ("Fingerprint", "フィンガープリント"), - ("Copy Fingerprint", "フィンガープリントをコピー"), - ("no fingerprints", "フィンガープリントがありません"), - ("Select a peer", "リモートコンピューターを選択"), - ("Select peers", "複数のリモートコンピューターを選択"), - ("Plugins", "プラグイン"), - ("Uninstall", "アンインストール"), - ("Update", "更新"), - ("Enable", "有効"), - ("Disable", "無効"), - ("Options", "設定"), - ("resolution_original_tip", "オリジナルの解像度"), - ("resolution_fit_local_tip", "ローカル解像度に合わせる"), - ("resolution_custom_tip", "カスタム解像度"), - ("Collapse toolbar", "ツールバーを折りたたむ"), - ("Accept and Elevate", "承認して権限を昇格する"), - ("accept_and_elevate_btn_tooltip", "接続を受け入れた上で、UAC権限を昇格します。"), - ("clipboard_wait_response_timeout_tip", "クリップボードのコピーがタイムアウトしました。"), - ("Incoming connection", "接続の受信"), - ("Outgoing connection", "接続の送信"), - ("Exit", "終了"), - ("Open", "開く"), - ("logout_tip", "本当にログアウトしますか?"), - ("Service", "サービス"), - ("Start", "開始"), - ("Stop", "停止"), - ("exceed_max_devices", "管理対象のデバイスが最大数に達しました。"), - ("Sync with recent sessions", "最近のセッションと同期"), - ("Sort tags", "タグで並べ替え"), - ("Open connection in new tab", "新しいタブで接続を開く"), - ("Move tab to new window", "新しいウィンドウにタブを移動する"), - ("Can not be empty", "空にすることはできません"), - ("Already exists", "すでに存在します"), - ("Change Password", "パスワードを変更"), - ("Refresh Password", "パスワードをリフレッシュ"), - ("ID", "ID"), - ("Grid View", "グリッドビュー"), - ("List View", "リストビュー"), - ("Select", "選択"), - ("Toggle Tags", "タグの切り替え"), - ("pull_ab_failed_tip", "アドレス帳の更新に失敗しました"), - ("push_ab_failed_tip", "サーバーへのアドレス帳の同期に失敗しました"), - ("synced_peer_readded_tip", "最近セッションを行ったデバイスはアドレス帳に同期されます。"), - ("Change Color", "色の変更"), - ("Primary Color", "プライマリ カラー"), - ("HSV Color", "HSVカラー"), - ("Installation Successful!", "インストールに成功しました!"), - ("Installation failed!", "インストールに失敗しました。"), - ("Reverse mouse wheel", "マウスホイールを反転する"), - ("{} sessions", "{}件のセッション"), - ("scam_title", "あなたは詐欺にあっているかもしれません!"), - ("scam_text1", "もし、知らない相手から電話でRustDeskのインストールやサービスの開始を依頼された場合、作業を進めずに、すぐに電話を切ってください。"), - ("scam_text2", "相手はあなたからお金や個人情報を盗もうとする詐欺師である可能性があります。"), - ("Don't show again", "今後表示しない"), - ("I Agree", "同意する"), - ("Decline", "同意しない"), - ("Timeout in minutes", "タイムアウトまでの時間(分)"), - ("auto_disconnect_option_tip", "ユーザーが非アクティブの場合、自動的に受信したセッションを閉じる"), - ("Connection failed due to inactivity", "リモートデスクトップ ユーザーが非アクティブなため、接続に失敗しました"), - ("Check for software update on startup", "起動時にソフトウェアの更新をチェック"), - ("upgrade_rustdesk_server_pro_to_{}_tip", "RustDesk Server Proをバージョン{}以上にアップグレードしてください!"), - ("pull_group_failed_tip", "グループの更新に失敗しました"), - ("Filter by intersection", "交差位置でフィルター"), - ("Remove wallpaper during incoming sessions", "セッションの受信中、デスクトップ背景を削除する"), - ("Test", "テスト"), - ("display_is_plugged_out_msg", "モニターが接続されていません。最初のモニターを選択してください。"), - ("No displays", "モニターがありません"), - ("Open in new window", "新しいウィンドウで開く"), - ("Show displays as individual windows", "モニターを別々のウィンドウとして表示"), - ("Use all my displays for the remote session", "すべてのディスプレイをセッションで使用する"), - ("selinux_tip", "SELinuxが有効になっているため、RustDeskが正常に動作しない可能性があります。"), - ("Change view", "表示変更"), - ("Big tiles", "大きなタイル"), - ("Small tiles", "小さなタイル"), - ("List", "リスト"), - ("Virtual display", "仮想モニター"), - ("Plug out all", "すべて切断する"), - ("True color (4:4:4)", "True color (4:4:4)"), - ("Enable blocking user input", "ユーザー入力のブロックを有効化"), - ("id_input_tip", "ID、IPアドレス、またはドメインとポート番号(<ドメイン>:<ポート>)を使用できます。\n他のサーバーのデバイスにアクセスしたい場合は、サーバーアドレス(@<サーバーアドレス>?key=<キーの値>)を追加してください。 \n(例:9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=)\nパブリックサーバーのデバイスに接続したい場合は、「@public」のように入力してください。パブリックサーバーの場合、キーは不要です。\n\n初回接続で中継接続を行いたい場合は、「9123456234/r」のように末尾に「/r」を付けてください。"), - ("privacy_mode_impl_mag_tip", "モード 1"), - ("privacy_mode_impl_virtual_display_tip", "モード 2"), - ("Enter privacy mode", "プライバシーモードを起動"), - ("Exit privacy mode", "プライバシーモードを終了"), - ("idd_not_support_under_win10_2004_tip", "Indirect display driverには対応していません。Windows 10 バージョン2004以降が必要です。"), - ("input_source_1_tip", "入力ソース 1"), - ("input_source_2_tip", "入力ソース 2"), - ("Swap control-command key", "ctrlとcommandキーを入れ替える"), - ("swap-left-right-mouse", "マウスのクリックを入れ替える"), - ("2FA code", "二要素認証コード"), - ("More", "詳細"), - ("enable-2fa-title", "二要素認証を有効化"), - ("enable-2fa-desc", "認証アプリをセットアップします。Authy、MicrosoftまたはGoogle AuthenticatorなどがPCまたはスマートフォンで利用できます。\n\nQRコードをスキャンし、アプリが表示するコードを入力することで二要素認証が有効になります。"), - ("wrong-2fa-code", "コードが違います。コードと端末の時刻設定が正しいかをご確認ください。"), - ("enter-2fa-title", "二要素認証"), - ("Email verification code must be 6 characters.", "電子メール認証コードは6文字である必要があります。"), - ("2FA code must be 6 digits.", "二要素認証コードは6文字である必要があります。"), - ("Multiple Windows sessions found", "複数のWindowsセッションが見つかりました"), - ("Please select the session you want to connect to", "接続したいセッションを選択してください"), - ("powered_by_me", "Powered by RustDesk"), - ("outgoing_only_desk_tip", "カスタマイズされたエディションを使用しています。\n他のコンピューターに接続できますが、他のコンピューターからのリクエストは受信できません。"), - ("preset_password_warning", "このエディションには、デフォルトで固定パスワードが設定されています。このパスワードを知っているユーザーはあなたのデバイスを完全にコントロールできるため、そのような危険がある場合は直ちにRustDeskをアンインストールして下さい!"), - ("Security Alert", "セキュリティ警告"), - ("My address book", "あなたのアドレス帳"), - ("Personal", "個人"), - ("Owner", "所有者"), - ("Set shared password", "共有パスワードの設定"), - ("Exist in", "既に存在します"), - ("Read-only", "読み取り専用"), - ("Read/Write", "読み取り/書き込み"), - ("Full Control", "フルアクセス"), - ("share_warning_tip", "フィールドは共有され、他の人からも閲覧できます。"), - ("Everyone", "全員"), - ("ab_web_console_tip", "webコンソールの詳細"), - ("allow-only-conn-window-open-tip", "RustDeskのウィンドウが開いている場合のみ接続を許可する"), - ("no_need_privacy_mode_no_physical_displays_tip", "物理モニターが存在しないため、プライバシーモードは不要です。"), - ("Follow remote cursor", "リモートカーソルに追従"), - ("Follow remote window focus", "リモートウィンドウのフォーカスに追従"), - ("default_proxy_tip", "デフォルトのプロトコルとポートはSocks5と1080です。"), - ("no_audio_input_device_tip", "オーディオ入力デバイスが見つかりません。"), - ("Incoming", "受信"), - ("Outgoing", "発信"), - ("Clear Wayland screen selection", "Waylandの画面選択をクリア"), - ("clear_Wayland_screen_selection_tip", "画面選択をクリア後、共有画面を再び選択できます。"), - ("confirm_clear_Wayland_screen_selection_tip", "本当にWaylandの画面選択をクリアしますか?"), - ("android_new_voice_call_tip", "新しい音声通話リクエストを受信しました。承認すると音声通話に切り替わります。"), - ("texture_render_tip", "テクスチャレンダリングを使用し、画像をより滑らかに描画します。レンダリングの問題が発生した場合は無効にしてみてください。"), - ("Use texture rendering", "テクスチャレンダリングを使用"), - ("Floating window", "フローティングウィンドウ"), - ("floating_window_tip", "RustDeskのバックグラウンドサービスを維持するために使用されます。"), - ("Keep screen on", "常に画面をオン"), - ("Never", "画面をオンにしない"), - ("During controlled", "操作中"), - ("During service is on", "サービスの動作中"), - ("Capture screen using DirectX", "DirectXを使用した画面キャプチャ"), - ("Back", "戻る"), - ("Apps", "アプリ"), - ("Volume up", "音量アップ"), - ("Volume down", "音量ダウン"), - ("Power", "電源"), - ("Telegram bot", "Telegram Bot"), - ("enable-bot-tip", "この機能を有効にすると、ボットから二要素認証コードを受け取ることができます。また、接続時の通知としても機能します。"), - ("enable-bot-desc", "1. @BotFatherのチャットを開きます。\n2. 「/newbot」コマンドを送信します。送信後、トークンを取得できます。\n3. 新しく作成したbotとチャットを開始します。「/hello」のようにスラッシュで始まるメッセージを送信して起動します。\n"), - ("cancel-2fa-confirm-tip", "本当に二要素認証をキャンセルしますか?"), - ("cancel-bot-confirm-tip", "本当にTelegram Botをキャンセルしますか?"), - ].iter().cloned().collect(); - } +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "状態"), + ("Your Desktop", "あなたのコンピューター"), + ("desk_tip", "下記のIDとパスワードであなたのコンピューターにアクセスできます。"), + ("Password", "パスワード"), + ("Ready", "準備完了"), + ("Established", "接続完了"), + ("connecting_status", "RuskDeskネットワークに接続中..."), + ("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", "TXPトンネリング"), + ("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", "認証/中継サーバー"), + ("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", "使用できるのは大文字・小文字のアルファベット、数字、アンダースコア(_)のみです。先頭の文字はアルファベット、長さは6文字から16文字である必要があります。"), + ("Website", "公式サイト"), + ("About", "RustDeskについて"), + ("Slogan_tip", "この混沌とした世界から、愛をこめて!"), + ("Privacy Statement", "プライバシーポリシー"), + ("Mute", "ミュート"), + ("Build Date", "ビルド日時"), + ("Version", "バージョン"), + ("Home", "ホーム"), + ("Audio Input", "音声入力"), + ("Enhancements", "拡張機能"), + ("Hardware Codec", "ハードウェアコーデック"), + ("Adaptive bitrate", "可変ビットレート"), + ("ID Server", "認証サーバー"), + ("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", "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", "ランデブーサーバーに接続できませんでした"), + ("Please try later", "後でもう一度お試しください"), + ("Remote desktop is offline", "リモートコンピューターがオフラインです"), + ("Key mismatch", "キーが一致しません"), + ("Timeout", "タイムアウト"), + ("Failed to connect to relay server", "中継サーバーに接続できませんでした"), + ("Failed to connect via rendezvous server", "ランデブーサーバー経由で接続できませんでした"), + ("Failed to connect via relay server", "中継サーバー経由で接続できませんでした"), + ("Failed to make direct connection to remote desktop", "リモートコンピューターと直接接続できませんでした"), + ("Set Password", "パスワードを設定"), + ("OS Password", "OSのパスワード"), + ("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", "切断"), + ("Enable 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", "資格情報が間違っています"), + ("The verification code is incorrect or has expired", "認証コードが間違っているか、有効期限が切れています"), + ("Edit Tag", "タグを編集"), + ("Forget Password", "パスワードを忘れる"), + ("Favorites", "お気に入り"), + ("Add to Favorites", "お気に入りに追加"), + ("Remove from Favorites", "お気に入りから削除"), + ("Empty", "空"), + ("Invalid folder name", "無効なフォルダ名"), + ("Socks5 Proxy", "SOCKS5プロキシ"), + ("Socks5/Http(s) Proxy", "Socks5/Http(s)プロキシ"), + ("Discovered", "発見済み"), + ("install_daemon_tip", "起動時にRustDeskを開始するには、システムサービスをインストールする必要があります。"), + ("Remote ID", "リモートID"), + ("Paste", "貼り付け"), + ("Paste here?", "ここに貼り付けますか?"), + ("Are you sure to close the connection?", "本当に切断しますか?"), + ("Download new version", "新しいバージョンをダウンロード"), + ("Touch mode", "タッチモード"), + ("Mouse mode", "マウスモード"), + ("One-Finger Tap", "1本指でタップ"), + ("Left Mouse", "マウス左クリック"), + ("One-Long Tap", "1本指でロングタップ"), + ("Two-Finger Tap", "2本指でタップ"), + ("Right Mouse", "マウス右クリック"), + ("One-Finger Move", "1本指でドラッグ"), + ("Double Tap & Move", "2本指でタップ&ドラッグ"), + ("Mouse Drag", "マウスドラッグ"), + ("Three-Finger vertically", "3本指で縦方向"), + ("Mouse Wheel", "マウスホイール"), + ("Two-Finger Move", "2本指でドラッグ"), + ("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端末をリモートコンピューターからマウスやタッチで操作するには、RustDeskに「アクセシビリティ」サービスの使用を許可する必要があります。"), + ("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", "終了"), + ("Help", "ヘルプ"), + ("Failed", "失敗"), + ("Succeeded", "成功"), + ("Someone turns on privacy mode, exit", "プライバシーモードがオンになりました。終了します。"), + ("Unsupported", "サポートされていません"), + ("Peer denied", "リモートホストに拒否されました"), + ("Please install plugins", "プラグインをインストールしてください"), + ("Peer exit", "リモートホストが退出しました"), + ("Failed to turn off", "オフにできませんでした"), + ("Turned off", "オフになりました"), + ("Language", "言語"), + ("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"), + ("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"), + ("android_open_battery_optimizations_tip", "この機能を使わない場合は、RestDeskアプリの設定ページから「バッテリー」に進み、「制限なし」のチェックを外してください"), + ("Start on boot", "起動時に自動実行する"), + ("Start the screen sharing service on boot, requires special permissions", "起動時に画面共有サービスを開始します。これには特別な権限が必要です。"), + ("Connection not allowed", "接続が許可されていません"), + ("Legacy mode", "レガシーモード"), + ("Map mode", "マップモード"), + ("Translate mode", "変換モード"), + ("Use permanent password", "固定パスワードを使用"), + ("Use both passwords", "どちらのパスワードも使用"), + ("Set permanent password", "固定パスワードを設定"), + ("Enable 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 Toolbar", "ツールバーを表示"), + ("Hide Toolbar", "ツールバーを隠す"), + ("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", "音声入力デバイス"), + ("Use IP Whitelisting", "IPホワイトリストを使用する"), + ("Network", "ネットワーク"), + ("Pin Toolbar", "ツールバーをピン止め"), + ("Unpin Toolbar", "ツールバーのピン止めを解除"), + ("Recording", "録画"), + ("Directory", "ディレクトリ"), + ("Automatically record incoming sessions", "受信したセッションを自動で記録する"), + ("Change", "変更"), + ("Start session recording", "セッションの録画を開始"), + ("Stop session recording", "セッションの録画を停止"), + ("Enable recording session", "セッションの録画を有効化"), + ("Enable LAN discovery", "LAN探索を有効化"), + ("Deny LAN discovery", "LAN探索を拒否"), + ("Write a message", "メッセージを書き込む"), + ("Prompt", "必須"), + ("Please wait for confirmation of UAC...", "UACの承認を待機しています..."), + ("elevated_foreground_window_tip", "リモートデスクトップでフォーカスされているウィンドウの操作にはより高い権限が必要なため、マウスとキーボードが一時的に使用できなくなっています。リモートユーザーにウィンドウを最小化、または接続管理画面から権限を昇格するよう要求してください。この問題を回避するには、リモートコンピューターにRustDeskをインストールしてください。"), + ("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デスクトップを試すか、OSを変更してください。"), + ("JumpLink", "View"), + ("Please Select the screen to be shared(Operate on the peer side).", "共有する画面を選択してください(リモートコンピューターが操作します)"), + ("Show RustDesk", "RustDeskを表示"), + ("This PC", "この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", "LinuxでNvidia製のグラフィックカードを使用していると、接続後すぐにリモートウィンドウが閉じてしまう場合があります。オープンソースのNouveauドライバに切り替え、ソフトウェアレンダリングを使用するよう設定すると解決するかもしれません。(RustDeskの再起動が必要です)"), + ("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", "リモートデスクトップ ユーザーがRustDeskを実行する際に、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", "FPS"), + ("Auto", "自動"), + ("Other Default Options", "その他のデフォルト設定"), + ("Voice call", "音声通話"), + ("Text chat", "テキストチャット"), + ("Stop voice call", "音声通話を終了"), + ("relay_hint_tip", "直接接続が行えない場合は、リレー経由での接続をお試しください。初回接続で中継接続を行いたい場合は末尾に「/r」を付けるか、最近のセッション履歴に「常に中継サーバー経由で接続」という設定がある場合はそちらを選択してください。"), + ("Reconnect", "再接続"), + ("Codec", "コーデック"), + ("Resolution", "解像度"), + ("No transfers in progress", "進行中の転送はありません"), + ("Set one-time password length", "ワンタイムパスワードの長さを設定する"), + ("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", "空のパスワード"), + ("Me", "あなた"), + ("identical_file_tip", "このファイルはリモートコンピューターと同一です。"), + ("show_monitors_tip", "ツールバーにモニターを表示します"), + ("View Mode", "表示モード"), + ("login_linux_tip", "Xデスクトップのセッションにログインするには、リモートコンピューターのLinuxアカウントにログインする必要があります。"), + ("verify_rustdesk_password_tip", "RustDeskのパスワードを確認する"), + ("remember_account_tip", "このアカウントを記憶する"), + ("os_account_desk_tip", "このアカウントは、リモートコンピューターのOSにログインし、ヘッドレスでセッションを有効化するために使用されます。"), + ("OS Account", "OSのアカウント"), + ("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デスクトップ環境をインストールしてください"), + ("No need to elevate", "権限昇格の必要はありません"), + ("System Sound", "システム音声"), + ("Default", "デフォルト"), + ("New RDP", "新しいRDP"), + ("Fingerprint", "フィンガープリント"), + ("Copy Fingerprint", "フィンガープリントをコピー"), + ("no fingerprints", "フィンガープリントがありません"), + ("Select a peer", "リモートコンピューターを選択"), + ("Select peers", "複数のリモートコンピューターを選択"), + ("Plugins", "プラグイン"), + ("Uninstall", "アンインストール"), + ("Update", "更新"), + ("Enable", "有効"), + ("Disable", "無効"), + ("Options", "設定"), + ("resolution_original_tip", "オリジナルの解像度"), + ("resolution_fit_local_tip", "ローカル解像度に合わせる"), + ("resolution_custom_tip", "カスタム解像度"), + ("Collapse toolbar", "ツールバーを折りたたむ"), + ("Accept and Elevate", "承認して権限を昇格する"), + ("accept_and_elevate_btn_tooltip", "接続を受け入れた上で、UAC権限を昇格します。"), + ("clipboard_wait_response_timeout_tip", "クリップボードのコピーがタイムアウトしました。"), + ("Incoming connection", "接続の受信"), + ("Outgoing connection", "接続の送信"), + ("Exit", "終了"), + ("Open", "開く"), + ("logout_tip", "本当にログアウトしますか?"), + ("Service", "サービス"), + ("Start", "開始"), + ("Stop", "停止"), + ("exceed_max_devices", "管理対象のデバイスが最大数に達しました。"), + ("Sync with recent sessions", "最近のセッションと同期"), + ("Sort tags", "タグで並べ替え"), + ("Open connection in new tab", "新しいタブで接続を開く"), + ("Move tab to new window", "新しいウィンドウにタブを移動する"), + ("Can not be empty", "空にすることはできません"), + ("Already exists", "すでに存在します"), + ("Change Password", "パスワードを変更"), + ("Refresh Password", "パスワードをリフレッシュ"), + ("ID", "ID"), + ("Grid View", "グリッドビュー"), + ("List View", "リストビュー"), + ("Select", "選択"), + ("Toggle Tags", "タグの切り替え"), + ("pull_ab_failed_tip", "アドレス帳の更新に失敗しました"), + ("push_ab_failed_tip", "サーバーへのアドレス帳の同期に失敗しました"), + ("synced_peer_readded_tip", "最近セッションを行ったデバイスはアドレス帳に同期されます。"), + ("Change Color", "色の変更"), + ("Primary Color", "プライマリ カラー"), + ("HSV Color", "HSVカラー"), + ("Installation Successful!", "インストールに成功しました!"), + ("Installation failed!", "インストールに失敗しました。"), + ("Reverse mouse wheel", "マウスホイールを反転する"), + ("{} sessions", "{}件のセッション"), + ("scam_title", "あなたは詐欺にあっているかもしれません!"), + ("scam_text1", "もし、知らない相手から電話でRustDeskのインストールやサービスの開始を依頼された場合、作業を進めずに、すぐに電話を切ってください。"), + ("scam_text2", "相手はあなたからお金や個人情報を盗もうとする詐欺師である可能性があります。"), + ("Don't show again", "今後表示しない"), + ("I Agree", "同意する"), + ("Decline", "同意しない"), + ("Timeout in minutes", "タイムアウトまでの時間(分)"), + ("auto_disconnect_option_tip", "ユーザーが非アクティブの場合、自動的に受信したセッションを閉じる"), + ("Connection failed due to inactivity", "リモートデスクトップ ユーザーが非アクティブなため、接続に失敗しました"), + ("Check for software update on startup", "起動時にソフトウェアの更新をチェック"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "RustDesk Server Proをバージョン{}以上にアップグレードしてください!"), + ("pull_group_failed_tip", "グループの更新に失敗しました"), + ("Filter by intersection", "交差位置でフィルター"), + ("Remove wallpaper during incoming sessions", "セッションの受信中、デスクトップ背景を削除する"), + ("Test", "テスト"), + ("display_is_plugged_out_msg", "モニターが接続されていません。最初のモニターを選択してください。"), + ("No displays", "モニターがありません"), + ("Open in new window", "新しいウィンドウで開く"), + ("Show displays as individual windows", "モニターを別々のウィンドウとして表示"), + ("Use all my displays for the remote session", "すべてのディスプレイをセッションで使用する"), + ("selinux_tip", "SELinuxが有効になっているため、RustDeskが正常に動作しない可能性があります。"), + ("Change view", "表示変更"), + ("Big tiles", "大きなタイル"), + ("Small tiles", "小さなタイル"), + ("List", "リスト"), + ("Virtual display", "仮想モニター"), + ("Plug out all", "すべて切断する"), + ("True color (4:4:4)", "True color (4:4:4)"), + ("Enable blocking user input", "ユーザー入力のブロックを有効化"), + ("id_input_tip", "ID、IPアドレス、またはドメインとポート番号(<ドメイン>:<ポート>)を使用できます。\n他のサーバーのデバイスにアクセスしたい場合は、サーバーアドレス(@<サーバーアドレス>?key=<キーの値>)を追加してください。 \n(例:9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=)\nパブリックサーバーのデバイスに接続したい場合は、「@public」のように入力してください。パブリックサーバーの場合、キーは不要です。\n\n初回接続で中継接続を行いたい場合は、「9123456234/r」のように末尾に「/r」を付けてください。"), + ("privacy_mode_impl_mag_tip", "モード 1"), + ("privacy_mode_impl_virtual_display_tip", "モード 2"), + ("Enter privacy mode", "プライバシーモードを起動"), + ("Exit privacy mode", "プライバシーモードを終了"), + ("idd_not_support_under_win10_2004_tip", "Indirect display driverには対応していません。Windows 10 バージョン2004以降が必要です。"), + ("input_source_1_tip", "入力ソース 1"), + ("input_source_2_tip", "入力ソース 2"), + ("Swap control-command key", "ctrlとcommandキーを入れ替える"), + ("swap-left-right-mouse", "マウスのクリックを入れ替える"), + ("2FA code", "二要素認証コード"), + ("More", "詳細"), + ("enable-2fa-title", "二要素認証を有効化"), + ("enable-2fa-desc", "認証アプリをセットアップします。Authy、MicrosoftまたはGoogle AuthenticatorなどがPCまたはスマートフォンで利用できます。\n\nQRコードをスキャンし、アプリが表示するコードを入力することで二要素認証が有効になります。"), + ("wrong-2fa-code", "コードが違います。コードと端末の時刻設定が正しいかをご確認ください。"), + ("enter-2fa-title", "二要素認証"), + ("Email verification code must be 6 characters.", "電子メール認証コードは6文字である必要があります。"), + ("2FA code must be 6 digits.", "二要素認証コードは6文字である必要があります。"), + ("Multiple Windows sessions found", "複数のWindowsセッションが見つかりました"), + ("Please select the session you want to connect to", "接続したいセッションを選択してください"), + ("powered_by_me", "Powered by RustDesk"), + ("outgoing_only_desk_tip", "カスタマイズされたエディションを使用しています。\n他のコンピューターに接続できますが、他のコンピューターからのリクエストは受信できません。"), + ("preset_password_warning", "このエディションには、デフォルトで固定パスワードが設定されています。このパスワードを知っているユーザーはあなたのデバイスを完全にコントロールできるため、そのような危険がある場合は直ちにRustDeskをアンインストールして下さい!"), + ("Security Alert", "セキュリティ警告"), + ("My address book", "あなたのアドレス帳"), + ("Personal", "個人"), + ("Owner", "所有者"), + ("Set shared password", "共有パスワードの設定"), + ("Exist in", "既に存在します"), + ("Read-only", "読み取り専用"), + ("Read/Write", "読み取り/書き込み"), + ("Full Control", "フルアクセス"), + ("share_warning_tip", "フィールドは共有され、他の人からも閲覧できます。"), + ("Everyone", "全員"), + ("ab_web_console_tip", "webコンソールの詳細"), + ("allow-only-conn-window-open-tip", "RustDeskのウィンドウが開いている場合のみ接続を許可する"), + ("no_need_privacy_mode_no_physical_displays_tip", "物理モニターが存在しないため、プライバシーモードは不要です。"), + ("Follow remote cursor", "リモートカーソルに追従"), + ("Follow remote window focus", "リモートウィンドウのフォーカスに追従"), + ("default_proxy_tip", "デフォルトのプロトコルとポートはSocks5と1080です。"), + ("no_audio_input_device_tip", "オーディオ入力デバイスが見つかりません。"), + ("Incoming", "受信"), + ("Outgoing", "発信"), + ("Clear Wayland screen selection", "Waylandの画面選択をクリア"), + ("clear_Wayland_screen_selection_tip", "画面選択をクリア後、共有画面を再び選択できます。"), + ("confirm_clear_Wayland_screen_selection_tip", "本当にWaylandの画面選択をクリアしますか?"), + ("android_new_voice_call_tip", "新しい音声通話リクエストを受信しました。承認すると音声通話に切り替わります。"), + ("texture_render_tip", "テクスチャレンダリングを使用し、画像をより滑らかに描画します。レンダリングの問題が発生した場合は無効にしてみてください。"), + ("Use texture rendering", "テクスチャレンダリングを使用"), + ("Floating window", "フローティングウィンドウ"), + ("floating_window_tip", "RustDeskのバックグラウンドサービスを維持するために使用されます。"), + ("Keep screen on", "常に画面をオン"), + ("Never", "画面をオンにしない"), + ("During controlled", "操作中"), + ("During service is on", "サービスの動作中"), + ("Capture screen using DirectX", "DirectXを使用した画面キャプチャ"), + ("Back", "戻る"), + ("Apps", "アプリ"), + ("Volume up", "音量アップ"), + ("Volume down", "音量ダウン"), + ("Power", "電源"), + ("Telegram bot", "Telegram Bot"), + ("enable-bot-tip", "この機能を有効にすると、ボットから二要素認証コードを受け取ることができます。また、接続時の通知としても機能します。"), + ("enable-bot-desc", "1. @BotFatherのチャットを開きます。\n2. 「/newbot」コマンドを送信します。送信後、トークンを取得できます。\n3. 新しく作成したbotとチャットを開始します。「/hello」のようにスラッシュで始まるメッセージを送信して起動します。\n"), + ("cancel-2fa-confirm-tip", "本当に二要素認証をキャンセルしますか?"), + ("cancel-bot-confirm-tip", "本当にTelegram Botをキャンセルしますか?"), + ("About RustDesk", "RustDeskについて"), + ].iter().cloned().collect(); +} \ No newline at end of file diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 56167b968c8..6b5873a3f48 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index f12bbf56a8a..70c08041eb5 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 2d902966c2d..184c0bf587d 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 1f2271edb72..7d95b2a4304 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1. Atveriet tērzēšanu ar @BotFather.\n2. Nosūtiet komandu \"/newbot\". Pēc šīs darbības pabeigšanas jūs saņemsit pilnvaru.\n3. Sāciet tērzēšanu ar jaunizveidoto robotprogrammatūru. Lai to aktivizētu, nosūtiet ziņojumu, kas sākas ar slīpsvītru (\"/\"), piemēram, \"/hello\".\n"), ("cancel-2fa-confirm-tip", "Vai tiešām vēlaties atcelt 2FA?"), ("cancel-bot-confirm-tip", "Vai tiešām vēlaties atcelt Telegram robotu?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 4efdd3416dc..aa237792955 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index c4981c3ffad..1d95389ddf3 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1, Open een chat met @BotFather.\n2, Verzend het commando \"/newbot\". Als deze stap voltooid is, ontvang je een token.\n3, Start een chat met de nieuw aangemaakte bot. Om hem te activeren stuurt u een bericht dat begint met een schuine streep (\"/\"), bijvoorbeeld \"/hello\".\n"), ("cancel-2fa-confirm-tip", "Weet je zeker dat je 2FA wilt annuleren?"), ("cancel-bot-confirm-tip", "Weet je zeker dat je de Telegram-bot wilt annuleren?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 71ec07e739b..e237d04905b 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 3266ad2f694..5863836a4e6 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 313296bed76..36d482cab37 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 371c0ed63c3..206df023266 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 02eca034cc1..ca7d1ad0486 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1) Откройте чат с @BotFather.\n2) Отправьте команду \"/newbot\". После выполнения этого шага вы получите токен.\n3) Начните чат с вашим только что созданным ботом. Отправьте сообщение, начинающееся с прямой косой черты (\"/\"), например, \"/hello\", чтобы его активировать.\n"), ("cancel-2fa-confirm-tip", "Отключить двухфакторную аутентификацию?"), ("cancel-bot-confirm-tip", "Отключить Telegram-бота?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 2ee8d2a3bf8..323f059d87b 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1, Otvorte chat s @BotFather.\n2, Odošlite príkaz \"/newbot\". Po dokončení tohto kroku dostanete token.\n3, Spustite chat s novo vytvoreným botom. Odošlite správu začínajúcu lomítkom vpred (\"/\"), napríklad \"/hello\", aby ste ho aktivovali.\n"), ("cancel-2fa-confirm-tip", "Ste si istí, že chcete zrušiť službu 2FA?"), ("cancel-bot-confirm-tip", "Ste si istí, že chcete zrušiť bota Telegramu?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 6eda0b94483..9dd3d719281 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 3767b20dde7..2f8188dde88 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index aee79c3651d..a08bf6e84bc 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 6bbee4b1963..f90bbb93bd1 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 787cadc0163..2fd83ca808d 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 05bab55ee1f..8236db99707 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index e7aab8d1af5..974e83945db 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 09b4411086a..2ee26e9f4f7 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1. 開啟與 @BotFather 的對話。\n2. 傳送指令 \"/newbot\"。 您將會在完成此步驟後收到權杖 (Token)。\n3. 開始與您剛創立的機器人的對話。 傳送一則以正斜槓 (\"/\") 開頭的訊息來啟用它,例如 \"/hello\"。"), ("cancel-2fa-confirm-tip", "確定要取消二步驟驗證嗎?"), ("cancel-bot-confirm-tip", "確定要取消 Telegram 機器人嗎?"), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index b08afc4b9b3..4226c4ca69f 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index daa0171bb82..28702a0e8c3 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -629,5 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", ""), ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), ].iter().cloned().collect(); } From 22c6f5e589bf2a69c24a062063d078599d99d458 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 23 Jul 2024 10:26:36 +0800 Subject: [PATCH 311/335] aggresive online query for self-hosting --- flutter/lib/common/widgets/peers_view.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index 787f4f77591..01727c86cd1 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -82,7 +82,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { final _curPeers = {}; var _lastChangeTime = DateTime.now(); var _lastQueryPeers = {}; - var _lastQueryTime = DateTime.now().add(const Duration(seconds: 30)); + var _lastQueryTime = DateTime.now(); var _queryCount = 0; var _exit = false; @@ -253,10 +253,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener { return body; } - final _queryInterval = const Duration(seconds: 20); + var _queryInterval = const Duration(seconds: 20); void _startCheckOnlines() { () async { + final p = await bind.mainIsUsingPublicServer(); + if (!p) { + _queryInterval = const Duration(seconds: 6); + } while (!_exit) { final now = DateTime.now(); if (!setEquals(_curPeers, _lastQueryPeers)) { @@ -264,7 +268,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { _queryOnlines(false); } } else { - if (_queryCount < _maxQueryCount) { + if (_queryCount < _maxQueryCount || !p) { if (now.difference(_lastQueryTime) >= _queryInterval) { if (_curPeers.isNotEmpty) { bind.queryOnlines(ids: _curPeers.toList(growable: false)); From 97f26f880b8dbc21d6a9d73b886c7aa27ca051f3 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 23 Jul 2024 10:46:50 +0800 Subject: [PATCH 312/335] default-connect-password option --- libs/hbb_common/src/config.rs | 2 ++ src/client.rs | 20 +++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index e33cf8ebbcf..ff6de4430a2 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -2113,6 +2113,7 @@ pub mod keys { pub const OPTION_HIDE_PROXY_SETTINGS: &str = "hide-proxy-settings"; pub const OPTION_HIDE_USERNAME_ON_CARD: &str = "hide-username-on-card"; pub const OPTION_HIDE_HELP_CARDS: &str = "hide-help-cards"; + pub const OPTION_DEFAULT_CONNECT_PASSWORD: &str = "default-connect-password"; // flutter local options pub const OPTION_FLUTTER_REMOTE_MENUBAR_STATE: &str = "remoteMenubarState"; @@ -2254,6 +2255,7 @@ pub mod keys { OPTION_HIDE_PROXY_SETTINGS, OPTION_HIDE_USERNAME_ON_CARD, OPTION_HIDE_HELP_CARDS, + OPTION_DEFAULT_CONNECT_PASSWORD, ]; } diff --git a/src/client.rs b/src/client.rs index 163aae1a1a3..1014bc23bbd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -543,7 +543,7 @@ impl Client { conn: &mut Stream, ) -> ResultType>> { let rs_pk = get_rs_pk(if key.is_empty() { - hbb_common::config::RS_PUB_KEY + config::RS_PUB_KEY } else { key }); @@ -2625,7 +2625,7 @@ struct LoginErrorMsgBox { lazy_static::lazy_static! { static ref LOGIN_ERROR_MAP: Arc> = { - use hbb_common::config::LINK_HEADLESS_LINUX_SUPPORT; + use config::LINK_HEADLESS_LINUX_SUPPORT; let map = HashMap::from([(LOGIN_SCREEN_WAYLAND, LoginErrorMsgBox{ msgtype: "error", title: "Login Error", @@ -2791,6 +2791,20 @@ pub async fn handle_hash( if password.is_empty() { try_get_password_from_personal_ab(lc.clone(), &mut password); } + + if password.is_empty() { + let p = + crate::ui_interface::get_buildin_option(config::keys::OPTION_DEFAULT_CONNECT_PASSWORD); + if !p.is_empty() { + let mut hasher = Sha256::new(); + hasher.update(p.clone()); + hasher.update(&hash.salt); + let res = hasher.finalize(); + password = res[..].into(); + lc.write().unwrap().password_source = PasswordSource::SharedAb(p); // reuse SharedAb here + } + } + lc.write().unwrap().password = password.clone(); let password = if password.is_empty() { // login without password, the remote side can click accept @@ -2813,7 +2827,7 @@ pub async fn handle_hash( #[inline] fn try_get_password_from_personal_ab(lc: Arc>, password: &mut Vec) { let access_token = LocalConfig::get_option("access_token"); - let ab = hbb_common::config::Ab::load(); + let ab = config::Ab::load(); if !access_token.is_empty() && access_token == ab.access_token { let id = lc.read().unwrap().id.clone(); if let Some(ab) = ab.ab_entries.iter().find(|a| a.personal()) { From eef091d4e816a5d57d897567461def74c0b99f6e Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 23 Jul 2024 21:35:05 +0800 Subject: [PATCH 313/335] fix hide ab tag when custom client startup (#8795) not custom: 'Y', '' custom: 'Y', 'N' should not use isNotEmpty Signed-off-by: 21pages --- flutter/lib/common/widgets/peer_tab_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index ae5de507d53..21a276f0de1 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -87,7 +87,7 @@ class _PeerTabPageState extends State : PeerUiType.list; } hideAbTagsPanel.value = - bind.mainGetLocalOption(key: kOptionHideAbTagsPanel).isNotEmpty; + bind.mainGetLocalOption(key: kOptionHideAbTagsPanel) == 'Y'; super.initState(); } From 2ffc2ad85b5f7252ba4abe5bb6b1d7efa704b081 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 23 Jul 2024 22:26:23 +0800 Subject: [PATCH 314/335] enhance https://github.com/rustdesk/rustdesk-server-pro/discussions/325 --- src/client.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index 1014bc23bbd..e5823e187d1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2374,7 +2374,7 @@ fn get_hwcodec_config() { let start = std::time::Instant::now(); if let Err(e) = crate::ipc::get_hwcodec_config_from_server() { log::error!( - "failed to get hwcodec config: {e:?}, elapsed: {:?}", + "Failed to get hwcodec config: {e:?}, elapsed: {:?}", start.elapsed() ); } else { @@ -2994,7 +2994,9 @@ pub trait Interface: Send + Clone + 'static + Sized { log::error!("Connection closed: {err}({errno})"); if direct == Some(true) && ((cfg!(windows) && (errno == 10054 || err.contains("10054"))) - || (!cfg!(windows) && (errno == 104 || err.contains("104")))) + || (!cfg!(windows) && (errno == 104 || err.contains("104"))) + || (!err.contains("Failed") && err.contains("deadline"))) + // deadline: https://github.com/rustdesk/rustdesk-server-pro/discussions/325, most likely comes from secure tcp timeout { relay_hint = true; if !received { From 614086a216c3866c7155644e903045dd431db71a Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Tue, 23 Jul 2024 23:28:03 +0800 Subject: [PATCH 315/335] fix: mobile, query onlines, on active (#8796) * fix: mobile, query onlines, on active Signed-off-by: dignow * Update peer_tab_page.dart --------- Signed-off-by: dignow Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com> --- flutter/lib/common/widgets/peer_tab_page.dart | 13 ++++--- flutter/lib/common/widgets/peers_view.dart | 34 ++++++++++++++----- flutter/lib/models/model.dart | 2 +- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 21a276f0de1..3b92ab1ebd5 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -875,11 +875,14 @@ class _PeerSortDropdownState extends State { @override void initState() { if (!PeerSortType.values.contains(peerSort.value)) { - peerSort.value = PeerSortType.remoteId; - bind.setLocalFlutterOption( - k: kOptionPeerSorting, - v: peerSort.value, - ); + Future.delayed(Duration.zero, () { + // do not change obx directly in initState, so do in future. + peerSort.value = PeerSortType.remoteId; + bind.setLocalFlutterOption( + k: kOptionPeerSorting, + v: peerSort.value, + ); + }); } super.initState(); } diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index 01727c86cd1..ec173e29477 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -70,7 +70,8 @@ class _PeersView extends StatefulWidget { } /// State for the peer widget. -class _PeersViewState extends State<_PeersView> with WindowListener { +class _PeersViewState extends State<_PeersView> + with WindowListener, WidgetsBindingObserver { static const int _maxQueryCount = 3; final HashMap _emptyMessages = HashMap.from({ LoadEvent.recent: 'empty_recent_tip', @@ -85,6 +86,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { var _lastQueryTime = DateTime.now(); var _queryCount = 0; var _exit = false; + bool _isActive = true; final _scrollController = ScrollController(); @@ -95,12 +97,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener { @override void initState() { windowManager.addListener(this); + WidgetsBinding.instance.addObserver(this); super.initState(); } @override void dispose() { windowManager.removeListener(this); + WidgetsBinding.instance.removeObserver(this); _exit = true; super.dispose(); } @@ -115,6 +119,20 @@ class _PeersViewState extends State<_PeersView> with WindowListener { _queryCount = _maxQueryCount; } + // This function is required for mobile. + // `onWindowFocus` works fine for desktop. + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (isDesktop) return; + if (state == AppLifecycleState.resumed) { + _isActive = true; + _queryCount = 0; + } else if (state == AppLifecycleState.inactive) { + _isActive = false; + } + } + @override Widget build(BuildContext context) { return ChangeNotifierProvider( @@ -268,7 +286,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener { _queryOnlines(false); } } else { - if (_queryCount < _maxQueryCount || !p) { + if (_isActive && (_queryCount < _maxQueryCount || !p)) { if (now.difference(_lastQueryTime) >= _queryInterval) { if (_curPeers.isNotEmpty) { bind.queryOnlines(ids: _curPeers.toList(growable: false)); @@ -286,14 +304,14 @@ class _PeersViewState extends State<_PeersView> with WindowListener { _queryOnlines(bool isLoadEvent) { if (_curPeers.isNotEmpty) { bind.queryOnlines(ids: _curPeers.toList(growable: false)); - _lastQueryPeers = {..._curPeers}; - if (isLoadEvent) { - _lastChangeTime = DateTime.now(); - } else { - _lastQueryTime = DateTime.now().subtract(_queryInterval); - } _queryCount = 0; } + _lastQueryPeers = {..._curPeers}; + if (isLoadEvent) { + _lastChangeTime = DateTime.now(); + } else { + _lastQueryTime = DateTime.now().subtract(_queryInterval); + } } Future>? matchPeers( diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index b10488b2931..a9962a58a7d 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -384,7 +384,7 @@ class FfiModel with ChangeNotifier { } else if (name == 'use_texture_render') { _handleUseTextureRender(evt, sessionId, peerId); } else { - debugPrint('Unknown event name: $name'); + debugPrint('Event is not handled in the fixed branch: $name'); } }; } From a72a8906b060c691624da1fa187558d5885ec9e9 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Tue, 23 Jul 2024 15:31:36 +0000 Subject: [PATCH 316/335] vcpkg deps (#8764) * Revert "Revert vcpkg ffmpeg (#8751)" This reverts commit 5c16a8302e6c9c1381873aff45dba1487ee51d4f. * vcpkg: Reland ffmpeg and try to fix sciter build Signed-off-by: Vasyl Gello * vcpkg: Detect AVX2 by requiring __m256i (ubuntu18.04 sciter) Signed-off-by: Vasyl Gello * Install nasm from debian buster and python3.7 ... from ubuntu universe [Skip CI] * vcpkg: Add libyuv port with fix for windows From https://github.com/dchapyshev/vcpkg4aspia/commit/abc59feabf5b3a70662631293eb67c941be4bb8a Found by @deep-soft Signed-off-by: Vasyl Gello * Bump vcpkg baseline to 2024.07.12 Signed-off-by: Vasyl Gello * Fix F-Droid version action I thought the latest release will be updated by the time hook starts but it is not the case. Get tag from GITHUB_REF instead if GITHUB_REF_TYPE is "tag". Signed-off-by: Vasyl Gello --------- Signed-off-by: Vasyl Gello --- .github/workflows/ci.yml | 6 +- .github/workflows/fdroid.yml | 7 +- .github/workflows/flutter-build.yml | 32 +- Cargo.lock | 6 +- flutter/build_fdroid.sh | 6 +- libs/scrap/build.rs | 47 ++ res/vcpkg/aom/aom-avx2.diff | 60 ++ res/vcpkg/aom/portfile.cmake | 1 + res/vcpkg/ffmpeg/0002-fix-msvc-link.patch | 11 + .../ffmpeg/0003-fix-windowsinclude.patch | 13 + res/vcpkg/ffmpeg/0005-fix-nasm.patch | 55 ++ .../ffmpeg/0012-Fix-ssl-110-detection.patch | 14 + res/vcpkg/ffmpeg/0013-define-WINVER.patch | 15 + ...dd-query_timeout-option-for-h264-hev.patch | 71 ++ ...-amfenc-reconfig-when-bitrate-change.patch | 71 ++ ...-release-7.0-s-qsvenc-update_bitrate.patch | 95 +++ .../ffmpeg/5.1/0004-amf-colorspace.patch | 161 ++++ ...1-android-mediacodec-encode-align-64.patch | 40 + res/vcpkg/ffmpeg/build.sh.in | 152 ++++ res/vcpkg/ffmpeg/portfile.cmake | 689 ++++++++++++++++++ res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake | 47 ++ res/vcpkg/ffmpeg/vcpkg.json | 44 ++ res/vcpkg/libyuv/fix-cmakelists.patch | 80 ++ res/vcpkg/libyuv/libyuv-config.cmake | 5 + res/vcpkg/libyuv/portfile.cmake | 81 ++ res/vcpkg/libyuv/usage | 4 + res/vcpkg/libyuv/usage-msvc | 9 + res/vcpkg/libyuv/vcpkg.json | 22 + vcpkg.json | 39 +- 29 files changed, 1859 insertions(+), 24 deletions(-) create mode 100644 res/vcpkg/aom/aom-avx2.diff create mode 100644 res/vcpkg/ffmpeg/0002-fix-msvc-link.patch create mode 100644 res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch create mode 100644 res/vcpkg/ffmpeg/0005-fix-nasm.patch create mode 100644 res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch create mode 100644 res/vcpkg/ffmpeg/0013-define-WINVER.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch create mode 100644 res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch create mode 100644 res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch create mode 100644 res/vcpkg/ffmpeg/build.sh.in create mode 100644 res/vcpkg/ffmpeg/portfile.cmake create mode 100644 res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake create mode 100644 res/vcpkg/ffmpeg/vcpkg.json create mode 100644 res/vcpkg/libyuv/fix-cmakelists.patch create mode 100644 res/vcpkg/libyuv/libyuv-config.cmake create mode 100644 res/vcpkg/libyuv/portfile.cmake create mode 100644 res/vcpkg/libyuv/usage create mode 100644 res/vcpkg/libyuv/usage-msvc create mode 100644 res/vcpkg/libyuv/vcpkg.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41c83370528..90f312968e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,9 @@ env: # MIN_SUPPORTED_RUST_VERSION: "1.46.0" # CICD_INTERMEDIATES_DIR: "_cicd-intermediates" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - # vcpkg version: 2023.10.19 + # vcpkg version: 2024.06.15 # for multiarch gcc compatibility - VCPKG_COMMIT_ID: "8eb57355a4ffb410a2e94c07b4dca2dffbee8e50" + VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" on: workflow_dispatch: @@ -112,6 +112,8 @@ jobs: libgstreamer-plugins-base1.0-dev \ libgtk-3-dev \ libpulse-dev \ + libva-dev \ + libvdpau-dev \ libxcb-randr0-dev \ libxcb-shape0-dev \ libxcb-xfixes0-dev \ diff --git a/.github/workflows/fdroid.yml b/.github/workflows/fdroid.yml index 77eb145bbca..94f8d3d7db2 100644 --- a/.github/workflows/fdroid.yml +++ b/.github/workflows/fdroid.yml @@ -19,7 +19,12 @@ jobs: steps: - name: Generate RustDesk version file run: | - UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')" + if [ "${GITHUB_REF_TYPE}" = "tag" ]; then + UPSTREAM_VERNAME="${GITHUB_REF##refs/tags/}" + UPSTREAM_VERNAME="${UPSTREAM_VERNAME##v}" + else + UPSTREAM_VERNAME="$(curl https://api.github.com/repos/rustdesk/rustdesk/releases/latest | jq -r .tag_name | sed 's/^v//')" + fi UPSTREAM_VERCODE="$(echo "$UPSTREAM_VERNAME" | tr '.' ' ' | tr '-' ' ' | while read -r MAJOR MINOR PATCH REV; do [ -z "$MAJOR" ] && MAJOR=0; [ -z "$MINOR" ] && MINOR=0; [ -z "$PATCH" ] && PATCH=0; [ -z "$REV" ] && REV=0; echo "$(( 1000000 * $MAJOR + 10000 * $MINOR + 100 * $PATCH + $REV ))"; done)" echo "versionName=$UPSTREAM_VERNAME" > rustdesk-version.txt echo "versionCode=$UPSTREAM_VERCODE" >> rustdesk-version.txt diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 91d8cc18979..c4385b70609 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -29,8 +29,8 @@ env: FLUTTER_ELINUX_VERSION: "3.16.9" TAG_NAME: "${{ inputs.upload-tag }}" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" - # vcpkg version: 2024.06.15 - VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625" + # vcpkg version: 2024.07.12 + VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1" VERSION: "1.2.7" NDK_VERSION: "r26d" #signing keys env variable checks @@ -115,6 +115,8 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + env: + VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }} run: | $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash @@ -255,6 +257,8 @@ jobs: vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - name: Install vcpkg dependencies + env: + VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }} run: | $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash @@ -424,7 +428,7 @@ jobs: - name: Install dependencies run: | - brew install nasm + brew install nasm yasm - name: Checkout source code uses: actions/checkout@v3 - name: Install flutter @@ -1290,6 +1294,7 @@ jobs: deb_arch: amd64, sciter_arch: x64, vcpkg-triplet: x64-linux, + extra_features: ",hwcodec", } - { arch: armv7, @@ -1299,6 +1304,7 @@ jobs: deb_arch: armhf, sciter_arch: arm32, vcpkg-triplet: arm-linux, + extra_features: "", } steps: - name: Export GitHub Actions cache environment variables @@ -1313,7 +1319,6 @@ jobs: sudo apt-get update sudo apt-get install -y \ cmake \ - crossbuild-essential-armhf \ curl \ g++ \ gcc \ @@ -1351,10 +1356,9 @@ jobs: vcpkgDirectory: /opt/artifacts/vcpkg vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - name: Install vcpkg dependencies + - name: Override Linux compiler detection in vcpkg run: | cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake - $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" shell: bash - uses: rustdesk-org/run-on-arch-action@amd64-support @@ -1371,7 +1375,7 @@ jobs: --volume "/opt/artifacts:/opt/artifacts" shell: /bin/bash install: | - apt-get update -y + apt-get update apt-get install -y \ build-essential \ clang \ @@ -1398,17 +1402,26 @@ jobs: libxcb-xfixes0-dev \ libxdo-dev \ libxfixes-dev \ - nasm \ ninja-build \ pkg-config \ python3 \ + python3.7 \ rpm \ unzip \ wget \ xz-utils + # install newer nasm for aom + wget --output-document nasm.deb "http://ftp.us.debian.org/debian/pool/main/n/nasm/nasm_2.14-1_${{ matrix.job.deb_arch }}.deb" + dpkg -i nasm.deb + rm -f nasm.deb run: | # disable git safe.directory git config --global --add safe.directory "*" + # Set python3.7 as default python3 + update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1 + # vcpkg + export VCPKG_ROOT=/opt/artifacts/vcpkg + $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" # rust pushd /opt # do not use rustup, because memory overflow in qemu @@ -1427,9 +1440,8 @@ jobs: # build pushd /workspace python3 ./res/inline-sciter.py - export VCPKG_ROOT=/opt/artifacts/vcpkg export CARGO_INCREMENTAL=0 - cargo build --features inline --release --bins --jobs 1 + cargo build --features inline${{ matrix.job.extra_features }} --release --bins --jobs 1 # package mkdir -p ./Release mv ./target/release/rustdesk ./Release/rustdesk diff --git a/Cargo.lock b/Cargo.lock index 7096c8b4eed..936d12b0132 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3087,8 +3087,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.6.0" -source = "git+https://github.com/21pages/hwcodec#89879f2f02c6f74e88a4a43744a1153aec5b7e7f" +version = "0.5.1" +source = "git+https://github.com/21pages/hwcodec#74e8288f776a9d43861f16aa62e86b57c7209868" dependencies = [ "bindgen 0.59.2", "cc", @@ -3558,7 +3558,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] diff --git a/flutter/build_fdroid.sh b/flutter/build_fdroid.sh index abf311b2f9e..2e0a20b6db6 100755 --- a/flutter/build_fdroid.sh +++ b/flutter/build_fdroid.sh @@ -43,15 +43,13 @@ arm64-v8a) FLUTTER_TARGET=android-arm64 NDK_TARGET=aarch64-linux-android RUST_TARGET=aarch64-linux-android -# RUSTDESK_FEATURES='flutter,hwcodec' - RUSTDESK_FEATURES='flutter' + RUSTDESK_FEATURES='flutter,hwcodec' ;; armeabi-v7a) FLUTTER_TARGET=android-arm NDK_TARGET=arm-linux-androideabi RUST_TARGET=armv7-linux-androideabi -# RUSTDESK_FEATURES='flutter,hwcodec' - RUSTDESK_FEATURES='flutter' + RUSTDESK_FEATURES='flutter,hwcodec' ;; x86_64) FLUTTER_TARGET=android-x64 diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index 1612a6b5bfe..55a68863381 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -188,6 +188,52 @@ fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &s generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex); } +// If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci +// Linux require link in hwcodec +/* +fn ffmpeg() { + // ffmpeg + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let static_libs = vec!["avcodec", "avutil", "avformat"]; + static_libs.iter().for_each(|lib| { + find_package(lib); + }); + if target_os == "windows" { + println!("cargo:rustc-link-lib=static=libmfx"); + } + + // os + let dyn_libs: Vec<&str> = if target_os == "windows" { + ["User32", "bcrypt", "ole32", "advapi32"].to_vec() + } else if target_os == "linux" { + let mut v = ["va", "va-drm", "va-x11", "vdpau", "X11", "stdc++"].to_vec(); + if target_arch == "x86_64" { + v.push("z"); + } + v + } else if target_os == "macos" || target_os == "ios" { + ["c++", "m"].to_vec() + } else if target_os == "android" { + ["z", "m", "android", "atomic"].to_vec() + } else { + panic!("unsupported os"); + }; + dyn_libs + .iter() + .map(|lib| println!("cargo:rustc-link-lib={}", lib)) + .count(); + + if target_os == "macos" || target_os == "ios" { + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=CoreVideo"); + println!("cargo:rustc-link-lib=framework=CoreMedia"); + println!("cargo:rustc-link-lib=framework=VideoToolbox"); + println!("cargo:rustc-link-lib=framework=AVFoundation"); + } +} +*/ + fn main() { // note: all link symbol names in x86 (32-bit) are prefixed wth "_". // run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc, @@ -204,6 +250,7 @@ fn main() { gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*"); gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*"); gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*"); + // ffmpeg(); // there is problem with cfg(target_os) in build.rs, so use our workaround let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); diff --git a/res/vcpkg/aom/aom-avx2.diff b/res/vcpkg/aom/aom-avx2.diff new file mode 100644 index 00000000000..d53cd0af4d3 --- /dev/null +++ b/res/vcpkg/aom/aom-avx2.diff @@ -0,0 +1,60 @@ +diff --git a/build/cmake/cpu.cmake b/build/cmake/cpu.cmake +index acebe20..8c67d89 100644 +--- a/build/cmake/cpu.cmake ++++ b/build/cmake/cpu.cmake +@@ -120,6 +120,19 @@ elseif("${AOM_TARGET_CPU}" MATCHES "^x86") + set(RTCD_ARCH_X86_64 "yes") + endif() + ++ # AVX2 requires __m256i definition starting v3.9.0 ++ ++ if(ENABLE_AVX2) ++ aom_check_source_compiles("x86_64_avx2_m256i_available" " ++#include ++#ifndef __m256i ++#error 1 ++#endif" HAVE_AVX2_M256I) ++ if(HAVE_AVX2_M256I EQUAL 0) ++ set(ENABLE_AVX2 0) ++ endif() ++ endif() ++ + set(X86_FLAVORS "MMX;SSE;SSE2;SSE3;SSSE3;SSE4_1;SSE4_2;AVX;AVX2") + foreach(flavor ${X86_FLAVORS}) + if(ENABLE_${flavor} AND NOT disable_remaining_flavors) +diff --git a/aom_dsp/x86/synonyms.h b/aom_dsp/x86/synonyms.h +index 0d51cdf..6744ec5 100644 +--- a/aom_dsp/x86/synonyms.h ++++ b/aom_dsp/x86/synonyms.h +@@ -46,13 +46,6 @@ static INLINE __m128i xx_loadu_128(const void *a) { + return _mm_loadu_si128((const __m128i *)a); + } + +-// Load 64 bits from each of hi and low, and pack into an SSE register +-// Since directly loading as `int64_t`s and using _mm_set_epi64 may violate +-// the strict aliasing rule, this takes a different approach +-static INLINE __m128i xx_loadu_2x64(const void *hi, const void *lo) { +- return _mm_unpacklo_epi64(_mm_loadu_si64(lo), _mm_loadu_si64(hi)); +-} +- + static INLINE void xx_storel_32(void *const a, const __m128i v) { + const int val = _mm_cvtsi128_si32(v); + memcpy(a, &val, sizeof(val)); +diff --git a/aom_dsp/x86/synonyms_avx2.h b/aom_dsp/x86/synonyms_avx2.h +index d4e8f69..45be17e 100644 +--- a/aom_dsp/x86/synonyms_avx2.h ++++ b/aom_dsp/x86/synonyms_avx2.h +@@ -25,6 +25,13 @@ + * Intrinsics prefixed with yy_ operate on or return 256bit YMM registers. + */ + ++// Load 64 bits from each of hi and low, and pack into an SSE register ++// Since directly loading as `int64_t`s and using _mm_set_epi64 may violate ++// the strict aliasing rule, this takes a different approach ++static INLINE __m128i xx_loadu_2x64(const void *hi, const void *lo) { ++ return _mm_unpacklo_epi64(_mm_loadu_si64(lo), _mm_loadu_si64(hi)); ++} ++ + // Loads and stores to do away with the tedium of casting the address + // to the right type. + static INLINE __m256i yy_load_256(const void *a) { diff --git a/res/vcpkg/aom/portfile.cmake b/res/vcpkg/aom/portfile.cmake index 0167a92631b..2df452a640e 100644 --- a/res/vcpkg/aom/portfile.cmake +++ b/res/vcpkg/aom/portfile.cmake @@ -14,6 +14,7 @@ vcpkg_from_git( REF 8ad484f8a18ed1853c094e7d3a4e023b2a92df28 # 3.9.1 PATCHES aom-uninitialized-pointer.diff + aom-avx2.diff # Can be dropped when https://bugs.chromium.org/p/aomedia/issues/detail?id=3029 is merged into the upstream aom-install.diff ) diff --git a/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch b/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch new file mode 100644 index 00000000000..c9aa7e75237 --- /dev/null +++ b/res/vcpkg/ffmpeg/0002-fix-msvc-link.patch @@ -0,0 +1,11 @@ +diff --git a/configure b/configure +--- a/configure ++++ b/configure +@@ -6162,6 +6162,7 @@ EOF + test -n "$extern_prefix" && append X86ASMFLAGS "-DPREFIX" + case "$objformat" in + elf*) enabled debug && append X86ASMFLAGS $x86asm_debug ;; ++ win*) enabled debug && append X86ASMFLAGS "-g" ;; + esac + + enabled avx512 && check_x86asm avx512_external "vmovdqa32 [eax]{k1}{z}, zmm0" diff --git a/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch b/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch new file mode 100644 index 00000000000..8b2e22b476f --- /dev/null +++ b/res/vcpkg/ffmpeg/0003-fix-windowsinclude.patch @@ -0,0 +1,13 @@ +diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c +--- a/fftools/cmdutils.c ++++ b/fftools/cmdutils.c +@@ -51,6 +51,8 @@ + #include "fopen_utf8.h" + #include "opt_common.h" + #ifdef _WIN32 ++#define _WIN32_WINNT 0x0502 ++#define WIN32_LEAN_AND_MEAN + #include + #include "compat/w32dlfcn.h" + #endif + diff --git a/res/vcpkg/ffmpeg/0005-fix-nasm.patch b/res/vcpkg/ffmpeg/0005-fix-nasm.patch new file mode 100644 index 00000000000..9308e714a6b --- /dev/null +++ b/res/vcpkg/ffmpeg/0005-fix-nasm.patch @@ -0,0 +1,55 @@ +diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile +--- a/libavcodec/x86/Makefile ++++ b/libavcodec/x86/Makefile +@@ -158,6 +158,8 @@ X86ASM-OBJS-$(CONFIG_ALAC_DECODER) += x86/alacdsp.o + X86ASM-OBJS-$(CONFIG_APNG_DECODER) += x86/pngdsp.o + X86ASM-OBJS-$(CONFIG_CAVS_DECODER) += x86/cavsidct.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_CFHD_ENCODER) += x86/cfhdencdsp.o ++endif + X86ASM-OBJS-$(CONFIG_CFHD_DECODER) += x86/cfhddsp.o + X86ASM-OBJS-$(CONFIG_DCA_DECODER) += x86/dcadsp.o x86/synth_filter.o + X86ASM-OBJS-$(CONFIG_DIRAC_DECODER) += x86/diracdsp.o \ +@@ -175,15 +177,21 @@ x86/hevc_sao_10bit.o + X86ASM-OBJS-$(CONFIG_JPEG2000_DECODER) += x86/jpeg2000dsp.o + X86ASM-OBJS-$(CONFIG_LSCR_DECODER) += x86/pngdsp.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp.o ++endif + X86ASM-OBJS-$(CONFIG_MPEG4_DECODER) += x86/xvididct.o + X86ASM-OBJS-$(CONFIG_PNG_DECODER) += x86/pngdsp.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_PRORES_DECODER) += x86/proresdsp.o + X86ASM-OBJS-$(CONFIG_PRORES_LGPL_DECODER) += x86/proresdsp.o ++endif + X86ASM-OBJS-$(CONFIG_RV40_DECODER) += x86/rv40dsp.o + X86ASM-OBJS-$(CONFIG_SBC_ENCODER) += x86/sbcdsp.o + X86ASM-OBJS-$(CONFIG_SVQ1_ENCODER) += x86/svq1enc.o + X86ASM-OBJS-$(CONFIG_TAK_DECODER) += x86/takdsp.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_TRUEHD_DECODER) += x86/mlpdsp.o ++endif + X86ASM-OBJS-$(CONFIG_TTA_DECODER) += x86/ttadsp.o + X86ASM-OBJS-$(CONFIG_TTA_ENCODER) += x86/ttaencdsp.o + X86ASM-OBJS-$(CONFIG_UTVIDEO_DECODER) += x86/utvideodsp.o +diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile +--- a/libavfilter/x86/Makefile ++++ b/libavfilter/x86/Makefile +@@ -44,6 +44,8 @@ + X86ASM-OBJS-$(CONFIG_AFIR_FILTER) += x86/af_afir.o + X86ASM-OBJS-$(CONFIG_ANLMDN_FILTER) += x86/af_anlmdn.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_ATADENOISE_FILTER) += x86/vf_atadenoise.o ++endif + X86ASM-OBJS-$(CONFIG_BLEND_FILTER) += x86/vf_blend.o + X86ASM-OBJS-$(CONFIG_BWDIF_FILTER) += x86/vf_bwdif.o + X86ASM-OBJS-$(CONFIG_COLORSPACE_FILTER) += x86/colorspacedsp.o +@@ -62,6 +62,8 @@ X86ASM-OBJS-$(CONFIG_LUT3D_FILTER) += x86/vf_lut3d.o + X86ASM-OBJS-$(CONFIG_MASKEDCLAMP_FILTER) += x86/vf_maskedclamp.o + X86ASM-OBJS-$(CONFIG_MASKEDMERGE_FILTER) += x86/vf_maskedmerge.o ++ifdef ARCH_X86_64 + X86ASM-OBJS-$(CONFIG_NLMEANS_FILTER) += x86/vf_nlmeans.o ++endif + X86ASM-OBJS-$(CONFIG_OVERLAY_FILTER) += x86/vf_overlay.o + X86ASM-OBJS-$(CONFIG_PP7_FILTER) += x86/vf_pp7.o + X86ASM-OBJS-$(CONFIG_PSNR_FILTER) += x86/vf_psnr.o diff --git a/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch b/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch new file mode 100644 index 00000000000..b2e5501a13a --- /dev/null +++ b/res/vcpkg/ffmpeg/0012-Fix-ssl-110-detection.patch @@ -0,0 +1,14 @@ +diff --git a/configure b/configure +index 2be953f7e7..e075949ffc 100755 +--- a/configure ++++ b/configure +@@ -6497,6 +6497,7 @@ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0 + { enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } || + { enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } || + check_pkg_config openssl openssl openssl/ssl.h OPENSSL_init_ssl || + check_pkg_config openssl openssl openssl/ssl.h SSL_library_init || ++ check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto $pthreads_extralibs -ldl || + check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto || + check_lib openssl openssl/ssl.h SSL_library_init -lssl -lcrypto || + check_lib openssl openssl/ssl.h SSL_library_init -lssl32 -leay32 || + diff --git a/res/vcpkg/ffmpeg/0013-define-WINVER.patch b/res/vcpkg/ffmpeg/0013-define-WINVER.patch new file mode 100644 index 00000000000..295a738e74f --- /dev/null +++ b/res/vcpkg/ffmpeg/0013-define-WINVER.patch @@ -0,0 +1,15 @@ +diff --color -Naur src_old/libavcodec/mf_utils.c src/libavcodec/mf_utils.c +--- src_old/libavcodec/mf_utils.c 2020-07-11 05:26:17.000000000 +0700 ++++ src/libavcodec/mf_utils.c 2020-11-13 12:55:57.226976400 +0700 +@@ -22,6 +22,11 @@ + #define _WIN32_WINNT 0x0602 + #endif + ++#if !defined(WINVER) || WINVER < 0x0602 ++#undef WINVER ++#define WINVER 0x0602 ++#endif ++ + #include "mf_utils.h" + #include "libavutil/pixdesc.h" + diff --git a/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch b/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch new file mode 100644 index 00000000000..245a470d39f --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch @@ -0,0 +1,71 @@ +From f0b694749b38b2cfd94df4eed10e667342c234e5 Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sat, 24 Feb 2024 15:33:24 +0800 +Subject: [PATCH 1/2] avcodec/amfenc: add query_timeout option for h264/hevc + +Signed-off-by: 21pages +--- + libavcodec/amfenc.h | 1 + + libavcodec/amfenc_h264.c | 4 ++++ + libavcodec/amfenc_hevc.c | 4 ++++ + 3 files changed, 9 insertions(+) + +diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h +index 1ab98d2f78..e92120ea39 100644 +--- a/libavcodec/amfenc.h ++++ b/libavcodec/amfenc.h +@@ -87,6 +87,7 @@ typedef struct AmfContext { + int quality; + int b_frame_delta_qp; + int ref_b_frame_delta_qp; ++ int64_t query_timeout; + + // Dynamic options, can be set after Init() call + +diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c +index efb04589f6..f55dbc80f0 100644 +--- a/libavcodec/amfenc_h264.c ++++ b/libavcodec/amfenc_h264.c +@@ -121,6 +121,7 @@ static const AVOption options[] = { + { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, ++ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE }, + + { NULL } + }; +@@ -155,6 +156,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) + + AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMERATE, framerate); + ++ if (ctx->query_timeout >= 0) ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUERY_TIMEOUT, ctx->query_timeout); ++ + switch (avctx->profile) { + case FF_PROFILE_H264_BASELINE: + profile = AMF_VIDEO_ENCODER_PROFILE_BASELINE; +diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c +index 8ab9330730..7a40bcad31 100644 +--- a/libavcodec/amfenc_hevc.c ++++ b/libavcodec/amfenc_hevc.c +@@ -89,6 +89,7 @@ static const AVOption options[] = { + { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, + + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, ++ { "query_timeout", "Timeout for QueryOutput call in ms", OFFSET(query_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, 1000, VE }, + { NULL } + }; + +@@ -122,6 +123,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + + AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate); + ++ if (ctx->query_timeout >= 0) ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QUERY_TIMEOUT, ctx->query_timeout); ++ + switch (avctx->profile) { + case FF_PROFILE_HEVC_MAIN: + profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch b/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch new file mode 100644 index 00000000000..13b055ef289 --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch @@ -0,0 +1,71 @@ +From 4d0d20d96ad458cfec0444b9be0182ca6085ee0c Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sat, 24 Feb 2024 16:02:44 +0800 +Subject: [PATCH 2/2] libavcodec/amfenc: reconfig when bitrate change + +Signed-off-by: 21pages +--- + libavcodec/amfenc.c | 20 ++++++++++++++++++++ + libavcodec/amfenc.h | 1 + + 2 files changed, 21 insertions(+) + +diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c +index a033e1220e..3eab01a903 100644 +--- a/libavcodec/amfenc.c ++++ b/libavcodec/amfenc.c +@@ -222,6 +222,7 @@ static int amf_init_context(AVCodecContext *avctx) + + ctx->hwsurfaces_in_queue = 0; + ctx->hwsurfaces_in_queue_max = 16; ++ ctx->av_bitrate = avctx->bit_rate; + + // configure AMF logger + // the return of these functions indicates old state and do not affect behaviour +@@ -575,6 +576,23 @@ static void amf_release_buffer_with_frame_ref(AMFBuffer *frame_ref_storage_buffe + frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer); + } + ++static int reconfig_encoder(AVCodecContext *avctx) ++{ ++ AmfContext *ctx = avctx->priv_data; ++ AMF_RESULT res = AMF_OK; ++ ++ if (ctx->av_bitrate != avctx->bit_rate) { ++ av_log(ctx, AV_LOG_INFO, "change bitrate from %d to %d\n", ctx->av_bitrate, avctx->bit_rate); ++ ctx->av_bitrate = avctx->bit_rate; ++ if (avctx->codec->id == AV_CODEC_ID_H264) { ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_TARGET_BITRATE, avctx->bit_rate); ++ } else if (avctx->codec->id == AV_CODEC_ID_HEVC) { ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TARGET_BITRATE, avctx->bit_rate); ++ } ++ } ++ return 0; ++} ++ + int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) + { + AmfContext *ctx = avctx->priv_data; +@@ -586,6 +604,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) + AVFrame *frame = ctx->delayed_frame; + int block_and_wait; + ++ reconfig_encoder(avctx); ++ + if (!ctx->encoder) + return AVERROR(EINVAL); + +diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h +index e92120ea39..31172645f2 100644 +--- a/libavcodec/amfenc.h ++++ b/libavcodec/amfenc.h +@@ -107,6 +107,7 @@ typedef struct AmfContext { + int me_half_pel; + int me_quarter_pel; + int aud; ++ int64_t av_bitrate; + + // HEVC - specific options + +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch b/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch new file mode 100644 index 00000000000..475fb627f3e --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch @@ -0,0 +1,95 @@ +From afe89a70f6bc7ebd0a6a0a31101801b88cbd60ee Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Sun, 5 May 2024 12:45:23 +0800 +Subject: [PATCH] use release/7.0's update_bitrate + +Signed-off-by: 21pages +--- + libavcodec/qsvenc.c | 39 +++++++++++++++++++++++++++++++++++++++ + libavcodec/qsvenc.h | 6 ++++++ + 2 files changed, 45 insertions(+) + +diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c +index 2382c2f5f7..9b34f37eb3 100644 +--- a/libavcodec/qsvenc.c ++++ b/libavcodec/qsvenc.c +@@ -714,6 +714,11 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) + brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes), + initial_delay_in_kilobytes) + 0x10000) / 0x10000; + ++ q->old_rc_buffer_size = avctx->rc_buffer_size; ++ q->old_rc_initial_buffer_occupancy = avctx->rc_initial_buffer_occupancy; ++ q->old_bit_rate = avctx->bit_rate; ++ q->old_rc_max_rate = avctx->rc_max_rate; ++ + switch (q->param.mfx.RateControlMethod) { + case MFX_RATECONTROL_CBR: + case MFX_RATECONTROL_VBR: +@@ -1657,6 +1662,39 @@ static int update_qp(AVCodecContext *avctx, QSVEncContext *q, + return updated; + } + ++static int update_bitrate(AVCodecContext *avctx, QSVEncContext *q) ++{ ++ int updated = 0; ++ int target_bitrate_kbps, max_bitrate_kbps, brc_param_multiplier; ++ int buffer_size_in_kilobytes, initial_delay_in_kilobytes; ++ ++ UPDATE_PARAM(q->old_rc_buffer_size, avctx->rc_buffer_size); ++ UPDATE_PARAM(q->old_rc_initial_buffer_occupancy, avctx->rc_initial_buffer_occupancy); ++ UPDATE_PARAM(q->old_bit_rate, avctx->bit_rate); ++ UPDATE_PARAM(q->old_rc_max_rate, avctx->rc_max_rate); ++ if (!updated) ++ return 0; ++ ++ buffer_size_in_kilobytes = avctx->rc_buffer_size / 8000; ++ initial_delay_in_kilobytes = avctx->rc_initial_buffer_occupancy / 8000; ++ target_bitrate_kbps = avctx->bit_rate / 1000; ++ max_bitrate_kbps = avctx->rc_max_rate / 1000; ++ brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes), ++ initial_delay_in_kilobytes) + 0x10000) / 0x10000; ++ ++ q->param.mfx.BufferSizeInKB = buffer_size_in_kilobytes / brc_param_multiplier; ++ q->param.mfx.InitialDelayInKB = initial_delay_in_kilobytes / brc_param_multiplier; ++ q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier; ++ q->param.mfx.MaxKbps = max_bitrate_kbps / brc_param_multiplier; ++ q->param.mfx.BRCParamMultiplier = brc_param_multiplier; ++ av_log(avctx, AV_LOG_VERBOSE, ++ "Reset BufferSizeInKB: %d; InitialDelayInKB: %d; " ++ "TargetKbps: %d; MaxKbps: %d; BRCParamMultiplier: %d\n", ++ q->param.mfx.BufferSizeInKB, q->param.mfx.InitialDelayInKB, ++ q->param.mfx.TargetKbps, q->param.mfx.MaxKbps, q->param.mfx.BRCParamMultiplier); ++ return updated; ++} ++ + static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, + const AVFrame *frame) + { +@@ -1666,6 +1704,7 @@ static int update_parameters(AVCodecContext *avctx, QSVEncContext *q, + return 0; + + needReset = update_qp(avctx, q, frame); ++ needReset |= update_bitrate(avctx, q); + if (!needReset) + return 0; + +diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h +index b754ac4b56..5745533165 100644 +--- a/libavcodec/qsvenc.h ++++ b/libavcodec/qsvenc.h +@@ -224,6 +224,12 @@ typedef struct QSVEncContext { + int min_qp_p; + int max_qp_b; + int min_qp_b; ++ ++ // These are used for bitrate control reset ++ int old_bit_rate; ++ int old_rc_buffer_size; ++ int old_rc_initial_buffer_occupancy; ++ int old_rc_max_rate; + } QSVEncContext; + + int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q); +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch b/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch new file mode 100644 index 00000000000..49aef694795 --- /dev/null +++ b/res/vcpkg/ffmpeg/5.1/0004-amf-colorspace.patch @@ -0,0 +1,161 @@ +From 8fd62e4ecd058b09abf8847be5fbbf0eef44a90f Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Tue, 16 Jul 2024 14:58:33 +0800 +Subject: [PATCH] amf colorspace + +Signed-off-by: 21pages +--- + libavcodec/amfenc.h | 1 + + libavcodec/amfenc_h264.c | 39 +++++++++++++++++++++++++++++++++ + libavcodec/amfenc_hevc.c | 47 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 87 insertions(+) + +diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h +index 31172645f2..493e01603d 100644 +--- a/libavcodec/amfenc.h ++++ b/libavcodec/amfenc.h +@@ -23,6 +23,7 @@ + + #include + #include ++#include + + #include "libavutil/fifo.h" + +diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c +index f55dbc80f0..5a6b6e164f 100644 +--- a/libavcodec/amfenc_h264.c ++++ b/libavcodec/amfenc_h264.c +@@ -139,6 +139,9 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) + AMFRate framerate; + AMFSize framesize = AMFConstructSize(avctx->width, avctx->height); + int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0; ++ amf_int64 color_depth; ++ amf_int64 color_profile; ++ enum AVPixelFormat pix_fmt; + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); +@@ -199,11 +202,47 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) + AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_ASPECT_RATIO, ratio); + } + ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; + /// Color Range (Partial/TV/MPEG or Full/PC/JPEG) + if (avctx->color_range == AVCOL_RANGE_JPEG) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 1); ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; ++ break; ++ } ++ } else { ++ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_FULL_RANGE_COLOR, 0); ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; ++ break; ++ } ++ } ++ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format : avctx->pix_fmt; ++ color_depth = AMF_COLOR_BIT_DEPTH_8; ++ if (pix_fmt == AV_PIX_FMT_P010) { ++ color_depth = AMF_COLOR_BIT_DEPTH_10; + } + ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_COLOR_BIT_DEPTH, color_depth); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, color_profile); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); + // autodetect rate control method + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN) { + if (ctx->qp_i != -1 || ctx->qp_p != -1 || ctx->qp_b != -1) { +diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c +index 7a40bcad31..0260f43c81 100644 +--- a/libavcodec/amfenc_hevc.c ++++ b/libavcodec/amfenc_hevc.c +@@ -106,6 +106,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + AMFRate framerate; + AMFSize framesize = AMFConstructSize(avctx->width, avctx->height); + int deblocking_filter = (avctx->flags & AV_CODEC_FLAG_LOOP_FILTER) ? 1 : 0; ++ amf_int64 color_depth; ++ amf_int64 color_profile; ++ enum AVPixelFormat pix_fmt; + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); +@@ -130,6 +133,9 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + case FF_PROFILE_HEVC_MAIN: + profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; + break; ++ case FF_PROFILE_HEVC_MAIN_10: ++ profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10; ++ break; + default: + break; + } +@@ -158,6 +164,47 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) + AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ASPECT_RATIO, ratio); + } + ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; ++ if (avctx->color_range == AVCOL_RANGE_JPEG) { ++ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, 1); ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; ++ break; ++ } ++ } else { ++ AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NOMINAL_RANGE, 0); ++ switch (avctx->colorspace) { ++ case AVCOL_SPC_SMPTE170M: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; ++ break; ++ case AVCOL_SPC_BT709: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; ++ break; ++ } ++ } ++ pix_fmt = avctx->hw_frames_ctx ? ((AVHWFramesContext*)avctx->hw_frames_ctx->data)->sw_format : avctx->pix_fmt; ++ color_depth = AMF_COLOR_BIT_DEPTH_8; ++ if (pix_fmt == AV_PIX_FMT_P010) { ++ color_depth = AMF_COLOR_BIT_DEPTH_10; ++ } ++ ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, color_depth); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE, color_profile); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); ++ AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); ++ + // Picture control properties + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_NUM_GOPS_PER_IDR, ctx->gops_per_idr); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_GOP_SIZE, avctx->gop_size); +-- +2.43.0.windows.1 + diff --git a/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch b/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch new file mode 100644 index 00000000000..d46c54af6da --- /dev/null +++ b/res/vcpkg/ffmpeg/7.0/0001-android-mediacodec-encode-align-64.patch @@ -0,0 +1,40 @@ +From be3d9d8092720bbe4239212648d2e9c4ffd7f40c Mon Sep 17 00:00:00 2001 +From: 21pages +Date: Wed, 22 May 2024 17:09:28 +0800 +Subject: [PATCH] android mediacodec encode align 64 + +Signed-off-by: 21pages +--- + libavcodec/mediacodecenc.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c +index 984014f1b1..8dcd3dcd64 100644 +--- a/libavcodec/mediacodecenc.c ++++ b/libavcodec/mediacodecenc.c +@@ -200,16 +200,17 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) + ff_AMediaFormat_setString(format, "mime", codec_mime); + // Workaround the alignment requirement of mediacodec. We can't do it + // silently for AV_PIX_FMT_MEDIACODEC. ++ const int align = 64; + if (avctx->pix_fmt != AV_PIX_FMT_MEDIACODEC) { +- s->width = FFALIGN(avctx->width, 16); +- s->height = FFALIGN(avctx->height, 16); ++ s->width = FFALIGN(avctx->width, align); ++ s->height = FFALIGN(avctx->height, align); + } else { + s->width = avctx->width; + s->height = avctx->height; +- if (s->width % 16 || s->height % 16) ++ if (s->width % align || s->height % align) + av_log(avctx, AV_LOG_WARNING, +- "Video size %dx%d isn't align to 16, it may have device compatibility issue\n", +- s->width, s->height); ++ "Video size %dx%d isn't align to %d, it may have device compatibility issue\n", ++ s->width, s->height, align); + } + ff_AMediaFormat_setInt32(format, "width", s->width); + ff_AMediaFormat_setInt32(format, "height", s->height); +-- +2.34.1 + diff --git a/res/vcpkg/ffmpeg/build.sh.in b/res/vcpkg/ffmpeg/build.sh.in new file mode 100644 index 00000000000..462737587b7 --- /dev/null +++ b/res/vcpkg/ffmpeg/build.sh.in @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +set -e + +export PATH="$PATH:/usr/bin" + +command -v cygpath >/dev/null && have_cygpath=1 + +cygpath() { + if [ -n "$have_cygpath" ]; then + command cygpath "$@" + else + eval _p='$'$# + printf '%s\n' "$_p" + fi +} + +move_binary() { + SOURCE=$1 + TARGET=$2 + BINARY=$3 + + # run lipo over the command to check whether it really + # is a binary that we need to merge architectures + lipo $SOURCE/$BINARY -info &> /dev/null || return 0 + + # get the directory name the file is in + DIRNAME=$(dirname $BINARY) + + # ensure the directory to move the binary to exists + mkdir -p $TARGET/$DIRNAME + + # now finally move the binary + mv $SOURCE/$BINARY $TARGET/$BINARY +} + +move_binaries() { + SOURCE=$1 + TARGET=$2 + + [ ! -d $SOURCE ] && return 0 + pushd $SOURCE + + for BINARY in $(find . -type f); do + move_binary $SOURCE $TARGET $BINARY + done + + popd +} + +merge_binaries() { + TARGET=$1 + SOURCE=$2 + + shift + shift + + pushd $SOURCE/$1 + BINARIES=$(find . -type f) + popd + + for BINARY in $BINARIES; do + COMMAND="lipo -create -output $TARGET/$BINARY" + + for ARCH in $@; do + COMMAND="$COMMAND -arch $ARCH $SOURCE/$ARCH/$BINARY" + done + + $($COMMAND) + done +} + +export PKG_CONFIG_PATH="$(cygpath -p "${PKG_CONFIG_PATH}")" + +# Export HTTP(S)_PROXY as http(s)_proxy: +[ -n "$HTTP_PROXY" ] && export http_proxy="$HTTP_PROXY" +[ -n "$HTTPS_PROXY" ] && export https_proxy="$HTTPS_PROXY" + +PATH_TO_BUILD_DIR=$( cygpath "@BUILD_DIR@") +PATH_TO_SRC_DIR=$( cygpath "@SOURCE_PATH@") +PATH_TO_PACKAGE_DIR=$(cygpath "@INST_PREFIX@") + +JOBS=@VCPKG_CONCURRENCY@ + +OSX_ARCHS="@OSX_ARCHS@" +OSX_ARCH_COUNT=0@OSX_ARCH_COUNT@ + +# Default to hardware concurrency if unset. +: ${JOBS:=$(nproc)} + +# Disable asm and x86asm on all android targets because they trigger build failures: +# arm64 Android build fails with 'relocation R_AARCH64_ADR_PREL_PG_HI21 cannot be used against symbol ff_cos_32; recompile with -fPIC' +# x86 Android build fails with 'error: inline assembly requires more registers than available'. +# x64 Android build fails with 'relocation R_X86_64_PC32 cannot be used against symbol ff_h264_cabac_tables; recompile with -fPIC' +if [ "@VCPKG_CMAKE_SYSTEM_NAME@" = "Android" ]; then + OPTIONS_arm=" --disable-asm --disable-x86asm" + OPTIONS_arm64=" --disable-asm --disable-x86asm" + OPTIONS_x86=" --disable-asm --disable-x86asm" + OPTIONS_x86_64="${OPTIONS_x86}" +else + OPTIONS_arm=" --disable-asm --disable-x86asm" + OPTIONS_arm64=" --enable-asm --disable-x86asm" + OPTIONS_x86=" --enable-asm --enable-x86asm" + OPTIONS_x86_64="${OPTIONS_x86}" +fi + +build_ffmpeg() { + # extract build architecture + BUILD_ARCH=$1 + shift + + echo "BUILD_ARCH=${BUILD_ARCH}" + + # get architecture-specific options + OPTION_VARIABLE="OPTIONS_${BUILD_ARCH}" + echo "OPTION_VARIABLE=${OPTION_VARIABLE}" + + echo "=== CONFIGURING ===" + + sh "$PATH_TO_SRC_DIR/configure" "--prefix=$PATH_TO_PACKAGE_DIR" @CONFIGURE_OPTIONS@ --arch=${BUILD_ARCH} ${!OPTION_VARIABLE} $@ + + echo "=== BUILDING ===" + + make -j${JOBS} V=1 + + echo "=== INSTALLING ===" + + make install +} + +cd "$PATH_TO_BUILD_DIR" + +if [ $OSX_ARCH_COUNT -gt 0 ]; then + for ARCH in $OSX_ARCHS; do + echo "=== CLEANING FOR $ARCH ===" + + make clean && make distclean + + build_ffmpeg $ARCH --extra-cflags=-arch --extra-cflags=$ARCH --extra-ldflags=-arch --extra-ldflags=$ARCH + + echo "=== COLLECTING BINARIES FOR $ARCH ===" + + move_binaries $PATH_TO_PACKAGE_DIR/lib $PATH_TO_BUILD_DIR/stage/$ARCH/lib + move_binaries $PATH_TO_PACKAGE_DIR/bin $PATH_TO_BUILD_DIR/stage/$ARCH/bin + done + + echo "=== MERGING ARCHITECTURES ===" + + merge_binaries $PATH_TO_PACKAGE_DIR $PATH_TO_BUILD_DIR/stage $OSX_ARCHS +else + build_ffmpeg @BUILD_ARCH@ +fi diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake new file mode 100644 index 00000000000..dc35752ff8b --- /dev/null +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -0,0 +1,689 @@ +if(VCPKG_TARGET_IS_WINDOWS OR VCPKG_TARGET_IS_LINUX) + set(FF_VERSION "n5.1.5") + set(FF_SHA512 "a933f18e53207ccc277b42c9a68db00f31cefec555e6d5d7c57db3409023b2c38fd93ebe2ccfcd17ba2397adb912e93f2388241ca970b7d8bd005ccfe86d5679") +else() + set(FF_VERSION "n7.0.1") + set(FF_SHA512 "1212ebcb78fdaa103b0304373d374e41bf1fe680e1fa4ce0f60624857491c26b4dda004c490c3ef32d4a0e10f42ae6b54546f9f318e2dcfbaa116117f687bc88") +endif() + +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO ffmpeg/ffmpeg + REF "${FF_VERSION}" + SHA512 "${FF_SHA512}" + HEAD_REF master + PATCHES + 0002-fix-msvc-link.patch # upstreamed in future version + 0003-fix-windowsinclude.patch + 0005-fix-nasm.patch # upstreamed in future version + 0012-Fix-ssl-110-detection.patch + 0013-define-WINVER.patch +) + +if(VCPKG_TARGET_IS_WINDOWS OR VCPKG_TARGET_IS_LINUX) + vcpkg_apply_patches( + SOURCE_PATH ${SOURCE_PATH} + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/5.1/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch + ${CMAKE_CURRENT_LIST_DIR}/5.1/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch + ${CMAKE_CURRENT_LIST_DIR}/5.1/0003-use-release-7.0-s-qsvenc-update_bitrate.patch + ${CMAKE_CURRENT_LIST_DIR}/5.1/0004-amf-colorspace.patch + ) +elseif(VCPKG_TARGET_IS_ANDROID) + vcpkg_apply_patches( + SOURCE_PATH ${SOURCE_PATH} + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/7.0/0001-android-mediacodec-encode-align-64.patch + ) +endif() + +if(SOURCE_PATH MATCHES " ") + message(FATAL_ERROR "Error: ffmpeg will not build with spaces in the path. Please use a directory with no spaces") +endif() + +if(NOT VCPKG_TARGET_ARCHITECTURE STREQUAL "wasm32") + vcpkg_find_acquire_program(NASM) + get_filename_component(NASM_EXE_PATH "${NASM}" DIRECTORY) + vcpkg_add_to_path("${NASM_EXE_PATH}") +endif() + +set(OPTIONS "\ +--disable-shared \ +--enable-static \ +--enable-pic \ +--disable-everything \ +--disable-programs \ +--disable-doc \ +--disable-htmlpages \ +--disable-manpages \ +--disable-podpages \ +--disable-txtpages \ +--disable-network \ +--disable-appkit \ +--disable-coreimage \ +--disable-metal \ +--disable-sdl2 \ +--disable-securetransport \ +--disable-vulkan \ +--disable-audiotoolbox \ +--disable-v4l2-m2m \ +--disable-debug \ +--disable-valgrind-backtrace \ +--disable-large-tests \ +--disable-avdevice \ +--enable-avcodec \ +--enable-avformat \ +--disable-avfilter \ +--disable-swresample \ +--disable-swscale \ +--disable-postproc \ +--enable-decoder=h264 \ +--enable-decoder=hevc \ +--enable-parser=h264 \ +--enable-parser=hevc \ +--enable-bsf=h264_mp4toannexb \ +--enable-bsf=hevc_mp4toannexb \ +--enable-bsf=h264_metadata \ +--enable-bsf=hevc_metadata \ +--enable-muxer=mp4 \ +--enable-protocol=file \ +") + +if(VCPKG_HOST_IS_WINDOWS) + vcpkg_acquire_msys(MSYS_ROOT PACKAGES automake1.16) + set(SHELL "${MSYS_ROOT}/usr/bin/bash.exe") + vcpkg_add_to_path("${MSYS_ROOT}/usr/share/automake-1.16") + string(APPEND OPTIONS " --pkg-config=${CURRENT_HOST_INSTALLED_DIR}/tools/pkgconf/pkgconf${VCPKG_HOST_EXECUTABLE_SUFFIX}") +else() + find_program(SHELL bash) +endif() + +if(VCPKG_TARGET_IS_LINUX) + string(APPEND OPTIONS "\ +--target-os=linux \ +--enable-pthreads \ +") + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") + else() + string(APPEND OPTIONS "\ +--enable-cuda \ +--enable-ffnvcodec \ +--enable-encoder=h264_nvenc \ +--enable-encoder=hevc_nvenc \ +--enable-hwaccel=h264_nvdec \ +--enable-hwaccel=hevc_nvdec \ +--enable-amf \ +--enable-encoder=h264_amf \ +--enable-encoder=hevc_amf \ +--enable-hwaccel=h264_vaapi \ +--enable-hwaccel=hevc_vaapi \ +--enable-encoder=h264_vaapi \ +--enable-encoder=hevc_vaapi \ +") + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + string(APPEND OPTIONS "\ + --enable-cuda_llvm \ +") + endif() + endif() +elseif(VCPKG_TARGET_IS_WINDOWS) + string(APPEND OPTIONS "\ +--target-os=win32 \ +--toolchain=msvc \ +--enable-gpl \ +--enable-d3d11va \ +--enable-cuda \ +--enable-ffnvcodec \ +--enable-hwaccel=h264_nvdec \ +--enable-hwaccel=hevc_nvdec \ +--enable-hwaccel=h264_d3d11va \ +--enable-hwaccel=hevc_d3d11va \ +--enable-hwaccel=h264_d3d11va2 \ +--enable-hwaccel=hevc_d3d11va2 \ +--enable-amf \ +--enable-encoder=h264_amf \ +--enable-encoder=hevc_amf \ +--enable-encoder=h264_nvenc \ +--enable-encoder=hevc_nvenc \ +--enable-libmfx \ +--enable-encoder=h264_qsv \ +--enable-encoder=hevc_qsv \ +") + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") + set(LIB_MACHINE_ARG /machine:x86) + string(APPEND OPTIONS " --arch=i686 --enable-cross-compile") + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(LIB_MACHINE_ARG /machine:x64) + string(APPEND OPTIONS " --arch=x86_64") + else() + message(FATAL_ERROR "Unsupported target architecture") + endif() +elseif(VCPKG_TARGET_IS_OSX) + string(APPEND OPTIONS "\ +--disable-autodetect \ +--enable-videotoolbox \ +--enable-encoder=h264_videotoolbox,hevc_videotoolbox \ +--enable-hwaccel=h264_videotoolbox,hevc_videotoolbox \ +") +elseif(VCPKG_TARGET_IS_IOS) + string(APPEND OPTIONS "\ +--arch=arm64 \ +--disable-autodetect \ +--disable-hwaccels \ +--disable-encoders \ +--disable-videotoolbox \ +--extra-cflags=\"-arch arm64 -mios-version-min=8.0 -fembed-bitcode\" \ +--extra-ldflags=\"-arch arm64 -mios-version-min=8.0 -fembed-bitcode\" \ +") +elseif(VCPKG_CMAKE_SYSTEM_NAME STREQUAL "Android") + string(APPEND OPTIONS "\ +--target-os=android \ +--disable-asm \ +--enable-jni \ +--enable-mediacodec \ +--disable-hwaccels \ +--enable-encoder=h264_mediacodec \ +--enable-encoder=hevc_mediacodec \ +--enable-decoder=h264_mediacodec \ +--enable-decoder=hevc_mediacodec \ +") +endif() + +if(VCPKG_TARGET_IS_OSX) + list(JOIN VCPKG_OSX_ARCHITECTURES " " OSX_ARCHS) + list(LENGTH VCPKG_OSX_ARCHITECTURES OSX_ARCH_COUNT) +endif() + +vcpkg_cmake_get_vars(cmake_vars_file) +include("${cmake_vars_file}") + +if(VCPKG_DETECTED_MSVC) + string(APPEND OPTIONS " --disable-inline-asm") # clang-cl has inline assembly but this leads to undefined symbols. + set(OPTIONS "--toolchain=msvc ${OPTIONS}") + + # This is required because ffmpeg depends upon optimizations to link correctly + string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -O2") + string(REGEX REPLACE "(^| )-RTC1( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") + string(REGEX REPLACE "(^| )-Od( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") + string(REGEX REPLACE "(^| )-Ob0( |$)" " " VCPKG_COMBINED_C_FLAGS_DEBUG "${VCPKG_COMBINED_C_FLAGS_DEBUG}") +endif() + +string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -I \"${CURRENT_INSTALLED_DIR}/include\"") +string(APPEND VCPKG_COMBINED_C_FLAGS_RELEASE " -I \"${CURRENT_INSTALLED_DIR}/include\"") + +# # Setup vcpkg toolchain +set(prog_env "") + +if(VCPKG_DETECTED_CMAKE_C_COMPILER) + get_filename_component(CC_path "${VCPKG_DETECTED_CMAKE_C_COMPILER}" DIRECTORY) + get_filename_component(CC_filename "${VCPKG_DETECTED_CMAKE_C_COMPILER}" NAME) + set(ENV{CC} "${CC_filename}") + string(APPEND OPTIONS " --cc=${CC_filename}") + + # string(APPEND OPTIONS " --host_cc=${CC_filename}") ffmpeg not yet setup for cross builds? + list(APPEND prog_env "${CC_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_CXX_COMPILER) + get_filename_component(CXX_path "${VCPKG_DETECTED_CMAKE_CXX_COMPILER}" DIRECTORY) + get_filename_component(CXX_filename "${VCPKG_DETECTED_CMAKE_CXX_COMPILER}" NAME) + set(ENV{CXX} "${CXX_filename}") + string(APPEND OPTIONS " --cxx=${CXX_filename}") + + # string(APPEND OPTIONS " --host_cxx=${CC_filename}") + list(APPEND prog_env "${CXX_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_RC_COMPILER) + get_filename_component(RC_path "${VCPKG_DETECTED_CMAKE_RC_COMPILER}" DIRECTORY) + get_filename_component(RC_filename "${VCPKG_DETECTED_CMAKE_RC_COMPILER}" NAME) + set(ENV{WINDRES} "${RC_filename}") + string(APPEND OPTIONS " --windres=${RC_filename}") + list(APPEND prog_env "${RC_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_LINKER AND VCPKG_TARGET_IS_WINDOWS AND NOT VCPKG_TARGET_IS_MINGW) + get_filename_component(LD_path "${VCPKG_DETECTED_CMAKE_LINKER}" DIRECTORY) + get_filename_component(LD_filename "${VCPKG_DETECTED_CMAKE_LINKER}" NAME) + set(ENV{LD} "${LD_filename}") + string(APPEND OPTIONS " --ld=${LD_filename}") + + # string(APPEND OPTIONS " --host_ld=${LD_filename}") + list(APPEND prog_env "${LD_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_NM) + get_filename_component(NM_path "${VCPKG_DETECTED_CMAKE_NM}" DIRECTORY) + get_filename_component(NM_filename "${VCPKG_DETECTED_CMAKE_NM}" NAME) + set(ENV{NM} "${NM_filename}") + string(APPEND OPTIONS " --nm=${NM_filename}") + list(APPEND prog_env "${NM_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_AR) + get_filename_component(AR_path "${VCPKG_DETECTED_CMAKE_AR}" DIRECTORY) + get_filename_component(AR_filename "${VCPKG_DETECTED_CMAKE_AR}" NAME) + + if(AR_filename MATCHES [[^(llvm-)?lib\.exe$]]) + set(ENV{AR} "ar-lib ${AR_filename}") + string(APPEND OPTIONS " --ar='ar-lib ${AR_filename}'") + else() + set(ENV{AR} "${AR_filename}") + string(APPEND OPTIONS " --ar='${AR_filename}'") + endif() + + list(APPEND prog_env "${AR_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_RANLIB) + get_filename_component(RANLIB_path "${VCPKG_DETECTED_CMAKE_RANLIB}" DIRECTORY) + get_filename_component(RANLIB_filename "${VCPKG_DETECTED_CMAKE_RANLIB}" NAME) + set(ENV{RANLIB} "${RANLIB_filename}") + string(APPEND OPTIONS " --ranlib=${RANLIB_filename}") + list(APPEND prog_env "${RANLIB_path}") +endif() + +if(VCPKG_DETECTED_CMAKE_STRIP) + get_filename_component(STRIP_path "${VCPKG_DETECTED_CMAKE_STRIP}" DIRECTORY) + get_filename_component(STRIP_filename "${VCPKG_DETECTED_CMAKE_STRIP}" NAME) + set(ENV{STRIP} "${STRIP_filename}") + string(APPEND OPTIONS " --strip=${STRIP_filename}") + list(APPEND prog_env "${STRIP_path}") +endif() + +list(REMOVE_DUPLICATES prog_env) +vcpkg_add_to_path(PREPEND ${prog_env}) + +# More? OBJCC BIN2C +file(REMOVE_RECURSE "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg" "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") + +set(FFMPEG_PKGCONFIG_MODULES libavutil) + +set(OPTIONS_CROSS "--enable-cross-compile") + +# ffmpeg needs --cross-prefix option to use appropriate tools for cross-compiling. +if(VCPKG_DETECTED_CMAKE_C_COMPILER MATCHES "([^\/]*-)gcc$") + string(APPEND OPTIONS_CROSS " --cross-prefix=${CMAKE_MATCH_1}") +endif() + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(BUILD_ARCH "x86_64") +else() + set(BUILD_ARCH ${VCPKG_TARGET_ARCHITECTURE}) +endif() + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm" OR VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") + if(VCPKG_TARGET_IS_WINDOWS) + vcpkg_find_acquire_program(GASPREPROCESSOR) + + foreach(GAS_PATH ${GASPREPROCESSOR}) + get_filename_component(GAS_ITEM_PATH ${GAS_PATH} DIRECTORY) + vcpkg_add_to_path("${GAS_ITEM_PATH}") + endforeach(GAS_PATH) + endif() +endif() + +set(OPTIONS_DEBUG "--disable-optimizations") +set(OPTIONS_RELEASE "--enable-optimizations") + +set(OPTIONS "${OPTIONS} ${OPTIONS_CROSS}") + +if(VCPKG_TARGET_IS_MINGW) + set(OPTIONS "${OPTIONS} --extra_cflags=-D_WIN32_WINNT=0x0601") +elseif(VCPKG_TARGET_IS_WINDOWS) + set(OPTIONS "${OPTIONS} --extra-cflags=-DHAVE_UNISTD_H=0") +endif() + +vcpkg_find_acquire_program(PKGCONFIG) +set(OPTIONS "${OPTIONS} --pkg-config=${PKGCONFIG}") + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + set(OPTIONS "${OPTIONS} --pkg-config-flags=--static") +endif() + +message(STATUS "Building Options: ${OPTIONS}") + +# Release build +if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "release") + if(VCPKG_DETECTED_MSVC) + set(OPTIONS_RELEASE "${OPTIONS_RELEASE} --extra-ldflags=-libpath:\"${CURRENT_INSTALLED_DIR}/lib\"") + else() + set(OPTIONS_RELEASE "${OPTIONS_RELEASE} --extra-ldflags=-L\"${CURRENT_INSTALLED_DIR}/lib\"") + endif() + + message(STATUS "Building Release Options: ${OPTIONS_RELEASE}") + set(ENV{PKG_CONFIG_PATH} "${CURRENT_INSTALLED_DIR}/lib/pkgconfig") + message(STATUS "Building ${PORT} for Release") + file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") + + # We use response files here as the only known way to handle spaces in paths + set(crsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/cflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_C_FLAGS_RELEASE_SANITIZED "${VCPKG_COMBINED_C_FLAGS_RELEASE}") + file(WRITE "${crsp}" "${VCPKG_COMBINED_C_FLAGS_RELEASE_SANITIZED}") + set(ldrsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/ldflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE_SANITIZED "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE}") + file(WRITE "${ldrsp}" "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_RELEASE_SANITIZED}") + set(ENV{CFLAGS} "@${crsp}") + + # All tools except the msvc arm{,64} assembler accept @... as response file syntax. + # For that assembler, there is no known way to pass in flags. We must hope that not passing flags will work acceptably. + if(NOT VCPKG_DETECTED_MSVC OR NOT VCPKG_TARGET_ARCHITECTURE MATCHES "^arm") + set(ENV{ASFLAGS} "@${crsp}") + endif() + + set(ENV{LDFLAGS} "@${ldrsp}") + set(ENV{ARFLAGS} "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_RELEASE}") + + set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel") + set(CONFIGURE_OPTIONS "${OPTIONS} ${OPTIONS_RELEASE}") + set(INST_PREFIX "${CURRENT_PACKAGES_DIR}") + + configure_file("${CMAKE_CURRENT_LIST_DIR}/build.sh.in" "${BUILD_DIR}/build.sh" @ONLY) + + z_vcpkg_setup_pkgconfig_path(CONFIG RELEASE) + + vcpkg_execute_required_process( + COMMAND "${SHELL}" ./build.sh + WORKING_DIRECTORY "${BUILD_DIR}" + LOGNAME "build-${TARGET_TRIPLET}-rel" + SAVE_LOG_FILES ffbuild/config.log + ) + + z_vcpkg_restore_pkgconfig_path() +endif() + +# Debug build +if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + if(VCPKG_DETECTED_MSVC) + set(OPTIONS_DEBUG "${OPTIONS_DEBUG} --extra-ldflags=-libpath:\"${CURRENT_INSTALLED_DIR}/debug/lib\"") + else() + set(OPTIONS_DEBUG "${OPTIONS_DEBUG} --extra-ldflags=-L\"${CURRENT_INSTALLED_DIR}/debug/lib\"") + endif() + + message(STATUS "Building Debug Options: ${OPTIONS_DEBUG}") + set(ENV{LDFLAGS} "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG}") + set(ENV{PKG_CONFIG_PATH} "${CURRENT_INSTALLED_DIR}/debug/lib/pkgconfig") + message(STATUS "Building ${PORT} for Debug") + file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg") + set(crsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/cflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_C_FLAGS_DEBUG_SANITIZED "${VCPKG_COMBINED_C_FLAGS_DEBUG}") + file(WRITE "${crsp}" "${VCPKG_COMBINED_C_FLAGS_DEBUG_SANITIZED}") + set(ldrsp "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg/ldflags.rsp") + string(REGEX REPLACE "-arch [A-Za-z0-9_]+" "" VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG_SANITIZED "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG}") + file(WRITE "${ldrsp}" "${VCPKG_COMBINED_SHARED_LINKER_FLAGS_DEBUG_SANITIZED}") + set(ENV{CFLAGS} "@${crsp}") + + if(NOT VCPKG_DETECTED_MSVC OR NOT VCPKG_TARGET_ARCHITECTURE MATCHES "^arm") + set(ENV{ASFLAGS} "@${crsp}") + endif() + + set(ENV{LDFLAGS} "@${ldrsp}") + set(ENV{ARFLAGS} "${VCPKG_COMBINED_STATIC_LINKER_FLAGS_DEBUG}") + + set(BUILD_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-dbg") + set(CONFIGURE_OPTIONS "${OPTIONS} ${OPTIONS_DEBUG}") + set(INST_PREFIX "${CURRENT_PACKAGES_DIR}/debug") + + configure_file("${CMAKE_CURRENT_LIST_DIR}/build.sh.in" "${BUILD_DIR}/build.sh" @ONLY) + + z_vcpkg_setup_pkgconfig_path(CONFIG DEBUG) + + vcpkg_execute_required_process( + COMMAND "${SHELL}" ./build.sh + WORKING_DIRECTORY "${BUILD_DIR}" + LOGNAME "build-${TARGET_TRIPLET}-dbg" + SAVE_LOG_FILES ffbuild/config.log + ) + + z_vcpkg_restore_pkgconfig_path() +endif() + +if(VCPKG_TARGET_IS_WINDOWS) + file(GLOB DEF_FILES "${CURRENT_PACKAGES_DIR}/lib/*.def" "${CURRENT_PACKAGES_DIR}/debug/lib/*.def") + + if(NOT VCPKG_TARGET_IS_MINGW) + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") + set(LIB_MACHINE_ARG /machine:ARM) + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") + set(LIB_MACHINE_ARG /machine:ARM64) + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") + set(LIB_MACHINE_ARG /machine:x86) + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(LIB_MACHINE_ARG /machine:x64) + else() + message(FATAL_ERROR "Unsupported target architecture") + endif() + + foreach(DEF_FILE ${DEF_FILES}) + get_filename_component(DEF_FILE_DIR "${DEF_FILE}" DIRECTORY) + get_filename_component(DEF_FILE_NAME "${DEF_FILE}" NAME) + string(REGEX REPLACE "-[0-9]*\\.def" "${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}" OUT_FILE_NAME "${DEF_FILE_NAME}") + file(TO_NATIVE_PATH "${DEF_FILE}" DEF_FILE_NATIVE) + file(TO_NATIVE_PATH "${DEF_FILE_DIR}/${OUT_FILE_NAME}" OUT_FILE_NATIVE) + message(STATUS "Generating ${OUT_FILE_NATIVE}") + vcpkg_execute_required_process( + COMMAND lib.exe "/def:${DEF_FILE_NATIVE}" "/out:${OUT_FILE_NATIVE}" ${LIB_MACHINE_ARG} + WORKING_DIRECTORY "${CURRENT_PACKAGES_DIR}" + LOGNAME "libconvert-${TARGET_TRIPLET}" + ) + endforeach() + endif() + + file(GLOB EXP_FILES "${CURRENT_PACKAGES_DIR}/lib/*.exp" "${CURRENT_PACKAGES_DIR}/debug/lib/*.exp") + file(GLOB LIB_FILES "${CURRENT_PACKAGES_DIR}/bin/*${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}" "${CURRENT_PACKAGES_DIR}/debug/bin/*${VCPKG_TARGET_STATIC_LIBRARY_SUFFIX}") + + if(VCPKG_TARGET_IS_MINGW) + file(GLOB LIB_FILES_2 "${CURRENT_PACKAGES_DIR}/bin/*.lib" "${CURRENT_PACKAGES_DIR}/debug/bin/*.lib") + endif() + + set(files_to_remove ${EXP_FILES} ${LIB_FILES} ${LIB_FILES_2} ${DEF_FILES}) + + if(files_to_remove) + file(REMOVE ${files_to_remove}) + endif() +endif() + +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include" "${CURRENT_PACKAGES_DIR}/debug/share") + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin") +endif() + +vcpkg_copy_pdbs() + +if(VCPKG_TARGET_IS_WINDOWS) + set(_dirs "/") + + if(NOT VCPKG_BUILD_TYPE OR VCPKG_BUILD_TYPE STREQUAL "debug") + list(APPEND _dirs "/debug/") + endif() + + foreach(_debug IN LISTS _dirs) + foreach(PKGCONFIG_MODULE IN LISTS FFMPEG_PKGCONFIG_MODULES) + set(PKGCONFIG_FILE "${CURRENT_PACKAGES_DIR}${_debug}lib/pkgconfig/${PKGCONFIG_MODULE}.pc") + + # remove redundant cygwin style -libpath entries + execute_process( + COMMAND "${MSYS_ROOT}/usr/bin/cygpath.exe" -u "${CURRENT_INSTALLED_DIR}" + OUTPUT_VARIABLE CYG_INSTALLED_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + vcpkg_replace_string("${PKGCONFIG_FILE}" "-libpath:${CYG_INSTALLED_DIR}${_debug}lib/pkgconfig/../../lib " "") + + # transform libdir, includedir, and prefix paths from cygwin style to windows style + file(READ "${PKGCONFIG_FILE}" PKGCONFIG_CONTENT) + + foreach(PATH_NAME prefix libdir includedir) + string(REGEX MATCH "${PATH_NAME}=[^\n]*" PATH_VALUE "${PKGCONFIG_CONTENT}") + string(REPLACE "${PATH_NAME}=" "" PATH_VALUE "${PATH_VALUE}") + + if(NOT PATH_VALUE) + message(FATAL_ERROR "failed to find pkgconfig variable ${PATH_NAME}") + endif() + + execute_process( + COMMAND "${MSYS_ROOT}/usr/bin/cygpath.exe" -w "${PATH_VALUE}" + OUTPUT_VARIABLE FIXED_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + file(TO_CMAKE_PATH "${FIXED_PATH}" FIXED_PATH) + vcpkg_replace_string("${PKGCONFIG_FILE}" "${PATH_NAME}=${PATH_VALUE}" "${PATH_NAME}=${FIXED_PATH}") + endforeach() + + # list libraries with -l flag (so pkgconf knows they are libraries and not just linker flags) + foreach(LIBS_ENTRY Libs Libs.private) + string(REGEX MATCH "${LIBS_ENTRY}: [^\n]*" LIBS_VALUE "${PKGCONFIG_CONTENT}") + + if(NOT LIBS_VALUE) + message(FATAL_ERROR "failed to find pkgconfig entry ${LIBS_ENTRY}") + endif() + + string(REPLACE "${LIBS_ENTRY}: " "" LIBS_VALUE "${LIBS_VALUE}") + + if(LIBS_VALUE) + set(LIBS_VALUE_OLD "${LIBS_VALUE}") + string(REGEX REPLACE "([^ ]+)[.]lib" "-l\\1" LIBS_VALUE "${LIBS_VALUE}") + set(LIBS_VALUE_NEW "${LIBS_VALUE}") + vcpkg_replace_string("${PKGCONFIG_FILE}" "${LIBS_ENTRY}: ${LIBS_VALUE_OLD}" "${LIBS_ENTRY}: ${LIBS_VALUE_NEW}") + endif() + endforeach() + endforeach() + endforeach() +endif() + +vcpkg_fixup_pkgconfig() + +# Handle dependencies +x_vcpkg_pkgconfig_get_modules(PREFIX FFMPEG_PKGCONFIG MODULES ${FFMPEG_PKGCONFIG_MODULES} LIBS) + +function(append_dependencies_from_libs out) + cmake_parse_arguments(PARSE_ARGV 1 "arg" "" "LIBS" "") + string(REGEX REPLACE "[ ]+" ";" contents "${arg_LIBS}") + list(FILTER contents EXCLUDE REGEX "^-F.+") + list(FILTER contents EXCLUDE REGEX "^-framework$") + list(FILTER contents EXCLUDE REGEX "^-L.+") + list(FILTER contents EXCLUDE REGEX "^-libpath:.+") + list(TRANSFORM contents REPLACE "^-Wl,-framework," "-l") + list(FILTER contents EXCLUDE REGEX "^-Wl,.+") + list(TRANSFORM contents REPLACE "^-l" "") + list(FILTER contents EXCLUDE REGEX "^avutil$") + list(FILTER contents EXCLUDE REGEX "^avcodec$") + list(FILTER contents EXCLUDE REGEX "^avdevice$") + list(FILTER contents EXCLUDE REGEX "^avfilter$") + list(FILTER contents EXCLUDE REGEX "^avformat$") + list(FILTER contents EXCLUDE REGEX "^postproc$") + list(FILTER contents EXCLUDE REGEX "^swresample$") + list(FILTER contents EXCLUDE REGEX "^swscale$") + + if(VCPKG_TARGET_IS_WINDOWS) + list(TRANSFORM contents TOLOWER) + endif() + + if(contents) + list(APPEND "${out}" "${contents}") + set("${out}" "${${out}}" PARENT_SCOPE) + endif() +endfunction() + +append_dependencies_from_libs(FFMPEG_DEPENDENCIES_RELEASE LIBS "${FFMPEG_PKGCONFIG_LIBS_RELEASE}") +append_dependencies_from_libs(FFMPEG_DEPENDENCIES_DEBUG LIBS "${FFMPEG_PKGCONFIG_LIBS_DEBUG}") + +# must remove duplicates from the front to respect link order so reverse first +list(REVERSE FFMPEG_DEPENDENCIES_RELEASE) +list(REVERSE FFMPEG_DEPENDENCIES_DEBUG) +list(REMOVE_DUPLICATES FFMPEG_DEPENDENCIES_RELEASE) +list(REMOVE_DUPLICATES FFMPEG_DEPENDENCIES_DEBUG) +list(REVERSE FFMPEG_DEPENDENCIES_RELEASE) +list(REVERSE FFMPEG_DEPENDENCIES_DEBUG) + +message(STATUS "Dependencies (release): ${FFMPEG_DEPENDENCIES_RELEASE}") +message(STATUS "Dependencies (debug): ${FFMPEG_DEPENDENCIES_DEBUG}") + +# Handle version strings +function(extract_regex_from_file out) + cmake_parse_arguments(PARSE_ARGV 1 "arg" "MAJOR" "FILE_WITHOUT_EXTENSION;REGEX" "") + file(READ "${arg_FILE_WITHOUT_EXTENSION}.h" contents) + + if(contents MATCHES "${arg_REGEX}") + if(NOT CMAKE_MATCH_COUNT EQUAL 1) + message(FATAL_ERROR "Could not identify match group in regular expression \"${arg_REGEX}\"") + endif() + else() + if(arg_MAJOR) + file(READ "${arg_FILE_WITHOUT_EXTENSION}_major.h" contents) + + if(contents MATCHES "${arg_REGEX}") + if(NOT CMAKE_MATCH_COUNT EQUAL 1) + message(FATAL_ERROR "Could not identify match group in regular expression \"${arg_REGEX}\"") + endif() + else() + message(WARNING "Could not find line matching \"${arg_REGEX}\" in file \"${arg_FILE_WITHOUT_EXTENSION}_major.h\"") + endif() + else() + message(WARNING "Could not find line matching \"${arg_REGEX}\" in file \"${arg_FILE_WITHOUT_EXTENSION}.h\"") + endif() + endif() + + set("${out}" "${CMAKE_MATCH_1}" PARENT_SCOPE) +endfunction() + +function(extract_version_from_component out) + cmake_parse_arguments(PARSE_ARGV 1 "arg" "" "COMPONENT" "") + string(TOLOWER "${arg_COMPONENT}" component_lower) + string(TOUPPER "${arg_COMPONENT}" component_upper) + extract_regex_from_file(major_version + FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" + MAJOR + REGEX "#define ${component_upper}_VERSION_MAJOR[ ]+([0-9]+)" + ) + extract_regex_from_file(minor_version + FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" + REGEX "#define ${component_upper}_VERSION_MINOR[ ]+([0-9]+)" + ) + extract_regex_from_file(micro_version + FILE_WITHOUT_EXTENSION "${SOURCE_PATH}/${component_lower}/version" + REGEX "#define ${component_upper}_VERSION_MICRO[ ]+([0-9]+)" + ) + set("${out}" "${major_version}.${minor_version}.${micro_version}" PARENT_SCOPE) +endfunction() + +extract_regex_from_file(FFMPEG_VERSION + FILE_WITHOUT_EXTENSION "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/libavutil/ffversion" + REGEX "#define FFMPEG_VERSION[ ]+\"(.+)\"" +) + +extract_version_from_component(LIBAVUTIL_VERSION + COMPONENT libavutil) +extract_version_from_component(LIBAVCODEC_VERSION + COMPONENT libavcodec) +extract_version_from_component(LIBAVDEVICE_VERSION + COMPONENT libavdevice) +extract_version_from_component(LIBAVFILTER_VERSION + COMPONENT libavfilter) +extract_version_from_component(LIBAVFORMAT_VERSION + COMPONENT libavformat) +extract_version_from_component(LIBSWRESAMPLE_VERSION + COMPONENT libswresample) +extract_version_from_component(LIBSWSCALE_VERSION + COMPONENT libswscale) + +# Handle copyright +file(STRINGS "${CURRENT_BUILDTREES_DIR}/build-${TARGET_TRIPLET}-rel-out.log" LICENSE_STRING REGEX "License: .*" LIMIT_COUNT 1) + +if(LICENSE_STRING STREQUAL "License: LGPL version 2.1 or later") + set(LICENSE_FILE "COPYING.LGPLv2.1") +elseif(LICENSE_STRING STREQUAL "License: LGPL version 3 or later") + set(LICENSE_FILE "COPYING.LGPLv3") +elseif(LICENSE_STRING STREQUAL "License: GPL version 2 or later") + set(LICENSE_FILE "COPYING.GPLv2") +elseif(LICENSE_STRING STREQUAL "License: GPL version 3 or later") + set(LICENSE_FILE "COPYING.GPLv3") +elseif(LICENSE_STRING STREQUAL "License: nonfree and unredistributable") + set(LICENSE_FILE "COPYING.NONFREE") + file(WRITE "${SOURCE_PATH}/${LICENSE_FILE}" "${LICENSE_STRING}") +else() + message(FATAL_ERROR "Failed to identify license (${LICENSE_STRING})") +endif() + +configure_file("${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" "${CURRENT_PACKAGES_DIR}/share/${PORT}/vcpkg-cmake-wrapper.cmake" @ONLY) +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/${LICENSE_FILE}") diff --git a/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake b/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake new file mode 100644 index 00000000000..233d61343a3 --- /dev/null +++ b/res/vcpkg/ffmpeg/vcpkg-cmake-wrapper.cmake @@ -0,0 +1,47 @@ +set(FFMPEG_PREV_MODULE_PATH ${CMAKE_MODULE_PATH}) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +include(SelectLibraryConfigurations) + +cmake_policy(SET CMP0012 NEW) + +set(vcpkg_no_avcodec_target ON) +set(vcpkg_no_avformat_target ON) +set(vcpkg_no_avutil_target ON) +if(TARGET FFmpeg::avcodec) + set(vcpkg_no_avcodec_target OFF) +endif() +if(TARGET FFmpeg::avformat) + set(vcpkg_no_avformat_target OFF) +endif() +if(TARGET FFmpeg::avutil) + set(vcpkg_no_avutil_target OFF) +endif() + +_find_package(${ARGS}) + +if(WIN32) + set(PKG_CONFIG_EXECUTABLE "${CMAKE_CURRENT_LIST_DIR}/../../../@_HOST_TRIPLET@/tools/pkgconf/pkgconf.exe" CACHE STRING "" FORCE) +endif() + +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) # Required for CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.1 which otherwise ignores CMAKE_PREFIX_PATH + +if(@WITH_MFX@) + find_package(PkgConfig ) + pkg_check_modules(libmfx IMPORTED_TARGET libmfx) + list(APPEND FFMPEG_LIBRARIES PkgConfig::libmfx) + if(vcpkg_no_avcodec_target AND TARGET FFmpeg::avcodec) + target_link_libraries(FFmpeg::avcodec INTERFACE PkgConfig::libmfx) + endif() + if(vcpkg_no_avutil_target AND TARGET FFmpeg::avutil) + target_link_libraries(FFmpeg::avutil INTERFACE PkgConfig::libmfx) + endif() +endif() + +set(FFMPEG_LIBRARY ${FFMPEG_LIBRARIES}) + +set(CMAKE_MODULE_PATH ${FFMPEG_PREV_MODULE_PATH}) + +unset(vcpkg_no_avformat_target) +unset(vcpkg_no_avcodec_target) +unset(vcpkg_no_avutil_target) diff --git a/res/vcpkg/ffmpeg/vcpkg.json b/res/vcpkg/ffmpeg/vcpkg.json new file mode 100644 index 00000000000..61ff2c8b549 --- /dev/null +++ b/res/vcpkg/ffmpeg/vcpkg.json @@ -0,0 +1,44 @@ +{ + "name": "ffmpeg", + "version": "7.0.1", + "port-version": 0, + "description": [ + "a library to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created.", + "FFmpeg is the leading multimedia framework, able to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created. It supports the most obscure ancient formats up to the cutting edge. No matter if they were designed by some standards committee, the community or a corporation. It is also highly portable: FFmpeg compiles, runs, and passes our testing infrastructure FATE across Linux, Mac OS X, Microsoft Windows, the BSDs, Solaris, etc. under a wide variety of build environments, machine architectures, and configurations." + ], + "homepage": "https://ffmpeg.org", + "license": null, + "dependencies": [ + { + "name": "vcpkg-cmake-get-vars", + "host": true + }, + { + "name": "vcpkg-pkgconfig-get-modules", + "host": true + } + ], + "default-features": [ + ], + "features": { + "amf": { + "description": "AMD AMF codec support", + "dependencies": [ + "amd-amf" + ] + }, + "nvcodec": { + "description": "Nvidia video decoding/encoding acceleration", + "supports": "linux | (!osx & !uwp & !(arm64 & windows))", + "dependencies": [ + "ffnvcodec" + ] + }, + "qsv": { + "description": "Intel QSV Codec", + "dependencies": [ + "mfx-dispatch" + ] + } + } +} \ No newline at end of file diff --git a/res/vcpkg/libyuv/fix-cmakelists.patch b/res/vcpkg/libyuv/fix-cmakelists.patch new file mode 100644 index 00000000000..43520573d88 --- /dev/null +++ b/res/vcpkg/libyuv/fix-cmakelists.patch @@ -0,0 +1,80 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index bc641685..42e72a39 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,19 +1,22 @@ ++CMAKE_MINIMUM_REQUIRED( VERSION 3.12 ) ++ + # CMakeLists for libyuv + # Originally created for "roxlu build system" to compile libyuv on windows + # Run with -DTEST=ON to build unit tests + + PROJECT ( YUV C CXX ) # "C" is required even for C++ projects +-CMAKE_MINIMUM_REQUIRED( VERSION 2.8.12 ) + OPTION( TEST "Built unit tests" OFF ) + ++SET( CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON ) ++ + SET ( ly_base_dir ${PROJECT_SOURCE_DIR} ) + SET ( ly_src_dir ${ly_base_dir}/source ) + SET ( ly_inc_dir ${ly_base_dir}/include ) + SET ( ly_tst_dir ${ly_base_dir}/unit_test ) + SET ( ly_lib_name yuv ) + SET ( ly_lib_static ${ly_lib_name} ) +-SET ( ly_lib_shared ${ly_lib_name}_shared ) + ++FILE ( GLOB_RECURSE ly_include_files ${ly_inc_dir}/libyuv/*.h ) + FILE ( GLOB_RECURSE ly_source_files ${ly_src_dir}/*.cc ) + LIST ( SORT ly_source_files ) + +@@ -28,27 +31,20 @@ endif() + + # this creates the static library (.a) + ADD_LIBRARY ( ${ly_lib_static} STATIC ${ly_source_files} ) +- +-# this creates the shared library (.so) +-ADD_LIBRARY ( ${ly_lib_shared} SHARED ${ly_source_files} ) +-SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES OUTPUT_NAME "${ly_lib_name}" ) +-SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES PREFIX "lib" ) +-if(WIN32) +- SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES IMPORT_PREFIX "lib" ) +-endif() ++SET_TARGET_PROPERTIES ( ${ly_lib_static} PROPERTIES PUBLIC_HEADER include/libyuv.h ) + + # this creates the conversion tool + ADD_EXECUTABLE ( yuvconvert ${ly_base_dir}/util/yuvconvert.cc ) +-TARGET_LINK_LIBRARIES ( yuvconvert ${ly_lib_static} ) ++TARGET_LINK_LIBRARIES ( yuvconvert ${ly_lib_static} ) + + # this creates the yuvconstants tool +-ADD_EXECUTABLE ( yuvconstants ${ly_base_dir}/util/yuvconstants.c ) +-TARGET_LINK_LIBRARIES ( yuvconstants ${ly_lib_static} ) ++ADD_EXECUTABLE ( yuvconstants ${ly_base_dir}/util/yuvconstants.c ) ++TARGET_LINK_LIBRARIES ( yuvconstants ${ly_lib_static} ) + + find_package ( JPEG ) + if (JPEG_FOUND) +- include_directories( ${JPEG_INCLUDE_DIR} ) +- target_link_libraries( ${ly_lib_shared} ${JPEG_LIBRARY} ) ++ include_directories( ${JPEG_INCLUDE_DIR}) ++ target_link_libraries(${ly_lib_static} PUBLIC ${JPEG_LIBRARY}) + add_definitions( -DHAVE_JPEG ) + endif() + +@@ -89,12 +85,11 @@ if(TEST) + endif() + endif() + +- + # install the conversion tool, .so, .a, and all the header files +-INSTALL ( PROGRAMS ${CMAKE_BINARY_DIR}/yuvconvert DESTINATION bin ) +-INSTALL ( TARGETS ${ly_lib_static} DESTINATION lib ) +-INSTALL ( TARGETS ${ly_lib_shared} LIBRARY DESTINATION lib RUNTIME DESTINATION bin ) +-INSTALL ( DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include ) ++INSTALL ( TARGETS yuvconvert DESTINATION tools ) ++INSTALL ( FILES ${ly_include_files} DESTINATION include/libyuv ) ++INSTALL ( TARGETS ${ly_lib_static} EXPORT libyuv-targets DESTINATION lib INCLUDES DESTINATION include PUBLIC_HEADER DESTINATION include ) ++INSTALL( EXPORT libyuv-targets DESTINATION share/cmake/libyuv/ EXPORT_LINK_INTERFACE_LIBRARIES ) + + # create the .deb and .rpm packages using cpack + INCLUDE ( CM_linux_packages.cmake ) diff --git a/res/vcpkg/libyuv/libyuv-config.cmake b/res/vcpkg/libyuv/libyuv-config.cmake new file mode 100644 index 00000000000..1e68c154066 --- /dev/null +++ b/res/vcpkg/libyuv/libyuv-config.cmake @@ -0,0 +1,5 @@ +include(CMakeFindDependencyMacro) +find_dependency(JPEG) + +set(libyuv_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/../../include") +include("${CMAKE_CURRENT_LIST_DIR}/libyuv-targets.cmake") diff --git a/res/vcpkg/libyuv/portfile.cmake b/res/vcpkg/libyuv/portfile.cmake new file mode 100644 index 00000000000..7ff9b07040c --- /dev/null +++ b/res/vcpkg/libyuv/portfile.cmake @@ -0,0 +1,81 @@ +vcpkg_check_linkage(ONLY_STATIC_LIBRARY) + +vcpkg_from_git( + OUT_SOURCE_PATH SOURCE_PATH + URL https://chromium.googlesource.com/libyuv/libyuv + REF 0faf8dd0e004520a61a603a4d2996d5ecc80dc3f + # Check https://chromium.googlesource.com/libyuv/libyuv/+/refs/heads/main/include/libyuv/version.h for a version! + PATCHES + fix-cmakelists.patch +) + +vcpkg_cmake_get_vars(cmake_vars_file) +include("${cmake_vars_file}") +if (VCPKG_DETECTED_CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND NOT VCPKG_TARGET_IS_UWP) + # Most of libyuv accelerated features need to be compiled by clang/gcc, so force use clang-cl, otherwise the performance is too poor. + # Manually build the port with clang-cl when using MSVC as compiler + + message(STATUS "Set compiler to clang-cl when using MSVC") + + # https://github.com/microsoft/vcpkg/pull/10398 + set(VCPKG_POLICY_SKIP_ARCHITECTURE_CHECK enabled) + + vcpkg_find_acquire_program(CLANG) + if (CLANG MATCHES "-NOTFOUND") + message(FATAL_ERROR "Clang is required.") + endif () + get_filename_component(CLANG "${CLANG}" DIRECTORY) + + if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm") + set(CLANG_TARGET "arm") + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm64") + set(CLANG_TARGET "aarch64") + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86") + set(CLANG_TARGET "i686") + elseif(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64") + set(CLANG_TARGET "x86_64") + else() + message(FATAL_ERROR "Unsupported target architecture") + endif() + + set(CLANG_TARGET "${CLANG_TARGET}-pc-windows-msvc") + + message(STATUS "Using clang target ${CLANG_TARGET}") + string(APPEND VCPKG_DETECTED_CMAKE_CXX_FLAGS --target=${CLANG_TARGET}) + string(APPEND VCPKG_DETECTED_CMAKE_C_FLAGS --target=${CLANG_TARGET}) + + set(BUILD_OPTIONS + -DCMAKE_CXX_COMPILER=${CLANG}/clang-cl.exe + -DCMAKE_C_COMPILER=${CLANG}/clang-cl.exe + -DCMAKE_CXX_FLAGS=${VCPKG_DETECTED_CMAKE_CXX_FLAGS} + -DCMAKE_C_FLAGS=${VCPKG_DETECTED_CMAKE_C_FLAGS}) +endif () + +vcpkg_cmake_configure( + SOURCE_PATH ${SOURCE_PATH} + DISABLE_PARALLEL_CONFIGURE + OPTIONS + ${BUILD_OPTIONS} + OPTIONS_DEBUG + -DCMAKE_DEBUG_POSTFIX=d +) + +vcpkg_cmake_install() +vcpkg_copy_pdbs() + +vcpkg_cmake_config_fixup(CONFIG_PATH share/cmake/libyuv) + +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include) +file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/share) + +configure_file(${CMAKE_CURRENT_LIST_DIR}/libyuv-config.cmake ${CURRENT_PACKAGES_DIR}/share/${PORT} COPYONLY) +file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright) + +vcpkg_cmake_get_vars(cmake_vars_file) +include("${cmake_vars_file}") +if (VCPKG_DETECTED_CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + message(WARNING "Use MSVC to compile libyuv results in a very slow library. (https://github.com/microsoft/vcpkg/issues/28446)") + file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage-msvc" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME "usage") +else () + file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +endif () diff --git a/res/vcpkg/libyuv/usage b/res/vcpkg/libyuv/usage new file mode 100644 index 00000000000..37c206328cc --- /dev/null +++ b/res/vcpkg/libyuv/usage @@ -0,0 +1,4 @@ +libyuv provides CMake targets: + + find_package(libyuv CONFIG REQUIRED) + target_link_libraries(main PRIVATE yuv) \ No newline at end of file diff --git a/res/vcpkg/libyuv/usage-msvc b/res/vcpkg/libyuv/usage-msvc new file mode 100644 index 00000000000..4c90c69855a --- /dev/null +++ b/res/vcpkg/libyuv/usage-msvc @@ -0,0 +1,9 @@ +libyuv provides CMake targets: + + find_package(libyuv CONFIG REQUIRED) + target_link_libraries(main PRIVATE yuv) + + # WARNING + # You are using MSVC to compile libyuv, which results in a very slow library. + # MSVC won't compile any of the acceleration codes. + # See workarounds: https://github.com/microsoft/vcpkg/issues/28446 diff --git a/res/vcpkg/libyuv/vcpkg.json b/res/vcpkg/libyuv/vcpkg.json new file mode 100644 index 00000000000..46d61c0c2ee --- /dev/null +++ b/res/vcpkg/libyuv/vcpkg.json @@ -0,0 +1,22 @@ +{ + "name": "libyuv", + "version": "1857", + "description": "libyuv is an open source project that includes YUV scaling and conversion functionality", + "homepage": "https://chromium.googlesource.com/libyuv/libyuv", + "license": null, + "dependencies": [ + "libjpeg-turbo", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + }, + { + "name": "vcpkg-cmake-get-vars", + "host": true + } + ] +} diff --git a/vcpkg.json b/vcpkg.json index 940783f2d8b..f1d7036eb5f 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -51,13 +51,44 @@ { "name": "libyuv", "host": false + }, + { + "name": "ffmpeg", + "host": true, + "features": [ + { + "name": "amf", + "platform": "((windows | linux) & static)" + }, + { + "name": "nvcodec", + "platform": "((windows | linux) & static)" + }, + { + "name": "qsv", + "platform": "(windows & static)" + } + ], + "platform": "((windows | (linux & !arm32) | osx) & static)" + }, + { + "name": "ffmpeg", + "host": false, + "platform": "((android | ios | (linux & arm32)) & static)" } ], "vcpkg-configuration": { "default-registry": { "kind": "builtin", - "baseline": "" + "baseline": "f7423ee180c4b7f40d43402c2feb3859161ef625" }, - "overlay-ports": [ "./res/vcpkg" ] - } -} + "overlay-ports": [ + "./res/vcpkg" + ] + }, + "overrides": [ + { "name": "ffnvcodec", "version": "11.1.5.2" }, + { "name": "amd-amf", "version": "1.4.29" }, + { "name": "mfx-dispatch", "version": "1.35.1" } + ] +} \ No newline at end of file From 2de81045ea45222ef7626caa52449594e2457f27 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Wed, 24 Jul 2024 03:41:25 +0200 Subject: [PATCH 317/335] Update de.rs (#8801) --- src/lang/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 546eb704765..4ff2023281d 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -629,6 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1. Öffnen Sie einen Chat mit @BotFather.\n2. Senden Sie den Befehl \"/newbot\". Sie erhalten ein Token, nachdem Sie diesen Schritt abgeschlossen haben.\n3. Starten Sie einen Chat mit Ihrem neu erstellten Bot. Senden Sie eine Nachricht, die mit einem Schrägstrich (\"/\") beginnt, z. B. \"/hello\", um ihn zu aktivieren.\n"), ("cancel-2fa-confirm-tip", "Sind Sie sicher, dass Sie 2FA abbrechen möchten?"), ("cancel-bot-confirm-tip", "Sind Sie sicher, dass Sie Telegram-Bot abbrechen möchten?"), - ("About RustDesk", ""), + ("About RustDesk", "Über RustDesk"), ].iter().cloned().collect(); } From b26acde4508787669232907d4040ce650ceeeefe Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Wed, 24 Jul 2024 03:41:43 +0200 Subject: [PATCH 318/335] Update Italian language (#8800) --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index b4510222215..81a7d408073 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -629,6 +629,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enable-bot-desc", "1. apri una chat con @BotFather.\n2. Invia il comando \"/newbot\", dopo aver completato questo passaggio riceverai un token.\n3. Avvia una chat con il tuo bot appena creato. Per attivarlo Invia un messaggio che inizia con una barra (\"/\") tipo \"/hello\".\n"), ("cancel-2fa-confirm-tip", "Sei sicuro di voler annullare 2FA?"), ("cancel-bot-confirm-tip", "Sei sicuro di voler annulare Telegram?"), - ("About RustDesk", ""), + ("About RustDesk", "Info su RustDesk"), ].iter().cloned().collect(); } From 51a60a7eed62e47b71b4a69548fffea062440ca9 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Wed, 24 Jul 2024 02:16:25 +0000 Subject: [PATCH 319/335] Fix arm32 sciter build (#8803) Signed-off-by: Vasyl Gello --- .github/workflows/flutter-build.yml | 39 ++++++++--------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index c4385b70609..3b67d1ceea3 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1314,22 +1314,6 @@ jobs: core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y \ - cmake \ - curl \ - g++ \ - gcc \ - git \ - nasm \ - ninja-build \ - pkg-config \ - unzip \ - xz-utils \ - zip - - name: Checkout source code uses: actions/checkout@v3 @@ -1350,17 +1334,6 @@ jobs: RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}') echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV - - name: Setup vcpkg with Github Actions binary cache - uses: lukka/run-vcpkg@v11 - with: - vcpkgDirectory: /opt/artifacts/vcpkg - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Override Linux compiler detection in vcpkg - run: | - cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake - shell: bash - - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk sciter binary for ${{ matrix.job.arch }} id: vcpkg @@ -1409,7 +1382,8 @@ jobs: rpm \ unzip \ wget \ - xz-utils + xz-utils \ + zip # install newer nasm for aom wget --output-document nasm.deb "http://ftp.us.debian.org/debian/pool/main/n/nasm/nasm_2.14-1_${{ matrix.job.deb_arch }}.deb" dpkg -i nasm.deb @@ -1421,6 +1395,15 @@ jobs: update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1 # vcpkg export VCPKG_ROOT=/opt/artifacts/vcpkg + pushd /opt/artifacts + git clone https://github.com/microsoft/vcpkg + pushd vcpkg + git reset --hard ${{ env.VCPKG_COMMIT_ID }} + sh bootstrap-vcpkg.sh -disableMetrics + popd + popd + # override Linux compiler detection in vcpkg + cp $PWD/res/vcpkg/linux.cmake $VCPKG_ROOT/scripts/toolchains/linux.cmake $VCPKG_ROOT/vcpkg install --triplet ${{ matrix.job.vcpkg-triplet }} --x-install-root="$VCPKG_ROOT/installed" # rust pushd /opt From 8a4a2b573218388906991f085248f155b8e14dd7 Mon Sep 17 00:00:00 2001 From: Vasyl Gello Date: Wed, 24 Jul 2024 03:23:49 +0000 Subject: [PATCH 320/335] Remove existing /opt/artifacts/vcpkg in sciter builds (#8804) Signed-off-by: Vasyl Gello --- .github/workflows/flutter-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 3b67d1ceea3..2669f06d7a4 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1396,6 +1396,7 @@ jobs: # vcpkg export VCPKG_ROOT=/opt/artifacts/vcpkg pushd /opt/artifacts + rm -rf vcpkg git clone https://github.com/microsoft/vcpkg pushd vcpkg git reset --hard ${{ env.VCPKG_COMMIT_ID }} From 57d1b1ecc40e3b53ce6d595502ca71605db2fcc2 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 24 Jul 2024 12:06:16 +0800 Subject: [PATCH 321/335] fix nextRgba not called when switching to texture render (#8792) Because rgba buffer render doesn't support multi display, when switch to multi display, it is possible that rgba.valid has been set to valid but nextRgab is not called, when switching back to single display, rgba.valid is still true. Fix by rgba buffer, rgba texture and gpu texture using different messages. Signed-off-by: 21pages --- flutter/lib/models/model.dart | 38 +++++++++++++++++------------------ src/flutter.rs | 4 ++-- src/flutter_ffi.rs | 2 +- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index a9962a58a7d..51578055d67 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -2547,32 +2547,30 @@ class FFI { } } else if (message is EventToUI_Rgba) { final display = message.field0; - if (imageModel.useTextureRender || ffiModel.pi.forceTextureRender) { - //debugPrint("EventToUI_Rgba display:$display"); - textureModel.setTextureType(display: display, gpuTexture: false); + // Fetch the image buffer from rust codes. + final sz = platformFFI.getRgbaSize(sessionId, display); + if (sz == 0) { + platformFFI.nextRgba(sessionId, display); + return; + } + final rgba = platformFFI.getRgba(sessionId, display, sz); + if (rgba != null) { onEvent2UIRgba(); + imageModel.onRgba(display, rgba); } else { - // Fetch the image buffer from rust codes. - final sz = platformFFI.getRgbaSize(sessionId, display); - if (sz == 0) { - platformFFI.nextRgba(sessionId, display); - return; - } - final rgba = platformFFI.getRgba(sessionId, display, sz); - if (rgba != null) { - onEvent2UIRgba(); - imageModel.onRgba(display, rgba); - } else { - platformFFI.nextRgba(sessionId, display); - } + platformFFI.nextRgba(sessionId, display); } } else if (message is EventToUI_Texture) { final display = message.field0; - debugPrint("EventToUI_Texture display:$display"); - if (hasGpuTextureRender) { - textureModel.setTextureType(display: display, gpuTexture: true); - onEvent2UIRgba(); + final gpuTexture = message.field1; + debugPrint( + "EventToUI_Texture display:$display, gpuTexture:$gpuTexture"); + if (gpuTexture && !hasGpuTextureRender) { + debugPrint('the gpuTexture is not supported.'); + return; } + textureModel.setTextureType(display: display, gpuTexture: gpuTexture); + onEvent2UIRgba(); } }(); }); diff --git a/src/flutter.rs b/src/flutter.rs index d1e44008b86..3da75450640 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -794,7 +794,7 @@ impl InvokeUiSession for FlutterHandler { for (_, session) in self.session_handlers.read().unwrap().iter() { if session.renderer.on_texture(display, texture) { if let Some(stream) = &session.event_stream { - stream.add(EventToUI::Texture(display)); + stream.add(EventToUI::Texture(display, true)); } } } @@ -1087,7 +1087,7 @@ impl FlutterHandler { if use_texture_render || session.displays.len() > 1 { if session.renderer.on_rgba(display, rgba) { if let Some(stream) = &session.event_stream { - stream.add(EventToUI::Rgba(display)); + stream.add(EventToUI::Texture(display, false)); } } } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 700c08704a3..870a65912f7 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -89,7 +89,7 @@ pub fn stop_global_event_stream(app_type: String) { pub enum EventToUI { Event(String), Rgba(usize), - Texture(usize), + Texture(usize, bool), // (display, gpu_texture) } pub fn host_stop_system_key_propagate(_stopped: bool) { From 79a1f888d6518f64363217cdc5cee683a4bb12d4 Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Wed, 24 Jul 2024 14:00:49 +0800 Subject: [PATCH 322/335] fix: flutter remove setState in initState (#8807) Signed-off-by: dignow --- flutter/lib/common/widgets/peer_tab_page.dart | 26 +++++++++++++------ .../lib/desktop/pages/file_manager_page.dart | 5 +++- .../lib/desktop/pages/port_forward_page.dart | 5 +++- flutter/lib/desktop/pages/remote_page.dart | 17 +++++++----- flutter/lib/desktop/pages/server_page.dart | 13 +++++++--- flutter/lib/desktop/widgets/popup_menu.dart | 6 +++++ .../lib/desktop/widgets/remote_toolbar.dart | 6 +++-- .../lib/desktop/widgets/tabbar_widget.dart | 6 +++-- flutter/lib/mobile/pages/connection_page.dart | 4 +-- flutter/lib/mobile/pages/remote_page.dart | 6 +++-- flutter/lib/mobile/pages/settings_page.dart | 4 +-- src/virtual_display_manager.rs | 2 +- 12 files changed, 69 insertions(+), 31 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 3b92ab1ebd5..66fba231239 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -78,6 +78,13 @@ class _PeerTabPageState extends State @override void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + _loadLocalOptions(); + }); + super.initState(); + } + + Future _loadLocalOptions() async { final uiType = bind.getLocalFlutterOption(k: kOptionPeerCardUiType); if (uiType != '') { peerCardUiType.value = int.parse(uiType) == 0 @@ -88,7 +95,6 @@ class _PeerTabPageState extends State } hideAbTagsPanel.value = bind.mainGetLocalOption(key: kOptionHideAbTagsPanel) == 'Y'; - super.initState(); } Future handleTabSelection(int tabIndex) async { @@ -875,18 +881,22 @@ class _PeerSortDropdownState extends State { @override void initState() { if (!PeerSortType.values.contains(peerSort.value)) { - Future.delayed(Duration.zero, () { - // do not change obx directly in initState, so do in future. - peerSort.value = PeerSortType.remoteId; - bind.setLocalFlutterOption( - k: kOptionPeerSorting, - v: peerSort.value, - ); + // do not change obx directly in initState, so do in future. + WidgetsBinding.instance.addPostFrameCallback((_) { + _loadLocalOptions(); }); } super.initState(); } + Future _loadLocalOptions() async { + peerSort.value = PeerSortType.remoteId; + bind.setLocalFlutterOption( + k: kOptionPeerSorting, + v: peerSort.value, + ); + } + @override Widget build(BuildContext context) { final style = TextStyle( diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 29ab0f80693..3b4428f99e4 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -98,7 +98,10 @@ class _FileManagerPageState extends State } debugPrint("File manager page init success with id ${widget.id}"); _ffi.dialogManager.setOverlayState(_overlayKeyState); - widget.tabController.onSelected?.call(widget.id); + // Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState. + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.tabController.onSelected?.call(widget.id); + }); } @override diff --git a/flutter/lib/desktop/pages/port_forward_page.dart b/flutter/lib/desktop/pages/port_forward_page.dart index 99f8df3409e..5541cb8b33b 100644 --- a/flutter/lib/desktop/pages/port_forward_page.dart +++ b/flutter/lib/desktop/pages/port_forward_page.dart @@ -65,7 +65,10 @@ class _PortForwardPageState extends State isRdp: widget.isRDP); Get.put(_ffi, tag: 'pf_${widget.id}'); debugPrint("Port forward page init success with id ${widget.id}"); - widget.tabController.onSelected?.call(widget.id); + // Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState. + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.tabController.onSelected?.call(widget.id); + }); } @override diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index f108f74445c..08656a4df53 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -135,11 +135,13 @@ class _RemotePageState extends State if (!isWeb) bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote); _ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); _ffi.dialogManager.loadMobileActionsOverlayVisible(); - // Session option should be set after models.dart/FFI.start - _showRemoteCursor.value = bind.sessionGetToggleOptionSync( - sessionId: sessionId, arg: 'show-remote-cursor'); - _zoomCursor.value = bind.sessionGetToggleOptionSync( - sessionId: sessionId, arg: kOptionZoomCursor); + WidgetsBinding.instance.addPostFrameCallback((_) { + // Session option should be set after models.dart/FFI.start + _showRemoteCursor.value = bind.sessionGetToggleOptionSync( + sessionId: sessionId, arg: 'show-remote-cursor'); + _zoomCursor.value = bind.sessionGetToggleOptionSync( + sessionId: sessionId, arg: kOptionZoomCursor); + }); DesktopMultiWindow.addListener(this); // if (!_isCustomCursorInited) { // customCursorController.registerNeedUpdateCursorCallback( @@ -154,7 +156,10 @@ class _RemotePageState extends State // } _blockableOverlayState.applyFfi(_ffi); - widget.tabController?.onSelected?.call(widget.id); + // Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState. + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.tabController?.onSelected?.call(widget.id); + }); } @override diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 1cac2706e2f..9701ca40913 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -129,7 +129,7 @@ class ConnectionManagerState extends State if (client != null) { gFFI.chatModel.changeCurrentKey(MessageKey(client.peerId, client.id)); if (client.unreadChatMessageCount.value > 0) { - Future.delayed(Duration.zero, () { + WidgetsBinding.instance.addPostFrameCallback((_) { client.unreadChatMessageCount.value = 0; gFFI.chatModel.showChatPage(MessageKey(client.peerId, client.id)); }); @@ -399,7 +399,10 @@ class _CmHeaderState extends State<_CmHeader> _time.value = _time.value + 1; } }); - gFFI.serverModel.tabController.onSelected?.call(client.id.toString()); + // Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState. + WidgetsBinding.instance.addPostFrameCallback((_) { + gFFI.serverModel.tabController.onSelected?.call(client.id.toString()); + }); } @override @@ -732,7 +735,8 @@ class _CmControlPanel extends StatelessWidget { child: buildButton(context, color: MyTheme.accent, onClick: null, onTapDown: (details) async { - final devicesInfo = await AudioInput.getDevicesInfo(true, true); + final devicesInfo = + await AudioInput.getDevicesInfo(true, true); List devices = devicesInfo['devices'] as List; if (devices.isEmpty) { msgBox( @@ -764,7 +768,8 @@ class _CmControlPanel extends StatelessWidget { value: d, groupValue: currentDevice, onChanged: (v) { - if (v != null) AudioInput.setDevice(v, true, true); + if (v != null) + AudioInput.setDevice(v, true, true); }, child: Container( child: Text( diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index 7d8f7c78cb7..003979a70fa 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -48,6 +48,12 @@ class MyPopupMenuItemState> @override void initState() { super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _initEnabled(); + }); + } + + Future _initEnabled() async { if (widget.enabled != null) { enabled.value = widget.enabled!.value; } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 9db65e072e6..cfc52c77796 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -372,7 +372,7 @@ class _RemoteToolbarState extends State { initState() { super.initState(); - Future.delayed(Duration.zero, () async { + WidgetsBinding.instance.addPostFrameCallback((_) async { _fractionX.value = double.tryParse(await bind.sessionGetOption( sessionId: widget.ffi.sessionId, arg: 'remote-menubar-drag-x') ?? @@ -1278,7 +1278,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { @override void initState() { super.initState(); - _getLocalResolutionWayland(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _getLocalResolutionWayland(); + }); } Rect? scaledRect() { diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 51344111398..a19ce105bbe 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -1271,12 +1271,14 @@ class ActionIcon extends StatefulWidget { } class _ActionIconState extends State { - var hover = false.obs; + final hover = false.obs; @override void initState() { super.initState(); - hover.value = false; + WidgetsBinding.instance.addPostFrameCallback((_) { + hover.value = false; + }); } @override diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index 4a2bb8b558a..914fdec3458 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -55,14 +55,14 @@ class _ConnectionPageState extends State { super.initState(); if (!isWeb) _uniLinksSubscription = listenUniLinks(); if (_idController.text.isEmpty) { - () async { + WidgetsBinding.instance.addPostFrameCallback((_) async { final lastRemoteId = await bind.mainGetLastRemoteId(); if (lastRemoteId != _idController.id) { setState(() { _idController.id = lastRemoteId; }); } - }(); + }); } if (isAndroid) { if (!bind.isCustomClient()) { diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 30598085f40..3b962a1c350 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -83,9 +83,11 @@ class _RemotePageState extends State { initSharedStates(widget.id); gFFI.chatModel .changeCurrentKey(MessageKey(widget.id, ChatModel.clientModeID)); - gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted; + WidgetsBinding.instance.addPostFrameCallback((_) { + gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted; + gFFI.dialogManager.loadMobileActionsOverlayVisible(); + }); _blockableOverlayState.applyFfi(gFFI); - gFFI.dialogManager.loadMobileActionsOverlayVisible(); } @override diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index dd95c42759b..72e48c6f5ea 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -118,7 +118,7 @@ class _SettingsState extends State with WidgetsBindingObserver { _hideNetwork = bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y'; - () async { + WidgetsBinding.instance.addPostFrameCallback((_) async { var update = false; if (_hasIgnoreBattery) { @@ -177,7 +177,7 @@ class _SettingsState extends State with WidgetsBindingObserver { if (update) { setState(() {}); } - }(); + }); } @override diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index af5eda9732d..7807bd3d548 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -2,7 +2,7 @@ use hbb_common::{bail, platform::windows::is_windows_version_or_greater, ResultT use std::sync::atomic; // This string is defined here. -// https://github.com/rustdesk/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 +// https://github.com/rustdesk-org/RustDeskIddDriver/blob/b370aad3f50028b039aad211df60c8051c4a64d6/RustDeskIddDriver/RustDeskIddDriver.inf#LL73C1-L73C40 pub const RUSTDESK_IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device\0"; pub const AMYUNI_IDD_DEVICE_STRING: &'static str = "USB Mobile Monitor Virtual Display\0"; From c04f460bbdd3a4d9deb2200d82ac1bcbc3d4c101 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 24 Jul 2024 17:20:58 +0800 Subject: [PATCH 323/335] fix more bool options (#8809) * fix more bool options * hide sort ab tags because it's already sorted Signed-off-by: 21pages --- flutter/lib/common/widgets/address_book.dart | 3 ++- flutter/lib/common/widgets/peer_card.dart | 4 ++-- flutter/lib/models/ab_model.dart | 10 +++------- flutter/lib/models/server_model.dart | 4 ++-- src/rendezvous_mediator.rs | 11 ++++++++--- src/ui/index.tis | 2 +- src/ui_cm_interface.rs | 9 +++++---- src/ui_interface.rs | 9 ++++++--- 8 files changed, 29 insertions(+), 23 deletions(-) diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 50bb35dcab4..bb553683ce5 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -425,7 +425,8 @@ class _AddressBookState extends State { if (canWrite) getEntry(translate("Add ID"), addIdToCurrentAb), if (canWrite) getEntry(translate("Add Tag"), abAddTag), getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags), - sortMenuItem(), + if (gFFI.abModel.legacyMode.value) + sortMenuItem(), // It's already sorted after pulling down if (canWrite) syncMenuItem(), filterMenuItem(), if (!gFFI.abModel.legacyMode.value && canWrite) diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 1b099cbeba6..f9dd73bbf6b 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -636,8 +636,8 @@ abstract class BasePeerCard extends StatelessWidget { @protected Future _isForceAlwaysRelay(String id) async { - return (await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay)) - .isNotEmpty; + return option2bool(kOptionForceAlwaysRelay, + (await bind.mainGetPeerOption(id: id, key: kOptionForceAlwaysRelay))); } @protected diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 9fdae087a56..8cd5cc92293 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -17,17 +17,17 @@ import '../common.dart'; final syncAbOption = 'sync-ab-with-recent-sessions'; bool shouldSyncAb() { - return bind.mainGetLocalOption(key: syncAbOption).isNotEmpty; + return bind.mainGetLocalOption(key: syncAbOption) == 'Y'; } final sortAbTagsOption = 'sync-ab-tags'; bool shouldSortTags() { - return bind.mainGetLocalOption(key: sortAbTagsOption).isNotEmpty; + return bind.mainGetLocalOption(key: sortAbTagsOption) == 'Y'; } final filterAbTagOption = 'filter-ab-by-intersection'; bool filterAbTagByIntersection() { - return bind.mainGetLocalOption(key: filterAbTagOption).isNotEmpty; + return bind.mainGetLocalOption(key: filterAbTagOption) == 'Y'; } const _personalAddressBookName = "My address book"; @@ -815,8 +815,6 @@ abstract class BaseAb { } class LegacyAb extends BaseAb { - final sortTags = shouldSortTags().obs; - final filterByIntersection = filterAbTagByIntersection().obs; bool get emtpy => peers.isEmpty && tags.isEmpty; // licensedDevices is obtained from personal ab, shared ab restrict it in server var licensedDevices = 0; @@ -1209,8 +1207,6 @@ class LegacyAb extends BaseAb { class Ab extends BaseAb { AbProfile profile; late final bool personal; - final sortTags = shouldSortTags().obs; - final filterByIntersection = filterAbTagByIntersection().obs; bool get emtpy => peers.isEmpty && tags.isEmpty; Ab(this.profile, this.personal); diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index b643a7c2098..613fee6ad11 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -196,7 +196,7 @@ class ServerModel with ChangeNotifier { bind.mainSetOption(key: kOptionEnableAudio, value: "N"); } else { final audioOption = await bind.mainGetOption(key: kOptionEnableAudio); - _audioOk = audioOption.isEmpty; + _audioOk = audioOption != 'N'; } // file @@ -206,7 +206,7 @@ class ServerModel with ChangeNotifier { } else { final fileOption = await bind.mainGetOption(key: kOptionEnableFileTransfer); - _fileOk = fileOption.isEmpty; + _fileOk = fileOption != 'N'; } notifyListeners(); diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index dcb538179f3..65bb6a2b3ed 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -12,7 +12,10 @@ use uuid::Uuid; use hbb_common::{ allow_err, anyhow::{self, bail}, - config::{self, Config, CONNECT_TIMEOUT, READ_TIMEOUT, REG_INTERVAL, RENDEZVOUS_PORT}, + config::{ + self, keys::*, option2bool, Config, CONNECT_TIMEOUT, READ_TIMEOUT, REG_INTERVAL, + RENDEZVOUS_PORT, + }, futures::future::join_all, log, protobuf::Message as _, @@ -637,8 +640,10 @@ async fn direct_server(server: ServerPtr) { let mut listener = None; let mut port = 0; loop { - let disabled = Config::get_option("direct-server").is_empty() - || !Config::get_option("stop-service").is_empty(); + let disabled = !option2bool( + OPTION_DIRECT_SERVER, + &Config::get_option(OPTION_DIRECT_SERVER), + ) || option2bool("stop-service", &Config::get_option("stop-service")); if !disabled && listener.is_none() { port = get_direct_port(); match hbb_common::tcp::listen_any(port as _).await { diff --git a/src/ui/index.tis b/src/ui/index.tis index d76cae58316..b10df76233e 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -1149,7 +1149,7 @@ function showSettings() { function checkConnectStatus() { handler.check_mouse_time(); // trigger connection status updater self.timer(1s, function() { - var tmp = !!handler.get_option("stop-service"); + var tmp = handler.get_option("stop-service") == "Y"; if (tmp != service_stopped) { service_stopped = tmp; app.update(); diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 8b4a31f3ddf..7dae13ebc98 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -21,7 +21,7 @@ use clipboard::ContextSend; use hbb_common::tokio::sync::mpsc::unbounded_channel; use hbb_common::{ allow_err, - config::Config, + config::{keys::*, option2bool, Config}, fs::is_write_need_confirmation, fs::{self, get_string, new_send_confirm, DigestCheckResult}, log, @@ -583,9 +583,10 @@ pub async fn start_ipc(cm: ConnectionManager) { feature = "unix-file-copy-paste" ), ))] - ContextSend::enable( - Config::get_option(hbb_common::config::keys::OPTION_ENABLE_FILE_TRANSFER).is_empty(), - ); + ContextSend::enable(option2bool( + OPTION_ENABLE_FILE_TRANSFER, + &Config::get_option(OPTION_ENABLE_FILE_TRANSFER), + )); match ipc::new_listener("_cm").await { Ok(mut incoming) => { diff --git a/src/ui_interface.rs b/src/ui_interface.rs index fec0380b534..a192265df07 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -3,7 +3,10 @@ use hbb_common::password_security; use hbb_common::{ allow_err, bytes::Bytes, - config::{self, Config, LocalConfig, PeerConfig, CONNECT_TIMEOUT, RENDEZVOUS_PORT}, + config::{ + self, keys::*, option2bool, Config, LocalConfig, PeerConfig, CONNECT_TIMEOUT, + RENDEZVOUS_PORT, + }, directories_next, futures::future::join_all, log, @@ -1156,9 +1159,9 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver Date: Wed, 24 Jul 2024 17:35:06 +0800 Subject: [PATCH 324/335] fix: siwtching display, clear last old image (#8810) * fix: siwtching display, clear last old image 1. Clear last old image. 2. Try refresh after switching display. 3. Add an interval check before refresh video service. Signed-off-by: dignow * simple changes Signed-off-by: dignow --------- Signed-off-by: dignow --- flutter/lib/common.dart | 10 ++++++++++ flutter/lib/models/model.dart | 2 ++ src/flutter.rs | 13 +++++++++++++ src/server/video_service.rs | 18 +++++++++++++++--- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index e58c361f8b9..328d53c381b 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2935,6 +2935,16 @@ openMonitorInTheSameTab(int i, FFI ffi, PeerInfo pi, final displays = i == kAllDisplayValue ? List.generate(pi.displays.length, (index) => index) : [i]; + // Try clear image model before switching from all displays + // 1. The remote side has multiple displays. + // 2. Do not use texture render. + // 3. Connect to Display 1. + // 4. Switch to multi-displays `kAllDisplayValue` + // 5. Switch to Display 2. + // Then the remote page will display last picture of Display 1 at the beginning. + if (pi.forceTextureRender && i != kAllDisplayValue) { + ffi.imageModel.clearImage(); + } bind.sessionSwitchDisplay( isDesktop: isDesktop, sessionId: ffi.sessionId, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 51578055d67..2ad9fdb9b78 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1173,6 +1173,8 @@ class ImageModel with ChangeNotifier { addCallbackOnFirstImage(Function(String) cb) => callbacksOnFirstImage.add(cb); + clearImage() => _image = null; + onRgba(int display, Uint8List rgba) { final pid = parent.target?.id; final rect = parent.target?.ffiModel.pi.getDisplayRect(display); diff --git a/src/flutter.rs b/src/flutter.rs index 3da75450640..1e7f1f2834c 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1889,6 +1889,8 @@ pub mod sessions { let mut write_lock = s.ui_handler.session_handlers.write().unwrap(); if let Some(h) = write_lock.get_mut(&session_id) { h.displays = value.iter().map(|x| *x as usize).collect::<_>(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] + let displays_refresh = value.clone(); if value.len() == 1 { // Switch display. // This operation will also cause the peer to send a switch display message. @@ -1912,6 +1914,17 @@ pub mod sessions { // Try capture all displays. s.capture_displays(vec![], vec![], value); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + let is_support_multi_ui_session = crate::common::is_support_multi_ui_session( + &s.ui_handler.peer_info.read().unwrap().version, + ); + if is_support_multi_ui_session { + for display in displays_refresh.iter() { + s.refresh_video(*display); + } + } + } break; } } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 7fd5dee1f82..54108515da9 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -68,6 +68,8 @@ use std::{ pub const NAME: &'static str = "video"; pub const OPTION_REFRESH: &'static str = "refresh"; +const REFRESH_MIN_INTERVAL_MILLIS: u128 = 300; + lazy_static::lazy_static! { static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option)>, Arc)>>>) = { let (tx, rx) = unbounded_channel(); @@ -76,6 +78,8 @@ lazy_static::lazy_static! { pub static ref VIDEO_QOS: Arc> = Default::default(); pub static ref IS_UAC_RUNNING: Arc> = Default::default(); pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc> = Default::default(); + // Avoid refreshing too frequently + static ref LAST_REFRESH_TIME: Arc>> = Default::default(); } #[inline] @@ -513,9 +517,17 @@ fn run(vs: VideoService) -> ResultType<()> { drop(video_qos); if sp.is_option_true(OPTION_REFRESH) { - let _ = try_broadcast_display_changed(&sp, display_idx, &c, true); - log::info!("switch to refresh"); - bail!("SWITCH"); + let mut last_refresh_lock = LAST_REFRESH_TIME.lock().unwrap(); + if last_refresh_lock + .get(&vs.idx) + .map(|x| x.elapsed().as_millis() > REFRESH_MIN_INTERVAL_MILLIS) + .unwrap_or(true) + { + let _ = try_broadcast_display_changed(&sp, display_idx, &c, true); + last_refresh_lock.insert(vs.idx, Instant::now()); + log::info!("switch to refresh"); + bail!("SWITCH"); + } } if codec_format != Encoder::negotiated_codec() { log::info!( From d73e0e1e5a2d1b785a51c479f6c7dcd0e49937f5 Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Wed, 24 Jul 2024 18:41:52 +0800 Subject: [PATCH 325/335] fix: video service (#8812) 1. Unset refresh flag if just refreshed. 2. Reduce the scope of the lock. Signed-off-by: dignow --- src/flutter.rs | 2 +- src/server/video_service.rs | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index 1e7f1f2834c..e9065636a1c 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1063,7 +1063,7 @@ impl FlutterHandler { } // We need `is_sent` here. Because we use texture render for multi-displays session. // - // Eg. We have to windows, one is display 1, the other is displays 0&1. + // Eg. We have two windows, one is display 1, the other is displays 0&1. // When image of display 0 is received, we will not send the event. // // 1. "display 1" will not send the event. diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 54108515da9..e4c16fd8269 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -517,16 +517,22 @@ fn run(vs: VideoService) -> ResultType<()> { drop(video_qos); if sp.is_option_true(OPTION_REFRESH) { - let mut last_refresh_lock = LAST_REFRESH_TIME.lock().unwrap(); - if last_refresh_lock + if LAST_REFRESH_TIME + .lock() + .unwrap() .get(&vs.idx) .map(|x| x.elapsed().as_millis() > REFRESH_MIN_INTERVAL_MILLIS) .unwrap_or(true) { let _ = try_broadcast_display_changed(&sp, display_idx, &c, true); - last_refresh_lock.insert(vs.idx, Instant::now()); + LAST_REFRESH_TIME + .lock() + .unwrap() + .insert(vs.idx, Instant::now()); log::info!("switch to refresh"); bail!("SWITCH"); + } else { + sp.set_option_bool(OPTION_REFRESH, false); } } if codec_format != Encoder::negotiated_codec() { From f7e9057a3965248b8dbb4cc1a1c9a8c1eae24529 Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Wed, 24 Jul 2024 21:59:25 +0800 Subject: [PATCH 326/335] fix: video service, do not skip refresh message (#8815) Signed-off-by: dignow --- src/flutter.rs | 6 ++++++ src/server/video_service.rs | 24 +++--------------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index e9065636a1c..cd6e51ea1b0 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1914,6 +1914,12 @@ pub mod sessions { // Try capture all displays. s.capture_displays(vec![], vec![], value); } + // When switching display, we also need to send "Refresh display" message. + // On the controlled side: + // 1. If this display is not currently captured -> Refresh -> Message "Refresh display" is not required. + // One more key frame (first frame) will be sent because the refresh message. + // 2. If this display is currently captured -> Not refresh -> Message "Refresh display" is required. + // Without the message, the control side cannot see the latest display image. #[cfg(not(any(target_os = "android", target_os = "ios")))] { let is_support_multi_ui_session = crate::common::is_support_multi_ui_session( diff --git a/src/server/video_service.rs b/src/server/video_service.rs index e4c16fd8269..7fd5dee1f82 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -68,8 +68,6 @@ use std::{ pub const NAME: &'static str = "video"; pub const OPTION_REFRESH: &'static str = "refresh"; -const REFRESH_MIN_INTERVAL_MILLIS: u128 = 300; - lazy_static::lazy_static! { static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option)>, Arc)>>>) = { let (tx, rx) = unbounded_channel(); @@ -78,8 +76,6 @@ lazy_static::lazy_static! { pub static ref VIDEO_QOS: Arc> = Default::default(); pub static ref IS_UAC_RUNNING: Arc> = Default::default(); pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc> = Default::default(); - // Avoid refreshing too frequently - static ref LAST_REFRESH_TIME: Arc>> = Default::default(); } #[inline] @@ -517,23 +513,9 @@ fn run(vs: VideoService) -> ResultType<()> { drop(video_qos); if sp.is_option_true(OPTION_REFRESH) { - if LAST_REFRESH_TIME - .lock() - .unwrap() - .get(&vs.idx) - .map(|x| x.elapsed().as_millis() > REFRESH_MIN_INTERVAL_MILLIS) - .unwrap_or(true) - { - let _ = try_broadcast_display_changed(&sp, display_idx, &c, true); - LAST_REFRESH_TIME - .lock() - .unwrap() - .insert(vs.idx, Instant::now()); - log::info!("switch to refresh"); - bail!("SWITCH"); - } else { - sp.set_option_bool(OPTION_REFRESH, false); - } + let _ = try_broadcast_display_changed(&sp, display_idx, &c, true); + log::info!("switch to refresh"); + bail!("SWITCH"); } if codec_format != Encoder::negotiated_codec() { log::info!( From 0451a1c45f911d46cbb7bcdf5c5a8b857314edac Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Thu, 25 Jul 2024 00:13:22 +0800 Subject: [PATCH 327/335] fix: mobile, server page, jumps on loading (#8819) Signed-off-by: dignow --- flutter/lib/common.dart | 63 +++++++++++++---------- flutter/lib/mobile/pages/server_page.dart | 2 +- src/flutter_ffi.rs | 6 +++ 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 328d53c381b..5db9d10f1b3 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -3326,6 +3326,42 @@ bool isInHomePage() { return controller.state.value.selected == 0; } +Widget _buildPresetPasswordWarning() { + if (bind.mainGetBuildinOption(key: kOptionRemovePresetPasswordWarning) != + 'N') { + return SizedBox.shrink(); + } + return Container( + color: Colors.yellow, + child: Column( + children: [ + Align( + child: Text( + translate("Security Alert"), + style: TextStyle( + color: Colors.red, + fontSize: + 18, // https://github.com/rustdesk/rustdesk-server-pro/issues/261 + fontWeight: FontWeight.bold, + ), + )).paddingOnly(bottom: 8), + Text( + translate("preset_password_warning"), + style: TextStyle(color: Colors.red), + ) + ], + ).paddingAll(8), + ); // Show a warning message if the Future completed with true +} + +Widget buildPresetPasswordWarningMobile() { + if (bind.isPresetPasswordMobileOnly()) { + return _buildPresetPasswordWarning(); + } else { + return SizedBox.shrink(); + } +} + Widget buildPresetPasswordWarning() { return FutureBuilder( future: bind.isPresetPassword(), @@ -3336,32 +3372,7 @@ Widget buildPresetPasswordWarning() { return Text( 'Error: ${snapshot.error}'); // Show an error message if the Future completed with an error } else if (snapshot.hasData && snapshot.data == true) { - if (bind.mainGetBuildinOption( - key: kOptionRemovePresetPasswordWarning) != - 'N') { - return SizedBox.shrink(); - } - return Container( - color: Colors.yellow, - child: Column( - children: [ - Align( - child: Text( - translate("Security Alert"), - style: TextStyle( - color: Colors.red, - fontSize: - 18, // https://github.com/rustdesk/rustdesk-server-pro/issues/261 - fontWeight: FontWeight.bold, - ), - )).paddingOnly(bottom: 8), - Text( - translate("preset_password_warning"), - style: TextStyle(color: Colors.red), - ) - ], - ).paddingAll(8), - ); // Show a warning message if the Future completed with true + return _buildPresetPasswordWarning(); } else { return SizedBox .shrink(); // Show nothing if the Future completed with false or null diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 66449617680..06258e5a5ae 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -187,7 +187,7 @@ class _ServerPageState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ - buildPresetPasswordWarning(), + buildPresetPasswordWarningMobile(), gFFI.serverModel.isStart ? ServerInfo() : ServiceNotRunningNotification(), diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 870a65912f7..08e79cbea94 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1966,6 +1966,12 @@ pub fn is_preset_password() -> bool { }) } +// Don't call this function for desktop version. +// We need this function because we want a sync return for mobile version. +pub fn is_preset_password_mobile_only() -> SyncReturn { + SyncReturn(is_preset_password()) +} + /// Send a url scheme through the ipc. /// /// * macOS only From 2aef79688b7b5ae363637ae7af3d48dd1c3e0040 Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Thu, 25 Jul 2024 00:43:14 +0800 Subject: [PATCH 328/335] fix: mobile, conn page, jump on exiting remote (#8820) Signed-off-by: dignow --- flutter/lib/common.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 5db9d10f1b3..5c362a71f7b 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -647,8 +647,12 @@ String formatDurationToTime(Duration duration) { closeConnection({String? id}) { if (isAndroid || isIOS) { - gFFI.chatModel.hideChatOverlay(); - Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/")); + () async { + await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, + overlays: SystemUiOverlay.values); + gFFI.chatModel.hideChatOverlay(); + Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/")); + }(); } else { if (isWeb) { Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/")); From b967d496ccced6a01687887f48467bcb9c453662 Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:45:51 +0800 Subject: [PATCH 329/335] refact: init values from initState to Constractor (#8817) * refact: init values from initState to Constractor Signed-off-by: dignow * fix: move RxBool init into Constructor Signed-off-by: dignow * peer sort option Signed-off-by: dignow * Remove empty initState() Signed-off-by: dignow --------- Signed-off-by: dignow --- flutter/lib/common.dart | 8 +++- flutter/lib/common/widgets/address_book.dart | 5 --- flutter/lib/common/widgets/login.dart | 5 --- flutter/lib/common/widgets/my_group.dart | 5 --- flutter/lib/common/widgets/overlay.dart | 21 +++++----- flutter/lib/common/widgets/peer_tab_page.dart | 21 +++------- flutter/lib/common/widgets/peers_view.dart | 8 +++- .../lib/desktop/pages/connection_page.dart | 4 +- .../desktop/pages/desktop_setting_page.dart | 9 ++--- .../lib/desktop/pages/desktop_tab_page.dart | 31 ++++++++------- .../desktop/pages/file_manager_tab_page.dart | 3 +- flutter/lib/desktop/pages/install_page.dart | 9 +++-- .../desktop/pages/port_forward_tab_page.dart | 3 +- flutter/lib/desktop/pages/remote_page.dart | 12 +++--- .../lib/desktop/pages/remote_tab_page.dart | 12 ++---- flutter/lib/desktop/pages/server_page.dart | 39 +++++++++++-------- flutter/lib/desktop/widgets/popup_menu.dart | 16 ++------ .../lib/desktop/widgets/remote_toolbar.dart | 5 --- .../lib/desktop/widgets/tabbar_widget.dart | 21 +--------- flutter/lib/mobile/pages/connection_page.dart | 14 ++++--- flutter/lib/mobile/pages/remote_page.dart | 18 ++++----- flutter/lib/mobile/pages/settings_page.dart | 12 +++--- flutter/lib/mobile/widgets/gesture_help.dart | 12 +++--- src/server/audio_service.rs | 1 - 24 files changed, 120 insertions(+), 174 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 5c362a71f7b..8c63816724f 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -31,7 +31,6 @@ import 'mobile/pages/file_manager_page.dart'; import 'mobile/pages/remote_page.dart'; import 'desktop/pages/remote_page.dart' as desktop_remote; import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; -import 'models/input_model.dart'; import 'models/model.dart'; import 'models/platform_model.dart'; @@ -3448,7 +3447,12 @@ setResizable(bool resizable) { isOptionFixed(String key) => bind.mainIsOptionFixed(key: key); -final isCustomClient = bind.isCustomClient(); +bool? _isCustomClient; +bool get isCustomClient { + _isCustomClient ??= bind.isCustomClient(); + return _isCustomClient!; +} + get defaultOptionLang => isCustomClient ? 'default' : ''; get defaultOptionTheme => isCustomClient ? 'system' : ''; get defaultOptionYes => isCustomClient ? 'Y' : ''; diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index bb553683ce5..52b2e3d6296 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -35,11 +35,6 @@ class AddressBook extends StatefulWidget { class _AddressBookState extends State { var menuPos = RelativeRect.fill; - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) => Obx(() { if (!gFFI.userModel.isLogin) { diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index 92528d5e3f3..71f3dacc3b2 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -142,11 +142,6 @@ class _WidgetOPState extends State { String _failedMsg = ''; String _url = ''; - @override - void initState() { - super.initState(); - } - @override void dispose() { super.dispose(); diff --git a/flutter/lib/common/widgets/my_group.dart b/flutter/lib/common/widgets/my_group.dart index fd8b9eeb82b..e139ce700d2 100644 --- a/flutter/lib/common/widgets/my_group.dart +++ b/flutter/lib/common/widgets/my_group.dart @@ -23,11 +23,6 @@ class _MyGroupState extends State { RxString get searchUserText => gFFI.groupModel.searchUserText; static TextEditingController searchUserController = TextEditingController(); - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { return Obx(() { diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index 145afc3b0a9..9b20136e150 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -353,7 +353,7 @@ class Draggable extends StatefulWidget { final Widget Function(BuildContext, GestureDragUpdateCallback) builder; @override - State createState() => _DraggableState(); + State createState() => _DraggableState(chatModel); } class _DraggableState extends State { @@ -362,10 +362,8 @@ class _DraggableState extends State { double _saveHeight = 0; double _lastBottomHeight = 0; - @override - void initState() { - super.initState(); - _chatModel = widget.chatModel; + _DraggableState(ChatModel? chatModel) { + _chatModel = chatModel; } get position => widget.position.pos; @@ -467,7 +465,8 @@ class IOSDraggable extends StatefulWidget { final Widget Function(BuildContext) builder; @override - IOSDraggableState createState() => IOSDraggableState(); + IOSDraggableState createState() => + IOSDraggableState(chatModel, width, height); } class IOSDraggableState extends State { @@ -478,12 +477,10 @@ class IOSDraggableState extends State { double _saveHeight = 0; double _lastBottomHeight = 0; - @override - void initState() { - super.initState(); - _chatModel = widget.chatModel; - _width = widget.width; - _height = widget.height; + IOSDraggableState(ChatModel? chatModel, double w, double h) { + _chatModel = chatModel; + _width = w; + _height = h; } DraggableKeyPosition get position => widget.position; diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 66fba231239..8fe73144999 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -76,15 +76,11 @@ class _PeerTabPageState extends State final isOptVisiableFixed = isOptionFixed(kOptionPeerTabVisible); - @override - void initState() { - WidgetsBinding.instance.addPostFrameCallback((_) { - _loadLocalOptions(); - }); - super.initState(); + _PeerTabPageState() { + _loadLocalOptions(); } - Future _loadLocalOptions() async { + void _loadLocalOptions() { final uiType = bind.getLocalFlutterOption(k: kOptionPeerCardUiType); if (uiType != '') { peerCardUiType.value = int.parse(uiType) == 0 @@ -878,18 +874,13 @@ class PeerSortDropdown extends StatefulWidget { } class _PeerSortDropdownState extends State { - @override - void initState() { + _PeerSortDropdownState() { if (!PeerSortType.values.contains(peerSort.value)) { - // do not change obx directly in initState, so do in future. - WidgetsBinding.instance.addPostFrameCallback((_) { - _loadLocalOptions(); - }); + _loadLocalOptions(); } - super.initState(); } - Future _loadLocalOptions() async { + void _loadLocalOptions() { peerSort.value = PeerSortType.remoteId; bind.setLocalFlutterOption( k: kOptionPeerSorting, diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index ec173e29477..ef9647eaa93 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -45,10 +45,14 @@ class LoadEvent { final peerSearchText = "".obs; /// for peer sort, global obs value -final peerSort = bind.getLocalFlutterOption(k: kOptionPeerSorting).obs; +RxString? _peerSort; +RxString get peerSort { + _peerSort ??= bind.getLocalFlutterOption(k: kOptionPeerSorting).obs; + return _peerSort!; +} // list for listener -final obslist = [peerSearchText, peerSort].obs; +RxList get obslist => [peerSearchText, peerSort].obs; final peerSearchTextController = TextEditingController(text: peerSearchText.value); diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 797cafcbec4..1403d449347 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -212,14 +212,14 @@ class _ConnectionPageState extends State void initState() { super.initState(); if (_idController.text.isEmpty) { - () async { + WidgetsBinding.instance.addPostFrameCallback((_) async { final lastRemoteId = await bind.mainGetLastRemoteId(); if (lastRemoteId != _idController.id) { setState(() { _idController.id = lastRemoteId; }); } - }(); + }); } Get.put(_idController); windowManager.addListener(this); diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 16b97848ce4..79c18c52118 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -78,7 +78,8 @@ class DesktopSettingPage extends StatefulWidget { DesktopSettingPage({Key? key, required this.initialTabkey}) : super(key: key); @override - State createState() => _DesktopSettingPageState(); + State createState() => + _DesktopSettingPageState(initialTabkey); static void switch2page(SettingsTabKey page) { try { @@ -111,10 +112,8 @@ class _DesktopSettingPageState extends State @override bool get wantKeepAlive => true; - @override - void initState() { - super.initState(); - var initialIndex = DesktopSettingPage.tabKeys.indexOf(widget.initialTabkey); + _DesktopSettingPageState(SettingsTabKey initialTabkey) { + var initialIndex = DesktopSettingPage.tabKeys.indexOf(initialTabkey); if (initialIndex == -1) { initialIndex = 0; } diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index ce4d02e375c..2e577e625ae 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -44,21 +44,9 @@ class _DesktopTabPageState extends State final RxBool _block = false.obs; // bool mouseIn = false; - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - super.didChangeAppLifecycleState(state); - if (state == AppLifecycleState.resumed) { - shouldBeBlocked(_block, canBeBlocked); - } else if (state == AppLifecycleState.inactive) {} - } - - @override - void initState() { - super.initState(); - // HardwareKeyboard.instance.addHandler(_handleKeyEvent); - WidgetsBinding.instance.addObserver(this); - Get.put(tabController); + _DesktopTabPageState() { RemoteCountState.init(); + Get.put(tabController); tabController.add(TabInfo( key: kTabLabelHomePage, label: kTabLabelHomePage, @@ -81,6 +69,21 @@ class _DesktopTabPageState extends State } } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + shouldBeBlocked(_block, canBeBlocked); + } else if (state == AppLifecycleState.inactive) {} + } + + @override + void initState() { + super.initState(); + // HardwareKeyboard.instance.addHandler(_handleKeyEvent); + WidgetsBinding.instance.addObserver(this); + } + /* bool _handleKeyEvent(KeyEvent event) { if (!mouseIn && event is KeyDownEvent) { diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 8680806e087..0cba76e00ec 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -34,6 +34,7 @@ class _FileManagerTabPageState extends State { WindowController.fromWindowId(windowId()) .setTitle(getWindowNameWithId(id)); }; + tabController.onRemoved = (_, id) => onRemoveId(id); tabController.add(TabInfo( key: params['id'], label: params['id'], @@ -54,8 +55,6 @@ class _FileManagerTabPageState extends State { void initState() { super.initState(); - tabController.onRemoved = (_, id) => onRemoveId(id); - rustDeskWinManager.setMethodHandler((call, fromWindowId) async { print( "[FileTransfer] call ${call.method} with args ${call.arguments} from window $fromWindowId to ${windowId()}"); diff --git a/flutter/lib/desktop/pages/install_page.dart b/flutter/lib/desktop/pages/install_page.dart index 203eabd25bb..a860fe89ea1 100644 --- a/flutter/lib/desktop/pages/install_page.dart +++ b/flutter/lib/desktop/pages/install_page.dart @@ -19,9 +19,7 @@ class InstallPage extends StatefulWidget { class _InstallPageState extends State { final tabController = DesktopTabController(tabType: DesktopTabType.main); - @override - void initState() { - super.initState(); + _InstallPageState() { Get.put(tabController); const label = "install"; tabController.add(TabInfo( @@ -73,10 +71,13 @@ class _InstallPageBodyState extends State<_InstallPageBody> padding: EdgeInsets.symmetric(vertical: 15, horizontal: 12), ); + _InstallPageBodyState() { + controller = TextEditingController(text: bind.installInstallPath()); + } + @override void initState() { windowManager.addListener(this); - controller = TextEditingController(text: bind.installInstallPath()); super.initState(); } diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index ac94890b6c8..b20d731b23f 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -34,6 +34,7 @@ class _PortForwardTabPageState extends State { WindowController.fromWindowId(windowId()) .setTitle(getWindowNameWithId(id)); }; + tabController.onRemoved = (_, id) => onRemoveId(id); tabController.add(TabInfo( key: params['id'], label: params['id'], @@ -54,8 +55,6 @@ class _PortForwardTabPageState extends State { void initState() { super.initState(); - tabController.onRemoved = (_, id) => onRemoveId(id); - rustDeskWinManager.setMethodHandler((call, fromWindowId) async { debugPrint( "[Port Forward] call ${call.method} with args ${call.arguments} from window $fromWindowId"); diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 08656a4df53..d1c1056bea7 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -64,7 +64,7 @@ class RemotePage extends StatefulWidget { @override State createState() { - final state = _RemotePageState(); + final state = _RemotePageState(id); _lastState.value = state; return state; } @@ -94,6 +94,10 @@ class _RemotePageState extends State SessionID get sessionId => _ffi.sessionId; + _RemotePageState(String id) { + _initStates(id); + } + void _initStates(String id) { initSharedStates(id); _zoomCursor = PeerBoolOption.find(id, kOptionZoomCursor); @@ -105,7 +109,6 @@ class _RemotePageState extends State @override void initState() { super.initState(); - _initStates(widget.id); _ffi = FFI(widget.sessionId); Get.put(_ffi, tag: widget.id); _ffi.imageModel.addCallbackOnFirstImage((String peerId) { @@ -570,11 +573,6 @@ class _ImagePaintState extends State { RxBool get remoteCursorMoved => widget.remoteCursorMoved; Widget Function(Widget)? get listenerBuilder => widget.listenerBuilder; - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { final m = Provider.of(context); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index f07b9538259..f4300875508 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -71,7 +71,7 @@ class _ConnectionTabPageState extends State { final ffi = remotePage.ffi; bind.setCurSessionId(sessionId: ffi.sessionId); } - WindowController.fromWindowId(windowId()) + WindowController.fromWindowId(params['windowId']) .setTitle(getWindowNameWithId(id)); UnreadChatCountState.find(id).value = 0; }; @@ -98,15 +98,14 @@ class _ConnectionTabPageState extends State { )); _update_remote_count(); } + tabController.onRemoved = (_, id) => onRemoveId(id); + rustDeskWinManager.setMethodHandler(_remoteMethodHandler); } @override void initState() { super.initState(); - tabController.onRemoved = (_, id) => onRemoveId(id); - - rustDeskWinManager.setMethodHandler(_remoteMethodHandler); if (!_isScreenRectSet) { Future.delayed(Duration.zero, () { restoreWindowPosition( @@ -121,11 +120,6 @@ class _ConnectionTabPageState extends State { } } - @override - void dispose() { - super.dispose(); - } - @override Widget build(BuildContext context) { final child = Scaffold( diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 9701ca40913..a052437479b 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -32,14 +32,18 @@ class DesktopServerPage extends StatefulWidget { class _DesktopServerPageState extends State with WindowListener, AutomaticKeepAliveClientMixin { final tabController = gFFI.serverModel.tabController; - @override - void initState() { + + _DesktopServerPageState() { gFFI.ffiModel.updateEventListener(gFFI.sessionId, ""); - windowManager.addListener(this); Get.put(tabController); tabController.onRemoved = (_, id) { onRemoveId(id); }; + } + + @override + void initState() { + windowManager.addListener(this); super.initState(); } @@ -108,19 +112,7 @@ class ConnectionManagerState extends State with WidgetsBindingObserver { final RxBool _block = false.obs; - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - super.didChangeAppLifecycleState(state); - if (state == AppLifecycleState.resumed) { - if (!allowRemoteCMModification()) { - shouldBeBlocked(_block, null); - } - } - } - - @override - void initState() { - gFFI.serverModel.updateClientState(); + ConnectionManagerState() { gFFI.serverModel.tabController.onSelected = (client_id_str) { final client_id = int.tryParse(client_id_str); if (client_id != null) { @@ -140,6 +132,21 @@ class ConnectionManagerState extends State } }; gFFI.chatModel.isConnManager = true; + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + if (state == AppLifecycleState.resumed) { + if (!allowRemoteCMModification()) { + shouldBeBlocked(_block, null); + } + } + } + + @override + void initState() { + gFFI.serverModel.updateClientState(); WidgetsBinding.instance.addObserver(this); super.initState(); } diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index 003979a70fa..28432a978f0 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -38,24 +38,16 @@ class PopupMenuChildrenItem extends mod_menu.PopupMenuEntry { @override MyPopupMenuItemState> createState() => - MyPopupMenuItemState>(); + MyPopupMenuItemState>(enabled?.value); } class MyPopupMenuItemState> extends State { RxBool enabled = true.obs; - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - _initEnabled(); - }); - } - - Future _initEnabled() async { - if (widget.enabled != null) { - enabled.value = widget.enabled!.value; + MyPopupMenuItemState(bool? e) { + if (e != null) { + enabled.value = e; } } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index cfc52c77796..98fa676144b 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1032,11 +1032,6 @@ class _DisplayMenuState extends State<_DisplayMenu> { FFI get ffi => widget.ffi; String get id => widget.id; - @override - void initState() { - super.initState(); - } - @override Widget build(BuildContext context) { _screenAdjustor.updateScreen(); diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index a19ce105bbe..fca6efb2e9b 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -227,8 +227,7 @@ typedef TabMenuBuilder = Widget Function(String key); typedef LabelGetter = Rx Function(String key); /// [_lastClickTime], help to handle double click -int _lastClickTime = - DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000; +int _lastClickTime = 0; class DesktopTab extends StatefulWidget { final bool showLogo; @@ -727,16 +726,6 @@ class WindowActionPanel extends StatefulWidget { } class WindowActionPanelState extends State { - @override - void initState() { - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - bool showTabDowndown() { return widget.tabController.state.value.tabs.length > 1 && (widget.tabController.tabType == DesktopTabType.remoteScreen || @@ -1273,14 +1262,6 @@ class ActionIcon extends StatefulWidget { class _ActionIconState extends State { final hover = false.obs; - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - hover.value = false; - }); - } - @override Widget build(BuildContext context) { return Tooltip( diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index 914fdec3458..02c552d716d 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -50,10 +50,17 @@ class _ConnectionPageState extends State { bool isPeersLoaded = false; StreamSubscription? _uniLinksSubscription; + _ConnectionPageState() { + if (!isWeb) _uniLinksSubscription = listenUniLinks(); + _idController.addListener(() { + _idEmpty.value = _idController.text.isEmpty; + }); + Get.put(_idController); + } + @override void initState() { super.initState(); - if (!isWeb) _uniLinksSubscription = listenUniLinks(); if (_idController.text.isEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) async { final lastRemoteId = await bind.mainGetLastRemoteId(); @@ -72,11 +79,6 @@ class _ConnectionPageState extends State { }); } } - - _idController.addListener(() { - _idEmpty.value = _idController.text.isEmpty; - }); - Get.put(_idController); } @override diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 3b962a1c350..74b56cd45fc 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -34,7 +34,7 @@ class RemotePage extends StatefulWidget { final bool? isSharedPassword; @override - State createState() => _RemotePageState(); + State createState() => _RemotePageState(id); } class _RemotePageState extends State { @@ -58,6 +58,12 @@ class _RemotePageState extends State { final TextEditingController _textController = TextEditingController(text: initText); + _RemotePageState(String id) { + initSharedStates(id); + gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted; + gFFI.dialogManager.loadMobileActionsOverlayVisible(); + } + @override void initState() { super.initState(); @@ -80,13 +86,8 @@ class _RemotePageState extends State { gFFI.qualityMonitorModel.checkShowQualityMonitor(sessionId); keyboardSubscription = keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged); - initSharedStates(widget.id); gFFI.chatModel .changeCurrentKey(MessageKey(widget.id, ChatModel.clientModeID)); - WidgetsBinding.instance.addPostFrameCallback((_) { - gFFI.chatModel.voiceCallStatus.value = VoiceCallStatus.notStarted; - gFFI.dialogManager.loadMobileActionsOverlayVisible(); - }); _blockableOverlayState.applyFfi(gFFI); } @@ -778,11 +779,6 @@ class _KeyHelpToolsState extends State { onPressed: onPressed); } - @override - void initState() { - super.initState(); - } - _updateRect() { RenderObject? renderObject = _key.currentContext?.findRenderObject(); if (renderObject == null) { diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 72e48c6f5ea..c817fda4e51 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -88,11 +88,7 @@ class _SettingsState extends State with WidgetsBindingObserver { var _hideProxy = false; var _hideNetwork = false; - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addObserver(this); - + _SettingsState() { _enableAbr = option2bool( kOptionEnableAbr, bind.mainGetOptionSync(key: kOptionEnableAbr)); _denyLANDiscovery = !option2bool(kOptionEnableLanDiscovery, @@ -117,6 +113,12 @@ class _SettingsState extends State with WidgetsBindingObserver { _hideProxy = bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y'; _hideNetwork = bind.mainGetBuildinOption(key: kOptionHideNetworkSetting) == 'Y'; + } + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addPostFrameCallback((_) async { var update = false; diff --git a/flutter/lib/mobile/widgets/gesture_help.dart b/flutter/lib/mobile/widgets/gesture_help.dart index f4c5a35a4d0..5ba6964897f 100644 --- a/flutter/lib/mobile/widgets/gesture_help.dart +++ b/flutter/lib/mobile/widgets/gesture_help.dart @@ -41,18 +41,16 @@ class GestureHelp extends StatefulWidget { final OnTouchModeChange onTouchModeChange; @override - State createState() => _GestureHelpState(); + State createState() => _GestureHelpState(touchMode); } class _GestureHelpState extends State { - var _selectedIndex; - var _touchMode; + late int _selectedIndex; + late bool _touchMode; - @override - void initState() { - _touchMode = widget.touchMode; + _GestureHelpState(bool touchMode) { + _touchMode = touchMode; _selectedIndex = _touchMode ? 1 : 0; - super.initState(); } @override diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index b717c66442f..f227bd23288 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -230,7 +230,6 @@ mod cpal_impl { #[cfg(windows)] fn get_device() -> ResultType<(Device, SupportedStreamConfig)> { let audio_input = super::get_audio_input(); - println!("REMOVE ME =============================== use audio input: {}", &audio_input); if !audio_input.is_empty() { return get_audio_input(&audio_input); } From baf70da2feab5371f8af2aefd63fe8e9a761bdb5 Mon Sep 17 00:00:00 2001 From: dignow <136106582+dignow@users.noreply.github.com> Date: Thu, 25 Jul 2024 14:44:58 +0800 Subject: [PATCH 330/335] fix: trackpad, reverse horizontal scroll (#8827) Signed-off-by: dignow --- src/server/input_service.rs | 3 +-- src/ui_session_interface.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 30a885663c5..eabb8844e04 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -973,12 +973,11 @@ pub fn handle_mouse_(evt: &MouseEvent, conn: i32) { }, MOUSE_TYPE_WHEEL | MOUSE_TYPE_TRACKPAD => { #[allow(unused_mut)] - let mut x = evt.x; + let mut x = -evt.x; #[allow(unused_mut)] let mut y = evt.y; #[cfg(not(windows))] { - x = -x; y = -y; } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 38144d5db70..4f39cd36cc0 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1018,7 +1018,7 @@ impl Session { } } - let (x, y) = if mask == MOUSE_TYPE_WHEEL { + let (x, y) = if mask == MOUSE_TYPE_WHEEL || mask == MOUSE_TYPE_TRACKPAD { self.get_scroll_xy((x, y)) } else { (x, y) From bbdce8d57bcebe07be87c822afe50a5d7b518fda Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 25 Jul 2024 15:21:28 +0800 Subject: [PATCH 331/335] Send clipboard keystroke, https://github.com/rustdesk/rustdesk/discussions/5451 --- flutter/lib/common/widgets/toolbar.dart | 7 +- src/lang/ar.rs | 1 + src/lang/be.rs | 1 + src/lang/bg.rs | 1 + src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/et.rs | 1 + src/lang/eu.rs | 1267 +++++++++++------------ src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/he.rs | 1 + src/lang/hr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 2 +- src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/lv.rs | 1 + src/lang/nb.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + src/ui_session_interface.rs | 1 - 45 files changed, 677 insertions(+), 641 deletions(-) diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index f2b3db7ad87..0b56d9f4c14 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -130,12 +130,9 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { ); } // paste - if (isMobile && - pi.platform != kPeerPlatformAndroid && - perms['keyboard'] != false && - perms['clipboard'] != false) { + if (pi.platform != kPeerPlatformAndroid && perms['keyboard'] != false) { v.add(TTextMenu( - child: Text(translate('Paste')), + child: Text(translate('Send clipboard keystrokes')), onPressed: () async { ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain); if (data != null && data.text != null) { diff --git a/src/lang/ar.rs b/src/lang/ar.rs index ff6b9d453ba..7fd258b81a1 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/be.rs b/src/lang/be.rs index d2a2589e099..78ed849ff9a 100644 --- a/src/lang/be.rs +++ b/src/lang/be.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs index 78eec7bc12f..cddbddac7e6 100644 --- a/src/lang/bg.rs +++ b/src/lang/bg.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index cca04e65dfd..f376d91df6e 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 6409e8ff9ff..df9fb530321 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "确定要取消双重认证吗?"), ("cancel-bot-confirm-tip", "确定要取消 Telegram 机器人吗?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 3113db4471d..a85a4d560b8 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Jste si jisti, že chcete zrušit 2FA?"), ("cancel-bot-confirm-tip", "Jste si jisti, že chcete zrušit bota Telegramu?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index af57a0cc963..679394599e5 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 4ff2023281d..c604167ef9f 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Sind Sie sicher, dass Sie 2FA abbrechen möchten?"), ("cancel-bot-confirm-tip", "Sind Sie sicher, dass Sie Telegram-Bot abbrechen möchten?"), ("About RustDesk", "Über RustDesk"), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 7b6c6a713ed..731e2a5f3ce 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το 2FA;"), ("cancel-bot-confirm-tip", "Είστε βέβαιοι ότι θέλετε να ακυρώσετε το Telegram bot;"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index b9a764046a7..190c693468d 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 4bf72e47556..0e94071e4a1 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "¿Seguro que quieres cancelar 2FA?"), ("cancel-bot-confirm-tip", "¿Seguro que quieres cancelar el bot de Telegram?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index ba65b4951a2..81fc2e77a21 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eu.rs b/src/lang/eu.rs index 00be90b961f..48efcf789bc 100644 --- a/src/lang/eu.rs +++ b/src/lang/eu.rs @@ -1,636 +1,635 @@ lazy_static::lazy_static! { - pub static ref T: std::collections::HashMap<&'static str, &'static str> = - [ - ("Status", "Egoera"), - ("Your Desktop", "Zure mahaigaina"), - ("desk_tip", "Mahaigainera zure ID eta pasahitzarekin sartu zaitezke"), - ("Password", "Pasahitza"), - ("Ready", "Prest"), - ("Established", "Ezarrita"), - ("connecting_status", "RustDesk sarera konektatzen..."), - ("Enable service", "Gaitu zerbitzua"), - ("Start service", "Hasi zerbitzua"), - ("Service is running", "Zerbitzua martxan dago"), - ("Service is not running", "Zerbitzua ez dago martxan"), - ("not_ready_status", "Ez dago prest. Mesedez, egiaztatu zure konexioa"), - ("Control Remote Desktop", "Kontrolatu urruneko mahaigaina"), - ("Transfer file", "Transferitu fitxategia"), - ("Connect", "Konektatu"), - ("Recent sessions", "Azken saioak"), - ("Address book", "Helbide-liburua"), - ("Confirmation", "Berrespena"), - ("TCP tunneling", "TCP tunela"), - ("Remove", "Kendu"), - ("Refresh random password", "Freskatu ausazko pasahitza"), - ("Set your own password", "Ezarri zure pasahitza"), - ("Enable keyboard/mouse", "Gaitu teklatua/sagua"), - ("Enable clipboard", "Gaitu arbela"), - ("Enable file transfer", "Gaitu fitxategien transferentzia"), - ("Enable TCP tunneling", "Gaitu TCP tunela"), - ("IP Whitelisting", "Onartutako IP helbideak"), - ("ID/Relay Server", "ID/Relay zerbitzaria"), - ("Import server config", "Inportatu zerbitzariaren konfigurazioa"), - ("Export Server Config", "Esportatu zerbitzariaren konfigurazioa"), - ("Import server configuration successfully", "Zerbitzariaren konfigurazioa ondo inportatu da"), - ("Export server configuration successfully", "Zerbitzariaren konfigurazioa ondo esportatu da"), - ("Invalid server configuration", "Zerbitzariaren konfigurazioa baliogabea da"), - ("Clipboard is empty", "Arbela hutsik dago"), - ("Stop service", "Gelditu zerbitzua"), - ("Change ID", "Aldatu IDa"), - ("Your new ID", "Zure ID berria"), - ("length %min% to %max%", "%min%(e)tik %max% arteko luzera"), - ("starts with a letter", "hizki batekin hasten da"), - ("allowed characters", "onartutako karaktereak"), - ("id_change_tip", "Soilik a-z, A-Z, 0-9 eta _ (barra baxua) karaktereak daude onartuta. Lehen hizkia a-z, A-Z izan behar da. Luzera 6 eta 16 artekoa izan behar da."), - ("Website", "Webgunea"), - ("About", "Honi buruz"), - ("Slogan_tip", "Bihotzez eginda mundu kaotiko honetan!"), - ("Privacy Statement", "Pribatutasun-politika"), - ("Mute", "Mututu"), - ("Build Date", "Konpilazio-data"), - ("Version", "Bertsioa"), - ("Home", "Hasiera"), - ("Audio Input", "Audio sarrera"), - ("Enhancements", "Hobespenak"), - ("Hardware Codec", "Hardware kodeka"), - ("Adaptive bitrate", "Bit-emari moldagarria"), - ("ID Server", "ID zerbitzaria"), - ("Relay Server", "Relay zerbitzaria"), - ("API Server", "API zerbitzaria"), - ("invalid_http", "http:// edo https://-rekin hasi behar da"), - ("Invalid IP", "IP baliogabea"), - ("Invalid format", "Formatu baliogabea"), - ("server_not_support", "Oraindik ez dago zerbitzariarengatik onartuta"), - ("Not available", "Ez dago eskuragarri"), - ("Too frequent", "Sarriegia"), - ("Cancel", "Utzi"), - ("Skip", "Saltatu"), - ("Close", "Itxi"), - ("Retry", "Saiatu berriro"), - ("OK", "Ondo"), - ("Password Required", "Pasahitza beharrezkoa da"), - ("Please enter your password", "Mesedez, sartu zure pasahitza"), - ("Remember password", "Gogoratu pasahitza"), - ("Wrong Password", "Pasahitz okerra"), - ("Do you want to enter again?", "Berriro sartu nahi zara?"), - ("Connection Error", "Konexio errorea"), - ("Error", "Errorea"), - ("Reset by the peer", "Konexioa parekidearengatik berrezarrita"), - ("Connecting...", "Konektatzen..."), - ("Connection in progress. Please wait.", "Konexioa abian da. Itxaron mesedez."), - ("Please try 1 minute later", "Mesedez, saiatu berrito minutu bat pasata"), - ("Login Error", "Saio-hasiera errorea"), - ("Successful", "Arrakastatsua"), - ("Connected, waiting for image...", "Konektatuta, irudiaren zain..."), - ("Name", "Izena"), - ("Type", "Mota"), - ("Modified", "Aldatua"), - ("Size", "Tamaina"), - ("Show Hidden Files", "Erakutsi ezkutuko fitxategiak"), - ("Receive", "Jaso"), - ("Send", "Bidali"), - ("Refresh File", "Freskatu fitxategia"), - ("Local", "Lokala"), - ("Remote", "Urrunekoa"), - ("Remote Computer", "Urruneko ordenagailua"), - ("Local Computer", "Ordenagailu lokala"), - ("Confirm Delete", "Berretsi ezabapena"), - ("Delete", "Ezabatu"), - ("Properties", "Ezaugarriak"), - ("Multi Select", "Multi-hautapena"), - ("Select All", "Hautatu guztiak"), - ("Unselect All", "Desautatu denak"), - ("Empty Directory", "Karpeta hutsa"), - ("Not an empty directory", "Ez da karpeta huts bat"), - ("Are you sure you want to delete this file?", "Ziur zaude fitxategi hau ezabatu nahi duzula?"), - ("Are you sure you want to delete this empty directory?", "Ziur zaude karpeta huts hau ezabatu nahi duzula?"), - ("Are you sure you want to delete the file of this directory?", "Ziur zaude karpeta honen fitxategia ezabatu nahi duzula?"), - ("Do this for all conflicts", "Egin hau gatazka guztietarako"), - ("This is irreversible!", "Hau ezin da atzera bueltatu!"), - ("Deleting", "Ezabatzen"), - ("files", "fitxategiak"), - ("Waiting", "Zain"), - ("Finished", "Bukatuta"), - ("Speed", "Abiadura"), - ("Custom Image Quality", "Irudi kalitate pertsonalizatua"), - ("Privacy mode", "Pribatutasun modua"), - ("Block user input", "Blokeatu erabiltzailearen sarrera"), - ("Unblock user input", "Desblokeatu erabiltzailearen sarrera"), - ("Adjust Window", "Doitu leihoa"), - ("Original", "Originala"), - ("Shrink", "Txikitu"), - ("Stretch", "Luzatu"), - ("Scrollbar", "Korritze-barra"), - ("ScrollAuto", "Korritze automatikoa"), - ("Good image quality", "Irudi kalitate ona"), - ("Balanced", "Orekatua"), - ("Optimize reaction time", "Optimizatu erreakzio-denbiora"), - ("Custom", "Pertsonalizatua"), - ("Show remote cursor", "Erakutsi urruneko kurtsorea"), - ("Show quality monitor", "Erakutsi kalitate monitorea"), - ("Disable clipboard", "Desgaitu arbela"), - ("Lock after session end", "Blokeatu sesioa amaitu ostean"), - ("Insert", "Sartu"), - ("Insert Lock", "Sarrera-blokeoa"), - ("Refresh", "Freskatu"), - ("ID does not exist", "IDa ez da existitzen"), - ("Failed to connect to rendezvous server", "Topaketa zerbitzarira konektatzeak huts egin du"), - ("Please try later", "Mesedez, saiatu berriro geroago"), - ("Remote desktop is offline", "Urruneko mahaigaina lineaz kanpo dago"), - ("Key mismatch", "Gakoak ez datoz bat"), - ("Timeout", "Denbora-muga"), - ("Failed to connect to relay server", "Igorpen zerbitzarira konektatzeak huts egin du"), - ("Failed to connect via rendezvous server", "Topaketa zerbitzariaren bidez konektatzeak huts egin du"), - ("Failed to connect via relay server", "Igorpen zerbitzariaren bidez konektatzeak huts egin du"), - ("Failed to make direct connection to remote desktop", "Urruneko mahaigainera zuzeneko konexio bat ezartzeak huts egin du"), - ("Set Password", "Ezarri pasahitza"), - ("OS Password", "Sistema eragilearen pasahitza"), - ("install_tip", "Erabiltzaile Kontuen Kontrolarengatik, RustDesk ezin du ondo funtzionatu urruneko mahaigainean. EKK saihesteko, mesedez, egin klik azpiko botoian RustDesk sistema mailan instalatzeko."), - ("Click to upgrade", "Egin klik bertsio-berritzeko"), - ("Click to download", "Egin klik deskargatzeko"), - ("Click to update", "Egin klik eguneratzeko"), - ("Configure", "Konfiguratu"), - ("config_acc", "Zure mahaigaina urrunetik kontrolatzeko, RustDesk-i \"Irisgarritasuna\" baimenak eman behar dituzu."), - ("config_screen", "Zure mahaigaina kanpotik kontrolatzeko, RustDesk-i \"Pantaila grabatu\" baimena eman behar duzu."), - ("Installing ...", "Instalantzen..."), - ("Install", "Instalatu"), - ("Installation", "Instalazioa"), - ("Installation Path", "Instalazio bide-izena"), - ("Create start menu shortcuts", "Sortu hasiera-menuko lasterbideak"), - ("Create desktop icon", "Sortu mahaigaineko ikonoa"), - ("agreement_tip", "Instalazioa hastean, lizentzia-kontratua onartzen duzu."), - ("Accept and Install", "Onartu eta instalatu"), - ("End-user license agreement", "Azken erabiltzailearen lizentzia akordioa"), - ("Generating ...", "Sortzen..."), - ("Your installation is lower version.", "Zure instalazioak bertsio zaharragoa du."), - ("not_close_tcp_tip", "Ez itxi leiho hau tunela erabili bitartean"), - ("Listening ...", "Entzuten..."), - ("Remote Host", "Urruneko ostalaria"), - ("Remote Port", "Urruneko ataka"), - ("Action", "Ekintza"), - ("Add", "Gehitu"), - ("Local Port", "Ataka lokala"), - ("Local Address", "Helbide lokala"), - ("Change Local Port", "Aldatu ataka lokala"), - ("setup_server_tip", "Konexio azkarragorako, konfiguratu zure zerbitzaria"), - ("Too short, at least 6 characters.", "Laburregia, 6 karaktere gutxienez."), - ("The confirmation is not identical.", "Berrespena ez dator bat."), - ("Permissions", "Baimenak"), - ("Accept", "Onartu"), - ("Dismiss", "Ezeztatu"), - ("Disconnect", "Deskonektatu"), - ("Enable file copy and paste", "Gaitu fitxategien kopiatze eta itsastea"), - ("Connected", "Konektatuta"), - ("Direct and encrypted connection", "Zifratutako konexio zuzena"), - ("Relayed and encrypted connection", "Zifratutako konexio igorria"), - ("Direct and unencrypted connection", "Zifratu gabeko konexio zuzena"), - ("Relayed and unencrypted connection", "Zifratu gabeko konexio igorria"), - ("Enter Remote ID", "Sartu urruneko IDa"), - ("Enter your password", "Sartu zure pasahitza"), - ("Logging in...", "Saioa hasten..."), - ("Enable RDP session sharing", "Gaitu RDP saio-partekatzea"), - ("Auto Login", "Saio-haste automatikoa"), - ("Enable direct IP access", "Gaitu IP sarbide zuzena"), - ("Rename", "Berrizendatu"), - ("Space", "Zuriunea"), - ("Create desktop shortcut", "Sortu mahaigaineko lasterbidea"), - ("Change Path", "Aldatu bide-izena"), - ("Create Folder", "Sortu karpeta"), - ("Please enter the folder name", "Mesedez, sartu karpetaren izena"), - ("Fix it", "Konpondu"), - ("Warning", "Oharra"), - ("Login screen using Wayland is not supported", "Saio-hasiera Wayland erabilita ez dago onartuta"), - ("Reboot required", "Berrabiaraztea beharrezkoa"), - ("Unsupported display server", "Bistaratze-zerbitzaria ez da bateragarria"), - ("x11 expected", "x11 espero zen"), - ("Port", "Ataka"), - ("Settings", "Ezarpenak"), - ("Username", "Erabiltzaile-izena"), - ("Invalid port", "Ataka baliogabea"), - ("Closed manually by the peer", "Parekideak konexioa eskuz itxi du"), - ("Enable remote configuration modification", "Gaitu urruneko konfigurazio-aldaketak"), - ("Run without install", "Exekutatu instalatu gabe"), - ("Connect via relay", "Konektatu igorpen-zerbitzari batetik"), - ("Always connect via relay", "Konektatu beti igorpen-zerbitzari batetik"), - ("whitelist_tip", "Baimendutako IPak soilik konektatu daitezke mahaigain honetara"), - ("Login", "Saio-hasiera"), - ("Verify", "Egiaztatu"), - ("Remember me", "Gogoratu"), - ("Trust this device", "Gailu honetaz fidatu"), - ("Verification code", "Egiaztapen-kodea"), - ("verification_tip", "Egiaztapen-kode bat bidali da erregistratutako helbide elektronikora. Sartu egiaztapen-kodea saio-hasiera jarraitzeko."), - ("Logout", "Saioa bukatu"), - ("Tags", "Etiketak"), - ("Search ID", "Bilatu IDa"), - ("whitelist_sep", "Koma, puntu koma, zuriune edo lerro berriengatik banatuta"), - ("Add ID", "Gehitu IDa"), - ("Add Tag", "Gehitu etiketa"), - ("Unselect all tags", "Desautatu etiketa guztiak"), - ("Network error", "Sare-errorea"), - ("Username missed", "Erabiltzaile-izena ahaztu duzu"), - ("Password missed", "Pasahitza ahaztu duzu"), - ("Wrong credentials", "Kredentzial baliogabeak"), - ("The verification code is incorrect or has expired", "Egiaztapen-kodea baliogabe edo iraungitua da"), - ("Edit Tag", "Editatu etiketa"), - ("Forget Password", "Ahaztu pasahitza"), - ("Favorites", "Gogokoenak"), - ("Add to Favorites", "Gehitu gogokoenetara"), - ("Remove from Favorites", "Kendu gpgokoenetatik"), - ("Empty", "Hutsik"), - ("Invalid folder name", "Karpeta-izen baliogabea"), - ("Socks5 Proxy", "Socks5 proxia"), - ("Socks5/Http(s) Proxy", "Socks5/Http(s) proxia"), - ("Discovered", "Aurkituta"), - ("install_daemon_tip", "Ordenagailua pizterakoan hasteko, sistemaren zerbitzua instalatu behar duzu."), - ("Remote ID", "Urruneko IDa"), - ("Paste", "Itsatsi"), - ("Paste here?", "Itsatsi hemen?"), - ("Are you sure to close the connection?", "Ziur zaude konexioa itxi nahi duzula?"), - ("Download new version", "Deskargatu bertsio berria"), - ("Touch mode", "Ukipen modua"), - ("Mouse mode", "Sagu modua"), - ("One-Finger Tap", "Hatz bakarreko ukipena"), - ("Left Mouse", "Ezkerreko botoia"), - ("One-Long Tap", "Ukipen luzea"), - ("Two-Finger Tap", "Bi hatzeko ukipena"), - ("Right Mouse", "Eskuineko botoia"), - ("One-Finger Move", "Hatz bakarreko mugimendua"), - ("Double Tap & Move", "Bi aldiz ukitu eta mugitu"), - ("Mouse Drag", "Saguarekin arrastatu"), - ("Three-Finger vertically", "Hiru hatz bertikalki"), - ("Mouse Wheel", "Saguaren gurpila"), - ("Two-Finger Move", "Bi hatzeko mugimendua"), - ("Canvas Move", "Oihal-mugimendua"), - ("Pinch to Zoom", "Atximurkatu zoom egiteko"), - ("Canvas Zoom", "Oihal-zooma"), - ("Reset canvas", "Berrezarri oihala"), - ("No permission of file transfer", "Ez duzu baimenik fitxategiak transferitzeko"), - ("Note", "Nota"), - ("Connection", "Konexioa"), - ("Share Screen", "Partekatu pantaila"), - ("Chat", "Txata"), - ("Total", "Guztira"), - ("items", "elementuak"), - ("Selected", "hautatuta"), - ("Screen Capture", "Pantaila-grabazioa"), - ("Input Control", "Sarrera-kontrola"), - ("Audio Capture", "Audio-grabazioa"), - ("File Connection", "Fitxategi-konexioa"), - ("Screen Connection", "Pantaila-konexioa"), - ("Do you accept?", "Onartzen al duzu?"), - ("Open System Setting", "Ireki sistemaren ezarpenak"), - ("How to get Android input permission?", "Nola lortu dezaket Android sarrera-baimena?"), - ("android_input_permission_tip1", "Urruneko gailu batek zure Android gailua saguaren edo ukipenaren bidez kontrolatzeko, RustDesk \"Irisgarritasuna\" zerbitzua erabiltzeko baimena eman behar diozu."), - ("android_input_permission_tip2", "Joan hurrengo irekiko den sistemaren konfigurazio orrira, bilatu eta sartu [Instalatutako zerbitzuak], aktibatu [RustDesk Input] zerbitzua."), - ("android_new_connection_tip", "Kontrol-eskaera berri bat jaso da uneko gailuarentzat."), - ("android_service_will_start_tip", "Pantaila-argazkia gaitzen baduzu, zerbitzua automatikoki abiaraziko da, eta beste gailu batzuek gailu honetatik konexioa eskatzeko aukera izango dute."), - ("android_stop_service_tip", "Zerbitzua ixteak ezarritako konexio guztiak automatikoki itxiko ditu."), - ("android_version_audio_tip", "Uneko Android bertsioak ez du audioa grabatzea onartzen; mesedez, eguneratu Android 10 edo berrira."), - ("android_start_service_tip", "Sakatu [Hasi zerbitzua] edo gaitu [Pantaila-grabazioa] baimena pantaila-partekatzea hasteko."), - ("android_permission_may_not_change_tip", "Ezarritako konexioen baimenak ez dira aldatuko berriro konektatu arte."), - ("Account", "Kontua"), - ("Overwrite", "Berridatzi"), - ("This file exists, skip or overwrite this file?", "Fitxategi hau existitzen da dagoeneko, saltatu edo berridatzi nahi duzu?"), - ("Quit", "Irten"), - ("Help", "Laguntza"), - ("Failed", "Huts egin du"), - ("Succeeded", "Arrakastatsua izan da"), - ("Someone turns on privacy mode, exit", "Norbaitek pribatutasun modua hasten du, irten"), - ("Unsupported", "Ez da onartzen"), - ("Peer denied", "Parekidea ukatuta"), - ("Please install plugins", "Mesedez, instalatu plugin hauek"), - ("Peer exit", "Parekidea irten da"), - ("Failed to turn off", "Itzaltzeak huts egin du"), - ("Turned off", "Itzalita"), - ("Language", "Hizkuntza"), - ("Keep RustDesk background service", "Mantendu RustDesk atzeko planoko zerbitzu bezala"), - ("Ignore Battery Optimizations", "Ezikusi bateria optimizazioak"), - ("android_open_battery_optimizations_tip", "Ezaugarri hau desgaitu nahi baduzu, joan zaitez RustDesk aplikazioaren ezarpen orrira, bilatu eta saltu [Bateria] orrira eta kendu [Mugarik gabe]"), - ("Start on boot", "Hasi abiaraztean"), - ("Start the screen sharing service on boot, requires special permissions", "Hasi pantaila-partekatze zerbitzua abiaraztean, baimen bereziak behar ditu"), - ("Connection not allowed", "Konexioa ez dago baimenduta"), - ("Legacy mode", "Legatu-modua"), - ("Map mode", "Mapa modua"), - ("Translate mode", "Itzultze modua"), - ("Use permanent password", "Erabili betirako pasahitza"), - ("Use both passwords", "Erabili bi pasahitzak"), - ("Set permanent password", "Ezarri betirako pasahitza"), - ("Enable remote restart", "Gaitu urruneko berrabiaraztea"), - ("Restart remote device", "Berrabiarazi urruneko gailua"), - ("Are you sure you want to restart", "Ziur zaude berrabiarazi nahi duzula?"), - ("Restarting remote device", "Urruneko gailua berrabiarazten"), - ("remote_restarting_tip", "Urruneko gailua berrabiarazten dabil. Mesedez, itxi mezu hau eta konektatu betirako pasahitzarekin une bat pasa ostean."), - ("Copied", "Kopiatuta"), - ("Exit Fullscreen", "Irten pantaila osotik"), - ("Fullscreen", "Pantaila osoa"), - ("Mobile Actions", "Mugikor-ekintzak"), - ("Select Monitor", "Hautatu monitorea"), - ("Control Actions", "Kontrol-ekintzak"), - ("Display Settings", "Pantailaren ezarpenak"), - ("Ratio", "Erlazioa"), - ("Image Quality", "Irudiaren kalitatea"), - ("Scroll Style", "Korritze estiloa"), - ("Show Toolbar", "Erakutsi tresna-barra"), - ("Hide Toolbar", "Ezkutatu tresna-barra"), - ("Direct Connection", "Konexio zuzena"), - ("Relay Connection", "Konexio igorria"), - ("Secure Connection", "Konexio segurua"), - ("Insecure Connection", "Konexio ez-segurua"), - ("Scale original", "Jatorrizko eskala"), - ("Scale adaptive", "Eskala moldagarria"), - ("General", "Orokorra"), - ("Security", "Segurtasuna"), - ("Theme", "Itxura"), - ("Dark Theme", "Itxura iluna"), - ("Light Theme", "Itxura argia"), - ("Dark", "Iluna"), - ("Light", "Argia"), - ("Follow System", "Jarraitu sistemaren itxura"), - ("Enable hardware codec", "Gaitu hardware kodeka"), - ("Unlock Security Settings", "Desblokeatu segurtasun ezarpenak"), - ("Enable audio", "Gaitu audioa"), - ("Unlock Network Settings", "Desblokeatu sare-ezarpenak"), - ("Server", "Zerbitzaria"), - ("Direct IP Access", "IP sarbide zuzena"), - ("Proxy", "Proxia"), - ("Apply", "Aplikatu"), - ("Disconnect all devices?", "Deskonektatu gailu guztiak?"), - ("Clear", "Garbitu"), - ("Audio Input Device", "Audio sarrera gailua"), - ("Use IP Whitelisting", "Erabili IP onartuen zerrenda"), - ("Network", "Sarea"), - ("Pin Toolbar", "Ainguratu tresna-barra"), - ("Unpin Toolbar", "Aingura kendu tresna-barrari"), - ("Recording", "Grabatzen"), - ("Directory", "Direktorioa"), - ("Automatically record incoming sessions", "Automatikoki grabatu sarrerako saioak"), - ("Change", "Aldatu"), - ("Start session recording", "Hasi saioaren grabaketa"), - ("Stop session recording", "Gelditu saioaren grabaketa"), - ("Enable recording session", "Gaitu saioen grabaketa"), - ("Enable LAN discovery", "Gaitu LAN ezagutza"), - ("Deny LAN discovery", "Ukatu LAN ezagutza"), - ("Write a message", "Idatzi mezu bat"), - ("Prompt", "Testu-gonbita"), - ("Please wait for confirmation of UAC...", "Mesedez, itxaron UAC berrespenari"), - ("elevated_foreground_window_tip", "Mahaigaineko uneko urruneko leihoak goi mailako baimenak behar ditu funtzionatzeko. Beraz, ezin duzu sagua eta teklatua erabili aldi baterako. Urruneko erabiltzaileari uneko leihoa minizatu edo kudeatze-leihoan maila igotzeko botoia erabili dezan eskatu diezaiokezu. Arazo hau saihesteko, programa instalatzea gomendatzen da urruneko gailuan."), - ("Disconnected", "Deskonektatuta"), - ("Other", "Besteak"), - ("Confirm before closing multiple tabs", "Berretsi fitxa ugari itxi baino lehen"), - ("Keyboard Settings", "Teklatuaren ezarpenak"), - ("Full Access", "Sarbide osoa"), - ("Screen Share", "Pantailaren partekatzea"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland Ubuntu 21.04 edo bertsio berriagoa behar du."), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland-ek linux banaketa berriago bat behar du. Saiatu X11 mahaigainarekin edo aldatu zure sistema eragilea."), - ("JumpLink", "Ikusi"), - ("Please Select the screen to be shared(Operate on the peer side).", "Mesedez, hautatu partekatuko den pantaila (Kudeatu parekidearen aldean)"), - ("Show RustDesk", "Erakutsi RustDesk"), - ("This PC", "PC hau"), - ("or", "edo"), - ("Continue with", "Jarraitu honekin"), - ("Elevate", "Igo maila"), - ("Zoom cursor", "Handitu kurtsorea"), - ("Accept sessions via password", "Onartu saioak pasahitzaren bidez"), - ("Accept sessions via click", "Onartu saioak klikaren bidez"), - ("Accept sessions via both", "Onatu saioak bientzako"), - ("Please wait for the remote side to accept your session request...", "Mesedez, itxaron urruneko aldeak zure saio eskaera onartu dezan..."), - ("One-time Password", "Aldi bateko pasahitza"), - ("Use one-time password", "Erabili aldi baterako pasahitza"), - ("One-time password length", "Aldi baterako pasahitzaren luzera"), - ("Request access to your device", "Eskatu sarrera zure gailura"), - ("Hide connection management window", "Ezkutatu konexio kudeatze leihoa"), - ("hide_cm_tip", "Utzi ezkutatzen saioak pasahitzarekin onartzen badira eta pasahitza betirakoa bada"), - ("wayland_experiment_tip", "Wayland euskarria oraindik fase esperimentalean dago, mesedez, erabili X11 arretarik gabeko sarrera behar baduzu."), - ("Right click to select tabs", "Eskuineko klika fitxak hautatzeko"), - ("Skipped", "Saltatuta"), - ("Add to address book", "Gehitu helbide-liburura"), - ("Group", "Taldea"), - ("Search", "Bilatu"), - ("Closed manually by web console", "Web kontsolarengatik eskuz itxita"), - ("Local keyboard type", "Teklatu mota lokala"), - ("Select local keyboard type", "Hautatu teklatu mota lokala"), - ("software_render_tip", "Nvidia bideo-txartela baduzu eta urruneko leihoa berehala ixten bada, nouveau driver-a instalatzea eta software errenderizazioa hautatzea lagundu dezake. Aplikazioa berrabiarazi behar da."), - ("Always use software rendering", "Erabili software bidezko errenderizazioa beti"), - ("config_input", "Urruneko mahaigaina teklatuaren bidez kontrolatzeko, RustDesk-i \"Sarrera monitorizazioa\" baimena eman behar diozu."), - ("config_microphone", "Urrunetik hitz egin ahal izateko, RustDeski-i \"Grabatu audioa\" baimena eman behar diozu."), - ("request_elevation_tip", "Pribilegioen maila igotzea eskatu ahal duzu ere norbait badago urruneko aldean"), - ("Wait", "Itxaron"), - ("Elevation Error", "Maila-igotze errorea"), - ("Ask the remote user for authentication", "Eskatu autentifikazioa urruneko erabiltzaileari"), - ("Choose this if the remote account is administrator", "Aukeratu urruneko kontua administratzailea bada"), - ("Transmit the username and password of administrator", "Igorri administratzailearen erabiltzailea eta pasahitza"), - ("still_click_uac_tip", "Oraindik beharrezkoa da urruneko erabiltzaileak Ondo botoiari klik egitea exekutatzen ari den RustDesk UAC leihoan"), - ("Request Elevation", "Eskatu pribilegioen maila igotzea"), - ("wait_accept_uac_tip", "Mesedez, itxaron urruneko erabiltzaileak UAC onartu arte."), - ("Elevate successfully", "Maila igotzea ondo joan da"), - ("uppercase", "maiuskula"), - ("lowercase", "minuskula"), - ("digit", "zenbakia"), - ("special character", "karaktere berezia"), - ("length>=8", "luzera>=8"), - ("Weak", "Ahula"), - ("Medium", "Ertaina"), - ("Strong", "Indartsua"), - ("Switch Sides", "Aldatu aldeak"), - ("Please confirm if you want to share your desktop?", "Mesedez, berretsi zure mahaigaina partekatu nahi duzula"), - ("Display", "Pantaila"), - ("Default View Style", "Ikuspen estilo lehenetsia"), - ("Default Scroll Style", "Korritze estilo lehenetsia"), - ("Default Image Quality", "Irudi kalitate lehenetsia"), - ("Default Codec", "Kodek lehenetsia"), - ("Bitrate", "Bit-tasa"), - ("FPS", "FPS"), - ("Auto", "Auto"), - ("Other Default Options", "Beste aukera lehenetsiak"), - ("Voice call", "Ahots-deia"), - ("Text chat", "Testu-txata"), - ("Stop voice call", "Gelditu ahots-deia"), - ("relay_hint_tip", "Posible da ezinezkoa izatea zuzen konektatzea. Igorpen zerbitzari baten bidez konektatzen saiatu zaitezke. Horrez gain, igorpena lehen aldiz erabili nahi baduzu, \"/r\" atzitu dezakezu IDari edo \"Beti igorpen zerbitzari baten bidez konektatu\" aukeratu azken saioen txartelan existitzen bada."), - ("Reconnect", "Berriro konektatu"), - ("Codec", "Kodeka"), - ("Resolution", "Bereizmena"), - ("No transfers in progress", "Ez dago transferentziarik abian"), - ("Set one-time password length", "Ezarri aldi baterako pasahitzaren luzera"), - ("RDP Settings", "RDP ezarpenak"), - ("Sort by", "Ordenatu honengatik"), - ("New Connection", "Konexio berria"), - ("Restore", "Berrezarri"), - ("Minimize", "Minimizatu"), - ("Maximize", "Maximizatu"), - ("Your Device", "Zure gailua"), - ("empty_recent_tip", "Ups, ez dago azken saiorik!\nBerri bat planifikatzeko ordua da."), - ("empty_favorite_tip", "Parekide gogokorik gabe oraindik?\nBilatu norbait konektatzeko eta gehitu zure gogokoetara!"), - ("empty_lan_tip", "Ai ez, badirudi ez duzula parekiderik aurkitu oraindik."), - ("empty_address_book_tip", "Badirudi ez dagoela parekiderik zure helbide-liburuan."), - ("eg: admin", "adib. admin"), - ("Empty Username", "Erabiltzaile-izena hutsik"), - ("Empty Password", "Pasahitza hutsik"), - ("Me", "Ni"), - ("identical_file_tip", "Fitxategi hau parekidearen berdina da."), - ("show_monitors_tip", "Erakutsi monitoreak tresna-barran"), - ("View Mode", "Ikuspen modua"), - ("login_linux_tip", "Urruneko Linux kontu batera hasi behar duzu saioa X mahaigain saio bat gaitzeko"), - ("verify_rustdesk_password_tip", "Berretsi RustDesk pasahitza"), - ("remember_account_tip", "Gogoratu kontu hau"), - ("os_account_desk_tip", "Kontu hau bururik gabe urruneko SE hasi eta mahaigaineko saioa gaitzeko erabiltzen da"), - ("OS Account", "SE kontua"), - ("another_user_login_title_tip", "Beste erabiltzaile batek saioa hasi du dagoeneko"), - ("another_user_login_text_tip", "Deskonektatu"), - ("xorg_not_found_title_tip", "Ez da Xorg aurkitu"), - ("xorg_not_found_text_tip", "Mesedez, instalatu ezazu Xorg"), - ("no_desktop_title_tip", "Ez dago mahaigainik eskuragarri"), - ("no_desktop_text_tip", "Mesedez, instalatu ezazu GNOME Desktop"), - ("No need to elevate", "Ez da beharrezkoa pribilegioen maila igotzea"), - ("System Sound", "Sistemaren soinua"), - ("Default", "Lehenetsia"), - ("New RDP", "RDP berria"), - ("Fingerprint", "Hatz-marka"), - ("Copy Fingerprint", "Kopiatu hatz-marka"), - ("no fingerprints", "hatz-markarik ez"), - ("Select a peer", "Hautatu parekidea"), - ("Select peers", "Hautatu parekideak"), - ("Plugins", "Pluginak"), - ("Uninstall", "Desinstalatu"), - ("Update", "Eguneratu"), - ("Enable", "Gaitu"), - ("Disable", "Desgaitu"), - ("Options", "Aukerak"), - ("resolution_original_tip", "Jatorrizko bereizmena"), - ("resolution_fit_local_tip", "Bereizmen lokala egokitu"), - ("resolution_custom_tip", "Bereizmen pertsonalizatua"), - ("Collapse toolbar", "Ezkutatu tresna-barra"), - ("Accept and Elevate", "Onartu eta igo maila"), - ("accept_and_elevate_btn_tooltip", "Konexioa onartu eta UAC baimenak mailaz igo."), - ("clipboard_wait_response_timeout_tip", "Kopiatzeko denbora-muga gainditu da."), - ("Incoming connection", "Sarrerako konexioa"), - ("Outgoing connection", "Irteerako konexioa"), - ("Exit", "Irten"), - ("Open", "Ireki"), - ("logout_tip", "Saioa itxi nahi duzu?"), - ("Service", "Zerbitzua"), - ("Start", "Hasi"), - ("Stop", "Gelditu"), - ("exceed_max_devices", "Kudeatutako gailuen mugara heldu zara."), - ("Sync with recent sessions", "Sinkronizatu azken saioekin"), - ("Sort tags", "Ordenatu etiketak"), - ("Open connection in new tab", "Ireki konexioa fitxa berri batean"), - ("Move tab to new window", "Mugitu fitxa leiho berri batera"), - ("Can not be empty", "Ezin da hutsik egon"), - ("Already exists", "Dagoeneko existitzen da"), - ("Change Password", "Aldatu pasahitza"), - ("Refresh Password", "Freskatu pasahitza"), - ("ID", "ID"), - ("Grid View", "Sareta-ikuspegia"), - ("List View", "Zerrenda-ikuspegia"), - ("Select", "Hautatu"), - ("Toggle Tags", "Aldatu etiketak"), - ("pull_ab_failed_tip", "Ezin izan da direktorio freskatu"), - ("push_ab_failed_tip", "Ezin izan da zerbitzariko direktorioarekin sinkronizatu"), - ("synced_peer_readded_tip", "Azken saioetako gailuak direktorioarekin sinkronizatuko dira"), - ("Change Color", "Aldatu kolorea"), - ("Primary Color", "Kolore nagusia"), - ("HSV Color", "HSV kolorea"), - ("Installation Successful!", "Instalazioa ondo joan da"), - ("Installation failed!", "Instalazioak huts egin du"), - ("Reverse mouse wheel", "Inbertitu saguaren gurpila"), - ("{} sessions", "{} saio"), - ("scam_title", "IRUZURTUA izan zintezke!"), - ("scam_text1", "Ezagutzen eta fidatzen EZ duzun norbaitekin telefonoz bazaude eta RustDesk erabiltzeko eta zerbitzua abiarazteko eskatu badizute, ez ezazu egin eta eskegi berehala."), - ("scam_text2", "Ziurrenik zure dirua edo informazio pribatua lapurtzen saiatzen ari diren iruzurgileak dira."), - ("Don't show again", "Ez erakutsi berriro"), - ("I Agree", "Onartzen dut"), - ("Decline", "Ukatu"), - ("Timeout in minutes", "Denbora-muga minututan"), - ("auto_disconnect_option_tip", "Itxi sarrerako konexioak automatikoki erabiltzailearen jarduera faltagatik."), - ("Connection failed due to inactivity", "Konexioak huts egin du jarduera faltagatik"), - ("Check for software update on startup", "Egiaztatu software eguneraketak abiatzerakoan"), - ("upgrade_rustdesk_server_pro_to_{}_tip", "Mesedez, bertsio-berritu RustDesk Server Pro {} bertsiora edo berriago batera!"), - ("pull_group_failed_tip", "Ezin izan da taldea freskatu"), - ("Filter by intersection", "Iragazi bidegurutzez"), - ("Remove wallpaper during incoming sessions", "Kendu horma-papera sarrerako saioetan"), - ("Test", "Probatu"), - ("display_is_plugged_out_msg", "Pantaila deskonektatuta dago, aldatu nagusira."), - ("No displays", "Ez dago pantailarik"), - ("elevated_switch_display_msg", "Aldatu pantaila nagusira pantaila ugari ez daudelako goi mailako moduan onartuta"), - ("Open in new window", "Ireki leiho berrian"), - ("Show displays as individual windows", "Erakutsi pantailak banakako leiho gisa"), - ("Use all my displays for the remote session", "Erabili nire pantaila guztiak urruneko saiorako"), - ("selinux_tip", "SELinux zure gailuan gaituta dago. Honek RustDesk kontrolatutako alde gisa dagoenean behar bezala ez exekutatzea ekarri dezake."), - ("Change view", "Aldatu ikuspegia"), - ("Big tiles", "Baldosa handiak"), - ("Small tiles", "Baldosa txikiak"), - ("List", "Zerrenda"), - ("Virtual display", "Pantaila birtuala"), - ("Plug out all", "Deskonektatu dena"), - ("True color (4:4:4)", "Kolore erreala (4:4:4)"), - ("Enable blocking user input", "Gaitu erabiltzailearen sarreraren blokeoa"), - ("id_input_tip", "ID bat, IP zuzena edo ataka duen domeinu bat sar dezakezu (:).\nBeste zerbitzari bateko gailu batera sartu nahi baduzu, erantsi zerbitzariaren helbidea (@?key=), adibidez,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nZerbitzari publiko bateko gailuan sartu nahi baduzu, idatzi \"@public\", gakoa ez da zerbitzari publikorako beharrezkoa.\n\nLehen konexioan igorpen-zerbitzari bat erabiltzea behartu nahi baduzu, gehitu \"/r\" IDaren amaieran, adibidez, \"9123456234/r\"."), - ("privacy_mode_impl_mag_tip", "1 modua"), - ("privacy_mode_impl_virtual_display_tip", "2 modua"), - ("Enter privacy mode", "Sartu modu pribatuan"), - ("Exit privacy mode", "Irten modu pribatutik"), - ("idd_not_support_under_win10_2004_tip", "Zeharkako pantaila kudeatzailea ez dago onartuta. Windows 10, 2004 bertsioa edo berriagoa behar da."), - ("switch_display_elevated_connections_tip", "Pantaila nagusia ez den batera aldatzea ez da onartzen goi mailako moduan hainbat konexio daudenean. Mesedez, saiatu berriro instalatu ondoren hainbat pantaila kontrolatu nahi badituzu."), - ("input_source_1_tip", "Sarrera-iturri 1"), - ("input_source_2_tip", "Sarrera-iturri 2"), - ("capture_display_elevated_connections_tip", "Pantaila anitz harrapatzea goi mailako pribilegioekin ez da onartzen. Mesedez, saiatu berriro instalatu ondoren hainbat pantaila kontrolatu nahi badituzu."), - ("Swap control-command key", "Aldatu kontrol-komando teklak"), - ("swap-left-right-mouse", "Aldatu saguaren ezker-eskuin botoiak"), - ("2FA code", "2FA kodea"), - ("More", "Gehiago"), - ("enable-2fa-title", "Gaitu bi faktoreko autentifikazioa"), - ("enable-2fa-desc", "Konfiguratu orain zure autentifikatzailea. Authy, Microsoft edo Google Authenticator bezalako aplikazio autentifikatzaile bat erabil dezakezu telefonoan edo mahaigainean.\n\nEskaneatu QR kodea zure aplikazioarekin eta idatzi aplikazioak erakusten duen kodea bi faktoreko autentifikazioa gaitzeko."), - ("wrong-2fa-code", "Ezin da kodea egiaztatu. Egiaztatu kodea eta tokiko orduaren ezarpenak zuzenak direla"), - ("enter-2fa-title", "Bi faktoreko autentifikazioa"), - ("Email verification code must be 6 characters.", "Posta elektronikoa egiaztatzeko kodeak 6 karaktere izan behar ditu."), - ("2FA code must be 6 digits.", "2FA kodeak 6 digitu izan behar ditu."), - ("Multiple Windows sessions found", "Windows saio anitz aurkitu dira"), - ("Please select the session you want to connect to", "Mesedez, aukeratu konektatu nahi duzun saioa"), - ("powered_by_me", "RustDesk-ek egina"), - ("outgoing_only_desk_tip", "Edizio pertsonalizatua da hau.\nBeste gailuetara konekta zaitezke, baina beste gailu batzuk ezin dira zure gailura konektatu."), - ("preset_password_warning", "Edizio pertsonalizatu hau aurrez ezarritako pasahitz batekin dator. Pasahitz hau ezagutzen duenak zure gailuaren kontrol osoa lor dezake. Hau espero ez bazenuen, desinstalatu softwarea berehala."), - ("Security Alert", "Segurtasun Alerta"), - ("My address book", "Nire helbide-liburua"), - ("Personal", "Pertsonala"), - ("Owner", "Jabea"), - ("Set shared password", "Ezarri pasahitz partekatua"), - ("Exist in", "Bertan existitzen da"), - ("Read-only", "Irakurtzeko soilik"), - ("Read/Write", "Irakurri/Idatzi"), - ("Full Control", "Kontrol Osoa"), - ("share_warning_tip", "Goiko eremuak partekatuak eta besteengatik ikusgai daude."), - ("Everyone", "Denek"), - ("ab_web_console_tip", "Gehiago web kontsolan"), - ("allow-only-conn-window-open-tip", "Baimendu konexioa RustDesk leihoa irekita badago"), - ("no_need_privacy_mode_no_physical_displays_tip", "Ez dago pantaila fisikorik, ez dago pribatutasun modua erabili beharrik."), - ("Follow remote cursor", "Jarraitu urruneko kurtsorea"), - ("Follow remote window focus", "Jarraitu urruneko leiho fokua"), - ("default_proxy_tip", "Lehenetsitako protokoloa eta ataka Socks5 eta 1080 dira"), - ("no_audio_input_device_tip", "Ez da aurkitu audio sarrerako gailurik."), - ("Incoming", "Sarrerakoa"), - ("Outgoing", "Irteerakoa"), - ("Clear Wayland screen selection", "Garbitu Wayland pantaila-hautapena"), - ("clear_Wayland_screen_selection_tip", "Pantaila hautapena garbitu ondoren, partekatzeko pantaila hauta dezakezu."), - ("confirm_clear_Wayland_screen_selection_tip", "Ziur Wayland pantaila-hautapena garbituko duzula?"), - ("android_new_voice_call_tip", "Ahots-dei eskaera berri bat jaso da. Onartzen baduzu, audioa ahots bidezko komunikaziora aldatuko da."), - ("texture_render_tip", "Erabili testura-errenderizazioa irudiak leunagoak egiteko."), - ("Use texture rendering", "Erabili testura-errenderizazioa"), - ("Floating window", "Leiho flotagarria"), - ("floating_window_tip", "RustDesk atzeko planoko zerbitzua mantentzen laguntzen du"), - ("Keep screen on", "Mantendu pantaila piztuta"), - ("Never", "Inoiz"), - ("During controlled", "Kontrolatu bitartean"), - ("During service is on", "Zerbitzua aktibo dagoen bitartean"), - ("Capture screen using DirectX", "Harrapatu pantaila DirectX erabiliz"), - ("Back", "Atzera"), - ("Apps", "Aplikazioak"), - ("Volume up", "Igo bolumena"), - ("Volume down", "Jaitsi bolumena"), - ("Power", "Piztu/Itzali"), - ("Telegram bot", "Telegrameko bot-a"), - ("enable-bot-tip", "Ezaugarri hau gaitzen baduzu, zure bot-etik 2FA kodea jaso dezakezu. Konexio jakinarazpenetarako ere balio dezake."), - ("enable-bot-desc", "1, Ireki txat bat @BotFather bot-arekin.\n2, Bidali \"/newbot\" agindua. Token bat jasoko duzu urrats honen ostean.\n3, Hasi txat bat zure bot berriarekin. Bidali mezu bat aurreko barra batekin (\"/\") \"/kaixo\" bezala gaitzeko.\n"), - ("About RustDesk", ""), +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Egoera"), + ("Your Desktop", "Zure mahaigaina"), + ("desk_tip", "Mahaigainera zure ID eta pasahitzarekin sartu zaitezke"), + ("Password", "Pasahitza"), + ("Ready", "Prest"), + ("Established", "Ezarrita"), + ("connecting_status", "RustDesk sarera konektatzen..."), + ("Enable service", "Gaitu zerbitzua"), + ("Start service", "Hasi zerbitzua"), + ("Service is running", "Zerbitzua martxan dago"), + ("Service is not running", "Zerbitzua ez dago martxan"), + ("not_ready_status", "Ez dago prest. Mesedez, egiaztatu zure konexioa"), + ("Control Remote Desktop", "Kontrolatu urruneko mahaigaina"), + ("Transfer file", "Transferitu fitxategia"), + ("Connect", "Konektatu"), + ("Recent sessions", "Azken saioak"), + ("Address book", "Helbide-liburua"), + ("Confirmation", "Berrespena"), + ("TCP tunneling", "TCP tunela"), + ("Remove", "Kendu"), + ("Refresh random password", "Freskatu ausazko pasahitza"), + ("Set your own password", "Ezarri zure pasahitza"), + ("Enable keyboard/mouse", "Gaitu teklatua/sagua"), + ("Enable clipboard", "Gaitu arbela"), + ("Enable file transfer", "Gaitu fitxategien transferentzia"), + ("Enable TCP tunneling", "Gaitu TCP tunela"), + ("IP Whitelisting", "Onartutako IP helbideak"), + ("ID/Relay Server", "ID/Relay zerbitzaria"), + ("Import server config", "Inportatu zerbitzariaren konfigurazioa"), + ("Export Server Config", "Esportatu zerbitzariaren konfigurazioa"), + ("Import server configuration successfully", "Zerbitzariaren konfigurazioa ondo inportatu da"), + ("Export server configuration successfully", "Zerbitzariaren konfigurazioa ondo esportatu da"), + ("Invalid server configuration", "Zerbitzariaren konfigurazioa baliogabea da"), + ("Clipboard is empty", "Arbela hutsik dago"), + ("Stop service", "Gelditu zerbitzua"), + ("Change ID", "Aldatu IDa"), + ("Your new ID", "Zure ID berria"), + ("length %min% to %max%", "%min%(e)tik %max% arteko luzera"), + ("starts with a letter", "hizki batekin hasten da"), + ("allowed characters", "onartutako karaktereak"), + ("id_change_tip", "Soilik a-z, A-Z, 0-9 eta _ (barra baxua) karaktereak daude onartuta. Lehen hizkia a-z, A-Z izan behar da. Luzera 6 eta 16 artekoa izan behar da."), + ("Website", "Webgunea"), + ("About", "Honi buruz"), + ("Slogan_tip", "Bihotzez eginda mundu kaotiko honetan!"), + ("Privacy Statement", "Pribatutasun-politika"), + ("Mute", "Mututu"), + ("Build Date", "Konpilazio-data"), + ("Version", "Bertsioa"), + ("Home", "Hasiera"), + ("Audio Input", "Audio sarrera"), + ("Enhancements", "Hobespenak"), + ("Hardware Codec", "Hardware kodeka"), + ("Adaptive bitrate", "Bit-emari moldagarria"), + ("ID Server", "ID zerbitzaria"), + ("Relay Server", "Relay zerbitzaria"), + ("API Server", "API zerbitzaria"), + ("invalid_http", "http:// edo https://-rekin hasi behar da"), + ("Invalid IP", "IP baliogabea"), + ("Invalid format", "Formatu baliogabea"), + ("server_not_support", "Oraindik ez dago zerbitzariarengatik onartuta"), + ("Not available", "Ez dago eskuragarri"), + ("Too frequent", "Sarriegia"), + ("Cancel", "Utzi"), + ("Skip", "Saltatu"), + ("Close", "Itxi"), + ("Retry", "Saiatu berriro"), + ("OK", "Ondo"), + ("Password Required", "Pasahitza beharrezkoa da"), + ("Please enter your password", "Mesedez, sartu zure pasahitza"), + ("Remember password", "Gogoratu pasahitza"), + ("Wrong Password", "Pasahitz okerra"), + ("Do you want to enter again?", "Berriro sartu nahi zara?"), + ("Connection Error", "Konexio errorea"), + ("Error", "Errorea"), + ("Reset by the peer", "Konexioa parekidearengatik berrezarrita"), + ("Connecting...", "Konektatzen..."), + ("Connection in progress. Please wait.", "Konexioa abian da. Itxaron mesedez."), + ("Please try 1 minute later", "Mesedez, saiatu berrito minutu bat pasata"), + ("Login Error", "Saio-hasiera errorea"), + ("Successful", "Arrakastatsua"), + ("Connected, waiting for image...", "Konektatuta, irudiaren zain..."), + ("Name", "Izena"), + ("Type", "Mota"), + ("Modified", "Aldatua"), + ("Size", "Tamaina"), + ("Show Hidden Files", "Erakutsi ezkutuko fitxategiak"), + ("Receive", "Jaso"), + ("Send", "Bidali"), + ("Refresh File", "Freskatu fitxategia"), + ("Local", "Lokala"), + ("Remote", "Urrunekoa"), + ("Remote Computer", "Urruneko ordenagailua"), + ("Local Computer", "Ordenagailu lokala"), + ("Confirm Delete", "Berretsi ezabapena"), + ("Delete", "Ezabatu"), + ("Properties", "Ezaugarriak"), + ("Multi Select", "Multi-hautapena"), + ("Select All", "Hautatu guztiak"), + ("Unselect All", "Desautatu denak"), + ("Empty Directory", "Karpeta hutsa"), + ("Not an empty directory", "Ez da karpeta huts bat"), + ("Are you sure you want to delete this file?", "Ziur zaude fitxategi hau ezabatu nahi duzula?"), + ("Are you sure you want to delete this empty directory?", "Ziur zaude karpeta huts hau ezabatu nahi duzula?"), + ("Are you sure you want to delete the file of this directory?", "Ziur zaude karpeta honen fitxategia ezabatu nahi duzula?"), + ("Do this for all conflicts", "Egin hau gatazka guztietarako"), + ("This is irreversible!", "Hau ezin da atzera bueltatu!"), + ("Deleting", "Ezabatzen"), + ("files", "fitxategiak"), + ("Waiting", "Zain"), + ("Finished", "Bukatuta"), + ("Speed", "Abiadura"), + ("Custom Image Quality", "Irudi kalitate pertsonalizatua"), + ("Privacy mode", "Pribatutasun modua"), + ("Block user input", "Blokeatu erabiltzailearen sarrera"), + ("Unblock user input", "Desblokeatu erabiltzailearen sarrera"), + ("Adjust Window", "Doitu leihoa"), + ("Original", "Originala"), + ("Shrink", "Txikitu"), + ("Stretch", "Luzatu"), + ("Scrollbar", "Korritze-barra"), + ("ScrollAuto", "Korritze automatikoa"), + ("Good image quality", "Irudi kalitate ona"), + ("Balanced", "Orekatua"), + ("Optimize reaction time", "Optimizatu erreakzio-denbiora"), + ("Custom", "Pertsonalizatua"), + ("Show remote cursor", "Erakutsi urruneko kurtsorea"), + ("Show quality monitor", "Erakutsi kalitate monitorea"), + ("Disable clipboard", "Desgaitu arbela"), + ("Lock after session end", "Blokeatu sesioa amaitu ostean"), + ("Insert", "Sartu"), + ("Insert Lock", "Sarrera-blokeoa"), + ("Refresh", "Freskatu"), + ("ID does not exist", "IDa ez da existitzen"), + ("Failed to connect to rendezvous server", "Topaketa zerbitzarira konektatzeak huts egin du"), + ("Please try later", "Mesedez, saiatu berriro geroago"), + ("Remote desktop is offline", "Urruneko mahaigaina lineaz kanpo dago"), + ("Key mismatch", "Gakoak ez datoz bat"), + ("Timeout", "Denbora-muga"), + ("Failed to connect to relay server", "Igorpen zerbitzarira konektatzeak huts egin du"), + ("Failed to connect via rendezvous server", "Topaketa zerbitzariaren bidez konektatzeak huts egin du"), + ("Failed to connect via relay server", "Igorpen zerbitzariaren bidez konektatzeak huts egin du"), + ("Failed to make direct connection to remote desktop", "Urruneko mahaigainera zuzeneko konexio bat ezartzeak huts egin du"), + ("Set Password", "Ezarri pasahitza"), + ("OS Password", "Sistema eragilearen pasahitza"), + ("install_tip", "Erabiltzaile Kontuen Kontrolarengatik, RustDesk ezin du ondo funtzionatu urruneko mahaigainean. EKK saihesteko, mesedez, egin klik azpiko botoian RustDesk sistema mailan instalatzeko."), + ("Click to upgrade", "Egin klik bertsio-berritzeko"), + ("Click to download", "Egin klik deskargatzeko"), + ("Click to update", "Egin klik eguneratzeko"), + ("Configure", "Konfiguratu"), + ("config_acc", "Zure mahaigaina urrunetik kontrolatzeko, RustDesk-i \"Irisgarritasuna\" baimenak eman behar dituzu."), + ("config_screen", "Zure mahaigaina kanpotik kontrolatzeko, RustDesk-i \"Pantaila grabatu\" baimena eman behar duzu."), + ("Installing ...", "Instalantzen..."), + ("Install", "Instalatu"), + ("Installation", "Instalazioa"), + ("Installation Path", "Instalazio bide-izena"), + ("Create start menu shortcuts", "Sortu hasiera-menuko lasterbideak"), + ("Create desktop icon", "Sortu mahaigaineko ikonoa"), + ("agreement_tip", "Instalazioa hastean, lizentzia-kontratua onartzen duzu."), + ("Accept and Install", "Onartu eta instalatu"), + ("End-user license agreement", "Azken erabiltzailearen lizentzia akordioa"), + ("Generating ...", "Sortzen..."), + ("Your installation is lower version.", "Zure instalazioak bertsio zaharragoa du."), + ("not_close_tcp_tip", "Ez itxi leiho hau tunela erabili bitartean"), + ("Listening ...", "Entzuten..."), + ("Remote Host", "Urruneko ostalaria"), + ("Remote Port", "Urruneko ataka"), + ("Action", "Ekintza"), + ("Add", "Gehitu"), + ("Local Port", "Ataka lokala"), + ("Local Address", "Helbide lokala"), + ("Change Local Port", "Aldatu ataka lokala"), + ("setup_server_tip", "Konexio azkarragorako, konfiguratu zure zerbitzaria"), + ("Too short, at least 6 characters.", "Laburregia, 6 karaktere gutxienez."), + ("The confirmation is not identical.", "Berrespena ez dator bat."), + ("Permissions", "Baimenak"), + ("Accept", "Onartu"), + ("Dismiss", "Ezeztatu"), + ("Disconnect", "Deskonektatu"), + ("Enable file copy and paste", "Gaitu fitxategien kopiatze eta itsastea"), + ("Connected", "Konektatuta"), + ("Direct and encrypted connection", "Zifratutako konexio zuzena"), + ("Relayed and encrypted connection", "Zifratutako konexio igorria"), + ("Direct and unencrypted connection", "Zifratu gabeko konexio zuzena"), + ("Relayed and unencrypted connection", "Zifratu gabeko konexio igorria"), + ("Enter Remote ID", "Sartu urruneko IDa"), + ("Enter your password", "Sartu zure pasahitza"), + ("Logging in...", "Saioa hasten..."), + ("Enable RDP session sharing", "Gaitu RDP saio-partekatzea"), + ("Auto Login", "Saio-haste automatikoa"), + ("Enable direct IP access", "Gaitu IP sarbide zuzena"), + ("Rename", "Berrizendatu"), + ("Space", "Zuriunea"), + ("Create desktop shortcut", "Sortu mahaigaineko lasterbidea"), + ("Change Path", "Aldatu bide-izena"), + ("Create Folder", "Sortu karpeta"), + ("Please enter the folder name", "Mesedez, sartu karpetaren izena"), + ("Fix it", "Konpondu"), + ("Warning", "Oharra"), + ("Login screen using Wayland is not supported", "Saio-hasiera Wayland erabilita ez dago onartuta"), + ("Reboot required", "Berrabiaraztea beharrezkoa"), + ("Unsupported display server", "Bistaratze-zerbitzaria ez da bateragarria"), + ("x11 expected", "x11 espero zen"), + ("Port", "Ataka"), + ("Settings", "Ezarpenak"), + ("Username", "Erabiltzaile-izena"), + ("Invalid port", "Ataka baliogabea"), + ("Closed manually by the peer", "Parekideak konexioa eskuz itxi du"), + ("Enable remote configuration modification", "Gaitu urruneko konfigurazio-aldaketak"), + ("Run without install", "Exekutatu instalatu gabe"), + ("Connect via relay", "Konektatu igorpen-zerbitzari batetik"), + ("Always connect via relay", "Konektatu beti igorpen-zerbitzari batetik"), + ("whitelist_tip", "Baimendutako IPak soilik konektatu daitezke mahaigain honetara"), + ("Login", "Saio-hasiera"), + ("Verify", "Egiaztatu"), + ("Remember me", "Gogoratu"), + ("Trust this device", "Gailu honetaz fidatu"), + ("Verification code", "Egiaztapen-kodea"), + ("verification_tip", "Egiaztapen-kode bat bidali da erregistratutako helbide elektronikora. Sartu egiaztapen-kodea saio-hasiera jarraitzeko."), + ("Logout", "Saioa bukatu"), + ("Tags", "Etiketak"), + ("Search ID", "Bilatu IDa"), + ("whitelist_sep", "Koma, puntu koma, zuriune edo lerro berriengatik banatuta"), + ("Add ID", "Gehitu IDa"), + ("Add Tag", "Gehitu etiketa"), + ("Unselect all tags", "Desautatu etiketa guztiak"), + ("Network error", "Sare-errorea"), + ("Username missed", "Erabiltzaile-izena ahaztu duzu"), + ("Password missed", "Pasahitza ahaztu duzu"), + ("Wrong credentials", "Kredentzial baliogabeak"), + ("The verification code is incorrect or has expired", "Egiaztapen-kodea baliogabe edo iraungitua da"), + ("Edit Tag", "Editatu etiketa"), + ("Forget Password", "Ahaztu pasahitza"), + ("Favorites", "Gogokoenak"), + ("Add to Favorites", "Gehitu gogokoenetara"), + ("Remove from Favorites", "Kendu gpgokoenetatik"), + ("Empty", "Hutsik"), + ("Invalid folder name", "Karpeta-izen baliogabea"), + ("Socks5 Proxy", "Socks5 proxia"), + ("Socks5/Http(s) Proxy", "Socks5/Http(s) proxia"), + ("Discovered", "Aurkituta"), + ("install_daemon_tip", "Ordenagailua pizterakoan hasteko, sistemaren zerbitzua instalatu behar duzu."), + ("Remote ID", "Urruneko IDa"), + ("Paste", "Itsatsi"), + ("Paste here?", "Itsatsi hemen?"), + ("Are you sure to close the connection?", "Ziur zaude konexioa itxi nahi duzula?"), + ("Download new version", "Deskargatu bertsio berria"), + ("Touch mode", "Ukipen modua"), + ("Mouse mode", "Sagu modua"), + ("One-Finger Tap", "Hatz bakarreko ukipena"), + ("Left Mouse", "Ezkerreko botoia"), + ("One-Long Tap", "Ukipen luzea"), + ("Two-Finger Tap", "Bi hatzeko ukipena"), + ("Right Mouse", "Eskuineko botoia"), + ("One-Finger Move", "Hatz bakarreko mugimendua"), + ("Double Tap & Move", "Bi aldiz ukitu eta mugitu"), + ("Mouse Drag", "Saguarekin arrastatu"), + ("Three-Finger vertically", "Hiru hatz bertikalki"), + ("Mouse Wheel", "Saguaren gurpila"), + ("Two-Finger Move", "Bi hatzeko mugimendua"), + ("Canvas Move", "Oihal-mugimendua"), + ("Pinch to Zoom", "Atximurkatu zoom egiteko"), + ("Canvas Zoom", "Oihal-zooma"), + ("Reset canvas", "Berrezarri oihala"), + ("No permission of file transfer", "Ez duzu baimenik fitxategiak transferitzeko"), + ("Note", "Nota"), + ("Connection", "Konexioa"), + ("Share Screen", "Partekatu pantaila"), + ("Chat", "Txata"), + ("Total", "Guztira"), + ("items", "elementuak"), + ("Selected", "hautatuta"), + ("Screen Capture", "Pantaila-grabazioa"), + ("Input Control", "Sarrera-kontrola"), + ("Audio Capture", "Audio-grabazioa"), + ("File Connection", "Fitxategi-konexioa"), + ("Screen Connection", "Pantaila-konexioa"), + ("Do you accept?", "Onartzen al duzu?"), + ("Open System Setting", "Ireki sistemaren ezarpenak"), + ("How to get Android input permission?", "Nola lortu dezaket Android sarrera-baimena?"), + ("android_input_permission_tip1", "Urruneko gailu batek zure Android gailua saguaren edo ukipenaren bidez kontrolatzeko, RustDesk \"Irisgarritasuna\" zerbitzua erabiltzeko baimena eman behar diozu."), + ("android_input_permission_tip2", "Joan hurrengo irekiko den sistemaren konfigurazio orrira, bilatu eta sartu [Instalatutako zerbitzuak], aktibatu [RustDesk Input] zerbitzua."), + ("android_new_connection_tip", "Kontrol-eskaera berri bat jaso da uneko gailuarentzat."), + ("android_service_will_start_tip", "Pantaila-argazkia gaitzen baduzu, zerbitzua automatikoki abiaraziko da, eta beste gailu batzuek gailu honetatik konexioa eskatzeko aukera izango dute."), + ("android_stop_service_tip", "Zerbitzua ixteak ezarritako konexio guztiak automatikoki itxiko ditu."), + ("android_version_audio_tip", "Uneko Android bertsioak ez du audioa grabatzea onartzen; mesedez, eguneratu Android 10 edo berrira."), + ("android_start_service_tip", "Sakatu [Hasi zerbitzua] edo gaitu [Pantaila-grabazioa] baimena pantaila-partekatzea hasteko."), + ("android_permission_may_not_change_tip", "Ezarritako konexioen baimenak ez dira aldatuko berriro konektatu arte."), + ("Account", "Kontua"), + ("Overwrite", "Berridatzi"), + ("This file exists, skip or overwrite this file?", "Fitxategi hau existitzen da dagoeneko, saltatu edo berridatzi nahi duzu?"), + ("Quit", "Irten"), + ("Help", "Laguntza"), + ("Failed", "Huts egin du"), + ("Succeeded", "Arrakastatsua izan da"), + ("Someone turns on privacy mode, exit", "Norbaitek pribatutasun modua hasten du, irten"), + ("Unsupported", "Ez da onartzen"), + ("Peer denied", "Parekidea ukatuta"), + ("Please install plugins", "Mesedez, instalatu plugin hauek"), + ("Peer exit", "Parekidea irten da"), + ("Failed to turn off", "Itzaltzeak huts egin du"), + ("Turned off", "Itzalita"), + ("Language", "Hizkuntza"), + ("Keep RustDesk background service", "Mantendu RustDesk atzeko planoko zerbitzu bezala"), + ("Ignore Battery Optimizations", "Ezikusi bateria optimizazioak"), + ("android_open_battery_optimizations_tip", "Ezaugarri hau desgaitu nahi baduzu, joan zaitez RustDesk aplikazioaren ezarpen orrira, bilatu eta saltu [Bateria] orrira eta kendu [Mugarik gabe]"), + ("Start on boot", "Hasi abiaraztean"), + ("Start the screen sharing service on boot, requires special permissions", "Hasi pantaila-partekatze zerbitzua abiaraztean, baimen bereziak behar ditu"), + ("Connection not allowed", "Konexioa ez dago baimenduta"), + ("Legacy mode", "Legatu-modua"), + ("Map mode", "Mapa modua"), + ("Translate mode", "Itzultze modua"), + ("Use permanent password", "Erabili betirako pasahitza"), + ("Use both passwords", "Erabili bi pasahitzak"), + ("Set permanent password", "Ezarri betirako pasahitza"), + ("Enable remote restart", "Gaitu urruneko berrabiaraztea"), + ("Restart remote device", "Berrabiarazi urruneko gailua"), + ("Are you sure you want to restart", "Ziur zaude berrabiarazi nahi duzula?"), + ("Restarting remote device", "Urruneko gailua berrabiarazten"), + ("remote_restarting_tip", "Urruneko gailua berrabiarazten dabil. Mesedez, itxi mezu hau eta konektatu betirako pasahitzarekin une bat pasa ostean."), + ("Copied", "Kopiatuta"), + ("Exit Fullscreen", "Irten pantaila osotik"), + ("Fullscreen", "Pantaila osoa"), + ("Mobile Actions", "Mugikor-ekintzak"), + ("Select Monitor", "Hautatu monitorea"), + ("Control Actions", "Kontrol-ekintzak"), + ("Display Settings", "Pantailaren ezarpenak"), + ("Ratio", "Erlazioa"), + ("Image Quality", "Irudiaren kalitatea"), + ("Scroll Style", "Korritze estiloa"), + ("Show Toolbar", "Erakutsi tresna-barra"), + ("Hide Toolbar", "Ezkutatu tresna-barra"), + ("Direct Connection", "Konexio zuzena"), + ("Relay Connection", "Konexio igorria"), + ("Secure Connection", "Konexio segurua"), + ("Insecure Connection", "Konexio ez-segurua"), + ("Scale original", "Jatorrizko eskala"), + ("Scale adaptive", "Eskala moldagarria"), + ("General", "Orokorra"), + ("Security", "Segurtasuna"), + ("Theme", "Itxura"), + ("Dark Theme", "Itxura iluna"), + ("Light Theme", "Itxura argia"), + ("Dark", "Iluna"), + ("Light", "Argia"), + ("Follow System", "Jarraitu sistemaren itxura"), + ("Enable hardware codec", "Gaitu hardware kodeka"), + ("Unlock Security Settings", "Desblokeatu segurtasun ezarpenak"), + ("Enable audio", "Gaitu audioa"), + ("Unlock Network Settings", "Desblokeatu sare-ezarpenak"), + ("Server", "Zerbitzaria"), + ("Direct IP Access", "IP sarbide zuzena"), + ("Proxy", "Proxia"), + ("Apply", "Aplikatu"), + ("Disconnect all devices?", "Deskonektatu gailu guztiak?"), + ("Clear", "Garbitu"), + ("Audio Input Device", "Audio sarrera gailua"), + ("Use IP Whitelisting", "Erabili IP onartuen zerrenda"), + ("Network", "Sarea"), + ("Pin Toolbar", "Ainguratu tresna-barra"), + ("Unpin Toolbar", "Aingura kendu tresna-barrari"), + ("Recording", "Grabatzen"), + ("Directory", "Direktorioa"), + ("Automatically record incoming sessions", "Automatikoki grabatu sarrerako saioak"), + ("Change", "Aldatu"), + ("Start session recording", "Hasi saioaren grabaketa"), + ("Stop session recording", "Gelditu saioaren grabaketa"), + ("Enable recording session", "Gaitu saioen grabaketa"), + ("Enable LAN discovery", "Gaitu LAN ezagutza"), + ("Deny LAN discovery", "Ukatu LAN ezagutza"), + ("Write a message", "Idatzi mezu bat"), + ("Prompt", "Testu-gonbita"), + ("Please wait for confirmation of UAC...", "Mesedez, itxaron UAC berrespenari"), + ("elevated_foreground_window_tip", "Mahaigaineko uneko urruneko leihoak goi mailako baimenak behar ditu funtzionatzeko. Beraz, ezin duzu sagua eta teklatua erabili aldi baterako. Urruneko erabiltzaileari uneko leihoa minizatu edo kudeatze-leihoan maila igotzeko botoia erabili dezan eskatu diezaiokezu. Arazo hau saihesteko, programa instalatzea gomendatzen da urruneko gailuan."), + ("Disconnected", "Deskonektatuta"), + ("Other", "Besteak"), + ("Confirm before closing multiple tabs", "Berretsi fitxa ugari itxi baino lehen"), + ("Keyboard Settings", "Teklatuaren ezarpenak"), + ("Full Access", "Sarbide osoa"), + ("Screen Share", "Pantailaren partekatzea"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland Ubuntu 21.04 edo bertsio berriagoa behar du."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland-ek linux banaketa berriago bat behar du. Saiatu X11 mahaigainarekin edo aldatu zure sistema eragilea."), + ("JumpLink", "Ikusi"), + ("Please Select the screen to be shared(Operate on the peer side).", "Mesedez, hautatu partekatuko den pantaila (Kudeatu parekidearen aldean)"), + ("Show RustDesk", "Erakutsi RustDesk"), + ("This PC", "PC hau"), + ("or", "edo"), + ("Continue with", "Jarraitu honekin"), + ("Elevate", "Igo maila"), + ("Zoom cursor", "Handitu kurtsorea"), + ("Accept sessions via password", "Onartu saioak pasahitzaren bidez"), + ("Accept sessions via click", "Onartu saioak klikaren bidez"), + ("Accept sessions via both", "Onatu saioak bientzako"), + ("Please wait for the remote side to accept your session request...", "Mesedez, itxaron urruneko aldeak zure saio eskaera onartu dezan..."), + ("One-time Password", "Aldi bateko pasahitza"), + ("Use one-time password", "Erabili aldi baterako pasahitza"), + ("One-time password length", "Aldi baterako pasahitzaren luzera"), + ("Request access to your device", "Eskatu sarrera zure gailura"), + ("Hide connection management window", "Ezkutatu konexio kudeatze leihoa"), + ("hide_cm_tip", "Utzi ezkutatzen saioak pasahitzarekin onartzen badira eta pasahitza betirakoa bada"), + ("wayland_experiment_tip", "Wayland euskarria oraindik fase esperimentalean dago, mesedez, erabili X11 arretarik gabeko sarrera behar baduzu."), + ("Right click to select tabs", "Eskuineko klika fitxak hautatzeko"), + ("Skipped", "Saltatuta"), + ("Add to address book", "Gehitu helbide-liburura"), + ("Group", "Taldea"), + ("Search", "Bilatu"), + ("Closed manually by web console", "Web kontsolarengatik eskuz itxita"), + ("Local keyboard type", "Teklatu mota lokala"), + ("Select local keyboard type", "Hautatu teklatu mota lokala"), + ("software_render_tip", "Nvidia bideo-txartela baduzu eta urruneko leihoa berehala ixten bada, nouveau driver-a instalatzea eta software errenderizazioa hautatzea lagundu dezake. Aplikazioa berrabiarazi behar da."), + ("Always use software rendering", "Erabili software bidezko errenderizazioa beti"), + ("config_input", "Urruneko mahaigaina teklatuaren bidez kontrolatzeko, RustDesk-i \"Sarrera monitorizazioa\" baimena eman behar diozu."), + ("config_microphone", "Urrunetik hitz egin ahal izateko, RustDeski-i \"Grabatu audioa\" baimena eman behar diozu."), + ("request_elevation_tip", "Pribilegioen maila igotzea eskatu ahal duzu ere norbait badago urruneko aldean"), + ("Wait", "Itxaron"), + ("Elevation Error", "Maila-igotze errorea"), + ("Ask the remote user for authentication", "Eskatu autentifikazioa urruneko erabiltzaileari"), + ("Choose this if the remote account is administrator", "Aukeratu urruneko kontua administratzailea bada"), + ("Transmit the username and password of administrator", "Igorri administratzailearen erabiltzailea eta pasahitza"), + ("still_click_uac_tip", "Oraindik beharrezkoa da urruneko erabiltzaileak Ondo botoiari klik egitea exekutatzen ari den RustDesk UAC leihoan"), + ("Request Elevation", "Eskatu pribilegioen maila igotzea"), + ("wait_accept_uac_tip", "Mesedez, itxaron urruneko erabiltzaileak UAC onartu arte."), + ("Elevate successfully", "Maila igotzea ondo joan da"), + ("uppercase", "maiuskula"), + ("lowercase", "minuskula"), + ("digit", "zenbakia"), + ("special character", "karaktere berezia"), + ("length>=8", "luzera>=8"), + ("Weak", "Ahula"), + ("Medium", "Ertaina"), + ("Strong", "Indartsua"), + ("Switch Sides", "Aldatu aldeak"), + ("Please confirm if you want to share your desktop?", "Mesedez, berretsi zure mahaigaina partekatu nahi duzula"), + ("Display", "Pantaila"), + ("Default View Style", "Ikuspen estilo lehenetsia"), + ("Default Scroll Style", "Korritze estilo lehenetsia"), + ("Default Image Quality", "Irudi kalitate lehenetsia"), + ("Default Codec", "Kodek lehenetsia"), + ("Bitrate", "Bit-tasa"), + ("FPS", "FPS"), + ("Auto", "Auto"), + ("Other Default Options", "Beste aukera lehenetsiak"), + ("Voice call", "Ahots-deia"), + ("Text chat", "Testu-txata"), + ("Stop voice call", "Gelditu ahots-deia"), + ("relay_hint_tip", "Posible da ezinezkoa izatea zuzen konektatzea. Igorpen zerbitzari baten bidez konektatzen saiatu zaitezke. Horrez gain, igorpena lehen aldiz erabili nahi baduzu, \"/r\" atzitu dezakezu IDari edo \"Beti igorpen zerbitzari baten bidez konektatu\" aukeratu azken saioen txartelan existitzen bada."), + ("Reconnect", "Berriro konektatu"), + ("Codec", "Kodeka"), + ("Resolution", "Bereizmena"), + ("No transfers in progress", "Ez dago transferentziarik abian"), + ("Set one-time password length", "Ezarri aldi baterako pasahitzaren luzera"), + ("RDP Settings", "RDP ezarpenak"), + ("Sort by", "Ordenatu honengatik"), + ("New Connection", "Konexio berria"), + ("Restore", "Berrezarri"), + ("Minimize", "Minimizatu"), + ("Maximize", "Maximizatu"), + ("Your Device", "Zure gailua"), + ("empty_recent_tip", "Ups, ez dago azken saiorik!\nBerri bat planifikatzeko ordua da."), + ("empty_favorite_tip", "Parekide gogokorik gabe oraindik?\nBilatu norbait konektatzeko eta gehitu zure gogokoetara!"), + ("empty_lan_tip", "Ai ez, badirudi ez duzula parekiderik aurkitu oraindik."), + ("empty_address_book_tip", "Badirudi ez dagoela parekiderik zure helbide-liburuan."), + ("eg: admin", "adib. admin"), + ("Empty Username", "Erabiltzaile-izena hutsik"), + ("Empty Password", "Pasahitza hutsik"), + ("Me", "Ni"), + ("identical_file_tip", "Fitxategi hau parekidearen berdina da."), + ("show_monitors_tip", "Erakutsi monitoreak tresna-barran"), + ("View Mode", "Ikuspen modua"), + ("login_linux_tip", "Urruneko Linux kontu batera hasi behar duzu saioa X mahaigain saio bat gaitzeko"), + ("verify_rustdesk_password_tip", "Berretsi RustDesk pasahitza"), + ("remember_account_tip", "Gogoratu kontu hau"), + ("os_account_desk_tip", "Kontu hau bururik gabe urruneko SE hasi eta mahaigaineko saioa gaitzeko erabiltzen da"), + ("OS Account", "SE kontua"), + ("another_user_login_title_tip", "Beste erabiltzaile batek saioa hasi du dagoeneko"), + ("another_user_login_text_tip", "Deskonektatu"), + ("xorg_not_found_title_tip", "Ez da Xorg aurkitu"), + ("xorg_not_found_text_tip", "Mesedez, instalatu ezazu Xorg"), + ("no_desktop_title_tip", "Ez dago mahaigainik eskuragarri"), + ("no_desktop_text_tip", "Mesedez, instalatu ezazu GNOME Desktop"), + ("No need to elevate", "Ez da beharrezkoa pribilegioen maila igotzea"), + ("System Sound", "Sistemaren soinua"), + ("Default", "Lehenetsia"), + ("New RDP", "RDP berria"), + ("Fingerprint", "Hatz-marka"), + ("Copy Fingerprint", "Kopiatu hatz-marka"), + ("no fingerprints", "hatz-markarik ez"), + ("Select a peer", "Hautatu parekidea"), + ("Select peers", "Hautatu parekideak"), + ("Plugins", "Pluginak"), + ("Uninstall", "Desinstalatu"), + ("Update", "Eguneratu"), + ("Enable", "Gaitu"), + ("Disable", "Desgaitu"), + ("Options", "Aukerak"), + ("resolution_original_tip", "Jatorrizko bereizmena"), + ("resolution_fit_local_tip", "Bereizmen lokala egokitu"), + ("resolution_custom_tip", "Bereizmen pertsonalizatua"), + ("Collapse toolbar", "Ezkutatu tresna-barra"), + ("Accept and Elevate", "Onartu eta igo maila"), + ("accept_and_elevate_btn_tooltip", "Konexioa onartu eta UAC baimenak mailaz igo."), + ("clipboard_wait_response_timeout_tip", "Kopiatzeko denbora-muga gainditu da."), + ("Incoming connection", "Sarrerako konexioa"), + ("Outgoing connection", "Irteerako konexioa"), + ("Exit", "Irten"), + ("Open", "Ireki"), + ("logout_tip", "Saioa itxi nahi duzu?"), + ("Service", "Zerbitzua"), + ("Start", "Hasi"), + ("Stop", "Gelditu"), + ("exceed_max_devices", "Kudeatutako gailuen mugara heldu zara."), + ("Sync with recent sessions", "Sinkronizatu azken saioekin"), + ("Sort tags", "Ordenatu etiketak"), + ("Open connection in new tab", "Ireki konexioa fitxa berri batean"), + ("Move tab to new window", "Mugitu fitxa leiho berri batera"), + ("Can not be empty", "Ezin da hutsik egon"), + ("Already exists", "Dagoeneko existitzen da"), + ("Change Password", "Aldatu pasahitza"), + ("Refresh Password", "Freskatu pasahitza"), + ("ID", "ID"), + ("Grid View", "Sareta-ikuspegia"), + ("List View", "Zerrenda-ikuspegia"), + ("Select", "Hautatu"), + ("Toggle Tags", "Aldatu etiketak"), + ("pull_ab_failed_tip", "Ezin izan da direktorio freskatu"), + ("push_ab_failed_tip", "Ezin izan da zerbitzariko direktorioarekin sinkronizatu"), + ("synced_peer_readded_tip", "Azken saioetako gailuak direktorioarekin sinkronizatuko dira"), + ("Change Color", "Aldatu kolorea"), + ("Primary Color", "Kolore nagusia"), + ("HSV Color", "HSV kolorea"), + ("Installation Successful!", "Instalazioa ondo joan da"), + ("Installation failed!", "Instalazioak huts egin du"), + ("Reverse mouse wheel", "Inbertitu saguaren gurpila"), + ("{} sessions", "{} saio"), + ("scam_title", "IRUZURTUA izan zintezke!"), + ("scam_text1", "Ezagutzen eta fidatzen EZ duzun norbaitekin telefonoz bazaude eta RustDesk erabiltzeko eta zerbitzua abiarazteko eskatu badizute, ez ezazu egin eta eskegi berehala."), + ("scam_text2", "Ziurrenik zure dirua edo informazio pribatua lapurtzen saiatzen ari diren iruzurgileak dira."), + ("Don't show again", "Ez erakutsi berriro"), + ("I Agree", "Onartzen dut"), + ("Decline", "Ukatu"), + ("Timeout in minutes", "Denbora-muga minututan"), + ("auto_disconnect_option_tip", "Itxi sarrerako konexioak automatikoki erabiltzailearen jarduera faltagatik."), + ("Connection failed due to inactivity", "Konexioak huts egin du jarduera faltagatik"), + ("Check for software update on startup", "Egiaztatu software eguneraketak abiatzerakoan"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "Mesedez, bertsio-berritu RustDesk Server Pro {} bertsiora edo berriago batera!"), + ("pull_group_failed_tip", "Ezin izan da taldea freskatu"), + ("Filter by intersection", "Iragazi bidegurutzez"), + ("Remove wallpaper during incoming sessions", "Kendu horma-papera sarrerako saioetan"), + ("Test", "Probatu"), + ("display_is_plugged_out_msg", "Pantaila deskonektatuta dago, aldatu nagusira."), + ("No displays", "Ez dago pantailarik"), + ("Open in new window", "Ireki leiho berrian"), + ("Show displays as individual windows", "Erakutsi pantailak banakako leiho gisa"), + ("Use all my displays for the remote session", "Erabili nire pantaila guztiak urruneko saiorako"), + ("selinux_tip", "SELinux zure gailuan gaituta dago. Honek RustDesk kontrolatutako alde gisa dagoenean behar bezala ez exekutatzea ekarri dezake."), + ("Change view", "Aldatu ikuspegia"), + ("Big tiles", "Baldosa handiak"), + ("Small tiles", "Baldosa txikiak"), + ("List", "Zerrenda"), + ("Virtual display", "Pantaila birtuala"), + ("Plug out all", "Deskonektatu dena"), + ("True color (4:4:4)", "Kolore erreala (4:4:4)"), + ("Enable blocking user input", "Gaitu erabiltzailearen sarreraren blokeoa"), + ("id_input_tip", "ID bat, IP zuzena edo ataka duen domeinu bat sar dezakezu (:).\nBeste zerbitzari bateko gailu batera sartu nahi baduzu, erantsi zerbitzariaren helbidea (@?key=), adibidez,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nZerbitzari publiko bateko gailuan sartu nahi baduzu, idatzi \"@public\", gakoa ez da zerbitzari publikorako beharrezkoa.\n\nLehen konexioan igorpen-zerbitzari bat erabiltzea behartu nahi baduzu, gehitu \"/r\" IDaren amaieran, adibidez, \"9123456234/r\"."), + ("privacy_mode_impl_mag_tip", "1 modua"), + ("privacy_mode_impl_virtual_display_tip", "2 modua"), + ("Enter privacy mode", "Sartu modu pribatuan"), + ("Exit privacy mode", "Irten modu pribatutik"), + ("idd_not_support_under_win10_2004_tip", "Zeharkako pantaila kudeatzailea ez dago onartuta. Windows 10, 2004 bertsioa edo berriagoa behar da."), + ("input_source_1_tip", "Sarrera-iturri 1"), + ("input_source_2_tip", "Sarrera-iturri 2"), + ("Swap control-command key", "Aldatu kontrol-komando teklak"), + ("swap-left-right-mouse", "Aldatu saguaren ezker-eskuin botoiak"), + ("2FA code", "2FA kodea"), + ("More", "Gehiago"), + ("enable-2fa-title", "Gaitu bi faktoreko autentifikazioa"), + ("enable-2fa-desc", "Konfiguratu orain zure autentifikatzailea. Authy, Microsoft edo Google Authenticator bezalako aplikazio autentifikatzaile bat erabil dezakezu telefonoan edo mahaigainean.\n\nEskaneatu QR kodea zure aplikazioarekin eta idatzi aplikazioak erakusten duen kodea bi faktoreko autentifikazioa gaitzeko."), + ("wrong-2fa-code", "Ezin da kodea egiaztatu. Egiaztatu kodea eta tokiko orduaren ezarpenak zuzenak direla"), + ("enter-2fa-title", "Bi faktoreko autentifikazioa"), + ("Email verification code must be 6 characters.", "Posta elektronikoa egiaztatzeko kodeak 6 karaktere izan behar ditu."), + ("2FA code must be 6 digits.", "2FA kodeak 6 digitu izan behar ditu."), + ("Multiple Windows sessions found", "Windows saio anitz aurkitu dira"), + ("Please select the session you want to connect to", "Mesedez, aukeratu konektatu nahi duzun saioa"), + ("powered_by_me", "RustDesk-ek egina"), + ("outgoing_only_desk_tip", "Edizio pertsonalizatua da hau.\nBeste gailuetara konekta zaitezke, baina beste gailu batzuk ezin dira zure gailura konektatu."), + ("preset_password_warning", "Edizio pertsonalizatu hau aurrez ezarritako pasahitz batekin dator. Pasahitz hau ezagutzen duenak zure gailuaren kontrol osoa lor dezake. Hau espero ez bazenuen, desinstalatu softwarea berehala."), + ("Security Alert", "Segurtasun Alerta"), + ("My address book", "Nire helbide-liburua"), + ("Personal", "Pertsonala"), + ("Owner", "Jabea"), + ("Set shared password", "Ezarri pasahitz partekatua"), + ("Exist in", "Bertan existitzen da"), + ("Read-only", "Irakurtzeko soilik"), + ("Read/Write", "Irakurri/Idatzi"), + ("Full Control", "Kontrol Osoa"), + ("share_warning_tip", "Goiko eremuak partekatuak eta besteengatik ikusgai daude."), + ("Everyone", "Denek"), + ("ab_web_console_tip", "Gehiago web kontsolan"), + ("allow-only-conn-window-open-tip", "Baimendu konexioa RustDesk leihoa irekita badago"), + ("no_need_privacy_mode_no_physical_displays_tip", "Ez dago pantaila fisikorik, ez dago pribatutasun modua erabili beharrik."), + ("Follow remote cursor", "Jarraitu urruneko kurtsorea"), + ("Follow remote window focus", "Jarraitu urruneko leiho fokua"), + ("default_proxy_tip", "Lehenetsitako protokoloa eta ataka Socks5 eta 1080 dira"), + ("no_audio_input_device_tip", "Ez da aurkitu audio sarrerako gailurik."), + ("Incoming", "Sarrerakoa"), + ("Outgoing", "Irteerakoa"), + ("Clear Wayland screen selection", "Garbitu Wayland pantaila-hautapena"), + ("clear_Wayland_screen_selection_tip", "Pantaila hautapena garbitu ondoren, partekatzeko pantaila hauta dezakezu."), + ("confirm_clear_Wayland_screen_selection_tip", "Ziur Wayland pantaila-hautapena garbituko duzula?"), + ("android_new_voice_call_tip", "Ahots-dei eskaera berri bat jaso da. Onartzen baduzu, audioa ahots bidezko komunikaziora aldatuko da."), + ("texture_render_tip", "Erabili testura-errenderizazioa irudiak leunagoak egiteko."), + ("Use texture rendering", "Erabili testura-errenderizazioa"), + ("Floating window", "Leiho flotagarria"), + ("floating_window_tip", "RustDesk atzeko planoko zerbitzua mantentzen laguntzen du"), + ("Keep screen on", "Mantendu pantaila piztuta"), + ("Never", "Inoiz"), + ("During controlled", "Kontrolatu bitartean"), + ("During service is on", "Zerbitzua aktibo dagoen bitartean"), + ("Capture screen using DirectX", "Harrapatu pantaila DirectX erabiliz"), + ("Back", "Atzera"), + ("Apps", "Aplikazioak"), + ("Volume up", "Igo bolumena"), + ("Volume down", "Jaitsi bolumena"), + ("Power", "Piztu/Itzali"), + ("Telegram bot", "Telegrameko bot-a"), + ("enable-bot-tip", "Ezaugarri hau gaitzen baduzu, zure bot-etik 2FA kodea jaso dezakezu. Konexio jakinarazpenetarako ere balio dezake."), + ("enable-bot-desc", "1, Ireki txat bat @BotFather bot-arekin.\n2, Bidali \"/newbot\" agindua. Token bat jasoko duzu urrats honen ostean.\n3, Hasi txat bat zure bot berriarekin. Bidali mezu bat aurreko barra batekin (\"/\") \"/kaixo\" bezala gaitzeko.\n"), + ("cancel-2fa-confirm-tip", ""), + ("cancel-bot-confirm-tip", ""), + ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); - } - \ No newline at end of file +} diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 2436ba3f5a0..e6754205a5b 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index feb75402744..e0b0306dd74 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/he.rs b/src/lang/he.rs index 7d41acc6e4b..401b52c2883 100644 --- a/src/lang/he.rs +++ b/src/lang/he.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hr.rs b/src/lang/hr.rs index 8c73ff2e657..778eeacfa67 100644 --- a/src/lang/hr.rs +++ b/src/lang/hr.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index db9a259e9ad..21e898c21c6 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 81b6e09c7ad..e13c088fde4 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 81a7d408073..c7950fbeb3c 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Sei sicuro di voler annullare 2FA?"), ("cancel-bot-confirm-tip", "Sei sicuro di voler annulare Telegram?"), ("About RustDesk", "Info su RustDesk"), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 8fddd9eb671..a1413f82b89 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -630,6 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "本当に二要素認証をキャンセルしますか?"), ("cancel-bot-confirm-tip", "本当にTelegram Botをキャンセルしますか?"), ("About RustDesk", "RustDeskについて"), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } - \ No newline at end of file diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 6b5873a3f48..48b39b4a708 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 70c08041eb5..a8f7fde92d7 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 184c0bf587d..7167663a52a 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 7d95b2a4304..b8add4aa86d 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Vai tiešām vēlaties atcelt 2FA?"), ("cancel-bot-confirm-tip", "Vai tiešām vēlaties atcelt Telegram robotu?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index aa237792955..36269a37708 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 1d95389ddf3..a3042b2f2da 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Weet je zeker dat je 2FA wilt annuleren?"), ("cancel-bot-confirm-tip", "Weet je zeker dat je de Telegram-bot wilt annuleren?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index e237d04905b..3e3d135a763 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 5863836a4e6..0192d1a44e0 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 36d482cab37..5a7646bdc88 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 206df023266..fba6707317c 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index ca7d1ad0486..c9a1b41eaf2 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Отключить двухфакторную аутентификацию?"), ("cancel-bot-confirm-tip", "Отключить Telegram-бота?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 323f059d87b..6d0eb2ac97d 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Ste si istí, že chcete zrušiť službu 2FA?"), ("cancel-bot-confirm-tip", "Ste si istí, že chcete zrušiť bota Telegramu?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 9dd3d719281..ef0c0404bcb 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 2f8188dde88..44de8fc2070 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index a08bf6e84bc..56b6afb49ae 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index f90bbb93bd1..0e99407c1f6 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 2fd83ca808d..b1559a65eca 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 8236db99707..6237b482245 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 974e83945db..e1180ba44d9 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 2ee26e9f4f7..40b9201e15e 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "確定要取消二步驟驗證嗎?"), ("cancel-bot-confirm-tip", "確定要取消 Telegram 機器人嗎?"), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 4226c4ca69f..d9dc28fe798 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 28702a0e8c3..f68786b836a 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -630,5 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", ""), ("cancel-bot-confirm-tip", ""), ("About RustDesk", ""), + ("Send clipboard keystrokes", ""), ].iter().cloned().collect(); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 4f39cd36cc0..02fdf1caa1a 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -779,7 +779,6 @@ impl Session { } } - // flutter only TODO new input pub fn input_string(&self, value: &str) { let mut key_event = KeyEvent::new(); key_event.set_seq(value.to_owned()); From 3999d498be4332a8427e90614b512c4527577c32 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Thu, 25 Jul 2024 21:52:57 +0800 Subject: [PATCH 332/335] fix: keep window pos after new conn (#8834) 1. Keep window pos 2. Do some init in StatefulWidget constructor. If try init in its state class, it may be too late. Because I see the init function is called after building the widget tree. Signed-off-by: fufesou --- flutter/lib/common.dart | 12 ++++++++---- flutter/lib/desktop/pages/remote_page.dart | 5 +++-- flutter/lib/desktop/pages/remote_tab_page.dart | 4 +++- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 8c63816724f..246cba1984e 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -2981,11 +2981,15 @@ openMonitorInNewTabOrWindow(int i, String peerId, PeerInfo pi, kMainWindowId, kWindowEventOpenMonitorSession, jsonEncode(args)); } -setNewConnectWindowFrame( - int windowId, String peerId, int? display, Rect? screenRect) async { +setNewConnectWindowFrame(int windowId, String peerId, int preSessionCount, + int? display, Rect? screenRect) async { if (screenRect == null) { - await restoreWindowPosition(WindowType.RemoteDesktop, - windowId: windowId, display: display, peerId: peerId); + // Do not restore window position to new connection if there's a pre-session. + // https://github.com/rustdesk/rustdesk/discussions/8825 + if (preSessionCount == 0) { + await restoreWindowPosition(WindowType.RemoteDesktop, + windowId: windowId, display: display, peerId: peerId); + } } else { await tryMoveToScreenAndSetFullscreen(screenRect); } diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index d1c1056bea7..341025c5f3c 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -45,7 +45,9 @@ class RemotePage extends StatefulWidget { this.switchUuid, this.forceRelay, this.isSharedPassword, - }) : super(key: key); + }) : super(key: key) { + initSharedStates(id); + } final String id; final SessionID? sessionId; @@ -99,7 +101,6 @@ class _RemotePageState extends State } void _initStates(String id) { - initSharedStates(id); _zoomCursor = PeerBoolOption.find(id, kOptionZoomCursor); _showRemoteCursor = ShowRemoteCursorState.find(id); _keyboardEnabled = KeyboardEnabledState.find(id); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index f4300875508..3ee23b9dd72 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -407,12 +407,14 @@ class _ConnectionTabPageState extends State { final display = args['display']; final displays = args['displays']; final screenRect = parseParamScreenRect(args); + final prePeerCount = tabController.length; Future.delayed(Duration.zero, () async { if (stateGlobal.fullscreen.isTrue) { await WindowController.fromWindowId(windowId()).setFullscreen(false); stateGlobal.setFullscreen(false, procWnd: false); } - await setNewConnectWindowFrame(windowId(), id!, display, screenRect); + await setNewConnectWindowFrame( + windowId(), id!, prePeerCount, display, screenRect); Future.delayed(Duration(milliseconds: isWindows ? 100 : 0), () async { await windowOnTop(windowId()); }); From 1850d32f4996d67ecf3828121e568c409f8e051e Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Fri, 26 Jul 2024 04:00:18 +0200 Subject: [PATCH 333/335] Update Italian language (#8838) --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index c7950fbeb3c..5231ddac48a 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -630,6 +630,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("cancel-2fa-confirm-tip", "Sei sicuro di voler annullare 2FA?"), ("cancel-bot-confirm-tip", "Sei sicuro di voler annulare Telegram?"), ("About RustDesk", "Info su RustDesk"), - ("Send clipboard keystrokes", ""), + ("Send clipboard keystrokes", "Invia sequenze tasti appunti"), ].iter().cloned().collect(); } From f0f50f0f03215fa71de43873eb6e8b1894bfa3ff Mon Sep 17 00:00:00 2001 From: zuiyu <1542844298@qq.com> Date: Fri, 26 Jul 2024 10:31:01 +0800 Subject: [PATCH 334/335] fix: remove getRadio todo and delete contentPadding param (#8839) --- flutter/lib/common.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 246cba1984e..eba626343d0 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1409,14 +1409,10 @@ class AndroidPermissionManager { } } -// TODO move this to mobile/widgets. -// Used only for mobile, pages remote, settings, dialog -// TODO remove argument contentPadding, it’s not used, getToggle() has not RadioListTile getRadio( Widget title, T toValue, T curValue, ValueChanged? onChange, - {EdgeInsetsGeometry? contentPadding, bool? dense}) { + {bool? dense}) { return RadioListTile( - contentPadding: contentPadding ?? EdgeInsets.zero, visualDensity: VisualDensity.compact, controlAffinity: ListTileControlAffinity.trailing, title: title, From be06c0d7381dcfce50161bceff3d1aac4e81ff46 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 26 Jul 2024 11:20:16 +0800 Subject: [PATCH 335/335] tab border for desktop subwindow (#8842) Signed-off-by: 21pages --- flutter/lib/desktop/pages/file_manager_tab_page.dart | 1 + flutter/lib/desktop/pages/port_forward_tab_page.dart | 1 + flutter/lib/desktop/pages/remote_tab_page.dart | 1 + 3 files changed, 3 insertions(+) diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 0cba76e00ec..a68e4feecdc 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -96,6 +96,7 @@ class _FileManagerTabPageState extends State { controller: tabController, onWindowCloseButton: handleWindowCloseButton, tail: const AddButton(), + selectedBorderColor: MyTheme.accent, labelGetter: DesktopTab.tablabelGetter, )); final tabWidget = isLinux diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index b20d731b23f..5534db85549 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -105,6 +105,7 @@ class _PortForwardTabPageState extends State { return true; }, tail: AddButton(), + selectedBorderColor: MyTheme.accent, labelGetter: DesktopTab.tablabelGetter, ), ); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 3ee23b9dd72..94535bc6dcf 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -128,6 +128,7 @@ class _ConnectionTabPageState extends State { controller: tabController, onWindowCloseButton: handleWindowCloseButton, tail: const AddButton(), + selectedBorderColor: MyTheme.accent, pageViewBuilder: (pageView) => pageView, labelGetter: DesktopTab.tablabelGetter, tabBuilder: (key, icon, label, themeConf) => Obx(() {