Merge remote-tracking branch 'rd/master' into feat/x11/clipboard-file/init

Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
ClSlaid
2023-10-26 18:16:08 +08:00
108 changed files with 2191 additions and 1084 deletions

View File

@@ -999,16 +999,19 @@ pub struct VideoHandler {
pub rgb: ImageRgb,
recorder: Arc<Mutex<Option<Recorder>>>,
record: bool,
_display: usize, // useful for debug
}
impl VideoHandler {
/// Create a new video handler.
pub fn new() -> Self {
pub fn new(_display: usize) -> Self {
log::info!("new video handler for display #{_display}");
VideoHandler {
decoder: Decoder::new(),
rgb: ImageRgb::new(ImageFormat::ARGB, crate::DST_STRIDE_RGBA),
recorder: Default::default(),
record: false,
_display,
}
}
@@ -1207,7 +1210,7 @@ impl LoginConfigHandler {
self.save_config(config);
}
/// Save reverse mouse wheel ("", "Y") to the current config.
/// Save "displays_as_individual_windows" ("", "Y") to the current config.
///
/// # Arguments
///
@@ -1218,6 +1221,17 @@ impl LoginConfigHandler {
self.save_config(config);
}
/// Save "use_all_my_displays_for_the_remote_session" ("", "Y") to the current config.
///
/// # Arguments
///
/// * `value` - The "use_all_my_displays_for_the_remote_session" value ("", "Y").
pub fn save_use_all_my_displays_for_the_remote_session(&mut self, value: String) {
let mut config = self.load_config();
config.use_all_my_displays_for_the_remote_session = value;
self.save_config(config);
}
/// Save scroll style to the current config.
///
/// # Arguments
@@ -1889,7 +1903,7 @@ where
if handler_controller_map.len() <= display {
for _i in handler_controller_map.len()..=display {
handler_controller_map.push(VideoHandlerController {
handler: VideoHandler::new(),
handler: VideoHandler::new(_i),
count: 0,
duration: std::time::Duration::ZERO,
skip_beginning: 0,
@@ -1949,6 +1963,7 @@ where
}
}
MediaData::RecordScreen(start, display, w, h, id) => {
log::info!("record screen command: start:{start}, display:{display}");
if handler_controller_map.len() == 1 {
// Compatible with the sciter version(single ui session).
// For the sciter version, there're no multi-ui-sessions for one connection.

View File

@@ -108,7 +108,7 @@ impl Drop for SimpleCallOnReturn {
pub fn global_init() -> bool {
#[cfg(target_os = "linux")]
{
if !*IS_X11 {
if !crate::platform::linux::is_x11() {
crate::server::wayland::init();
}
}
@@ -956,7 +956,10 @@ pub async fn post_request_sync(url: String, body: String, header: &str) -> Resul
}
#[inline]
pub fn make_privacy_mode_msg_with_details(state: back_notification::PrivacyModeState, details: String) -> Message {
pub fn make_privacy_mode_msg_with_details(
state: back_notification::PrivacyModeState,
details: String,
) -> Message {
let mut misc = Misc::new();
let mut back_notification = BackNotification {
details,
@@ -990,17 +993,6 @@ pub fn get_supported_keyboard_modes(version: i64) -> Vec<KeyboardMode> {
.collect::<Vec<_>>()
}
#[cfg(not(target_os = "linux"))]
lazy_static::lazy_static! {
pub static ref IS_X11: bool = false;
}
#[cfg(target_os = "linux")]
lazy_static::lazy_static! {
pub static ref IS_X11: bool = hbb_common::platform::linux::is_x11_or_headless();
}
pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String {
use serde_json::json;
let mut fd_json = serde_json::Map::new();

View File

@@ -1,4 +1,4 @@
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(windows)]
use crate::client::translate;
#[cfg(not(debug_assertions))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]

View File

@@ -1377,6 +1377,9 @@ pub fn get_cur_session() -> Option<FlutterSession> {
// sessions mod is used to avoid the big lock of sessions' map.
pub mod sessions {
#[cfg(feature = "flutter_texture_render")]
use std::collections::HashSet;
use super::*;
lazy_static::lazy_static! {
@@ -1441,12 +1444,44 @@ pub mod sessions {
let mut remove_peer_key = None;
for (peer_key, s) in SESSIONS.write().unwrap().iter_mut() {
let mut write_lock = s.ui_handler.session_handlers.write().unwrap();
if write_lock.remove(id).is_some() {
let remove_ret = write_lock.remove(id);
#[cfg(not(feature = "flutter_texture_render"))]
if remove_ret.is_some() {
if write_lock.is_empty() {
remove_peer_key = Some(peer_key.clone());
}
break;
}
#[cfg(feature = "flutter_texture_render")]
match remove_ret {
Some(_) => {
if write_lock.is_empty() {
remove_peer_key = Some(peer_key.clone());
} else {
// Set capture displays if some are not used any more.
let mut remains_displays = HashSet::new();
for (_, h) in write_lock.iter() {
remains_displays.extend(
h.renderer
.map_display_sessions
.read()
.unwrap()
.keys()
.cloned(),
);
}
if !remains_displays.is_empty() {
s.capture_displays(
vec![],
vec![],
remains_displays.iter().map(|d| *d as i32).collect(),
);
}
}
break;
}
None => {}
}
}
SESSIONS.write().unwrap().remove(&remove_peer_key?)
}

View File

@@ -39,11 +39,15 @@ fn initialize(app_dir: &str) {
*config::APP_DIR.write().unwrap() = app_dir.to_owned();
#[cfg(target_os = "android")]
{
// flexi_logger can't work when android_logger initialized.
#[cfg(debug_assertions)]
android_logger::init_once(
android_logger::Config::default()
.with_max_level(log::LevelFilter::Debug) // limit log level
.with_tag("ffi"), // logs will show under mytag tag
);
#[cfg(not(debug_assertions))]
hbb_common::init_log(false, "");
#[cfg(feature = "mediacodec")]
scrap::mediacodec::check_mediacodec();
crate::common::test_rendezvous_server();
@@ -206,6 +210,7 @@ pub fn session_reconnect(session_id: SessionID, force_relay: bool) {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
session.reconnect(force_relay);
}
session_on_waiting_for_image_dialog_show(session_id);
}
pub fn session_toggle_option(session_id: SessionID, value: String) {
@@ -339,7 +344,9 @@ pub fn session_set_reverse_mouse_wheel(session_id: SessionID, value: String) {
}
}
pub fn session_get_displays_as_individual_windows(session_id: SessionID) -> SyncReturn<Option<String>> {
pub fn session_get_displays_as_individual_windows(
session_id: SessionID,
) -> SyncReturn<Option<String>> {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
SyncReturn(Some(session.get_displays_as_individual_windows()))
} else {
@@ -353,6 +360,27 @@ pub fn session_set_displays_as_individual_windows(session_id: SessionID, value:
}
}
pub fn session_get_use_all_my_displays_for_the_remote_session(
session_id: SessionID,
) -> SyncReturn<Option<String>> {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
SyncReturn(Some(
session.get_use_all_my_displays_for_the_remote_session(),
))
} else {
SyncReturn(None)
}
}
pub fn session_set_use_all_my_displays_for_the_remote_session(
session_id: SessionID,
value: String,
) {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
session.save_use_all_my_displays_for_the_remote_session(value);
}
}
pub fn session_get_custom_image_quality(session_id: SessionID) -> Option<Vec<i32>> {
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
Some(session.get_custom_image_quality())
@@ -928,6 +956,25 @@ pub fn main_load_recent_peers_sync() -> SyncReturn<String> {
SyncReturn("".to_string())
}
pub fn main_load_lan_peers_sync() -> SyncReturn<String> {
let data = HashMap::from([
("name", "load_lan_peers".to_owned()),
(
"peers",
serde_json::to_string(&get_lan_peers()).unwrap_or_default(),
),
]);
return SyncReturn(serde_json::ser::to_string(&data).unwrap_or("".to_owned()));
}
pub fn main_load_ab_sync() -> SyncReturn<String> {
return SyncReturn(serde_json::to_string(&config::Ab::load()).unwrap_or_default());
}
pub fn main_load_group_sync() -> SyncReturn<String> {
return SyncReturn(serde_json::to_string(&config::Group::load()).unwrap_or_default());
}
pub fn main_load_recent_peers_for_ab(filter: String) -> String {
let id_filters = serde_json::from_str::<Vec<String>>(&filter).unwrap_or_default();
let id_filters = if id_filters.is_empty() {
@@ -1069,6 +1116,29 @@ pub fn main_get_main_display() -> SyncReturn<String> {
SyncReturn(display_info)
}
pub fn main_get_displays() -> SyncReturn<String> {
#[cfg(target_os = "ios")]
let display_info = "".to_owned();
#[cfg(not(target_os = "ios"))]
let mut display_info = "".to_owned();
#[cfg(not(target_os = "ios"))]
if let Ok(displays) = crate::display_service::try_get_displays() {
let displays = displays
.iter()
.map(|d| {
HashMap::from([
("x", d.origin().0),
("y", d.origin().1),
("w", d.width() as i32),
("h", d.height() as i32),
])
})
.collect::<Vec<_>>();
display_info = serde_json::to_string(&displays).unwrap_or_default();
}
SyncReturn(display_info)
}
pub fn session_add_port_forward(
session_id: SessionID,
local_port: i32,
@@ -1515,7 +1585,7 @@ pub fn main_is_installed() -> SyncReturn<bool> {
pub fn main_start_grab_keyboard() -> SyncReturn<bool> {
#[cfg(target_os = "linux")]
if !*crate::common::IS_X11 {
if !crate::platform::linux::is_x11() {
return SyncReturn(false);
}
crate::keyboard::client::start_grab_loop();
@@ -1866,6 +1936,17 @@ pub fn is_support_multi_ui_session(version: String) -> SyncReturn<bool> {
SyncReturn(crate::common::is_support_multi_ui_session(&version))
}
pub fn is_selinux_enforcing() -> SyncReturn<bool> {
#[cfg(target_os = "linux")]
{
SyncReturn(crate::platform::linux::is_selinux_enforcing())
}
#[cfg(not(target_os = "linux"))]
{
SyncReturn(false)
}
}
#[cfg(target_os = "android")]
pub mod server_side {
use hbb_common::{config, log};

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
].iter().cloned().collect();
}

View File

@@ -555,14 +555,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Check for software update on startup", "Kontrola aktualizace softwaru při spuštění"),
("upgrade_rustdesk_server_pro_to_{}_tip", "Aktualizujte prosím RustDesk Server Pro na verzi {} nebo novější!"),
("pull_group_failed_tip", "Nepodařilo se obnovit skupinu"),
("Filter by intersection", ""),
("Remove wallpaper during incoming sessions", ""),
("Test", ""),
("switch_display_elevated_connections_tip", ""),
("display_is_plugged_out_msg", ""),
("No displays", ""),
("elevated_switch_display_msg", ""),
("Open in new window", ""),
("Show displays as individual windows", ""),
("Filter by intersection", "Filtrovat podle průsečíku"),
("Remove wallpaper during incoming sessions", "Odstranit tapetu během příchozích relací"),
("Test", "Test"),
("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."),
("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"),
("selinux_tip", ""),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -440,7 +440,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Strong", "Stark"),
("Switch Sides", "Seiten wechseln"),
("Please confirm if you want to share your desktop?", "Bitte bestätigen Sie, wenn Sie Ihren Desktop freigeben möchten."),
("Display", "Anzeige"),
("Display", "Bildschirm"),
("Default View Style", "Standard-Ansichtsstil"),
("Default Scroll Style", "Standard-Scroll-Stil"),
("Default Image Quality", "Standard-Bildqualität"),
@@ -476,7 +476,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Empty Password", "Leeres Passwort"),
("Me", "Ich"),
("identical_file_tip", "Diese Datei ist identisch mit der Datei der Gegenstelle."),
("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"),
("show_monitors_tip", "Bildschirme in der Symbolleiste anzeigen"),
("View Mode", "Ansichtsmodus"),
("login_linux_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."),
("verify_rustdesk_password_tip", "RustDesk-Passwort bestätigen"),
@@ -558,11 +558,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Filter by intersection", "Nach Schnittmenge filtern"),
("Remove wallpaper during incoming sessions", "Hintergrundbild während eingehender Sitzungen entfernen"),
("Test", "Test"),
("switch_display_elevated_connections_tip", "Das Umschalten auf eine nicht primäre Anzeige wird mit erhöhten Rechten nicht unterstützt, wenn mehrere Verbindungen bestehen. Bitte versuchen Sie es nach der Installation erneut, wenn Sie mehrere Anzeigen steuern möchten."),
("display_is_plugged_out_msg", "Das Anzeigegerät ist nicht angeschlossen, schalten Sie auf das erste Anzeigegerät um."),
("No displays", "Keine Anzeigegeräte"),
("elevated_switch_display_msg", "Wechseln Sie zur primären Anzeige, da die Mehrfachanzeige im erweiterten Modus nicht unterstützt wird."),
("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."),
("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 Modus nicht unterstützt werden."),
("Open in new window", "In einem neuen Fenster öffnen"),
("Show displays as individual windows", "Anzeigen als einzelne Fenster darstellen"),
("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"),
("selinux_tip", "SELinux ist auf Ihrem Gerät aktiviert, was dazu führen kann, dass RustDesk als kontrollierte Seite nicht richtig läuft."),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -223,7 +223,8 @@ 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/manual/linux/#x11-required"),
("switch_display_elevated_connections_tip", "Switching to non-primary display is not supported in the elevated mode when there are multiple connections. Please try again after installation if you want to control multiple displays."),
("display_is_plugged_out_msg", "The diplay is plugged out, switch to the first display."),
("elevated_switch_display_msg", "Switch to the primary display because multiple display is not supported in elevated mode."),
("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 mode."),
("selinux_tip", "SELinux is enabled on your device, which may prevent RustDesk from running properly as controlled side."),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -555,14 +555,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Check for software update on startup", "Comprobar actualización al iniciar"),
("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", ""),
("Remove wallpaper during incoming sessions", ""),
("Test", ""),
("switch_display_elevated_connections_tip", ""),
("display_is_plugged_out_msg", ""),
("No displays", ""),
("elevated_switch_display_msg", ""),
("Open in new window", ""),
("Show displays as individual windows", ""),
("Filter by intersection", "Filtrar por intersección"),
("Remove wallpaper during incoming sessions", "Quitar el fonde de pantalla durante sesiones entrantes"),
("Test", "Probar"),
("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."),
("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"),
("selinux_tip", "SELinux está activado en tu dispositivo, lo que puede hacer que RustDesk no se ejecute correctamente como lado controlado."),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -52,7 +52,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Home", ""),
("Audio Input", "Input Audio"),
("Enhancements", "Peningkatan"),
("Hardware Codec", "Codec Perangkat Keras"),
("Hardware Codec", "Kodek Perangkat Keras"),
("Adaptive bitrate", "Kecepatan Bitrate Adaptif"),
("ID Server", "Server ID"),
("Relay Server", "Server Relay"),
@@ -352,7 +352,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Dark", "Gelap"),
("Light", "Terang"),
("Follow System", "Ikuti Sistem"),
("Enable hardware codec", "Aktifkan codec perangkat keras"),
("Enable hardware codec", "Aktifkan kodek perangkat keras"),
("Unlock Security Settings", "Buka Keamanan Pengaturan"),
("Enable Audio", "Aktifkan Audio"),
("Unlock Network Settings", "Buka Keamanan Pengaturan Jaringan"),
@@ -369,7 +369,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Unpin Toolbar", "Batal sematkan Toolbar"),
("Recording", "Perekaman"),
("Directory", "Direktori"),
("Automatically record incoming sessions", "Secara otomatis merekam sesi masuk"),
("Automatically record incoming sessions", "Otomatis merekam sesi masuk"),
("Change", "Ubah"),
("Start session recording", "Mulai sesi perekaman"),
("Stop session recording", "Hentikan sesi perekaman"),
@@ -444,7 +444,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Default View Style", "Gaya Tampilan Default"),
("Default Scroll Style", "Gaya Scroll Default"),
("Default Image Quality", "Kualitas Gambar Default"),
("Default Codec", "Codec default"),
("Default Codec", "Kodek default"),
("Bitrate", "Bitrate"),
("FPS", "FPS"),
("Auto", "Otomatis"),
@@ -454,9 +454,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop voice call", "Hentikan panggilan suara"),
("relay_hint_tip", "Tidak memungkinkan untuk terhubung secara langsung; anda bisa mencoba terhubung via relay. Selain itu, jika ingin menggunakan relay pada percobaan pertama, silahkan tambah akhiran \"/r\" pada ID atau pilih \"Selalu terhubung via relay\" di pilihan sesi terbaru."),
("Reconnect", "Menyambungkan ulang"),
("Codec", "Codec"),
("Codec", "Kodek"),
("Resolution", "Resolusi"),
("No transfers in progress", "Tidak ada transfer data yang sedang berlangsung"),
("No transfers in progress", "Tidak ada proses transfer data"),
("Set one-time password length", "Atur panjang kata sandi sekali pakai"),
("install_cert_tip", "Install sertifikat RustDesk"),
("confirm_install_cert_tip", "Ini adalah sertifikat pengujian RustDesk, yang dapat dipercaya. Sertifikat ini akan digunakan untuk menginstal driver RustDesk saat diperlukan"),
@@ -541,7 +541,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("HSV Color", "Warna HSV"),
("Installation Successful!", "Instalasi berhasil!"),
("Installation failed!", "Instalasi gagal!"),
("Reverse mouse wheel", "Balikkan arah scroll mouse!"),
("Reverse mouse wheel", "Balikkan arah scroll mouse"),
("{} sessions", "sesi {}"),
("scam_title", "Kemungkinan Anda Sedang DITIPU!"),
("scam_text1", "Jika Anda sedang berbicara di telepon dengan seseorang yang TIDAK dikenal dan mereka meminta anda untuk menggunakan RustDesk, jangan lanjutkan dan segera tutup panggilan."),
@@ -563,6 +563,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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 layar sebagai jendela terpisah"),
("Show displays as individual windows", "Tampilkan dengan jendela terpisah"),
("Use all my displays for the remote session", "Gunakan semua layar untuk sesi remote"),
("selinux_tip", ""),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -365,7 +365,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Audio Input Device", "Dispositivo ingresso audio"),
("Use IP Whitelisting", "Usa elenco IP autorizzati"),
("Network", "Rete"),
("Enable RDP", "Abilita RDP"),
("Pin Toolbar", "Blocca barra strumenti"),
("Unpin Toolbar", "Sblocca barra strumenti"),
("Recording", "Registrazione"),
@@ -565,5 +564,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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"),
("selinux_tip", ""),
("selinux_tip", "In questo dispositivo è abilitato SELinux, che potrebbe impedire il corretto funzionamento di RustDesk come lato controllato."),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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"),
("selinux_tip", "Jūsu ierīcē ir iespējots SELinux, kas var neļaut RustDesk pareizi darboties kā kontrolētajai pusei."),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -555,14 +555,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Check for software update on startup", "Checken voor updates bij opstarten"),
("upgrade_rustdesk_server_pro_to_{}_tip", "Upgrade RustDesk Server Pro naar versie {} of nieuwer!"),
("pull_group_failed_tip", "Vernieuwen van groep mislukt"),
("Filter by intersection", ""),
("Remove wallpaper during incoming sessions", ""),
("Test", ""),
("switch_display_elevated_connections_tip", ""),
("display_is_plugged_out_msg", ""),
("No displays", ""),
("elevated_switch_display_msg", ""),
("Open in new window", ""),
("Show displays as individual windows", ""),
("Filter by intersection", "Filter op kruising"),
("Remove wallpaper during incoming sessions", "Achtergrond verwijderen tijdens inkomende sessies"),
("Test", "Test"),
("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."),
("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"),
("selinux_tip", ""),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("selinux_tip", ""),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", "Список"),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -138,7 +138,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Please try later", "Будь ласка, спробуйте пізніше"),
("Remote desktop is offline", "Віддалена стільниця не в мережі"),
("Key mismatch", "Невідповідність ключів"),
("Timeout", "Тайм-аут"),
("Timeout", "Час очікування"),
("Failed to connect to relay server", "Не вдалося підключитися до сервера реле"),
("Failed to connect via rendezvous server", "Не вдалося підключитися через проміжний сервер"),
("Failed to connect via relay server", "Не вдалося підключитися через сервер реле"),
@@ -223,7 +223,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Verification code", "Код підтвердження"),
("verification_tip", "Виявлено новий пристрій, код підтвердження надіслано на зареєстровану email-адресу, введіть код підтвердження для продовження авторизації."),
("Logout", "Вийти"),
("Tags", "Ключові слова"),
("Tags", "Теги"),
("Search ID", "Пошук за ID"),
("whitelist_sep", "Розділені комою, крапкою з комою, пробілом або новим рядком"),
("Add ID", "Додати ID"),
@@ -231,7 +231,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Unselect all tags", "Скасувати вибір усіх тегів"),
("Network error", "Помилка мережі"),
("Username missed", "Імʼя користувача відсутнє"),
("Password missed", "Забули пароль"),
("Password missed", "Пароль відсутній"),
("Wrong credentials", "Неправильні дані"),
("The verification code is incorrect or has expired", "Код підтвердження некоректний або протермінований"),
("Edit Tag", "Редагувати тег"),
@@ -269,7 +269,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Reset canvas", "Відновлення полотна"),
("No permission of file transfer", "Немає дозволу на передачу файлів"),
("Note", "Примітка"),
("Connection", "Зʼєднання"),
("Connection", "Підключення"),
("Share Screen", "Поділитися екраном"),
("Chat", "Чат"),
("Total", "Всього"),
@@ -516,53 +516,59 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("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", ""),
("switch_display_elevated_connections_tip", ""),
("display_is_plugged_out_msg", ""),
("No displays", ""),
("elevated_switch_display_msg", ""),
("Open in new window", ""),
("Show displays as individual windows", ""),
("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", "Тест"),
("switch_display_elevated_connections_tip", "В режимі розширених прав, коли є декілька підключень, не підтримується перемикання на неосновний дисплей. Якщо вам потрібен контроль декількох дисплеїв, будь ласка, спробуйте ще раз після встановлення"),
("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", ""),
("Change view", ""),
("Big tiles", ""),
("Small tiles", ""),
("List", ""),
].iter().cloned().collect();
}

View File

@@ -564,5 +564,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("elevated_switch_display_msg", ""),
("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", ""),
].iter().cloned().collect();
}

View File

@@ -35,6 +35,10 @@ type Xdo = *const c_void;
pub const PA_SAMPLE_RATE: u32 = 48000;
static mut UNMODIFIED: bool = true;
lazy_static::lazy_static! {
pub static ref IS_X11: bool = hbb_common::platform::linux::is_x11_or_headless();
}
thread_local! {
static XDO: RefCell<Xdo> = RefCell::new(unsafe { xdo_new(std::ptr::null()) });
static DISPLAY: RefCell<*mut c_void> = RefCell::new(unsafe { XOpenDisplay(std::ptr::null())});
@@ -1360,3 +1364,26 @@ impl Drop for WallPaperRemover {
}
}
}
#[inline]
pub fn is_x11() -> bool {
*IS_X11
}
#[inline]
pub fn is_selinux_enforcing() -> bool {
match run_cmds("getenforce") {
Ok(output) => output.trim() == "Enforcing",
Err(_) => match run_cmds("sestatus") {
Ok(output) => {
for line in output.lines() {
if line.contains("Current mode:") {
return line.contains("enforcing");
}
}
false
}
Err(_) => false,
},
}
}

View File

@@ -1839,20 +1839,19 @@ pub fn uninstall_cert() -> ResultType<()> {
mod cert {
use hbb_common::{allow_err, bail, log, ResultType};
use std::{path::Path, str::from_utf8};
use std::{ffi::OsStr, io::Error, os::windows::ffi::OsStrExt, path::Path, str::from_utf8};
use winapi::{
shared::{
minwindef::{BYTE, DWORD, FALSE, TRUE},
ntdef::NULL,
},
um::{
errhandlingapi::GetLastError,
wincrypt::{
CertAddEncodedCertificateToStore, CertCloseStore, CertDeleteCertificateFromStore,
CertEnumCertificatesInStore, CertNameToStrA, CertOpenSystemStoreW,
CryptHashCertificate, ALG_ID, CALG_SHA1, CERT_ID_SHA1_HASH,
CERT_STORE_ADD_REPLACE_EXISTING, CERT_X500_NAME_STR, PCCERT_CONTEXT,
X509_ASN_ENCODING,
CertEnumCertificatesInStore, CertNameToStrA, CertOpenStore, CryptHashCertificate,
ALG_ID, CALG_SHA1, CERT_ID_SHA1_HASH, CERT_STORE_ADD_REPLACE_EXISTING,
CERT_STORE_PROV_SYSTEM_W, CERT_SYSTEM_STORE_LOCAL_MACHINE, CERT_X500_NAME_STR,
PCCERT_CONTEXT, PKCS_7_ASN_ENCODING, X509_ASN_ENCODING,
},
winreg::HKEY_LOCAL_MACHINE,
},
@@ -1866,8 +1865,12 @@ mod cert {
"SOFTWARE\\Microsoft\\SystemCertificates\\ROOT\\Certificates\\";
const THUMBPRINT_ALG: ALG_ID = CALG_SHA1;
const THUMBPRINT_LEN: DWORD = 20;
const CERT_ISSUER_1: &str = "CN=\"WDKTestCert admin,133225435702113567\"\0";
const CERT_ENCODING_TYPE: DWORD = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
lazy_static::lazy_static! {
static ref CERT_STORE_LOC: Vec<u16> = OsStr::new("ROOT\0").encode_wide().collect::<Vec<_>>();
}
#[inline]
unsafe fn compute_thumbprint(pb_encoded: *const BYTE, cb_encoded: DWORD) -> (Vec<u8>, String) {
@@ -1946,24 +1949,46 @@ mod cert {
fn install_cert_add_cert_store(cert_bytes: &mut [u8]) -> ResultType<()> {
unsafe {
let store_handle = CertOpenSystemStoreW(0 as _, "ROOT\0".as_ptr() as _);
let store_handle = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
0,
0,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
CERT_STORE_LOC.as_ptr() as _,
);
if store_handle.is_null() {
bail!("Error opening certificate store: {}", GetLastError());
bail!(
"Error opening certificate store: {}",
Error::last_os_error()
);
}
let mut cert_ctx: PCCERT_CONTEXT = std::ptr::null_mut();
// Create the certificate context
let cert_context = winapi::um::wincrypt::CertCreateCertificateContext(
CERT_ENCODING_TYPE,
cert_bytes.as_ptr(),
cert_bytes.len() as DWORD,
);
if cert_context.is_null() {
bail!(
"Error creating certificate context: {}",
Error::last_os_error()
);
}
if FALSE
== CertAddEncodedCertificateToStore(
store_handle,
X509_ASN_ENCODING,
cert_bytes.as_mut_ptr(),
cert_bytes.len() as _,
CERT_ENCODING_TYPE,
(*cert_context).pbCertEncoded,
(*cert_context).cbCertEncoded,
CERT_STORE_ADD_REPLACE_EXISTING,
&mut cert_ctx as _,
std::ptr::null_mut(),
)
{
log::error!(
"Failed to call CertAddEncodedCertificateToStore: {}",
GetLastError()
Error::last_os_error()
);
} else {
log::info!("Add cert to store successfully");
@@ -1981,12 +2006,20 @@ mod cert {
let mut buf = [0u8; 1024];
unsafe {
let store_handle = CertOpenSystemStoreW(0 as _, "ROOT\0".as_ptr() as _);
let store_handle = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
0,
0,
CERT_SYSTEM_STORE_LOCAL_MACHINE,
CERT_STORE_LOC.as_ptr() as _,
);
if store_handle.is_null() {
bail!("Error opening certificate store: {}", GetLastError());
bail!(
"Error opening certificate store: {}",
Error::last_os_error()
);
}
let mut vec_ctx = Vec::new();
let mut cert_ctx: PCCERT_CONTEXT = CertEnumCertificatesInStore(store_handle, NULL as _);
while !cert_ctx.is_null() {
// https://stackoverflow.com/a/66432736
@@ -1998,11 +2031,9 @@ mod cert {
buf.len() as _,
);
if cb_size != 1 {
let mut add_ctx = false;
if let Ok(issuer) = from_utf8(&buf[..cb_size as _]) {
for iss in issuers_to_rm.iter() {
if issuer == *iss {
add_ctx = true;
let (_, thumbprint) = compute_thumbprint(
(*cert_ctx).pbCertEncoded,
(*cert_ctx).cbCertEncoded,
@@ -2010,18 +2041,15 @@ mod cert {
if !thumbprint.is_empty() {
thumbprints.push(thumbprint);
}
// Delete current cert context and re-enumerate.
CertDeleteCertificateFromStore(cert_ctx);
cert_ctx = CertEnumCertificatesInStore(store_handle, NULL as _);
}
}
}
if add_ctx {
vec_ctx.push(cert_ctx);
}
}
cert_ctx = CertEnumCertificatesInStore(store_handle, cert_ctx);
}
for ctx in vec_ctx {
CertDeleteCertificateFromStore(ctx);
}
CertCloseStore(store_handle, 0);
}
@@ -2033,7 +2061,8 @@ mod cert {
let reg_cert_key = unsafe { open_reg_cert_store()? };
log::info!("Found {} certs to remove", thumbprints.len());
for thumbprint in thumbprints.iter() {
allow_err!(reg_cert_key.delete_subkey(thumbprint));
// Deleting cert from registry may fail, because the CertDeleteCertificateFromStore() is called before.
let _ = reg_cert_key.delete_subkey(thumbprint);
}
Ok(())
}

View File

@@ -611,7 +611,7 @@ impl Connection {
_ => {},
}
}
Some(message::Union::PeerInfo(_)) => {
Some(message::Union::PeerInfo(..)) => {
conn.refresh_video_display(None);
}
_ => {}
@@ -1132,11 +1132,13 @@ impl Connection {
self.send(msg_out).await;
}
match super::display_service::get_displays().await {
match super::display_service::update_get_sync_displays().await {
Err(err) => {
res.set_error(format!("{}", err));
}
Ok(displays) => {
// For compatibility with old versions, we need to send the displays to the peer.
// But the displays may be updated later, before creating the video capturer.
pi.displays = displays.clone();
pi.current_display = self.display_idx as _;
res.set_peer_info(pi);
@@ -2102,7 +2104,7 @@ impl Connection {
display,
video_service::OPTION_REFRESH,
super::service::SERVICE_OPTION_VALUE_TRUE,
)
);
});
}
@@ -2139,13 +2141,13 @@ impl Connection {
..Default::default()
});
}
}
// send display changed message
if let Some(msg_out) =
video_service::make_display_changed_msg(self.display_idx, None)
{
self.send(msg_out).await;
}
// Send display changed message.
// For compatibility with old versions ( < 1.2.4 ).
// sciter need it in new version
if let Some(msg_out) = video_service::make_display_changed_msg(self.display_idx, None) {
self.send(msg_out).await;
}
}
}

View File

@@ -1,4 +1,6 @@
use super::*;
#[cfg(target_os = "linux")]
use crate::platform::linux::is_x11;
#[cfg(all(windows, feature = "virtual_display_driver"))]
use crate::virtual_display_manager;
#[cfg(windows)]
@@ -6,6 +8,8 @@ use hbb_common::get_version_number;
use hbb_common::protobuf::MessageField;
use scrap::Display;
// https://github.com/rustdesk/rustdesk/discussions/6042, avoiding dbus call
pub const NAME: &'static str = "display";
struct ChangedResolution {
@@ -19,6 +23,71 @@ lazy_static::lazy_static! {
// Initial primary display index.
// It should only be updated when the rustdesk server is started, and should not be updated when displays changed.
pub static ref PRIMARY_DISPLAY_IDX: usize = get_primary();
static ref SYNC_DISPLAYS: Arc<Mutex<SyncDisplaysInfo>> = Default::default();
}
#[derive(Default)]
struct SyncDisplaysInfo {
displays: Vec<DisplayInfo>,
is_synced: bool,
}
impl SyncDisplaysInfo {
fn check_changed(&mut self, displays: Vec<DisplayInfo>) {
if self.displays.len() != displays.len() {
self.displays = displays;
self.is_synced = false;
return;
}
for (i, d) in displays.iter().enumerate() {
if d != &self.displays[i] {
self.displays = displays;
self.is_synced = false;
return;
}
}
}
fn get_update_sync_displays(&mut self) -> Option<Vec<DisplayInfo>> {
if self.is_synced {
return None;
}
self.is_synced = true;
Some(self.displays.clone())
}
}
// This function is really useful, though a duplicate check if display changed.
// The video server will then send the following messages to the client:
// 1. the supported resolutions of the {idx} display
// 2. the switch resolution message, so that the client can record the custom resolution.
pub(super) fn check_display_changed(
ndisplay: usize,
idx: usize,
(x, y, w, h): (i32, i32, usize, usize),
) -> Option<DisplayInfo> {
#[cfg(target_os = "linux")]
{
// wayland do not support changing display for now
if !is_x11() {
return None;
}
}
let lock = SYNC_DISPLAYS.lock().unwrap();
// If plugging out a monitor && lock.displays.get(idx) is None.
// 1. The client version < 1.2.4. The client side has to reconnect.
// 2. The client version > 1.2.4, The client side can handle the case becuase sync peer info message will be sent.
// But it is acceptable to for the user to reconnect manually, becuase the monitor is unplugged.
let d = lock.displays.get(idx)?;
if ndisplay != lock.displays.len() {
return Some(d.clone());
}
if !(d.x == x && d.y == y && d.width == w as i32 && d.height == h as i32) {
Some(d.clone())
} else {
None
}
}
#[inline]
@@ -74,45 +143,29 @@ pub fn is_privacy_mode_supported() -> bool {
return false;
}
#[derive(Default)]
struct StateDisplay {
synced_displays: Vec<DisplayInfo>,
}
impl super::service::Reset for StateDisplay {
fn reset(&mut self) {
self.synced_displays.clear();
}
}
pub fn new() -> GenericService {
let svc = EmptyExtraFieldService::new(NAME.to_owned(), false);
GenericService::repeat::<StateDisplay, _, _>(&svc.clone(), 300, run);
let svc = EmptyExtraFieldService::new(NAME.to_owned(), true);
GenericService::run(&svc.clone(), run);
svc.sp
}
fn check_get_displays_changed_msg(last_synced_displays: &mut Vec<DisplayInfo>) -> Option<Message> {
let displays = try_get_displays().ok()?;
if displays.len() == last_synced_displays.len() {
return None;
}
fn displays_to_msg(displays: Vec<DisplayInfo>) -> Message {
let mut pi = PeerInfo {
..Default::default()
};
pi.displays = displays.clone();
// current_display should not be used in server.
// It is set to 0 for compatibility with old clients.
pi.current_display = 0;
let mut msg_out = Message::new();
msg_out.set_peer_info(pi);
msg_out
}
// Display to DisplayInfo
let displays = to_display_info(&displays);
if last_synced_displays.len() == 0 {
*last_synced_displays = displays;
None
} else {
let mut pi = PeerInfo {
..Default::default()
};
pi.displays = displays.clone();
pi.current_display = 0;
let mut msg_out = Message::new();
msg_out.set_peer_info(pi);
*last_synced_displays = displays;
Some(msg_out)
}
fn check_get_displays_changed_msg() -> Option<Message> {
check_update_displays(&try_get_displays().ok()?);
let displays = SYNC_DISPLAYS.lock().unwrap().get_update_sync_displays()?;
Some(displays_to_msg(displays))
}
#[cfg(all(windows, feature = "virtual_display_driver"))]
@@ -120,11 +173,23 @@ pub fn try_plug_out_virtual_display() {
let _res = virtual_display_manager::plug_out_headless();
}
fn run(sp: EmptyExtraFieldService, state: &mut StateDisplay) -> ResultType<()> {
if let Some(msg_out) = check_get_displays_changed_msg(&mut state.synced_displays) {
sp.send(msg_out);
log::info!("Displays changed");
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
while sp.ok() {
sp.snapshot(|sps| {
if sps.has_subscribes() {
SYNC_DISPLAYS.lock().unwrap().is_synced = false;
bail!("new subscriber");
}
Ok(())
})?;
if let Some(msg_out) = check_get_displays_changed_msg() {
sp.send(msg_out);
log::info!("Displays changed");
}
std::thread::sleep(Duration::from_millis(300));
}
Ok(())
}
@@ -167,8 +232,20 @@ pub(super) fn get_original_resolution(
.into()
}
pub fn to_display_info(all: &Vec<Display>) -> Vec<DisplayInfo> {
all.iter()
#[cfg(target_os = "linux")]
pub(super) fn get_sync_displays() -> Vec<DisplayInfo> {
SYNC_DISPLAYS.lock().unwrap().displays.clone()
}
pub(super) fn get_display_info(idx: usize) -> Option<DisplayInfo> {
SYNC_DISPLAYS.lock().unwrap().displays.get(idx).cloned()
}
// Display to DisplayInfo
// The DisplayInfo is be sent to the peer.
pub(super) fn check_update_displays(all: &Vec<Display>) {
let displays = all
.iter()
.map(|d| {
let display_name = d.name();
let original_resolution = get_original_resolution(&display_name, d.width(), d.height());
@@ -184,32 +261,34 @@ pub fn to_display_info(all: &Vec<Display>) -> Vec<DisplayInfo> {
..Default::default()
}
})
.collect::<Vec<DisplayInfo>>()
.collect::<Vec<DisplayInfo>>();
SYNC_DISPLAYS.lock().unwrap().check_changed(displays);
}
pub fn is_inited_msg() -> Option<Message> {
#[cfg(target_os = "linux")]
if !scrap::is_x11() {
if !is_x11() {
return super::wayland::is_inited();
}
None
}
pub async fn get_displays() -> ResultType<Vec<DisplayInfo>> {
pub async fn update_get_sync_displays() -> ResultType<Vec<DisplayInfo>> {
#[cfg(target_os = "linux")]
{
if !scrap::is_x11() {
if !is_x11() {
return super::wayland::get_displays().await;
}
}
Ok(to_display_info(&try_get_displays()?))
check_update_displays(&try_get_displays()?);
Ok(SYNC_DISPLAYS.lock().unwrap().displays.clone())
}
#[inline]
pub fn get_primary() -> usize {
#[cfg(target_os = "linux")]
{
if !scrap::is_x11() {
if !is_x11() {
return match super::wayland::get_primary() {
Ok(n) => n,
Err(_) => 0,

View File

@@ -1,8 +1,6 @@
use super::*;
#[cfg(target_os = "macos")]
use crate::common::is_server;
#[cfg(target_os = "linux")]
use crate::common::IS_X11;
use crate::input::*;
#[cfg(target_os = "macos")]
use dispatch::Queue;
@@ -1152,7 +1150,7 @@ fn map_keyboard_mode(evt: &KeyEvent) {
// Wayland
#[cfg(target_os = "linux")]
if !*IS_X11 {
if !crate::platform::linux::is_x11() {
let mut en = ENIGO.lock().unwrap();
let code = evt.chr() as u16;

View File

@@ -18,7 +18,16 @@
// to-do:
// https://slhck.info/video/2017/03/01/rate-control.html
use super::{service::ServiceTmpl, video_qos::VideoQoS, *};
use super::{
display_service::{check_display_changed, get_display_info},
service::ServiceTmpl,
video_qos::VideoQoS,
*,
};
#[cfg(target_os = "linux")]
use crate::common::SimpleCallOnReturn;
#[cfg(target_os = "linux")]
use crate::platform::linux::is_x11;
#[cfg(windows)]
use crate::{platform::windows::is_process_consent_running, privacy_win_mag};
use hbb_common::{
@@ -37,8 +46,6 @@ use scrap::{
vpxcodec::{VpxEncoderConfig, VpxVideoCodecId},
CodecName, Display, TraitCapturer,
};
#[cfg(target_os = "linux")]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(windows)]
use std::sync::Once;
use std::{
@@ -49,7 +56,6 @@ use std::{
};
pub const NAME: &'static str = "video";
pub const OPTION_DISPLAY_CHANGED: &'static str = "changed";
pub const OPTION_REFRESH: &'static str = "refresh";
lazy_static::lazy_static! {
@@ -63,10 +69,6 @@ lazy_static::lazy_static! {
pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc<Mutex<bool>> = Default::default();
}
// https://github.com/rustdesk/rustdesk/discussions/6042, avoiding dbus call
#[cfg(target_os = "linux")]
static IS_X11: AtomicBool = AtomicBool::new(false);
#[inline]
pub fn notify_video_frame_fetched(conn_id: i32, frame_tm: Option<Instant>) {
FRAME_FETCHED_NOTIFIER.0.send((conn_id, frame_tm)).ok();
@@ -165,35 +167,6 @@ pub fn new(idx: usize) -> GenericService {
vs.sp
}
fn check_display_changed(
last_n: usize,
last_current: usize,
last_width: usize,
last_height: usize,
) -> bool {
#[cfg(target_os = "linux")]
{
// wayland do not support changing display for now
if !IS_X11.load(Ordering::SeqCst) {
return false;
}
}
let displays = match try_get_displays() {
Ok(d) => d,
_ => return false,
};
let n = displays.len();
if n != last_n {
return true;
};
match displays.get(last_current) {
Some(d) => d.width() != last_width || d.height() != last_height,
None => true,
}
}
// Capturer object is expensive, avoiding to create it frequently.
fn create_capturer(
privacy_mode_id: i32,
@@ -323,7 +296,6 @@ fn check_uac_switch(privacy_mode_id: i32, capturer_privacy_mode_id: i32) -> Resu
}
pub(super) struct CapturerInfo {
pub name: String,
pub origin: (i32, i32),
pub width: usize,
pub height: usize,
@@ -355,7 +327,7 @@ fn get_capturer(
) -> ResultType<CapturerInfo> {
#[cfg(target_os = "linux")]
{
if !IS_X11.load(Ordering::SeqCst) {
if !is_x11() {
return super::wayland::get_capturer();
}
}
@@ -415,7 +387,6 @@ fn get_capturer(
portable_service_running,
)?;
Ok(CapturerInfo {
name,
origin,
width,
height,
@@ -431,16 +402,21 @@ fn run(vs: VideoService) -> ResultType<()> {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let _wake_lock = get_wake_lock();
#[cfg(target_os = "linux")]
{
IS_X11.store(scrap::is_x11(), Ordering::SeqCst);
}
// Wayland only support one video capturer for now. It is ok to call ensure_inited() here.
//
// ensure_inited() is needed because clear() may be called.
// to-do: wayland ensure_inited should pass current display index.
// But for now, we do not support multi-screen capture on wayland.
#[cfg(target_os = "linux")]
super::wayland::ensure_inited()?;
#[cfg(target_os = "linux")]
let _wayland_call_on_ret = SimpleCallOnReturn {
b: true,
f: Box::new(|| {
super::wayland::clear();
}),
};
#[cfg(windows)]
let last_portable_service_running = crate::portable_service::client::running();
#[cfg(not(windows))]
@@ -458,8 +434,7 @@ fn run(vs: VideoService) -> ResultType<()> {
log::info!("init quality={:?}, abr enabled:{}", quality, abr);
let codec_name = Encoder::negotiated_codec();
let recorder = get_recorder(c.width, c.height, &codec_name);
let last_recording =
(recorder.lock().unwrap().is_some() || video_qos.record()) && codec_name != CodecName::AV1;
let last_recording = recorder.lock().unwrap().is_some() || video_qos.record();
drop(video_qos);
let encoder_cfg = get_encoder_config(&c, quality, last_recording);
@@ -471,16 +446,6 @@ fn run(vs: VideoService) -> ResultType<()> {
c.set_use_yuv(encoder.use_yuv());
VIDEO_QOS.lock().unwrap().store_bitrate(encoder.bitrate());
if sp.is_option_true(OPTION_DISPLAY_CHANGED) {
log::debug!("Broadcasting display changed");
broadcast_display_changed(
display_idx,
&sp,
Some((c.name.clone(), c.origin.clone(), c.width, c.height)),
);
sp.set_option_bool(OPTION_DISPLAY_CHANGED, false);
}
if sp.is_option_true(OPTION_REFRESH) {
sp.set_option_bool(OPTION_REFRESH, false);
}
@@ -511,14 +476,14 @@ fn run(vs: VideoService) -> ResultType<()> {
allow_err!(encoder.set_quality(quality));
video_qos.store_bitrate(encoder.bitrate());
}
let recording = (recorder.lock().unwrap().is_some() || video_qos.record())
&& codec_name != CodecName::AV1;
let recording = recorder.lock().unwrap().is_some() || video_qos.record();
if recording != last_recording {
bail!("SWITCH");
}
drop(video_qos);
if sp.is_option_true(OPTION_DISPLAY_CHANGED) || sp.is_option_true(OPTION_REFRESH) {
if sp.is_option_true(OPTION_REFRESH) {
let _ = try_broadcast_display_changed(&sp, display_idx, &c);
bail!("SWITCH");
}
if codec_name != Encoder::negotiated_codec() {
@@ -540,14 +505,9 @@ fn run(vs: VideoService) -> ResultType<()> {
let now = time::Instant::now();
if last_check_displays.elapsed().as_millis() > 1000 {
last_check_displays = now;
// Capturer on macos does not return Err event the solution is changed.
#[cfg(target_os = "macos")]
if check_display_changed(c.ndisplay, c.current, c.width, c.height) {
sp.set_option_bool(OPTION_DISPLAY_CHANGED, true);
log::info!("Displays changed");
bail!("SWITCH");
}
// This check may be redundant, but it is better to be safe.
// The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough.
try_broadcast_display_changed(&sp, display_idx, &c)?;
}
frame_controller.reset();
@@ -609,7 +569,7 @@ fn run(vs: VideoService) -> ResultType<()> {
#[cfg(target_os = "linux")]
{
would_block_count += 1;
if !IS_X11.load(Ordering::SeqCst) {
if !is_x11() {
if would_block_count >= 100 {
// to-do: Unknown reason for WouldBlock 100 times (seconds = 100 * 1 / fps)
// https://github.com/rustdesk/rustdesk/blob/63e6b2f8ab51743e77a151e2b7ff18816f5fa2fb/libs/scrap/src/common/wayland.rs#L81
@@ -624,13 +584,9 @@ fn run(vs: VideoService) -> ResultType<()> {
}
}
Err(err) => {
if check_display_changed(c.ndisplay, c.current, c.width, c.height) {
log::info!("Displays changed");
#[cfg(target_os = "linux")]
super::wayland::clear();
sp.set_option_bool(OPTION_DISPLAY_CHANGED, true);
bail!("SWITCH");
}
// This check may be redundant, but it is better to be safe.
// The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough.
try_broadcast_display_changed(&sp, display_idx, &c)?;
#[cfg(windows)]
if !c.is_gdi() {
@@ -638,7 +594,6 @@ fn run(vs: VideoService) -> ResultType<()> {
log::info!("dxgi error, fall back to gdi: {:?}", err);
continue;
}
return Err(err.into());
}
_ => {
@@ -671,9 +626,6 @@ fn run(vs: VideoService) -> ResultType<()> {
}
}
#[cfg(target_os = "linux")]
super::wayland::clear();
Ok(())
}
@@ -797,7 +749,7 @@ fn handle_one_frame(
pub fn is_inited_msg() -> Option<Message> {
#[cfg(target_os = "linux")]
if !IS_X11.load(Ordering::SeqCst) {
if !is_x11() {
return super::wayland::is_inited();
}
None
@@ -889,58 +841,52 @@ fn get_wake_lock() -> crate::platform::WakeLock {
}
#[inline]
fn broadcast_display_changed(
display_idx: usize,
fn try_broadcast_display_changed(
sp: &GenericService,
display_meta: Option<(String, (i32, i32), usize, usize)>,
) {
if let Some(msg_out) = make_display_changed_msg(display_idx, display_meta) {
sp.send(msg_out);
}
}
fn get_display_info_simple_meta(display_idx: usize) -> Option<(String, (i32, i32), usize, usize)> {
let displays = display_service::try_get_displays().ok()?;
if let Some(display) = displays.get(display_idx) {
Some((
display.name(),
display.origin(),
display.width(),
display.height(),
))
} else {
None
display_idx: usize,
cap: &CapturerInfo,
) -> ResultType<()> {
if let Some(display) = check_display_changed(
cap.ndisplay,
cap.current,
(cap.origin.0, cap.origin.1, cap.width, cap.height),
) {
log::info!("Display {} changed", display);
if let Some(msg_out) = make_display_changed_msg(display_idx, Some(display)) {
sp.send(msg_out);
bail!("SWITCH");
}
}
Ok(())
}
pub fn make_display_changed_msg(
display_idx: usize,
display_meta: Option<(String, (i32, i32), usize, usize)>,
opt_display: Option<DisplayInfo>,
) -> Option<Message> {
let mut misc = Misc::new();
let (name, origin, width, height) = match display_meta {
let display = match opt_display {
Some(d) => d,
None => get_display_info_simple_meta(display_idx)?,
None => get_display_info(display_idx)?,
};
let original_resolution = display_service::get_original_resolution(&name, width, height);
let mut misc = Misc::new();
misc.set_switch_display(SwitchDisplay {
display: display_idx as _,
x: origin.0,
y: origin.1,
width: width as _,
height: height as _,
x: display.x,
y: display.y,
width: display.width,
height: display.height,
cursor_embedded: display_service::capture_cursor_embedded(),
#[cfg(not(any(target_os = "android", target_os = "ios")))]
resolutions: Some(SupportedResolutions {
resolutions: if name.is_empty() {
resolutions: if display.name.is_empty() {
vec![]
} else {
crate::platform::resolutions(&name)
crate::platform::resolutions(&display.name)
},
..SupportedResolutions::default()
})
.into(),
original_resolution,
original_resolution: display.original_resolution,
..Default::default()
});
let mut msg_out = Message::new();

View File

@@ -4,8 +4,11 @@ use scrap::{is_cursor_embedded, set_map_err, Capturer, Display, Frame, TraitCapt
use std::io;
use std::process::{Command, Output};
use crate::client::{
SCRAP_OTHER_VERSION_OR_X11_REQUIRED, SCRAP_UBUNTU_HIGHER_REQUIRED, SCRAP_X11_REQUIRED,
use crate::{
client::{
SCRAP_OTHER_VERSION_OR_X11_REQUIRED, SCRAP_UBUNTU_HIGHER_REQUIRED, SCRAP_X11_REQUIRED,
},
platform::linux::is_x11,
};
lazy_static::lazy_static! {
@@ -96,7 +99,7 @@ pub(super) async fn ensure_inited() -> ResultType<()> {
}
pub(super) fn is_inited() -> Option<Message> {
if scrap::is_x11() {
if is_x11() {
None
} else {
if *CAP_DISPLAY_INFO.read().unwrap() == 0 {
@@ -133,7 +136,7 @@ fn get_max_desktop_resolution() -> Option<String> {
}
pub(super) async fn check_init() -> ResultType<()> {
if !scrap::is_x11() {
if !is_x11() {
let mut minx = 0;
let mut maxx = 0;
let mut miny = 0;
@@ -146,7 +149,8 @@ pub(super) async fn check_init() -> ResultType<()> {
let num = all.len();
let primary = super::display_service::get_primary_2(&all);
let current = primary;
let mut displays = super::display_service::to_display_info(&all);
super::display_service::check_update_displays(&all);
let mut displays = super::display_service::get_sync_displays();
for display in displays.iter_mut() {
display.cursor_embedded = is_cursor_embedded();
}
@@ -173,12 +177,15 @@ pub(super) async fn check_init() -> ResultType<()> {
Some(result) if !result.is_empty() => {
let resolution: Vec<&str> = result.split(" ").collect();
let w: i32 = resolution[0].parse().unwrap_or(origin.0 + width as i32);
let h: i32 = resolution[2].trim_end_matches(",").parse().unwrap_or(origin.1 + height as i32);
let h: i32 = resolution[2]
.trim_end_matches(",")
.parse()
.unwrap_or(origin.1 + height as i32);
(w, h)
}
_ => (origin.0 + width as i32, origin.1 + height as i32)
_ => (origin.0 + width as i32, origin.1 + height as i32),
};
minx = 0;
maxx = max_width;
miny = 0;
@@ -242,7 +249,7 @@ pub(super) fn get_primary() -> ResultType<usize> {
}
pub fn clear() {
if scrap::is_x11() {
if is_x11() {
return;
}
let mut write_lock = CAP_DISPLAY_INFO.write().unwrap();
@@ -257,7 +264,7 @@ pub fn clear() {
}
pub(super) fn get_capturer() -> ResultType<super::video_service::CapturerInfo> {
if scrap::is_x11() {
if is_x11() {
bail!("Do not call this function if not wayland");
}
let addr = *CAP_DISPLAY_INFO.read().unwrap();
@@ -267,9 +274,6 @@ pub(super) fn get_capturer() -> ResultType<super::video_service::CapturerInfo> {
let cap_display_info = &*cap_display_info;
let rect = cap_display_info.rects[cap_display_info.current];
Ok(super::video_service::CapturerInfo {
name: cap_display_info.displays[cap_display_info.current]
.name
.clone(),
origin: rect.0,
width: rect.1,
height: rect.2,

View File

@@ -1,9 +1,12 @@
use crate::{client::translate, ipc::Data};
use hbb_common::{allow_err, log, tokio};
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use crate::client::translate;
#[cfg(windows)]
use crate::ipc::Data;
#[cfg(windows)]
use hbb_common::tokio;
use hbb_common::{allow_err, log};
use std::sync::{Arc, Mutex};
#[cfg(windows)]
use std::time::Duration;
pub fn start_tray() {
allow_err!(make_tray());

View File

@@ -144,7 +144,7 @@ class Header: Reactor.Component {
<span #action>{svg_action}</span>
<span #display>{svg_display}</span>
<span #keyboard>{svg_keyboard}</span>
{recording_enabled && qualityMonitorData[4] != "AV1" ? <span #recording>{recording ? svg_recording_on : svg_recording_off}</span> : ""}
{recording_enabled ? <span #recording>{recording ? svg_recording_on : svg_recording_off}</span> : ""}
{this.renderKeyboardPop()}
{this.renderDisplayPop()}
{this.renderActionPop()}
@@ -299,14 +299,22 @@ class Header: Reactor.Component {
header.update();
handler.record_status(recording);
// 0 is just a dummy value. It will be ignored by the handler.
if (recording)
if (recording) {
handler.refresh_video(0);
else
handler.record_screen(false, 0, display_width, display_height);
if (handler.version_cmp(pi.version, '1.2.4') >= 0) handler.record_screen(recording, pi.current_display, display_width, display_height);
}
else {
handler.record_screen(recording, pi.current_display, display_width, display_height);
}
}
event click $(#screen) (_, me) {
if (pi.current_display == me.index) return;
if (recording) {
recording = false;
handler.record_screen(false, pi.current_display, display_width, display_height);
handler.record_status(false);
}
handler.switch_display(me.index);
}

View File

@@ -243,6 +243,7 @@ impl InvokeUiSession for SciterHandler {
pi_sciter.set_item("sas_enabled", pi.sas_enabled);
pi_sciter.set_item("displays", Self::make_displays_array(&pi.displays));
pi_sciter.set_item("current_display", pi.current_display);
pi_sciter.set_item("version", pi.version.clone());
self.call("updatePi", &make_args!(pi_sciter));
}
@@ -469,6 +470,7 @@ impl sciter::EventHandler for SciterSession {
fn restart_remote_device();
fn request_voice_call();
fn close_voice_call();
fn version_cmp(String, String);
}
}
@@ -757,6 +759,10 @@ impl SciterSession {
log::error!("Failed to spawn IP tunneling: {}", err);
}
}
fn version_cmp(&self, v1: String, v2: String) -> i32 {
(hbb_common::get_version_number(&v1) - hbb_common::get_version_number(&v2)) as i32
}
}
pub fn make_fd(id: i32, entries: &Vec<FileEntry>, only_count: bool) -> Value {

View File

@@ -522,7 +522,6 @@ handler.updateQualityStatus = function(speed, fps, delay, bitrate, codec_format)
bitrate ? qualityMonitorData[3] = bitrate:null;
codec_format ? qualityMonitorData[4] = codec_format:null;
qualityMonitor.update();
if (codec_format) header.update();
}
handler.setPermission = function(name, enabled) {

View File

@@ -240,6 +240,10 @@ impl<T: InvokeUiSession> Session<T> {
self.lc.read().unwrap().displays_as_individual_windows.clone()
}
pub fn get_use_all_my_displays_for_the_remote_session(&self) -> String {
self.lc.read().unwrap().use_all_my_displays_for_the_remote_session.clone()
}
pub fn save_reverse_mouse_wheel(&self, value: String) {
self.lc.write().unwrap().save_reverse_mouse_wheel(value);
}
@@ -248,6 +252,10 @@ impl<T: InvokeUiSession> Session<T> {
self.lc.write().unwrap().save_displays_as_individual_windows(value);
}
pub fn save_use_all_my_displays_for_the_remote_session(&self, value: String) {
self.lc.write().unwrap().save_use_all_my_displays_for_the_remote_session(value);
}
pub fn save_view_style(&self, value: String) {
self.lc.write().unwrap().save_view_style(value);
}