mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-20 11:41:05 +03:00
terminal works basically. (#12189)
* terminal works basically. todo: - persistent - sessions restore - web - mobile * missed terminal persistent option change * android sdk 34 -> 35 * +#![cfg_attr(lt_1_77, feature(c_str_literals))] * fixing ci * fix ci * fix ci for android * try "Fix Android SDK Platform 35" * fix android 34 * revert flutter_plugin_android_lifecycle to 2.0.17 which used in rustdesk 1.4.0 * refactor, but break something of desktop terminal (new tab showing loading) * fix connecting...
This commit is contained in:
@@ -47,8 +47,8 @@ use hbb_common::{
|
||||
anyhow::{anyhow, Context},
|
||||
bail,
|
||||
config::{
|
||||
self, use_ws, Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT,
|
||||
READ_TIMEOUT, RELAY_PORT, RENDEZVOUS_PORT, RENDEZVOUS_SERVERS,
|
||||
self, keys, use_ws, Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution,
|
||||
CONNECT_TIMEOUT, READ_TIMEOUT, RELAY_PORT, RENDEZVOUS_PORT, RENDEZVOUS_SERVERS,
|
||||
},
|
||||
fs::JobType,
|
||||
futures::future::{select_ok, FutureExt},
|
||||
@@ -274,7 +274,6 @@ impl Client {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if crate::get_ipv6_punch_enabled() {
|
||||
crate::test_ipv6().await;
|
||||
}
|
||||
@@ -1784,6 +1783,9 @@ impl LoginConfigHandler {
|
||||
/// * `v` - value of option
|
||||
pub fn set_option(&mut self, k: String, v: String) {
|
||||
let mut config = self.load_config();
|
||||
if v == self.get_option(&k) {
|
||||
return;
|
||||
}
|
||||
config.options.insert(k, v);
|
||||
self.save_config(config);
|
||||
}
|
||||
@@ -1952,6 +1954,14 @@ impl LoginConfigHandler {
|
||||
BoolOption::No
|
||||
})
|
||||
.into();
|
||||
} else if name == keys::OPTION_TERMINAL_PERSISTENT {
|
||||
config.terminal_persistent.v = !config.terminal_persistent.v;
|
||||
option.terminal_persistent = (if config.terminal_persistent.v {
|
||||
BoolOption::Yes
|
||||
} else {
|
||||
BoolOption::No
|
||||
})
|
||||
.into();
|
||||
} else if name == "privacy-mode" {
|
||||
// try toggle privacy mode
|
||||
option.privacy_mode = (if config.privacy_mode.v {
|
||||
@@ -2049,6 +2059,14 @@ impl LoginConfigHandler {
|
||||
return None;
|
||||
}
|
||||
let mut msg = OptionMessage::new();
|
||||
if self.conn_type.eq(&ConnType::TERMINAL) {
|
||||
if self.get_toggle_option(keys::OPTION_TERMINAL_PERSISTENT) {
|
||||
msg.terminal_persistent = BoolOption::Yes.into();
|
||||
return Some(msg);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let q = self.image_quality.clone();
|
||||
if let Some(q) = self.get_image_quality_enum(&q, ignore_default) {
|
||||
msg.image_quality = q.into();
|
||||
@@ -2094,7 +2112,7 @@ impl LoginConfigHandler {
|
||||
if self.get_toggle_option("disable-audio") {
|
||||
msg.disable_audio = BoolOption::Yes.into();
|
||||
}
|
||||
if !view_only && self.get_toggle_option(config::keys::OPTION_ENABLE_FILE_COPY_PASTE) {
|
||||
if !view_only && self.get_toggle_option(keys::OPTION_ENABLE_FILE_COPY_PASTE) {
|
||||
msg.enable_file_transfer = BoolOption::Yes.into();
|
||||
}
|
||||
if view_only || self.get_toggle_option("disable-clipboard") {
|
||||
@@ -2150,9 +2168,11 @@ impl LoginConfigHandler {
|
||||
self.config.show_remote_cursor.v
|
||||
} else if name == "lock-after-session-end" {
|
||||
self.config.lock_after_session_end.v
|
||||
} else if name == keys::OPTION_TERMINAL_PERSISTENT {
|
||||
self.config.terminal_persistent.v
|
||||
} else if name == "privacy-mode" {
|
||||
self.config.privacy_mode.v
|
||||
} else if name == config::keys::OPTION_ENABLE_FILE_COPY_PASTE {
|
||||
} else if name == keys::OPTION_ENABLE_FILE_COPY_PASTE {
|
||||
self.config.enable_file_copy_paste.v
|
||||
} else if name == "disable-audio" {
|
||||
self.config.disable_audio.v
|
||||
@@ -2452,7 +2472,7 @@ impl LoginConfigHandler {
|
||||
} else {
|
||||
(my_id, self.id.clone())
|
||||
};
|
||||
let mut display_name = get_builtin_option(config::keys::OPTION_DISPLAY_NAME);
|
||||
let mut display_name = get_builtin_option(keys::OPTION_DISPLAY_NAME);
|
||||
if display_name.is_empty() {
|
||||
display_name =
|
||||
serde_json::from_str::<serde_json::Value>(&LocalConfig::get_option("user_info"))
|
||||
@@ -2522,6 +2542,11 @@ impl LoginConfigHandler {
|
||||
port: self.port_forward.1,
|
||||
..Default::default()
|
||||
}),
|
||||
ConnType::TERMINAL => {
|
||||
let mut terminal = Terminal::new();
|
||||
terminal.service_id = self.get_option("terminal-service-id");
|
||||
lr.set_terminal(terminal);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -3237,8 +3262,7 @@ pub async fn handle_hash(
|
||||
}
|
||||
|
||||
if password.is_empty() {
|
||||
let p =
|
||||
crate::ui_interface::get_builtin_option(config::keys::OPTION_DEFAULT_CONNECT_PASSWORD);
|
||||
let p = crate::ui_interface::get_builtin_option(keys::OPTION_DEFAULT_CONNECT_PASSWORD);
|
||||
if !p.is_empty() {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(p.clone());
|
||||
@@ -3789,11 +3813,9 @@ pub mod peer_online {
|
||||
}
|
||||
// Retry for 2 times to get the online response
|
||||
for _ in 0..2 {
|
||||
if let Some(msg_in) = crate::get_next_nonkeyexchange_msg(
|
||||
&mut socket,
|
||||
Some(timeout.as_millis() as _),
|
||||
)
|
||||
.await
|
||||
if let Some(msg_in) =
|
||||
crate::get_next_nonkeyexchange_msg(&mut socket, Some(timeout.as_millis() as _))
|
||||
.await
|
||||
{
|
||||
match msg_in.union {
|
||||
Some(rendezvous_message::Union::OnlineResponse(online_response)) => {
|
||||
|
||||
@@ -85,6 +85,7 @@ struct ParsedPeerInfo {
|
||||
is_installed: bool,
|
||||
idd_impl: String,
|
||||
support_view_camera: bool,
|
||||
support_terminal: bool,
|
||||
}
|
||||
|
||||
impl ParsedPeerInfo {
|
||||
@@ -131,10 +132,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let _file_clip_context_holder = {
|
||||
// `is_port_forward()` will not reach here, but we still check it for clarity.
|
||||
if !self.handler.is_file_transfer()
|
||||
&& !self.handler.is_port_forward()
|
||||
&& !self.handler.is_view_camera()
|
||||
{
|
||||
if self.handler.is_default() {
|
||||
// It is ok to call this function multiple times.
|
||||
ContextSend::enable(true);
|
||||
Some(crate::SimpleCallOnReturn {
|
||||
@@ -159,6 +157,8 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
ConnType::FILE_TRANSFER
|
||||
} else if self.handler.is_view_camera() {
|
||||
ConnType::VIEW_CAMERA
|
||||
} else if self.handler.is_terminal() {
|
||||
ConnType::TERMINAL
|
||||
} else {
|
||||
ConnType::default()
|
||||
};
|
||||
@@ -195,11 +195,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
let mut rx_clip_client_holder = (Arc::new(TokioMutex::new(rx)), None);
|
||||
#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste"))]
|
||||
{
|
||||
let is_conn_not_default = self.handler.is_file_transfer()
|
||||
|| self.handler.is_port_forward()
|
||||
|| self.handler.is_rdp()
|
||||
|| self.handler.is_view_camera();
|
||||
if !is_conn_not_default {
|
||||
if self.handler.is_default() {
|
||||
(self.client_conn_id, rx_clip_client_holder.0) =
|
||||
clipboard::get_rx_cliprdr_client(&self.handler.get_id());
|
||||
log::debug!("get cliprdr client for conn_id {}", self.client_conn_id);
|
||||
@@ -338,12 +334,12 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
.set_disconnected(round);
|
||||
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
if !self.handler.is_view_camera() && _set_disconnected_ok {
|
||||
if self.handler.is_default() && _set_disconnected_ok {
|
||||
Client::try_stop_clipboard();
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste"))]
|
||||
if !self.handler.is_view_camera() && _set_disconnected_ok {
|
||||
if self.handler.is_default() && _set_disconnected_ok {
|
||||
crate::clipboard::try_empty_clipboard_files(ClipboardSide::Client, self.client_conn_id);
|
||||
}
|
||||
}
|
||||
@@ -437,7 +433,10 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
|
||||
// Start a voice call recorder, records audio and send to remote
|
||||
fn start_voice_call(&mut self) -> Option<std::sync::mpsc::Sender<()>> {
|
||||
if self.handler.is_file_transfer() || self.handler.is_port_forward() {
|
||||
if self.handler.is_file_transfer()
|
||||
|| self.handler.is_port_forward()
|
||||
|| self.handler.is_terminal()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
// iOS does not have this server.
|
||||
@@ -1230,6 +1229,24 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
fn check_terminal_support(&self, peer_version: &str) -> bool {
|
||||
if self.peer_info.support_terminal {
|
||||
return true;
|
||||
}
|
||||
if hbb_common::get_version_number(&peer_version) < hbb_common::get_version_number("1.4.1") {
|
||||
self.handler.msgbox(
|
||||
"error",
|
||||
"Remote terminal not supported",
|
||||
"Remote terminal is not supported by the remote side. Please upgrade to version 1.4.1 or higher.",
|
||||
"",
|
||||
);
|
||||
} else {
|
||||
self.handler
|
||||
.on_error("Remote terminal is not supported by the remote side");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
||||
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
||||
match msg_in.union {
|
||||
@@ -1290,13 +1307,16 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if self.handler.is_terminal() {
|
||||
if !self.check_terminal_support(&peer_version) {
|
||||
self.handler.lc.write().unwrap().handle_peer_info(&pi);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
self.handler.handle_peer_info(pi);
|
||||
#[cfg(all(target_os = "windows", not(feature = "flutter")))]
|
||||
self.check_clipboard_file_context();
|
||||
if !(self.handler.is_file_transfer()
|
||||
|| self.handler.is_port_forward()
|
||||
|| self.handler.is_view_camera())
|
||||
{
|
||||
if self.handler.is_default() {
|
||||
#[cfg(feature = "flutter")]
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
let rx = Client::try_start_clipboard(None);
|
||||
@@ -1661,9 +1681,6 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(Permission::Camera) => {
|
||||
self.handler.set_permission("camera", p.enabled);
|
||||
}
|
||||
Ok(Permission::Restart) => {
|
||||
self.handler.set_permission("restart", p.enabled);
|
||||
}
|
||||
@@ -1923,6 +1940,18 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
self.handler
|
||||
.handle_screenshot_resp(response.sid, response.msg);
|
||||
}
|
||||
Some(message::Union::TerminalResponse(response)) => {
|
||||
use hbb_common::message_proto::terminal_response::Union;
|
||||
if let Some(Union::Opened(opened)) = &response.union {
|
||||
if opened.success && !opened.service_id.is_empty() {
|
||||
self.handler.lc.write().unwrap().set_option(
|
||||
"terminal-service-id".to_owned(),
|
||||
opened.service_id.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.handler.handle_terminal_response(response);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -1931,6 +1960,12 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
|
||||
fn set_peer_info(&mut self, pi: &PeerInfo) {
|
||||
self.peer_info.platform = pi.platform.clone();
|
||||
|
||||
// Check features field for terminal support
|
||||
if let Some(features) = pi.features.as_ref() {
|
||||
self.peer_info.support_terminal = features.terminal;
|
||||
}
|
||||
|
||||
if let Ok(platform_additions) =
|
||||
serde_json::from_str::<HashMap<String, serde_json::Value>>(&pi.platform_additions)
|
||||
{
|
||||
|
||||
@@ -1105,6 +1105,60 @@ impl InvokeUiSession for FlutterHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_terminal_response(&self, response: TerminalResponse) {
|
||||
use hbb_common::message_proto::terminal_response::Union;
|
||||
|
||||
match response.union {
|
||||
Some(Union::Opened(opened)) => {
|
||||
let mut event_data: Vec<(&str, serde_json::Value)> = vec![
|
||||
("type", json!("opened")),
|
||||
("terminal_id", json!(opened.terminal_id)),
|
||||
("success", json!(opened.success)),
|
||||
("message", json!(&opened.message)),
|
||||
("pid", json!(opened.pid)),
|
||||
("service_id", json!(&opened.service_id)),
|
||||
];
|
||||
self.push_event_("terminal_response", &event_data, &[], &[]);
|
||||
}
|
||||
Some(Union::Data(data)) => {
|
||||
// Decompress data if needed
|
||||
let output_data = if data.compressed {
|
||||
hbb_common::compress::decompress(&data.data)
|
||||
} else {
|
||||
data.data.to_vec()
|
||||
};
|
||||
|
||||
let encoded = crate::encode64(&output_data);
|
||||
let event_data: Vec<(&str, serde_json::Value)> = vec![
|
||||
("type", json!("data")),
|
||||
("terminal_id", json!(data.terminal_id)),
|
||||
("data", json!(&encoded)),
|
||||
];
|
||||
self.push_event_("terminal_response", &event_data, &[], &[]);
|
||||
}
|
||||
Some(Union::Closed(closed)) => {
|
||||
let event_data: Vec<(&str, serde_json::Value)> = vec![
|
||||
("type", json!("closed")),
|
||||
("terminal_id", json!(closed.terminal_id)),
|
||||
("exit_code", json!(closed.exit_code)),
|
||||
];
|
||||
self.push_event_("terminal_response", &event_data, &[], &[]);
|
||||
}
|
||||
Some(Union::Error(error)) => {
|
||||
let event_data: Vec<(&str, serde_json::Value)> = vec![
|
||||
("type", json!("error")),
|
||||
("terminal_id", json!(error.terminal_id)),
|
||||
("message", json!(&error.message)),
|
||||
];
|
||||
self.push_event_("terminal_response", &event_data, &[], &[]);
|
||||
}
|
||||
None => {}
|
||||
Some(_) => {
|
||||
log::warn!("Unhandled terminal response type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlutterHandler {
|
||||
@@ -1221,6 +1275,7 @@ pub fn session_add(
|
||||
is_view_camera: bool,
|
||||
is_port_forward: bool,
|
||||
is_rdp: bool,
|
||||
is_terminal: bool,
|
||||
switch_uuid: &str,
|
||||
force_relay: bool,
|
||||
password: String,
|
||||
@@ -1231,6 +1286,8 @@ pub fn session_add(
|
||||
ConnType::FILE_TRANSFER
|
||||
} else if is_view_camera {
|
||||
ConnType::VIEW_CAMERA
|
||||
} else if is_terminal {
|
||||
ConnType::TERMINAL
|
||||
} else if is_port_forward {
|
||||
if is_rdp {
|
||||
ConnType::RDP
|
||||
|
||||
@@ -103,6 +103,8 @@ pub fn peer_get_sessions_count(id: String, conn_type: i32) -> SyncReturn<usize>
|
||||
ConnType::PORT_FORWARD
|
||||
} else if conn_type == ConnType::RDP as i32 {
|
||||
ConnType::RDP
|
||||
} else if conn_type == ConnType::TERMINAL as i32 {
|
||||
ConnType::TERMINAL
|
||||
} else {
|
||||
ConnType::DEFAULT_CONN
|
||||
};
|
||||
@@ -129,6 +131,7 @@ pub fn session_add_sync(
|
||||
is_view_camera: bool,
|
||||
is_port_forward: bool,
|
||||
is_rdp: bool,
|
||||
is_terminal: bool,
|
||||
switch_uuid: String,
|
||||
force_relay: bool,
|
||||
password: String,
|
||||
@@ -142,6 +145,7 @@ pub fn session_add_sync(
|
||||
is_view_camera,
|
||||
is_port_forward,
|
||||
is_rdp,
|
||||
is_terminal,
|
||||
&switch_uuid,
|
||||
force_relay,
|
||||
password,
|
||||
@@ -613,6 +617,33 @@ pub fn session_send_chat(session_id: SessionID, text: String) {
|
||||
}
|
||||
}
|
||||
|
||||
// Terminal functions
|
||||
pub fn session_open_terminal(session_id: SessionID, terminal_id: i32, rows: u32, cols: u32) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.open_terminal(terminal_id, rows, cols);
|
||||
} else {
|
||||
log::error!("[flutter_ffi] Session not found for session_id: {}", session_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_send_terminal_input(session_id: SessionID, terminal_id: i32, data: String) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.send_terminal_input(terminal_id, data);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_resize_terminal(session_id: SessionID, terminal_id: i32, rows: u32, cols: u32) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.resize_terminal(terminal_id, rows, cols);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_close_terminal(session_id: SessionID, terminal_id: i32) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.close_terminal(terminal_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_peer_option(session_id: SessionID, name: String, value: String) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.set_option(name, value);
|
||||
|
||||
@@ -190,6 +190,7 @@ pub enum Data {
|
||||
id: i32,
|
||||
is_file_transfer: bool,
|
||||
is_view_camera: bool,
|
||||
is_terminal: bool,
|
||||
peer_id: String,
|
||||
name: String,
|
||||
authorized: bool,
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "لا يوجد اذن نقل الملف"),
|
||||
("Note", "ملاحظة"),
|
||||
("Connection", "الاتصال"),
|
||||
("Share Screen", "مشاركة الشاشة"),
|
||||
("Share screen", "مشاركة الشاشة"),
|
||||
("Chat", "محادثة"),
|
||||
("Total", "الاجمالي"),
|
||||
("items", "عناصر"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "لقط الشاشة"),
|
||||
("Input Control", "تحكم الادخال"),
|
||||
("Audio Capture", "لقط الصوت"),
|
||||
("File Connection", "اتصال الملف"),
|
||||
("Screen Connection", "اتصال الشاشة"),
|
||||
("Do you accept?", "هل تقبل؟"),
|
||||
("Open System Setting", "فتح اعدادات النظام"),
|
||||
("How to get Android input permission?", "كيف تحصل على اذن الادخال في اندرويد؟"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "غير موسوم"),
|
||||
("new-version-of-{}-tip", "تحديث جديد متاح لـ {}"),
|
||||
("Accessible devices", "الأجهزة القابلة للوصول"),
|
||||
("View camera", "عرض الكاميرا"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "ترقية عميل RustDesk البعيد إلى {}"),
|
||||
("view_camera_unsupported_tip", "عرض الكاميرا غير مدعوم في هذا الجهاز"),
|
||||
("Enable camera", "تمكين الكاميرا"),
|
||||
("No cameras", "لا توجد كاميرات"),
|
||||
("d3d_render_tip", "تمكين العرض باستخدام D3D"),
|
||||
("Use D3D rendering", "استخدام عرض D3D"),
|
||||
("Printer", "الطابعة"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "كلمة مرور رقمية لمرة واحدة"),
|
||||
("Enable IPv6 P2P connection", "تمكين اتصال نظير إلى نظير عبر IPv6"),
|
||||
("Enable UDP hole punching", "تمكين تقنية حفر الثغرات عبر UDP"),
|
||||
("View camera", "عرض الكاميرا"),
|
||||
("Enable camera", "تمكين الكاميرا"),
|
||||
("No cameras", "لا توجد كاميرات"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Няма дазволу на перадачу файлаў"),
|
||||
("Note", "Нататка"),
|
||||
("Connection", "Падключэнне"),
|
||||
("Share Screen", "Дзяліцца экранам"),
|
||||
("Share screen", "Дзяліцца экранам"),
|
||||
("Chat", "Чат"),
|
||||
("Total", "Усяго"),
|
||||
("items", "элементы"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Захоп экрана"),
|
||||
("Input Control", "Кіраванне ўводам"),
|
||||
("Audio Capture", "Захоп аўдыё"),
|
||||
("File Connection", "Падлучэнне перадачы файлаў"),
|
||||
("Screen Connection", "Падлучэнне прагляду/кіравання экранам"),
|
||||
("Do you accept?", "Ці вы згодны?"),
|
||||
("Open System Setting", "Адкрыць налады сістэмы"),
|
||||
("How to get Android input permission?", "Як атрымаць дазвол на ўвод Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Прагляд камеры"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Калі ласка, абнавіце кліент RustDesk да версіі {} або навейшай на аддаленым баку!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Прагляд камеры"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Няма разрешение за прехвърляне на файлове"),
|
||||
("Note", "Бележка"),
|
||||
("Connection", "Връзка"),
|
||||
("Share Screen", "Сподели екран"),
|
||||
("Share screen", "Сподели екран"),
|
||||
("Chat", "Чат"),
|
||||
("Total", "Общо"),
|
||||
("items", "неща"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Заснемане на екрана"),
|
||||
("Input Control", "Управление на въвеждане"),
|
||||
("Audio Capture", "Аудиозапис"),
|
||||
("File Connection", "Файлова връзка"),
|
||||
("Screen Connection", "Екранна връзка"),
|
||||
("Do you accept?", "Приемате ли?"),
|
||||
("Open System Setting", "Отворете системните настройки"),
|
||||
("How to get Android input permission?", "Как да получим право за въвеждане при Андроид?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Преглед на камерата"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Моля, надстройте клиента RustDesk до версия {} или по-нова от отдалечената страна!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Преглед на камерата"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Cap permís per a transferència de fitxers"),
|
||||
("Note", "Nota"),
|
||||
("Connection", "Connexió"),
|
||||
("Share Screen", "Compartició de pantalla"),
|
||||
("Share screen", "Compartició de pantalla"),
|
||||
("Chat", "Xat"),
|
||||
("Total", "Total"),
|
||||
("items", "elements"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Captura de pantalla"),
|
||||
("Input Control", "Control d'entrada"),
|
||||
("Audio Capture", "Captura d'àudio"),
|
||||
("File Connection", "Connexió de fitxer"),
|
||||
("Screen Connection", "Connexió de pantalla"),
|
||||
("Do you accept?", "Voleu acceptar?"),
|
||||
("Open System Setting", "Obre la configuració del sistema"),
|
||||
("How to get Android input permission?", "Com modificar els permisos a Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Sense etiquetar"),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", "Dispositius accessibles"),
|
||||
("View camera", "Mostra la càmera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Veuillez mettre à niveau le client RustDesk vers la version {} ou plus récente du côté distant !"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Mostra la càmera"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "没有文件传输权限"),
|
||||
("Note", "备注"),
|
||||
("Connection", "连接"),
|
||||
("Share Screen", "共享屏幕"),
|
||||
("Share screen", "共享屏幕"),
|
||||
("Chat", "聊天消息"),
|
||||
("Total", "总计"),
|
||||
("items", "个项目"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "屏幕录制"),
|
||||
("Input Control", "输入控制"),
|
||||
("Audio Capture", "音频录制"),
|
||||
("File Connection", "文件连接"),
|
||||
("Screen Connection", "屏幕连接"),
|
||||
("Do you accept?", "是否接受?"),
|
||||
("Open System Setting", "打开系统设置"),
|
||||
("How to get Android input permission?", "如何获取安卓的输入权限?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "无标签"),
|
||||
("new-version-of-{}-tip", "{} 版本更新"),
|
||||
("Accessible devices", "可访问的设备"),
|
||||
("View camera", "查看摄像头"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "请在远程端将 RustDesk 客户端升级至版本 {} 或更新版本!"),
|
||||
("view_camera_unsupported_tip", "您的远程端不支持查看摄像头。"),
|
||||
("Enable camera", "允许查看摄像头"),
|
||||
("No cameras", "没有摄像头"),
|
||||
("d3d_render_tip", "当启用 D3D 渲染时,某些机器可能无法显示远程画面。"),
|
||||
("Use D3D rendering", "使用 D3D 渲染"),
|
||||
("Printer", "打印机"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "一次性密码为数字"),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "查看摄像头"),
|
||||
("Enable camera", "允许查看摄像头"),
|
||||
("No cameras", "没有摄像头"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Žádné oprávnění k přenosu souborů"),
|
||||
("Note", "Poznámka"),
|
||||
("Connection", "Připojení"),
|
||||
("Share Screen", "Sdílet obrazovku"),
|
||||
("Share screen", "Sdílet obrazovku"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Celkem"),
|
||||
("items", "Položek"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Zachytávání obrazovky"),
|
||||
("Input Control", "Ovládání vstupních zařízení"),
|
||||
("Audio Capture", "Zachytávání zvuku"),
|
||||
("File Connection", "Souborové spojení"),
|
||||
("Screen Connection", "Spojení obrazovky"),
|
||||
("Do you accept?", "Přijímáte?"),
|
||||
("Open System Setting", "Otevřít nastavení systému"),
|
||||
("How to get Android input permission?", "Jak v systému Android získat oprávnění pro vstupní zařízení?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Zobrazit kameru"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Upgradujte prosím klienta RustDesk na verzi {} nebo novější na vzdálené straně!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Zobrazit kameru"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Ingen tilladelse til at overføre filen"),
|
||||
("Note", "Note"),
|
||||
("Connection", "Forbindelse"),
|
||||
("Share Screen", "Del skærmen"),
|
||||
("Share screen", "Del skærmen"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Total"),
|
||||
("items", "artikel"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Skærmoptagelse"),
|
||||
("Input Control", "Inputkontrol"),
|
||||
("Audio Capture", "Lydoptagelse"),
|
||||
("File Connection", "Filforbindelse"),
|
||||
("Screen Connection", "Færdiggørelse"),
|
||||
("Do you accept?", "Accepterer du?"),
|
||||
("Open System Setting", "Åbn systemindstillingen"),
|
||||
("How to get Android input permission?", "Hvordan får jeg en Android-input tilladelse?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Se kamera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Opgrader venligst RustDesk-klienten til version {} eller nyere på fjernsiden!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Se kamera"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Keine Berechtigung für die Dateiübertragung"),
|
||||
("Note", "Hinweis"),
|
||||
("Connection", "Verbindung"),
|
||||
("Share Screen", "Bildschirm freigeben"),
|
||||
("Share screen", "Bildschirm freigeben"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Gesamt"),
|
||||
("items", "Einträge"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Bildschirmaufnahme"),
|
||||
("Input Control", "Eingabesteuerung"),
|
||||
("Audio Capture", "Audioaufnahme"),
|
||||
("File Connection", "Dateiverbindung"),
|
||||
("Screen Connection", "Bildschirmverbindung"),
|
||||
("Do you accept?", "Verbindung zulassen?"),
|
||||
("Open System Setting", "Systemeinstellung öffnen"),
|
||||
("How to get Android input permission?", "Wie erhalte ich eine Android-Eingabeberechtigung?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Unmarkiert"),
|
||||
("new-version-of-{}-tip", "Es ist eine neue Version von {} verfügbar"),
|
||||
("Accessible devices", "Erreichbare Geräte"),
|
||||
("View camera", "Kamera anzeigen"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Bitte aktualisieren Sie den RustDesk-Client auf der Remote-Seite auf Version {} oder neuer!"),
|
||||
("view_camera_unsupported_tip", "Das entfernte Gerät kann die Kamera nicht anzeigen."),
|
||||
("Enable camera", "Kamera zulassen"),
|
||||
("No cameras", "Keine Kameras"),
|
||||
("d3d_render_tip", "Wenn das D3D-Rendering aktiviert ist, kann der entfernte Bildschirm auf manchen Rechnern schwarz sein."),
|
||||
("Use D3D rendering", "D3D-Rendering verwenden"),
|
||||
("Printer", "Drucker"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "Numerisches Einmalpasswort"),
|
||||
("Enable IPv6 P2P connection", "IPv6-P2P-Verbindung aktivieren"),
|
||||
("Enable UDP hole punching", "UDP-Hole-Punching aktivieren"),
|
||||
("View camera", "Kamera anzeigen"),
|
||||
("Enable camera", "Kamera zulassen"),
|
||||
("No cameras", "Keine Kameras"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Δεν υπάρχει άδεια για μεταφορά αρχείων"),
|
||||
("Note", "Σημείωση"),
|
||||
("Connection", "Σύνδεση"),
|
||||
("Share Screen", "Κοινή χρήση οθόνης"),
|
||||
("Share screen", "Κοινή χρήση οθόνης"),
|
||||
("Chat", "Κουβέντα"),
|
||||
("Total", "Σύνολο"),
|
||||
("items", "στοιχεία"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Αποτύπωση οθόνης"),
|
||||
("Input Control", "Έλεγχος εισόδου"),
|
||||
("Audio Capture", "Εγγραφή ήχου"),
|
||||
("File Connection", "Σύνδεση αρχείου"),
|
||||
("Screen Connection", "Σύνδεση οθόνης"),
|
||||
("Do you accept?", "Δέχεσαι;"),
|
||||
("Open System Setting", "Άνοιγμα ρυθμίσεων συστήματος"),
|
||||
("How to get Android input permission?", "Πώς να αποκτήσω άδεια εισαγωγής Android;"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Χωρίς ετικέτα"),
|
||||
("new-version-of-{}-tip", "Υπάρχει διαθέσιμη νέα έκδοση του {}"),
|
||||
("Accessible devices", "Προσβάσιμες συσκευές"),
|
||||
("View camera", "Προβολή κάμερας"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Αναβαθμίστε τον πελάτη RustDesk στην έκδοση {} ή νεότερη στην απομακρυσμένη πλευρά!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Προβολή κάμερας"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -77,12 +77,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Canvas Move", "Canvas move"),
|
||||
("Pinch to Zoom", "Pinch to zoom"),
|
||||
("Canvas Zoom", "Canvas zoom"),
|
||||
("Share Screen", "Share screen"),
|
||||
("Screen Capture", "Screen capture"),
|
||||
("Input Control", "Input control"),
|
||||
("Audio Capture", "Audio capture"),
|
||||
("File Connection", "File connection"),
|
||||
("Screen Connection", "Screen connection"),
|
||||
("Open System Setting", "Open system setting"),
|
||||
("android_input_permission_tip1", "In order for a remote device to control your Android device via mouse or touch, you need to allow RustDesk to use the \"Accessibility\" service."),
|
||||
("android_input_permission_tip2", "Please go to the next system settings page, find and enter [Installed Services], turn on [RustDesk Input] service."),
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Neniu permeso de dosiertransigo"),
|
||||
("Note", "Notu"),
|
||||
("Connection", "Konekto"),
|
||||
("Share Screen", "Kunhavigi Ekranon"),
|
||||
("Share screen", "Kunhavigi Ekranon"),
|
||||
("Chat", "Babilo"),
|
||||
("Total", "Sumo"),
|
||||
("items", "eroj"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Ekrankapto"),
|
||||
("Input Control", "Eniga Kontrolo"),
|
||||
("Audio Capture", "Sonkontrolo"),
|
||||
("File Connection", "Dosiero Konekto"),
|
||||
("Screen Connection", "Ekrono konekto"),
|
||||
("Do you accept?", "Ĉu vi akceptas?"),
|
||||
("Open System Setting", "Malfermi Sistemajn Agordojn"),
|
||||
("How to get Android input permission?", "Kiel akiri Android enigajn permesojn"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Rigardi kameron"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Rigardi kameron"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Sin permiso de transferencia de archivos"),
|
||||
("Note", "Nota"),
|
||||
("Connection", "Conexión"),
|
||||
("Share Screen", "Compartir pantalla"),
|
||||
("Share screen", "Compartir pantalla"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Total"),
|
||||
("items", "items"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Captura de pantalla"),
|
||||
("Input Control", "Control de entrada"),
|
||||
("Audio Capture", "Captura de audio"),
|
||||
("File Connection", "Conexión de archivos"),
|
||||
("Screen Connection", "Conexión de pantalla"),
|
||||
("Do you accept?", "¿Aceptas?"),
|
||||
("Open System Setting", "Configuración del sistema abierto"),
|
||||
("How to get Android input permission?", "¿Cómo obtener el permiso de entrada de Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Sin itiquetar"),
|
||||
("new-version-of-{}-tip", "Hay una nueva versión de {} disponible"),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Ver cámara"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Por favor, actualiza el cliente RustDesk a la versión {} o superior en el lado remoto"),
|
||||
("view_camera_unsupported_tip", "El dispositivo remoto no soporta la visualización de la cámara."),
|
||||
("Enable camera", "Habilitar cámara"),
|
||||
("No cameras", "No hay cámaras"),
|
||||
("d3d_render_tip", "Al activar el renderizado D3D, la pantalla de control remoto puede verse negra en algunos equipos."),
|
||||
("Use D3D rendering", "Usar renderizado D3D"),
|
||||
("Printer", "Impresora"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Ver cámara"),
|
||||
("Enable camera", "Habilitar cámara"),
|
||||
("No cameras", "No hay cámaras"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Failiülekande luba puudub"),
|
||||
("Note", "Märkus"),
|
||||
("Connection", "Ühendus"),
|
||||
("Share Screen", "Jaga ekraani"),
|
||||
("Share screen", "Jaga ekraani"),
|
||||
("Chat", "Vestlus"),
|
||||
("Total", "Kokku"),
|
||||
("items", "üksust"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Ekraanisalvestus"),
|
||||
("Input Control", "Sisendjuhtimine"),
|
||||
("Audio Capture", "Helisalvestus"),
|
||||
("File Connection", "Failiühendus"),
|
||||
("Screen Connection", "Kuvaühendus"),
|
||||
("Do you accept?", "Kas nõustud?"),
|
||||
("Open System Setting", "Ava süsteemisätted"),
|
||||
("How to get Android input permission?", "Kuidas saada Androidi sisendi luba?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Sildistamata"),
|
||||
("new-version-of-{}-tip", "Saadaval on {} uus versioon"),
|
||||
("Accessible devices", "Ligipääsetavad seadmed"),
|
||||
("View camera", "Vaata kaamerat"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Täiendage RustDeski klient kaugküljel versioonile {} või uuemale!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Vaata kaamerat"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Ez duzu baimenik fitxategiak transferitzeko"),
|
||||
("Note", "Nota"),
|
||||
("Connection", "Konexioa"),
|
||||
("Share Screen", "Partekatu pantaila"),
|
||||
("Share screen", "Partekatu pantaila"),
|
||||
("Chat", "Txata"),
|
||||
("Total", "Guztira"),
|
||||
("items", "elementuak"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("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?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Ikusi kamera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Mesedez, eguneratu RustDesk bezeroa {} bertsiora edo berriagoa urruneko aldean!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Ikusi kamera"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "مجوز انتقال فایل داده نشده"),
|
||||
("Note", "یادداشت"),
|
||||
("Connection", "ارتباط"),
|
||||
("Share Screen", "اشتراک گذاری صفحه"),
|
||||
("Share screen", "اشتراک گذاری صفحه"),
|
||||
("Chat", "چت"),
|
||||
("Total", "مجموع"),
|
||||
("items", "آیتم ها"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "ضبط صفحه"),
|
||||
("Input Control", "کنترل ورودی"),
|
||||
("Audio Capture", "ضبط صدا"),
|
||||
("File Connection", "ارتباط فایل"),
|
||||
("Screen Connection", "ارتباط صفحه"),
|
||||
("Do you accept?", "آیا می پذیرید؟"),
|
||||
("Open System Setting", "باز کردن تنظیمات سیستم"),
|
||||
("How to get Android input permission?", "چگونه مجوز ورود به سیستم اندروید را دریافت کنیم؟"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "بدون برچسب"),
|
||||
("new-version-of-{}-tip", "نسخه جدید {} در دسترس است"),
|
||||
("Accessible devices", "دستگاههای در دسترس"),
|
||||
("View camera", "نمایش دوربین"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "لطفاً RustDesk را به نسخه {} یا جدیدتر در سمت راه دور ارتقا دهید"),
|
||||
("view_camera_unsupported_tip", "دوربین در این دستگاه پشتیبانی نمیشود"),
|
||||
("Enable camera", "فعال کردن دوربین"),
|
||||
("No cameras", "هیچ دوربینی یافت نشد"),
|
||||
("d3d_render_tip", "فعال کردن رندر D3D برای عملکرد بهتر"),
|
||||
("Use D3D rendering", "استفاده از رندر D3D"),
|
||||
("Printer", "چاپگر"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "رمز عبور یکبار مصرف عددی"),
|
||||
("Enable IPv6 P2P connection", "فعالسازی اتصال همتابههمتای IPv6"),
|
||||
("Enable UDP hole punching", "فعالسازی تکنیک UDP hole punching"),
|
||||
("View camera", "نمایش دوربین"),
|
||||
("Enable camera", "فعال کردن دوربین"),
|
||||
("No cameras", "هیچ دوربینی یافت نشد"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Absence de l’autorisation de transfert de fichiers"),
|
||||
("Note", "Note"),
|
||||
("Connection", "Connexion"),
|
||||
("Share Screen", "Partage d’écran"),
|
||||
("Share screen", "Partage d’écran"),
|
||||
("Chat", "Discussion"),
|
||||
("Total", "Total"),
|
||||
("items", "éléments"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Capture de l’écran"),
|
||||
("Input Control", "Contrôle de la saisie"),
|
||||
("Audio Capture", "Capture de l’audio"),
|
||||
("File Connection", "Connexion aux fichiers"),
|
||||
("Screen Connection", "Connexion à l’écran"),
|
||||
("Do you accept?", "Acceptez-vous ?"),
|
||||
("Open System Setting", "Ouvrir les paramètres système"),
|
||||
("How to get Android input permission?", "Comment obtenir l’autorisation de contrôle de la saisie sur Android ?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Sans étiquette"),
|
||||
("new-version-of-{}-tip", "Une nouvelle version de {} est disponible"),
|
||||
("Accessible devices", "Appareils accessibles"),
|
||||
("View camera", "Afficher la caméra"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Veuillez mettre le client RustDesk distant à jour vers la version {} ou ultérieure !"),
|
||||
("view_camera_unsupported_tip", "L’appareil distant ne prend pas en charge l’affichage de la caméra."),
|
||||
("Enable camera", "Activer la caméra"),
|
||||
("No cameras", "Aucune caméra"),
|
||||
("d3d_render_tip", "Sur certaines machines, l’écran du contrôle à distance peut rester noir lors de l’utilisation du rendu D3D."),
|
||||
("Use D3D rendering", "Utiliser le rendu D3D"),
|
||||
("Printer", "Imprimante"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "Mot de passe à usage unique numérique"),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Afficher la caméra"),
|
||||
("Enable camera", "Activer la caméra"),
|
||||
("No cameras", "Aucune caméra"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "ფაილების გადაცემის უფლება არ არის"),
|
||||
("Note", "შენიშვნა"),
|
||||
("Connection", "კავშირი"),
|
||||
("Share Screen", "ეკრანის დემონსტრაცია"),
|
||||
("Share screen", "ეკრანის დემონსტრაცია"),
|
||||
("Chat", "ჩატი"),
|
||||
("Total", "სულ"),
|
||||
("items", "ელემენტები"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "ეკრანის ჩაწერა"),
|
||||
("Input Control", "შეყვანის კონტროლი"),
|
||||
("Audio Capture", "აუდიოს ჩაწერა"),
|
||||
("File Connection", "ფაილების გადაცემის დაკავშირება"),
|
||||
("Screen Connection", "ეკრანის ნახვის/მართვის დაკავშირება"),
|
||||
("Do you accept?", "თანახმა ხართ?"),
|
||||
("Open System Setting", "სისტემის პარამეტრების გახსნა"),
|
||||
("How to get Android input permission?", "როგორ მივიღოთ Android-ის შეყვანის უფლება?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "უტეგო"),
|
||||
("new-version-of-{}-tip", "ხელმისაწვდომია ახალი ვერსია {}"),
|
||||
("Accessible devices", "ხელმისაწვდომი მოწყობილობები"),
|
||||
("View camera", "კამერის ნახვა"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "განაახლეთ RustDesk კლიენტი ვერსიამდე {} ან უფრო ახალი დისტანციურ მხარეზე!"),
|
||||
("view_camera_unsupported_tip", "დისტანციური მოწყობილობა არ უჭერს მხარს კამერის ნახვას."),
|
||||
("Enable camera", "კამერის ჩართვა"),
|
||||
("No cameras", "კამერა არ არის"),
|
||||
("d3d_render_tip", "D3D ვიზუალიზაციის ჩართვისას ზოგიერთ მოწყობილობაზე დისტანციური ეკრანი შეიძლება იყოს შავი."),
|
||||
("Use D3D rendering", "D3D ვიზუალიზაციის გამოყენება"),
|
||||
("Printer", "პრინტერი"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "კამერის ნახვა"),
|
||||
("Enable camera", "კამერის ჩართვა"),
|
||||
("No cameras", "კამერა არ არის"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "אין הרשאת העברת קבצים"),
|
||||
("Note", "הערה"),
|
||||
("Connection", "התחברות"),
|
||||
("Share Screen", "שיתוף מסך"),
|
||||
("Share screen", "שיתוף מסך"),
|
||||
("Chat", "צ'אט"),
|
||||
("Total", "הכל"),
|
||||
("items", "פריטים"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "לכידת מסך"),
|
||||
("Input Control", "בקרת קלט"),
|
||||
("Audio Capture", "לכידת שמע"),
|
||||
("File Connection", "חיבור להעברת קבצים"),
|
||||
("Screen Connection", "חיבור תצוגה"),
|
||||
("Do you accept?", "האם אתה מקבל?"),
|
||||
("Open System Setting", "פתח הגדרות מערכת"),
|
||||
("How to get Android input permission?", "כיצד לקבל הרשאת קלט באנדרואיד?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "לא מתוייג"),
|
||||
("new-version-of-{}-tip", "גרסה חדשה של {} זמינה"),
|
||||
("Accessible devices", "מכשירים נגישים"),
|
||||
("View camera", "הצג מצלמה"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "אנא שדרג את לקוח RustDesk לגרסה {} או חדשה יותר בצד המרוחק!"),
|
||||
("view_camera_unsupported_tip", "הצגת מצלמה אינה נתמכת במכשיר המרוחק"),
|
||||
("Enable camera", "הפעל מצלמה"),
|
||||
("No cameras", "אין מצלמות"),
|
||||
("d3d_render_tip", "שימוש בעיבוד Direct3D עשוי לשפר ביצועים בחלק מהמקרים"),
|
||||
("Use D3D rendering", "השתמש בעיבוד D3D"),
|
||||
("Printer", "מדפסת"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "הצג מצלמה"),
|
||||
("Enable camera", "הפעל מצלמה"),
|
||||
("No cameras", "אין מצלמות"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nemate pravo prijenosa datoteka"),
|
||||
("Note", "Bilješka"),
|
||||
("Connection", "Povezivanje"),
|
||||
("Share Screen", "Podijeli zaslon"),
|
||||
("Share screen", "Podijeli zaslon"),
|
||||
("Chat", "Dopisivanje"),
|
||||
("Total", "Ukupno"),
|
||||
("items", "stavki"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Snimanje zaslona"),
|
||||
("Input Control", "Kontrola unosa"),
|
||||
("Audio Capture", "Snimanje zvuka"),
|
||||
("File Connection", "Spajanje preko datoteke"),
|
||||
("Screen Connection", "Podijelite vezu"),
|
||||
("Do you accept?", "Prihvaćate li?"),
|
||||
("Open System Setting", "Postavke otvorenog sustava"),
|
||||
("How to get Android input permission?", "Kako dobiti pristup za unos na Androidu?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Pregled kamere"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Molimo ažurirajte RustDesk klijent na verziju {} ili noviju na udaljenoj strani!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Pregled kamere"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nincs engedély a fájlátvitelre"),
|
||||
("Note", "Megjegyzés"),
|
||||
("Connection", "Kapcsolat"),
|
||||
("Share Screen", "Képernyőmegosztás"),
|
||||
("Share screen", "Képernyőmegosztás"),
|
||||
("Chat", "Csevegés"),
|
||||
("Total", "Összes"),
|
||||
("items", "elemek"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Képernyőrögzítés"),
|
||||
("Input Control", "Távoli vezérlés"),
|
||||
("Audio Capture", "Hangrögzítés"),
|
||||
("File Connection", "Fájlátvitel"),
|
||||
("Screen Connection", "Képátvitel"),
|
||||
("Do you accept?", "Elfogadás?"),
|
||||
("Open System Setting", "Rendszerbeállítások megnyitása"),
|
||||
("How to get Android input permission?", "Hogyan állítható be az Androidos beviteli engedély?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Címkézetlen"),
|
||||
("new-version-of-{}-tip", "A(z) {} új verziója"),
|
||||
("Accessible devices", "Hozzáférhető eszközök"),
|
||||
("View camera", "Kamera megtekintése"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Frissítse a RustDesk klienst {} vagy újabb verziójára a távoli oldalon!"),
|
||||
("view_camera_unsupported_tip", "A kameranézet nem támogatott"),
|
||||
("Enable camera", "Kamera engedélyezése"),
|
||||
("No cameras", "Nincs kamera"),
|
||||
("d3d_render_tip", "D3D renderelés"),
|
||||
("Use D3D rendering", "D3D renderelés használata"),
|
||||
("Printer", "Nyomtató"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Kamera megtekintése"),
|
||||
("Enable camera", "Kamera engedélyezése"),
|
||||
("No cameras", "Nincs kamera"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Tidak ada izin untuk mengirim file"),
|
||||
("Note", "Catatan"),
|
||||
("Connection", "Koneksi"),
|
||||
("Share Screen", "Bagikan Layar"),
|
||||
("Share screen", "Bagikan Layar"),
|
||||
("Chat", "Obrolan"),
|
||||
("Total", "Total"),
|
||||
("items", "item"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Tangkapan Layar"),
|
||||
("Input Control", "Kontrol input"),
|
||||
("Audio Capture", "Rekam Suara"),
|
||||
("File Connection", "Koneksi File"),
|
||||
("Screen Connection", "Koneksi layar"),
|
||||
("Do you accept?", "Apakah anda setuju?"),
|
||||
("Open System Setting", "Buka Pengaturan Sistem"),
|
||||
("How to get Android input permission?", "Bagaimana cara mendapatkan izin input dari Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", "Versi {} sudah tersedia."),
|
||||
("Accessible devices", "Perangkat yang tersedia"),
|
||||
("View camera", "Lihat Kamera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Silahkan perbarui aplikasi RustDesk ke versi {} atau yang lebih baru pada komputer yang akan terhubung!"),
|
||||
("view_camera_unsupported_tip", "Perangkat yang terhubung tidak mendukung tampilan kamera."),
|
||||
("Enable camera", "Aktifkan kamera"),
|
||||
("No cameras", "Tidak ada kamera"),
|
||||
("d3d_render_tip", "Ketika rendering D3D diaktifkan, layar kontrol jarak jauh bisa tampak hitam di beberapa komputer"),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Lihat Kamera"),
|
||||
("Enable camera", "Aktifkan kamera"),
|
||||
("No cameras", "Tidak ada kamera"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nessun permesso per il trasferimento file"),
|
||||
("Note", "Nota"),
|
||||
("Connection", "Connessione"),
|
||||
("Share Screen", "Condividi schermo"),
|
||||
("Share screen", "Condividi schermo"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Totale"),
|
||||
("items", "Oggetti"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Cattura schermo"),
|
||||
("Input Control", "Controllo input"),
|
||||
("Audio Capture", "Acquisizione audio"),
|
||||
("File Connection", "Connessione file"),
|
||||
("Screen Connection", "Connessione schermo"),
|
||||
("Do you accept?", "Accetti?"),
|
||||
("Open System Setting", "Apri impostazioni di sistema"),
|
||||
("How to get Android input permission?", "Come ottenere l'autorizzazione input in Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Senza tag"),
|
||||
("new-version-of-{}-tip", "È disponibile una nuova versione di {}"),
|
||||
("Accessible devices", "Dispositivi accessibili"),
|
||||
("View camera", "Visualizza telecamera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Aggiorna il client RustDesk remoto alla versione {} o successiva!"),
|
||||
("view_camera_unsupported_tip", "Il dispositivo remoto non supporta la visualizzazione della camera."),
|
||||
("Enable camera", "Abilita camera"),
|
||||
("No cameras", "Nessuna camera"),
|
||||
("d3d_render_tip", "Quando è abilitato il rendering D3D, in alcuni computer la schermata del telecomando potrebbe essere nera."),
|
||||
("Use D3D rendering", "Usa rendering D3D"),
|
||||
("Printer", "Stampante"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "Password numerica monouso"),
|
||||
("Enable IPv6 P2P connection", "Abilita connessione P2P IPv6"),
|
||||
("Enable UDP hole punching", "Abilita hole punching UDP"),
|
||||
("View camera", "Visualizza telecamera"),
|
||||
("Enable camera", "Abilita camera"),
|
||||
("No cameras", "Nessuna camera"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "ファイル転送の権限がありません"),
|
||||
("Note", "ノート"),
|
||||
("Connection", "接続"),
|
||||
("Share Screen", "画面を共有"),
|
||||
("Share screen", "画面を共有"),
|
||||
("Chat", "チャット"),
|
||||
("Total", "計"),
|
||||
("items", "個のアイテム"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "画面キャプチャ"),
|
||||
("Input Control", "入力操作"),
|
||||
("Audio Capture", "音声キャプチャ"),
|
||||
("File Connection", "ファイルの接続"),
|
||||
("Screen Connection", "画面の接続"),
|
||||
("Do you accept?", "許可しますか?"),
|
||||
("Open System Setting", "システム設定を開く"),
|
||||
("How to get Android input permission?", "Androidの入力権限を取得するには?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "カメラを表示"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "リモート側のRustDeskクライアントをバージョン{}以上にアップグレードしてください!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "カメラを表示"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "파일 전송 권한이 없습니다."),
|
||||
("Note", "메모"),
|
||||
("Connection", "연결"),
|
||||
("Share Screen", "화면 공유"),
|
||||
("Share screen", "화면 공유"),
|
||||
("Chat", "채팅"),
|
||||
("Total", "총"),
|
||||
("items", "개"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "화면 캡처"),
|
||||
("Input Control", "입력 제어"),
|
||||
("Audio Capture", "오디오 캡처"),
|
||||
("File Connection", "파일 전송"),
|
||||
("Screen Connection", "화면 전송"),
|
||||
("Do you accept?", "수락하시겠습니까?"),
|
||||
("Open System Setting", "시스템 설정 열기"),
|
||||
("How to get Android input permission?", "Android 입력 권한을 얻는 방법"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "태그 없음"),
|
||||
("new-version-of-{}-tip", "{}의 새 버전이 출시되었습니다."),
|
||||
("Accessible devices", "연결 가능한 기기"),
|
||||
("View camera", "카메라 보기"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "원격 기기의 RustDesk 클라이언트를 {} 버전 이상으로 업그레이드하십시오!"),
|
||||
("view_camera_unsupported_tip", "원격 기기에서 카메라 보기를 지원하지 않습니다."),
|
||||
("Enable camera", "카메라 보기 허용"),
|
||||
("No cameras", "카메라 없음"),
|
||||
("d3d_render_tip", "D3D 렌더링을 활성화하면 일부 기기에서 원격 화면이 표시되지 않을 수 있습니다."),
|
||||
("Use D3D rendering", "D3D 렌더링 활성화"),
|
||||
("Printer", "프린터"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "일회용 비밀번호"),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "카메라 보기"),
|
||||
("Enable camera", "카메라 보기 허용"),
|
||||
("No cameras", "카메라 없음"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Файыл алмасуға рұқсат берілмеген"),
|
||||
("Note", "Нота"),
|
||||
("Connection", "Қосылым"),
|
||||
("Share Screen", "Екіренді Бөлісу"),
|
||||
("Share screen", "Екіренді Бөлісу"),
|
||||
("Chat", "Чат"),
|
||||
("Total", "Барлығы"),
|
||||
("items", "зат"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Екіренді Түсіру"),
|
||||
("Input Control", "Еңгізуді Басқару/Қадағалау"),
|
||||
("Audio Capture", "Аудио Түсіру"),
|
||||
("File Connection", "Файыл Қосылымы"),
|
||||
("Screen Connection", "Екірен Қосылымы"),
|
||||
("Do you accept?", "Қабылдайсыз ба?"),
|
||||
("Open System Setting", "Жүйе Орнатпаларын Ашу"),
|
||||
("How to get Android input permission?", "Android еңгізу рұқсатын қалай алуға болады?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Камераны Көру"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Қашықтағы жақтағы RustDesk клиентін {} немесе одан жоғары нұсқаға жаңартуды өтінеміз!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Камераны Көру"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nėra leidimo perkelti failus"),
|
||||
("Note", "Pastaba"),
|
||||
("Connection", "Ryšys"),
|
||||
("Share Screen", "Bendrinti ekraną"),
|
||||
("Share screen", "Bendrinti ekraną"),
|
||||
("Chat", "Pokalbis"),
|
||||
("Total", "Iš viso"),
|
||||
("items", "elementai"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Ekrano nuotrauka"),
|
||||
("Input Control", "Įvesties valdymas"),
|
||||
("Audio Capture", "Garso fiksavimas"),
|
||||
("File Connection", "Failo ryšys"),
|
||||
("Screen Connection", "Ekrano jungtis"),
|
||||
("Do you accept?", "Ar sutinki?"),
|
||||
("Open System Setting", "Atviros sistemos nustatymas"),
|
||||
("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Peržiūrėti kamerą"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Prašome atnaujinti nuotolinės pusės RustDesk klientą į {} ar naujesnę versiją!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Peržiūrėti kamerą"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nav atļaujas failu pārsūtīšanai"),
|
||||
("Note", "Piezīme"),
|
||||
("Connection", "Savienojums"),
|
||||
("Share Screen", "Koplietot ekrānu"),
|
||||
("Share screen", "Koplietot ekrānu"),
|
||||
("Chat", "Tērzēšana"),
|
||||
("Total", "Kopā"),
|
||||
("items", "vienumi"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Ekrāna tveršana"),
|
||||
("Input Control", "Ievades vadība"),
|
||||
("Audio Capture", "Audio tveršana"),
|
||||
("File Connection", "Failu savienojums"),
|
||||
("Screen Connection", "Ekrāna savienojums"),
|
||||
("Do you accept?", "Vai Jūs pieņemat?"),
|
||||
("Open System Setting", "Atvērt sistēmas iestatījumus"),
|
||||
("How to get Android input permission?", "Kā iegūt Android ievades atļauju?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Neatzīmēts"),
|
||||
("new-version-of-{}-tip", "Ir pieejama jauna {} versija"),
|
||||
("Accessible devices", "Pieejamas ierīces"),
|
||||
("View camera", "Skatīt kameru"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Lūdzu, jauniniet attālās puses RustDesk klientu uz versiju {} vai jaunāku!"),
|
||||
("view_camera_unsupported_tip", "Attālā ierīce neatbalsta kameras skatīšanos."),
|
||||
("Enable camera", "Iespējot kameru"),
|
||||
("No cameras", "Nav kameru"),
|
||||
("d3d_render_tip", "Ja ir iespējota D3D renderēšana, dažās ierīcēs tālvadības pults ekrāns var būt melns."),
|
||||
("Use D3D rendering", "Izmantot D3D renderēšanu"),
|
||||
("Printer", "Printeris"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "Vienreiz lietojama ciparu parole"),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Skatīt kameru"),
|
||||
("Enable camera", "Iespējot kameru"),
|
||||
("No cameras", "Nav kameru"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Ingen tillatelse til å overføre filen"),
|
||||
("Note", "Notat"),
|
||||
("Connection", "Tilkobling"),
|
||||
("Share Screen", "Del skjermen"),
|
||||
("Share screen", "Del skjermen"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Total"),
|
||||
("items", "Objekter"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Skjermopptak"),
|
||||
("Input Control", "Input kontroll"),
|
||||
("Audio Capture", "Lydopptak"),
|
||||
("File Connection", "Filtilkobling"),
|
||||
("Screen Connection", "Skjermtilkobing"),
|
||||
("Do you accept?", "Akepterer du?"),
|
||||
("Open System Setting", "Åpne systeminnstillinger"),
|
||||
("How to get Android input permission?", "Hvordan får jeg en Android-input tillatelse?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Vis kamera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Vis kamera"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Geen toestemming voor bestandsoverdracht"),
|
||||
("Note", "Opmerking"),
|
||||
("Connection", "Verbinding"),
|
||||
("Share Screen", "Scherm Delen"),
|
||||
("Share screen", "Scherm Delen"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Totaal"),
|
||||
("items", "items"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Schermopname"),
|
||||
("Input Control", "Invoercontrole"),
|
||||
("Audio Capture", "Audio Opnemen"),
|
||||
("File Connection", "Bestandsverbinding"),
|
||||
("Screen Connection", "Schermverbinding"),
|
||||
("Do you accept?", "Geeft u toestemming?"),
|
||||
("Open System Setting", "Systeeminstelling Openen"),
|
||||
("How to get Android input permission?", "Hoe krijg ik Android invoer toestemming?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Ongemarkeerd"),
|
||||
("new-version-of-{}-tip", "Er is een nieuwe versie van {} beschikbaar"),
|
||||
("Accessible devices", "Toegankelijke apparaten"),
|
||||
("View camera", "Camera bekijken"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Upgrade de RustDesk client naar versie {} of nieuwer op de externe computer!"),
|
||||
("view_camera_unsupported_tip", "Het externe apparaat ondersteunt geen cameraweergave."),
|
||||
("Enable camera", "Camera inschakelen"),
|
||||
("No cameras", "Geen camera's"),
|
||||
("d3d_render_tip", "Wanneer D3D-rendering is ingeschakeld kan het externe scherm op sommige apparaten, zwart zijn."),
|
||||
("Use D3D rendering", "Gebruik D3D-rendering"),
|
||||
("Printer", "Printer"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "Eenmalig numeriek wachtwoord"),
|
||||
("Enable IPv6 P2P connection", "IPv6 P2P-verbinding inschakelen"),
|
||||
("Enable UDP hole punching", "UDP-hole punching inschakelen"),
|
||||
("View camera", "Camera bekijken"),
|
||||
("Enable camera", "Camera inschakelen"),
|
||||
("No cameras", "Geen camera's"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Brak uprawnień na przesyłanie plików"),
|
||||
("Note", "Notatka"),
|
||||
("Connection", "Połączenie"),
|
||||
("Share Screen", "Udostępnij ekran"),
|
||||
("Share screen", "Udostępnij ekran"),
|
||||
("Chat", "Czat"),
|
||||
("Total", "Łącznie"),
|
||||
("items", "elementów"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Przechwytywanie ekranu"),
|
||||
("Input Control", "Kontrola wejścia"),
|
||||
("Audio Capture", "Przechwytywanie dźwięku"),
|
||||
("File Connection", "Przekazywanie plików"),
|
||||
("Screen Connection", "Przekazywanie ekranu"),
|
||||
("Do you accept?", "Akceptujesz?"),
|
||||
("Open System Setting", "Otwórz ustawienia systemowe"),
|
||||
("How to get Android input permission?", "Jak uzyskać uprawnienia do wprowadzania danych w systemie Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Bez etykiety"),
|
||||
("new-version-of-{}-tip", "Dostępna jest nowa wersja {}"),
|
||||
("Accessible devices", "Dostępne urządzenia"),
|
||||
("View camera", "Podgląd kamery"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Proszę zaktualizować zdalny klient RustDesk do wersji {} lub nowszej!"),
|
||||
("view_camera_unsupported_tip", "Zdalne urządzenie nie obsługuje podglądu kamery."),
|
||||
("Enable camera", "Włącz kamerę"),
|
||||
("No cameras", "Brak kamer"),
|
||||
("d3d_render_tip", "Kiedy włączenie renderowania D3D jest włączone, ekran zdalnej kontroli może być czarny w niektórych przypadkach"),
|
||||
("Use D3D rendering", "Użyj renderowania D3D"),
|
||||
("Printer", "Drukarka"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "Jednorazowe hasło cyfrowe"),
|
||||
("Enable IPv6 P2P connection", "Włącz połączenie P2P IPv6"),
|
||||
("Enable UDP hole punching", "Włącz tworzenie tunelu UDP"),
|
||||
("View camera", "Podgląd kamery"),
|
||||
("Enable camera", "Włącz kamerę"),
|
||||
("No cameras", "Brak kamer"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Sem permissões de transferência de ficheiro"),
|
||||
("Note", "Nota"),
|
||||
("Connection", "Ligação"),
|
||||
("Share Screen", "Partilhar ecrã"),
|
||||
("Share screen", "Partilhar ecrã"),
|
||||
("Chat", "Conversar"),
|
||||
("Total", "Total"),
|
||||
("items", "itens"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Captura de Ecran"),
|
||||
("Input Control", "Controle de Entrada"),
|
||||
("Audio Capture", "Captura de Áudio"),
|
||||
("File Connection", "Ligação de Arquivo"),
|
||||
("Screen Connection", "Ligação de Ecran"),
|
||||
("Do you accept?", "Aceita?"),
|
||||
("Open System Setting", "Abrir Configurações do Sistema"),
|
||||
("How to get Android input permission?", "Como activar a permissão de entrada do Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Ver câmara"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Ver câmara"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Sem permissão para transferência de arquivo"),
|
||||
("Note", "Nota"),
|
||||
("Connection", "Conexão"),
|
||||
("Share Screen", "Compartilhar Tela"),
|
||||
("Share screen", "Compartilhar Tela"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Total"),
|
||||
("items", "itens"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Captura de Tela"),
|
||||
("Input Control", "Controle de Entrada"),
|
||||
("Audio Capture", "Captura de Áudio"),
|
||||
("File Connection", "Conexão de Arquivo"),
|
||||
("Screen Connection", "Conexão de Tela"),
|
||||
("Do you accept?", "Você aceita?"),
|
||||
("Open System Setting", "Abrir Configurações do Sistema"),
|
||||
("How to get Android input permission?", "Como habilitar a permissão de entrada do Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Sem etiqueta"),
|
||||
("new-version-of-{}-tip", "Uma nova versão de {} está disponível"),
|
||||
("Accessible devices", "Dispositivos acessíveis"),
|
||||
("View camera", "Visualizar câmera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Atualize o cliente RustDesk para a versão {} ou superior no lado remoto."),
|
||||
("view_camera_unsupported_tip", "O dispositivo remoto não suporta visualização da câmera."),
|
||||
("Enable camera", "Ativar câmera"),
|
||||
("No cameras", "Sem câmeras"),
|
||||
("d3d_render_tip", "Em algumas máquinas, a tela do controle remoto pode ficar preta ao usar a renderização D3D."),
|
||||
("Use D3D rendering", "Usar renderização D3D"),
|
||||
("Printer", "Impressora"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Visualizar câmera"),
|
||||
("Enable camera", "Ativar câmera"),
|
||||
("No cameras", "Sem câmeras"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nicio permisiune pentru transferul de fișiere"),
|
||||
("Note", "Reține"),
|
||||
("Connection", "Conexiune"),
|
||||
("Share Screen", "Partajează ecran"),
|
||||
("Share screen", "Partajează ecran"),
|
||||
("Chat", "Mesaje"),
|
||||
("Total", "Total"),
|
||||
("items", "elemente"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Capturare ecran"),
|
||||
("Input Control", "Control intrări"),
|
||||
("Audio Capture", "Capturare audio"),
|
||||
("File Connection", "Conexiune fișier"),
|
||||
("Screen Connection", "Conexiune ecran"),
|
||||
("Do you accept?", "Accepți?"),
|
||||
("Open System Setting", "Deschide setări sistem"),
|
||||
("How to get Android input permission?", "Cum autorizez dispozitive de intrare pe Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Vezi camera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Vezi camera"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Нет разрешения на передачу файлов"),
|
||||
("Note", "Заметка"),
|
||||
("Connection", "Подключение"),
|
||||
("Share Screen", "Демонстрация экрана"),
|
||||
("Share screen", "Демонстрация экрана"),
|
||||
("Chat", "Чат"),
|
||||
("Total", "Всего"),
|
||||
("items", "элементы"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Захват экрана"),
|
||||
("Input Control", "Управление вводом"),
|
||||
("Audio Capture", "Захват аудио"),
|
||||
("File Connection", "Подключение передачи файлов"),
|
||||
("Screen Connection", "Подключение просмотра/управления экраном"),
|
||||
("Do you accept?", "Вы согласны?"),
|
||||
("Open System Setting", "Открыть настройки системы"),
|
||||
("How to get Android input permission?", "Как получить разрешение на ввод Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Без метки"),
|
||||
("new-version-of-{}-tip", "Доступна новая версия {}"),
|
||||
("Accessible devices", "Доступные устройства"),
|
||||
("View camera", "Просмотр камеры"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Обновите клиент RustDesk до версии {} или новее на удалённой стороне!"),
|
||||
("view_camera_unsupported_tip", "Удалённое устройство не поддерживает просмотр камеры."),
|
||||
("Enable camera", "Включить камеру"),
|
||||
("No cameras", "Камера отсутствует"),
|
||||
("d3d_render_tip", "При включении визуализации D3D на некоторых устройствах удалённый экран может быть чёрным."),
|
||||
("Use D3D rendering", "Использовать визуализацию D3D"),
|
||||
("Printer", "Принтер"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "Цифровой одноразовый пароль"),
|
||||
("Enable IPv6 P2P connection", "Использовать подключение IPv6 P2P"),
|
||||
("Enable UDP hole punching", "Использовать UDP hole punching"),
|
||||
("View camera", "Просмотр камеры"),
|
||||
("Enable camera", "Включить камеру"),
|
||||
("No cameras", "Камера отсутствует"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Perunu permissu pro sa tràmuda de documentos"),
|
||||
("Note", "Nota"),
|
||||
("Connection", "Connessione"),
|
||||
("Share Screen", "Cumpartzi ischermu"),
|
||||
("Share screen", "Cumpartzi ischermu"),
|
||||
("Chat", "Tzarrada"),
|
||||
("Total", "Totale"),
|
||||
("items", "Elementos"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Catura de ischermu"),
|
||||
("Input Control", "Controllu atziones"),
|
||||
("Audio Capture", "Catura de s'àudio"),
|
||||
("File Connection", "Connessione documentos"),
|
||||
("Screen Connection", "Connessione ischermu"),
|
||||
("Do you accept?", "Atzetas?"),
|
||||
("Open System Setting", "Aberi sas impostatziones de sistema"),
|
||||
("How to get Android input permission?", "Comente otènnere s'autorizatzione de intrada (input) in Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Chene tag"),
|
||||
("new-version-of-{}-tip", "B'at una versione noa de {} a disponimentu"),
|
||||
("Accessible devices", "Dispositivos atzessìbiles"),
|
||||
("View camera", "Mustra sa càmera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Atualiza su cliente RustDesk remotu a sa versione {} o prus noa!"),
|
||||
("view_camera_unsupported_tip", "Su dispositivu remotu non suportat sa visualizatzione de sa càmera"),
|
||||
("Enable camera", "Abìlita sa càmera"),
|
||||
("No cameras", "Peruna càmera"),
|
||||
("d3d_render_tip", "Cando sa renderizatzione D3D est abilitada, s'ischermu de controllu remotu diat pòdere èssere nieddu in unas cantas màchinas"),
|
||||
("Use D3D rendering", "Imprea sa renderizatzione D3D"),
|
||||
("Printer", "Imprentadora"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Mustra sa càmera"),
|
||||
("Enable camera", "Abìlita sa càmera"),
|
||||
("No cameras", "Peruna càmera"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Prenos súborov nie je povolený"),
|
||||
("Note", "Poznámka"),
|
||||
("Connection", "Pripojenie"),
|
||||
("Share Screen", "Zdielať obrazovku"),
|
||||
("Share screen", "Zdielať obrazovku"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Celkom"),
|
||||
("items", "položiek"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Snímanie obrazovky"),
|
||||
("Input Control", "Ovládanie vstupných zariadení"),
|
||||
("Audio Capture", "Snímanie zvuku"),
|
||||
("File Connection", "Pripojenie súborov"),
|
||||
("Screen Connection", "Pripojenie obrazu"),
|
||||
("Do you accept?", "Súhlasíte?"),
|
||||
("Open System Setting", "Otvorenie nastavení systému"),
|
||||
("How to get Android input permission?", "Ako v systéme Android povoliť oprávnenie písať zo vstupného zariadenia?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Zobraziť kameru"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Aktualizujte klienta RustDesk na verziu {} alebo novšiu na vzdialenej strane!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Zobraziť kameru"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Ni pravic za prenos datotek"),
|
||||
("Note", "Opomba"),
|
||||
("Connection", "Povezava"),
|
||||
("Share Screen", "Deli zaslon"),
|
||||
("Share screen", "Deli zaslon"),
|
||||
("Chat", "Pogovor"),
|
||||
("Total", "Skupaj"),
|
||||
("items", "elementi"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Zajem zaslona"),
|
||||
("Input Control", "Nadzor vnosa"),
|
||||
("Audio Capture", "Zajem zvoka"),
|
||||
("File Connection", "Datotečna povezava"),
|
||||
("Screen Connection", "Zaslonska povezava"),
|
||||
("Do you accept?", "Ali sprejmete?"),
|
||||
("Open System Setting", "Odpri sistemske nastavitve"),
|
||||
("How to get Android input permission?", "Kako pridobiti dovoljenje za vnos na Androidu?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Neoznačeno"),
|
||||
("new-version-of-{}-tip", "Na voljo je nova različica {}"),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Pogled kamere"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Prosimo, nadgradite RustDesk odjemalec na različico {} ali novejšo na oddaljeni strani."),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Pogled kamere"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nuk ka leje për transferimin e dosjesve"),
|
||||
("Note", "Shënime"),
|
||||
("Connection", "Lidhja"),
|
||||
("Share Screen", "Ndaj ekranin"),
|
||||
("Share screen", "Ndaj ekranin"),
|
||||
("Chat", "Biseda"),
|
||||
("Total", "Total"),
|
||||
("items", "artikuj"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Kapja e ekranit"),
|
||||
("Input Control", "Kontrollo inputin"),
|
||||
("Audio Capture", "Kapja e zërit"),
|
||||
("File Connection", "Lidhja e skedarëve"),
|
||||
("Screen Connection", "Lidhja e ekranit"),
|
||||
("Do you accept?", "E pranoni"),
|
||||
("Open System Setting", "Hapni cilësimet e sistemit"),
|
||||
("How to get Android input permission?", "Si të merrni leje e inputit të Android"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", ""),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Nemate pravo prenosa datoteka"),
|
||||
("Note", "Primedba"),
|
||||
("Connection", "Konekcija"),
|
||||
("Share Screen", "Podeli ekran"),
|
||||
("Share screen", "Podeli ekran"),
|
||||
("Chat", "Dopisivanje"),
|
||||
("Total", "Ukupno"),
|
||||
("items", "stavki"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Snimanje ekrana"),
|
||||
("Input Control", "Kontrola unosa"),
|
||||
("Audio Capture", "Snimanje zvuka"),
|
||||
("File Connection", "Spajanje preko datoteke"),
|
||||
("Screen Connection", "Podeli konekciju"),
|
||||
("Do you accept?", "Prihvatate?"),
|
||||
("Open System Setting", "Postavke otvorenog sistema"),
|
||||
("How to get Android input permission?", "Kako dobiti pristup za Android unos?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Pregled kamere"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Pregled kamere"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Rättigheter saknas"),
|
||||
("Note", "Notering"),
|
||||
("Connection", "Anslutning"),
|
||||
("Share Screen", "Dela skärm"),
|
||||
("Share screen", "Dela skärm"),
|
||||
("Chat", "Chatt"),
|
||||
("Total", "Totalt"),
|
||||
("items", "föremål"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Skärminspelning"),
|
||||
("Input Control", "Inputkontroll"),
|
||||
("Audio Capture", "Ljudinspelning"),
|
||||
("File Connection", "Fil anslutning"),
|
||||
("Screen Connection", "Skärm anslutning"),
|
||||
("Do you accept?", "Accepterar du?"),
|
||||
("Open System Setting", "Öppna systeminställnig"),
|
||||
("How to get Android input permission?", "Hur får man Android rättigheter?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Visa kamera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Visa kamera"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", ""),
|
||||
("Note", ""),
|
||||
("Connection", ""),
|
||||
("Share Screen", ""),
|
||||
("Share screen", ""),
|
||||
("Chat", ""),
|
||||
("Total", ""),
|
||||
("items", ""),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", ""),
|
||||
("Input Control", ""),
|
||||
("Audio Capture", ""),
|
||||
("File Connection", ""),
|
||||
("Screen Connection", ""),
|
||||
("Do you accept?", ""),
|
||||
("Open System Setting", ""),
|
||||
("How to get Android input permission?", ""),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", ""),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", ""),
|
||||
("Note", ""),
|
||||
("Connection", ""),
|
||||
("Share Screen", ""),
|
||||
("Share screen", ""),
|
||||
("Chat", ""),
|
||||
("Total", ""),
|
||||
("items", ""),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", ""),
|
||||
("Input Control", ""),
|
||||
("Audio Capture", ""),
|
||||
("File Connection", ""),
|
||||
("Screen Connection", ""),
|
||||
("Do you accept?", ""),
|
||||
("Open System Setting", ""),
|
||||
("How to get Android input permission?", ""),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", ""),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "ไม่มีสิทธิ์ในการถ่ายโอนไฟล์"),
|
||||
("Note", "บันทึกข้อความ"),
|
||||
("Connection", "การเชื่อมต่อ"),
|
||||
("Share Screen", "แชร์หน้าจอ"),
|
||||
("Share screen", "แชร์หน้าจอ"),
|
||||
("Chat", "แชท"),
|
||||
("Total", "รวม"),
|
||||
("items", "รายการ"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "บันทึกหน้าจอ"),
|
||||
("Input Control", "ควบคุมอินพุท"),
|
||||
("Audio Capture", "บันทึกเสียง"),
|
||||
("File Connection", "การเชื่อมต่อไฟล์"),
|
||||
("Screen Connection", "การเชื่อมต่อหน้าจอ"),
|
||||
("Do you accept?", "ยอมรับหรือไม่?"),
|
||||
("Open System Setting", "เปิดการตั้งค่าระบบ"),
|
||||
("How to get Android input permission?", "เปิดสิทธิ์การใช้งานอินพุทของแอนดรอยด์ได้อย่างไร?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "ดูกล้อง"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "กรุณาอัปเดต RustDesk ไคลเอนต์ไปยังเวอร์ชัน {} หรือใหม่กว่าที่ฝั่งปลายทาง!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "ดูกล้อง"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Dosya aktarımı izni yok"),
|
||||
("Note", "Not"),
|
||||
("Connection", "Bağlantı"),
|
||||
("Share Screen", "Ekranı Paylaş"),
|
||||
("Share screen", "Ekranı Paylaş"),
|
||||
("Chat", "Mesajlaş"),
|
||||
("Total", "Toplam"),
|
||||
("items", "öğeler"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Ekran görüntüsü"),
|
||||
("Input Control", "Giriş Kontrolü"),
|
||||
("Audio Capture", "Ses Yakalama"),
|
||||
("File Connection", "Dosya Bağlantısı"),
|
||||
("Screen Connection", "Ekran Bağlantısı"),
|
||||
("Do you accept?", "Kabul ediyor musun?"),
|
||||
("Open System Setting", "Sistem Ayarını Aç"),
|
||||
("How to get Android input permission?", "Android giriş izni nasıl alınır?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Kamerayı görüntüle"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Kamerayı görüntüle"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "沒有檔案傳輸權限"),
|
||||
("Note", "備註"),
|
||||
("Connection", "連線"),
|
||||
("Share Screen", "螢幕分享"),
|
||||
("Share screen", "螢幕分享"),
|
||||
("Chat", "聊天"),
|
||||
("Total", "總計"),
|
||||
("items", "個項目"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "畫面錄製"),
|
||||
("Input Control", "輸入控制"),
|
||||
("Audio Capture", "音訊錄製"),
|
||||
("File Connection", "檔案連線"),
|
||||
("Screen Connection", "畫面連線"),
|
||||
("Do you accept?", "是否接受?"),
|
||||
("Open System Setting", "開啟系統設定"),
|
||||
("How to get Android input permission?", "如何取得 Android 的輸入權限?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "無標籤"),
|
||||
("new-version-of-{}-tip", "有新版本的 {} 可用"),
|
||||
("Accessible devices", "可存取的裝置"),
|
||||
("View camera", "檢視相機"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "請將遠端 RustDesk 客戶端升級到 {} 或更新版本!"),
|
||||
("view_camera_unsupported_tip", "您的遠端設備不支援查看鏡頭"),
|
||||
("Enable camera", "允許查看鏡頭"),
|
||||
("No cameras", "沒有鏡頭"),
|
||||
("d3d_render_tip", "當啟用 D3D 渲染時,某些機器可能會無法顯示遠端畫面。"),
|
||||
("Use D3D rendering", "使用 D3D 渲染"),
|
||||
("Printer", "印表機"),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", "數字一次性密碼"),
|
||||
("Enable IPv6 P2P connection", "啟用 IPv6 P2P 連線"),
|
||||
("Enable UDP hole punching", "啟用 UDP 打洞"),
|
||||
("View camera", "檢視相機"),
|
||||
("Enable camera", "允許查看鏡頭"),
|
||||
("No cameras", "沒有鏡頭"),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Немає дозволу на передачу файлів"),
|
||||
("Note", "Примітка"),
|
||||
("Connection", "Підключення"),
|
||||
("Share Screen", "Поділитися екраном"),
|
||||
("Share screen", "Поділитися екраном"),
|
||||
("Chat", "Чат"),
|
||||
("Total", "Всього"),
|
||||
("items", "елементи"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Захоплення екрана"),
|
||||
("Input Control", "Керування введенням"),
|
||||
("Audio Capture", "Захоплення аудіо"),
|
||||
("File Connection", "Файлове підключення"),
|
||||
("Screen Connection", "Підключення екрана"),
|
||||
("Do you accept?", "Ви згодні?"),
|
||||
("Open System Setting", "Відкрити налаштування системи"),
|
||||
("How to get Android input permission?", "Як отримати дозвіл на введення в Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", "Без міток"),
|
||||
("new-version-of-{}-tip", "Доступна нова версія {}"),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Перегляд камери"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", "Будь ласка, оновіть RustDesk клієнт на віддаленому пристрої до версії {} чи новіше!"),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Перегляд камери"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("No permission of file transfer", "Không có quyền truyền tệp tin"),
|
||||
("Note", "Ghi nhớ"),
|
||||
("Connection", "Kết nối"),
|
||||
("Share Screen", "Chia sẻ màn hình"),
|
||||
("Share screen", "Chia sẻ màn hình"),
|
||||
("Chat", "Chat"),
|
||||
("Total", "Tổng"),
|
||||
("items", "items"),
|
||||
@@ -275,8 +275,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Screen Capture", "Ghi màn hình"),
|
||||
("Input Control", "Điều khiển đầu vào"),
|
||||
("Audio Capture", "Ghi âm thanh"),
|
||||
("File Connection", "Kết nối tệp tin"),
|
||||
("Screen Connection", "Kết nối màn hình"),
|
||||
("Do you accept?", "Bạn có chấp nhận không?"),
|
||||
("Open System Setting", "Mở cài đặt hệ thống"),
|
||||
("How to get Android input permission?", "Cách để có quyền nhập trên Android?"),
|
||||
@@ -657,11 +655,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Untagged", ""),
|
||||
("new-version-of-{}-tip", ""),
|
||||
("Accessible devices", ""),
|
||||
("View camera", "Xem camera"),
|
||||
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
|
||||
("view_camera_unsupported_tip", ""),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("d3d_render_tip", ""),
|
||||
("Use D3D rendering", ""),
|
||||
("Printer", ""),
|
||||
@@ -701,5 +696,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Numeric one-time password", ""),
|
||||
("Enable IPv6 P2P connection", ""),
|
||||
("Enable UDP hole punching", ""),
|
||||
("View camera", "Xem camera"),
|
||||
("Enable camera", ""),
|
||||
("No cameras", ""),
|
||||
("Terminal", ""),
|
||||
("Enable terminal", ""),
|
||||
("New tab", ""),
|
||||
("Keep terminal sessions on disconnect", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
@@ -72,4 +72,4 @@ pub mod privacy_mode;
|
||||
#[cfg(windows)]
|
||||
pub mod virtual_display_manager;
|
||||
|
||||
mod kcp_stream;
|
||||
mod kcp_stream;
|
||||
|
||||
@@ -33,6 +33,8 @@ use video_service::VideoSource;
|
||||
use crate::ipc::Data;
|
||||
|
||||
pub mod audio_service;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub mod terminal_service;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(target_os = "ios"))] {
|
||||
mod clipboard_service;
|
||||
@@ -146,6 +148,7 @@ pub fn new() -> ServerPtr {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Terminal service is created per connection, not globally
|
||||
Arc::new(RwLock::new(server))
|
||||
}
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ pub enum AuthConnType {
|
||||
FileTransfer,
|
||||
PortForward,
|
||||
ViewCamera,
|
||||
Terminal,
|
||||
}
|
||||
|
||||
pub struct Connection {
|
||||
@@ -182,6 +183,7 @@ pub struct Connection {
|
||||
file_timer: crate::RustDeskInterval,
|
||||
file_transfer: Option<(String, bool)>,
|
||||
view_camera: bool,
|
||||
terminal: bool,
|
||||
port_forward_socket: Option<Framed<TcpStream, BytesCodec>>,
|
||||
port_forward_address: String,
|
||||
tx_to_cm: mpsc::UnboundedSender<ipc::Data>,
|
||||
@@ -250,6 +252,9 @@ pub struct Connection {
|
||||
// For post requests that need to be sent sequentially.
|
||||
// eg. post_conn_audit
|
||||
tx_post_seq: mpsc::UnboundedSender<(String, Value)>,
|
||||
terminal_service_id: String,
|
||||
terminal_persistent: bool,
|
||||
terminal_generic_service: Option<Box<GenericService>>,
|
||||
}
|
||||
|
||||
impl ConnInner {
|
||||
@@ -347,6 +352,7 @@ impl Connection {
|
||||
file_timer: crate::rustdesk_interval(time::interval(SEC30)),
|
||||
file_transfer: None,
|
||||
view_camera: false,
|
||||
terminal: false,
|
||||
port_forward_socket: None,
|
||||
port_forward_address: "".to_owned(),
|
||||
tx_to_cm,
|
||||
@@ -410,6 +416,9 @@ impl Connection {
|
||||
tx_from_authed,
|
||||
printer_data: Vec::new(),
|
||||
tx_post_seq,
|
||||
terminal_service_id: "".to_owned(),
|
||||
terminal_persistent: false,
|
||||
terminal_generic_service: None,
|
||||
};
|
||||
let addr = hbb_common::try_into_v4(addr);
|
||||
if !conn.on_open(addr).await {
|
||||
@@ -450,7 +459,7 @@ impl Connection {
|
||||
let mut last_recv_time = Instant::now();
|
||||
|
||||
conn.stream.set_send_timeout(
|
||||
if conn.file_transfer.is_some() || conn.port_forward_socket.is_some() {
|
||||
if conn.file_transfer.is_some() || conn.port_forward_socket.is_some() || conn.terminal {
|
||||
SEND_TIMEOUT_OTHER
|
||||
} else {
|
||||
SEND_TIMEOUT_VIDEO
|
||||
@@ -1253,6 +1262,8 @@ impl Connection {
|
||||
(2, AuthConnType::PortForward)
|
||||
} else if self.view_camera {
|
||||
(3, AuthConnType::ViewCamera)
|
||||
} else if self.terminal {
|
||||
(4, AuthConnType::Terminal)
|
||||
} else {
|
||||
(0, AuthConnType::Remote)
|
||||
};
|
||||
@@ -1361,8 +1372,7 @@ impl Connection {
|
||||
return;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() && !self.view_camera
|
||||
{
|
||||
if self.is_remote() {
|
||||
let mut msg = "".to_string();
|
||||
if crate::platform::linux::is_login_screen_wayland() {
|
||||
msg = crate::client::LOGIN_SCREEN_WAYLAND.to_owned()
|
||||
@@ -1393,7 +1403,8 @@ impl Connection {
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if self.file_transfer.is_some() {
|
||||
if crate::platform::is_prelogin() { // }|| self.tx_to_cm.send(ipc::Data::Test).is_err() {
|
||||
if crate::platform::is_prelogin() {
|
||||
// }|| self.tx_to_cm.send(ipc::Data::Test).is_err() {
|
||||
username = "".to_owned();
|
||||
}
|
||||
}
|
||||
@@ -1408,6 +1419,8 @@ impl Connection {
|
||||
pi.sas_enabled = sas_enabled;
|
||||
pi.features = Some(Features {
|
||||
privacy_mode: privacy_mode::is_privacy_mode_supported(),
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
terminal: true, // Terminal feature is supported on desktop only
|
||||
..Default::default()
|
||||
})
|
||||
.into();
|
||||
@@ -1417,7 +1430,7 @@ impl Connection {
|
||||
let mut wait_session_id_confirm = false;
|
||||
#[cfg(windows)]
|
||||
self.handle_windows_specific_session(&mut pi, &mut wait_session_id_confirm);
|
||||
if self.file_transfer.is_some() {
|
||||
if self.file_transfer.is_some() || self.terminal {
|
||||
res.set_peer_info(pi);
|
||||
} else if self.view_camera {
|
||||
let supported_encoding = scrap::codec::Encoder::supported_encoding();
|
||||
@@ -1509,6 +1522,10 @@ impl Connection {
|
||||
} else {
|
||||
self.delayed_read_dir = Some((dir.to_owned(), show_hidden));
|
||||
}
|
||||
} else if self.terminal {
|
||||
self.keyboard = false;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
self.init_terminal_service().await;
|
||||
} else if self.view_camera {
|
||||
if !wait_session_id_confirm {
|
||||
self.try_sub_camera_displays();
|
||||
@@ -1531,9 +1548,16 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_remote(&self) -> bool {
|
||||
self.file_transfer.is_none()
|
||||
&& self.port_forward_socket.is_none()
|
||||
&& !self.view_camera
|
||||
&& !self.terminal
|
||||
}
|
||||
|
||||
fn try_sub_monitor_services(&mut self) {
|
||||
let is_remote =
|
||||
self.file_transfer.is_none() && self.port_forward_socket.is_none() && !self.view_camera;
|
||||
let is_remote = self.is_remote();
|
||||
if is_remote && !self.services_subed {
|
||||
self.services_subed = true;
|
||||
if let Some(s) = self.server.upgrade() {
|
||||
@@ -1651,6 +1675,7 @@ impl Connection {
|
||||
id: self.inner.id(),
|
||||
is_file_transfer: self.file_transfer.is_some(),
|
||||
is_view_camera: self.view_camera,
|
||||
is_terminal: self.terminal,
|
||||
port_forward: self.port_forward_address.clone(),
|
||||
peer_id,
|
||||
name,
|
||||
@@ -1902,6 +1927,19 @@ impl Connection {
|
||||
}
|
||||
self.view_camera = true;
|
||||
}
|
||||
Some(login_request::Union::Terminal(terminal)) => {
|
||||
if !Connection::permission(keys::OPTION_ENABLE_TERMINAL) {
|
||||
self.send_login_error("No permission of terminal").await;
|
||||
sleep(1.).await;
|
||||
return false;
|
||||
}
|
||||
self.terminal = true;
|
||||
if let Some(o) = self.options_in_login.as_ref() {
|
||||
self.terminal_persistent =
|
||||
o.terminal_persistent.enum_value() == Ok(BoolOption::Yes);
|
||||
}
|
||||
self.terminal_service_id = terminal.service_id;
|
||||
}
|
||||
Some(login_request::Union::PortForward(mut pf)) => {
|
||||
if !Connection::permission("enable-tunnel") {
|
||||
self.send_login_error("No permission of IP tunneling").await;
|
||||
@@ -2791,7 +2829,7 @@ impl Connection {
|
||||
}
|
||||
} else if self.view_camera {
|
||||
self.try_sub_camera_displays();
|
||||
} else {
|
||||
} else if !self.terminal {
|
||||
self.try_sub_monitor_services();
|
||||
}
|
||||
}
|
||||
@@ -2843,6 +2881,12 @@ impl Connection {
|
||||
self.refresh_video_display(Some(request.display as usize));
|
||||
}
|
||||
}
|
||||
Some(message::Union::TerminalAction(action)) => {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
allow_err!(self.handle_terminal_action(action).await);
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
log::warn!("Terminal action received but not supported on this platform");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -3371,6 +3415,12 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if let Ok(q) = o.terminal_persistent.enum_value() {
|
||||
if q != BoolOption::NotSet {
|
||||
self.update_terminal_persistence(q == BoolOption::Yes).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn turn_on_privacy(&mut self, impl_key: String) {
|
||||
@@ -3562,12 +3612,7 @@ impl Connection {
|
||||
|
||||
#[cfg(windows)]
|
||||
fn portable_check(&mut self) {
|
||||
if self.portable.is_installed
|
||||
|| self.file_transfer.is_some()
|
||||
|| self.view_camera
|
||||
|| self.port_forward_socket.is_some()
|
||||
|| !self.keyboard
|
||||
{
|
||||
if self.portable.is_installed || !self.is_remote() || !self.keyboard {
|
||||
return;
|
||||
}
|
||||
let running = portable_client::running();
|
||||
@@ -3779,6 +3824,55 @@ impl Connection {
|
||||
msg_out.set_message_box(res);
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
async fn update_terminal_persistence(&mut self, persistent: bool) {
|
||||
self.terminal_persistent = persistent;
|
||||
terminal_service::set_persistent(&self.terminal_service_id, persistent).ok();
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
async fn init_terminal_service(&mut self) {
|
||||
if self.terminal_service_id.is_empty() {
|
||||
self.terminal_service_id = terminal_service::generate_service_id();
|
||||
}
|
||||
let s = Box::new(terminal_service::new(
|
||||
self.terminal_service_id.clone(),
|
||||
self.terminal_persistent,
|
||||
));
|
||||
s.on_subscribe(self.inner.clone());
|
||||
self.terminal_generic_service = Some(s);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
async fn handle_terminal_action(&mut self, action: TerminalAction) -> ResultType<()> {
|
||||
let mut proxy = terminal_service::TerminalServiceProxy::new(
|
||||
self.terminal_service_id.clone(),
|
||||
Some(self.terminal_persistent),
|
||||
);
|
||||
|
||||
match proxy.handle_action(&action) {
|
||||
Ok(Some(response)) => {
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_terminal_response(response);
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
Ok(None) => {
|
||||
// No response needed
|
||||
}
|
||||
Err(err) => {
|
||||
let mut response = TerminalResponse::new();
|
||||
let mut error = TerminalError::new();
|
||||
error.message = format!("Failed to handle action: {}", err);
|
||||
response.set_error(error);
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_terminal_response(response);
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
|
||||
@@ -4151,6 +4245,10 @@ impl Drop for Connection {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
self.release_pressed_modifiers();
|
||||
|
||||
if let Some(s) = self.terminal_generic_service.as_ref() {
|
||||
s.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
968
src/server/terminal_service.rs
Normal file
968
src/server/terminal_service.rs
Normal file
@@ -0,0 +1,968 @@
|
||||
use super::*;
|
||||
use hbb_common::{
|
||||
anyhow::{anyhow, Context, Result},
|
||||
compress,
|
||||
};
|
||||
use portable_pty::{Child, CommandBuilder, PtySize};
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
io::{Read, Write},
|
||||
sync::{
|
||||
mpsc::{self, Receiver, SyncSender},
|
||||
Arc, Mutex,
|
||||
},
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
const MAX_OUTPUT_BUFFER_SIZE: usize = 1024 * 1024; // 1MB per terminal
|
||||
const MAX_BUFFER_LINES: usize = 10000;
|
||||
const MAX_SERVICES: usize = 100; // Maximum number of persistent terminal services
|
||||
const SERVICE_IDLE_TIMEOUT: Duration = Duration::from_secs(3600); // 1 hour idle timeout
|
||||
const CHANNEL_BUFFER_SIZE: usize = 100; // Number of messages to buffer in channel
|
||||
const COMPRESS_THRESHOLD: usize = 512; // Compress terminal data larger than this
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
// Global registry of persistent terminal services indexed by service_id
|
||||
static ref TERMINAL_SERVICES: Arc<Mutex<HashMap<String, Arc<Mutex<PersistentTerminalService>>>>> =
|
||||
Arc::new(Mutex::new(HashMap::new()));
|
||||
|
||||
// Cleanup task handle
|
||||
static ref CLEANUP_TASK: Arc<Mutex<Option<std::thread::JoinHandle<()>>>> = Arc::new(Mutex::new(None));
|
||||
|
||||
// List of terminal child processes to check for zombies
|
||||
static ref TERMINAL_TASKS: Arc<Mutex<Vec<Box<dyn Child + Send + Sync>>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
}
|
||||
|
||||
/// Service metadata that is sent to clients
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ServiceMetadata {
|
||||
pub service_id: String,
|
||||
pub created_at: Instant,
|
||||
pub terminal_count: usize,
|
||||
pub is_persistent: bool,
|
||||
}
|
||||
|
||||
/// Generate a new persistent service ID
|
||||
pub fn generate_service_id() -> String {
|
||||
format!("ts_{}", uuid::Uuid::new_v4())
|
||||
}
|
||||
|
||||
fn get_default_shell() -> String {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Try PowerShell Core first (cross-platform version)
|
||||
// Common installation paths for PowerShell Core
|
||||
let pwsh_paths = [
|
||||
"pwsh.exe",
|
||||
r"C:\Program Files\PowerShell\7\pwsh.exe",
|
||||
r"C:\Program Files\PowerShell\6\pwsh.exe",
|
||||
];
|
||||
|
||||
for path in &pwsh_paths {
|
||||
if std::path::Path::new(path).exists() {
|
||||
return path.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Try Windows PowerShell (should be available on all Windows systems)
|
||||
let powershell_path = r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
|
||||
if std::path::Path::new(powershell_path).exists() {
|
||||
return powershell_path.to_string();
|
||||
}
|
||||
|
||||
// Final fallback to cmd.exe
|
||||
std::env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".to_string())
|
||||
}
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
// First try the SHELL environment variable
|
||||
if let Ok(shell) = std::env::var("SHELL") {
|
||||
if !shell.is_empty() {
|
||||
return shell;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for common shells in order of preference
|
||||
let shells = ["/bin/bash", "/bin/zsh", "/bin/sh"];
|
||||
for shell in &shells {
|
||||
if std::path::Path::new(shell).exists() {
|
||||
return shell.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback to /bin/sh which should exist on all POSIX systems
|
||||
"/bin/sh".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get or create a persistent terminal service
|
||||
fn get_or_create_service(
|
||||
service_id: String,
|
||||
is_persistent: bool,
|
||||
) -> Result<Arc<Mutex<PersistentTerminalService>>> {
|
||||
let mut services = TERMINAL_SERVICES.lock().unwrap();
|
||||
|
||||
// Check service limit
|
||||
if !services.contains_key(&service_id) && services.len() >= MAX_SERVICES {
|
||||
return Err(anyhow!(
|
||||
"Maximum number of terminal services ({}) reached",
|
||||
MAX_SERVICES
|
||||
));
|
||||
}
|
||||
|
||||
let service = services
|
||||
.entry(service_id.clone())
|
||||
.or_insert_with(|| {
|
||||
log::info!(
|
||||
"Creating new terminal service: {} (persistent: {})",
|
||||
service_id,
|
||||
is_persistent
|
||||
);
|
||||
Arc::new(Mutex::new(PersistentTerminalService::new(
|
||||
service_id.clone(),
|
||||
is_persistent,
|
||||
)))
|
||||
})
|
||||
.clone();
|
||||
|
||||
// Ensure cleanup task is running
|
||||
ensure_cleanup_task();
|
||||
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
/// Remove a service from the global registry
|
||||
fn remove_service(service_id: &str) {
|
||||
let mut services = TERMINAL_SERVICES.lock().unwrap();
|
||||
if let Some(service) = services.remove(service_id) {
|
||||
log::info!("Removed service: {}", service_id);
|
||||
// Close all terminals in the service
|
||||
let sessions = service.lock().unwrap().sessions.clone();
|
||||
for (_, session) in sessions.iter() {
|
||||
let mut session = session.lock().unwrap();
|
||||
if let Some(mut child) = session.child.take() {
|
||||
// Kill the process
|
||||
let _ = child.kill();
|
||||
add_to_reaper(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// List all active terminal services
|
||||
pub fn list_services() -> Vec<ServiceMetadata> {
|
||||
let services = TERMINAL_SERVICES.lock().unwrap();
|
||||
services
|
||||
.iter()
|
||||
.filter_map(|(id, service)| {
|
||||
service.lock().ok().map(|svc| ServiceMetadata {
|
||||
service_id: id.clone(),
|
||||
created_at: svc.created_at,
|
||||
terminal_count: svc.sessions.len(),
|
||||
is_persistent: svc.is_persistent,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get service by ID
|
||||
pub fn get_service(service_id: &str) -> Option<Arc<Mutex<PersistentTerminalService>>> {
|
||||
let services = TERMINAL_SERVICES.lock().unwrap();
|
||||
services.get(service_id).cloned()
|
||||
}
|
||||
|
||||
/// Clean up inactive services
|
||||
pub fn cleanup_inactive_services() {
|
||||
let services = TERMINAL_SERVICES.lock().unwrap();
|
||||
let now = Instant::now();
|
||||
let mut to_remove = Vec::new();
|
||||
|
||||
for (service_id, service) in services.iter() {
|
||||
if let Ok(svc) = service.lock() {
|
||||
// Remove non-persistent services after idle timeout
|
||||
if !svc.is_persistent && now.duration_since(svc.last_activity) > SERVICE_IDLE_TIMEOUT {
|
||||
to_remove.push(service_id.clone());
|
||||
log::info!("Cleaning up idle non-persistent service: {}", service_id);
|
||||
}
|
||||
// Remove persistent services with no active terminals after longer timeout
|
||||
else if svc.is_persistent
|
||||
&& svc.sessions.is_empty()
|
||||
&& now.duration_since(svc.last_activity) > SERVICE_IDLE_TIMEOUT * 2
|
||||
{
|
||||
to_remove.push(service_id.clone());
|
||||
log::info!("Cleaning up empty persistent service: {}", service_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove outside of iteration to avoid deadlock
|
||||
drop(services);
|
||||
for id in to_remove {
|
||||
remove_service(&id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a child process to the zombie reaper
|
||||
fn add_to_reaper(child: Box<dyn Child + Send + Sync>) {
|
||||
if let Ok(mut tasks) = TERMINAL_TASKS.lock() {
|
||||
tasks.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check and reap zombie terminal processes
|
||||
fn check_zombie_terminals() {
|
||||
let mut tasks = match TERMINAL_TASKS.lock() {
|
||||
Ok(t) => t,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
while i < tasks.len() {
|
||||
match tasks[i].try_wait() {
|
||||
Ok(Some(_)) => {
|
||||
// Process has exited, remove it
|
||||
log::info!("Process exited: {:?}", tasks[i].process_id());
|
||||
tasks.remove(i);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Still running
|
||||
i += 1;
|
||||
}
|
||||
Err(err) => {
|
||||
// Error checking status, remove it
|
||||
log::info!(
|
||||
"Process exited with error: {:?}, err: {err}",
|
||||
tasks[i].process_id()
|
||||
);
|
||||
tasks.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure the cleanup task is running
|
||||
fn ensure_cleanup_task() {
|
||||
let mut task_handle = CLEANUP_TASK.lock().unwrap();
|
||||
if task_handle.is_none() {
|
||||
let handle = std::thread::spawn(|| {
|
||||
log::info!("Started cleanup task");
|
||||
let mut last_service_cleanup = Instant::now();
|
||||
loop {
|
||||
// Check for zombie processes every 100ms
|
||||
check_zombie_terminals();
|
||||
|
||||
// Check for inactive services every 5 minutes
|
||||
if last_service_cleanup.elapsed() > Duration::from_secs(300) {
|
||||
cleanup_inactive_services();
|
||||
last_service_cleanup = Instant::now();
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
});
|
||||
*task_handle = Some(handle);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(service_id: String, is_persistent: bool) -> GenericService {
|
||||
// Create the service with initial persistence setting
|
||||
allow_err!(get_or_create_service(service_id.clone(), is_persistent));
|
||||
let svc = EmptyExtraFieldService::new(service_id.clone(), false);
|
||||
GenericService::run(&svc.clone(), move |sp| run(sp, service_id.clone()));
|
||||
svc.sp
|
||||
}
|
||||
|
||||
fn run(sp: EmptyExtraFieldService, service_id: String) -> ResultType<()> {
|
||||
while sp.ok() {
|
||||
let responses = TerminalServiceProxy::new(service_id.clone(), None).read_outputs();
|
||||
for response in responses {
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_terminal_response(response);
|
||||
sp.send(msg_out);
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(30)); // Read at ~33fps for responsive terminal
|
||||
}
|
||||
|
||||
// Clean up non-persistent service when loop exits
|
||||
if let Some(service) = get_service(&service_id) {
|
||||
let should_remove = !service.lock().unwrap().is_persistent;
|
||||
if should_remove {
|
||||
remove_service(&service_id);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Output buffer for terminal session
|
||||
struct OutputBuffer {
|
||||
lines: VecDeque<Vec<u8>>,
|
||||
total_size: usize,
|
||||
last_line_incomplete: bool,
|
||||
}
|
||||
|
||||
impl OutputBuffer {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
lines: VecDeque::new(),
|
||||
total_size: 0,
|
||||
last_line_incomplete: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, data: &[u8]) {
|
||||
if data.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle incomplete lines
|
||||
let mut start = 0;
|
||||
if self.last_line_incomplete {
|
||||
if let Some(last_line) = self.lines.back_mut() {
|
||||
// Find first newline in new data
|
||||
if let Some(newline_pos) = data.iter().position(|&b| b == b'\n') {
|
||||
last_line.extend_from_slice(&data[..=newline_pos]);
|
||||
start = newline_pos + 1;
|
||||
self.last_line_incomplete = false;
|
||||
} else {
|
||||
// Still no newline, append all
|
||||
last_line.extend_from_slice(data);
|
||||
self.total_size += data.len();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process remaining data
|
||||
let remaining = &data[start..];
|
||||
let ends_with_newline = remaining.last() == Some(&b'\n');
|
||||
|
||||
// Split by lines
|
||||
let lines: Vec<&[u8]> = remaining.split(|&b| b == b'\n').collect();
|
||||
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
if i == lines.len() - 1 && !ends_with_newline && !line.is_empty() {
|
||||
// Last line without newline
|
||||
self.last_line_incomplete = true;
|
||||
}
|
||||
|
||||
if !line.is_empty() || i < lines.len() - 1 {
|
||||
let mut line_data = line.to_vec();
|
||||
if i < lines.len() - 1 || ends_with_newline {
|
||||
line_data.push(b'\n');
|
||||
}
|
||||
|
||||
self.total_size += line_data.len();
|
||||
self.lines.push_back(line_data);
|
||||
}
|
||||
}
|
||||
|
||||
// Trim old data if buffer is too large
|
||||
while self.total_size > MAX_OUTPUT_BUFFER_SIZE || self.lines.len() > MAX_BUFFER_LINES {
|
||||
if let Some(removed) = self.lines.pop_front() {
|
||||
self.total_size -= removed.len();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_recent(&self, max_bytes: usize) -> Vec<u8> {
|
||||
let mut result = Vec::new();
|
||||
let mut size = 0;
|
||||
|
||||
// Get recent lines up to max_bytes
|
||||
for line in self.lines.iter().rev() {
|
||||
if size + line.len() > max_bytes {
|
||||
break;
|
||||
}
|
||||
size += line.len();
|
||||
result.splice(0..0, line.iter().cloned());
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TerminalSession {
|
||||
pub created_at: Instant,
|
||||
last_activity: Instant,
|
||||
pty_pair: Option<portable_pty::PtyPair>,
|
||||
child: Option<Box<dyn Child + std::marker::Send + Sync>>,
|
||||
// Channel for sending input to the writer thread
|
||||
input_tx: Option<SyncSender<Vec<u8>>>,
|
||||
// Channel for receiving output from the reader thread
|
||||
output_rx: Option<Receiver<Vec<u8>>>,
|
||||
// Thread handles
|
||||
reader_thread: Option<thread::JoinHandle<()>>,
|
||||
writer_thread: Option<thread::JoinHandle<()>>,
|
||||
output_buffer: OutputBuffer,
|
||||
title: String,
|
||||
pid: u32,
|
||||
rows: u16,
|
||||
cols: u16,
|
||||
// Track if we've already sent the closed message
|
||||
closed_message_sent: bool,
|
||||
}
|
||||
|
||||
impl TerminalSession {
|
||||
fn new(terminal_id: i32, rows: u16, cols: u16) -> Self {
|
||||
Self {
|
||||
created_at: Instant::now(),
|
||||
last_activity: Instant::now(),
|
||||
pty_pair: None,
|
||||
child: None,
|
||||
input_tx: None,
|
||||
output_rx: None,
|
||||
reader_thread: None,
|
||||
writer_thread: None,
|
||||
output_buffer: OutputBuffer::new(),
|
||||
title: format!("Terminal {}", terminal_id),
|
||||
pid: 0,
|
||||
rows,
|
||||
cols,
|
||||
closed_message_sent: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_activity(&mut self) {
|
||||
self.last_activity = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TerminalSession {
|
||||
fn drop(&mut self) {
|
||||
// Drop the input channel to signal writer thread to exit
|
||||
drop(self.input_tx.take());
|
||||
|
||||
// Wait for threads to finish
|
||||
if let Some(writer_thread) = self.writer_thread.take() {
|
||||
let _ = writer_thread.join();
|
||||
}
|
||||
if let Some(reader_thread) = self.reader_thread.take() {
|
||||
let _ = reader_thread.join();
|
||||
}
|
||||
|
||||
// Ensure child process is properly handled when session is dropped
|
||||
if let Some(mut child) = self.child.take() {
|
||||
let _ = child.kill();
|
||||
add_to_reaper(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Persistent terminal service that can survive connection drops
|
||||
pub struct PersistentTerminalService {
|
||||
service_id: String,
|
||||
sessions: HashMap<i32, Arc<Mutex<TerminalSession>>>,
|
||||
pub created_at: Instant,
|
||||
last_activity: Instant,
|
||||
pub is_persistent: bool,
|
||||
}
|
||||
|
||||
impl PersistentTerminalService {
|
||||
pub fn new(service_id: String, is_persistent: bool) -> Self {
|
||||
Self {
|
||||
service_id,
|
||||
sessions: HashMap::new(),
|
||||
created_at: Instant::now(),
|
||||
last_activity: Instant::now(),
|
||||
is_persistent,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_activity(&mut self) {
|
||||
self.last_activity = Instant::now();
|
||||
}
|
||||
|
||||
/// Get list of terminal metadata
|
||||
pub fn list_terminals(&self) -> Vec<(i32, String, u32, Instant)> {
|
||||
self.sessions
|
||||
.iter()
|
||||
.map(|(id, session)| {
|
||||
let s = session.lock().unwrap();
|
||||
(*id, s.title.clone(), s.pid, s.created_at)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get buffered output for a terminal
|
||||
pub fn get_terminal_buffer(&self, terminal_id: i32, max_bytes: usize) -> Option<Vec<u8>> {
|
||||
self.sessions.get(&terminal_id).map(|session| {
|
||||
let session = session.lock().unwrap();
|
||||
session.output_buffer.get_recent(max_bytes)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get terminal info for recovery
|
||||
pub fn get_terminal_info(&self, terminal_id: i32) -> Option<(u16, u16, Vec<u8>)> {
|
||||
self.sessions.get(&terminal_id).map(|session| {
|
||||
let session = session.lock().unwrap();
|
||||
(
|
||||
session.rows,
|
||||
session.cols,
|
||||
session.output_buffer.get_recent(4096),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if service has active terminals
|
||||
pub fn has_active_terminals(&self) -> bool {
|
||||
!self.sessions.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TerminalServiceProxy {
|
||||
service_id: String,
|
||||
is_persistent: bool,
|
||||
}
|
||||
|
||||
pub fn set_persistent(service_id: &str, is_persistent: bool) -> Result<()> {
|
||||
if let Some(service) = get_service(service_id) {
|
||||
service.lock().unwrap().is_persistent = is_persistent;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!("Service {} not found", service_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl TerminalServiceProxy {
|
||||
pub fn new(service_id: String, is_persistent: Option<bool>) -> Self {
|
||||
// Get persistence from the service if it exists
|
||||
let is_persistent =
|
||||
is_persistent.unwrap_or(if let Some(service) = get_service(&service_id) {
|
||||
service.lock().unwrap().is_persistent
|
||||
} else {
|
||||
false
|
||||
});
|
||||
TerminalServiceProxy {
|
||||
service_id,
|
||||
is_persistent,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_service_id(&self) -> &str {
|
||||
&self.service_id
|
||||
}
|
||||
|
||||
pub fn handle_action(&mut self, action: &TerminalAction) -> Result<Option<TerminalResponse>> {
|
||||
let service = match get_service(&self.service_id) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
let mut response = TerminalResponse::new();
|
||||
let mut error = TerminalError::new();
|
||||
error.message = format!("Terminal service {} not found", self.service_id);
|
||||
response.set_error(error);
|
||||
return Ok(Some(response));
|
||||
}
|
||||
};
|
||||
service.lock().unwrap().update_activity();
|
||||
match &action.union {
|
||||
Some(terminal_action::Union::Open(open)) => {
|
||||
self.handle_open(&mut service.lock().unwrap(), open)
|
||||
}
|
||||
Some(terminal_action::Union::Resize(resize)) => {
|
||||
let session = service
|
||||
.lock()
|
||||
.unwrap()
|
||||
.sessions
|
||||
.get(&resize.terminal_id)
|
||||
.cloned();
|
||||
self.handle_resize(session, resize)
|
||||
}
|
||||
Some(terminal_action::Union::Data(data)) => {
|
||||
let session = service
|
||||
.lock()
|
||||
.unwrap()
|
||||
.sessions
|
||||
.get(&data.terminal_id)
|
||||
.cloned();
|
||||
self.handle_data(session, data)
|
||||
}
|
||||
Some(terminal_action::Union::Close(close)) => {
|
||||
self.handle_close(&mut service.lock().unwrap(), close)
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_open(
|
||||
&self,
|
||||
service: &mut PersistentTerminalService,
|
||||
open: &OpenTerminal,
|
||||
) -> Result<Option<TerminalResponse>> {
|
||||
let mut response = TerminalResponse::new();
|
||||
|
||||
// Check if terminal already exists
|
||||
if let Some(session_arc) = service.sessions.get(&open.terminal_id) {
|
||||
// Reconnect to existing terminal
|
||||
let session = session_arc.lock().unwrap();
|
||||
let mut opened = TerminalOpened::new();
|
||||
opened.terminal_id = open.terminal_id;
|
||||
opened.success = true;
|
||||
opened.message = "Reconnected to existing terminal".to_string();
|
||||
opened.pid = session.pid;
|
||||
// Return service_id for persistent sessions
|
||||
if self.is_persistent {
|
||||
opened.service_id = self.service_id.clone();
|
||||
}
|
||||
response.set_opened(opened);
|
||||
|
||||
// Send buffered output
|
||||
let buffer = session.output_buffer.get_recent(4096);
|
||||
if !buffer.is_empty() {
|
||||
// We'll need to send this separately or extend the protocol
|
||||
// For now, just acknowledge the reconnection
|
||||
}
|
||||
|
||||
return Ok(Some(response));
|
||||
}
|
||||
|
||||
// Create new terminal session
|
||||
log::info!(
|
||||
"Creating new terminal {} for service: {}",
|
||||
open.terminal_id,
|
||||
service.service_id
|
||||
);
|
||||
let mut session =
|
||||
TerminalSession::new(open.terminal_id, open.rows as u16, open.cols as u16);
|
||||
|
||||
let pty_size = PtySize {
|
||||
rows: open.rows as u16,
|
||||
cols: open.cols as u16,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
};
|
||||
|
||||
log::debug!("Opening PTY with size: {}x{}", open.rows, open.cols);
|
||||
let pty_system = portable_pty::native_pty_system();
|
||||
let pty_pair = pty_system.openpty(pty_size).context("Failed to open PTY")?;
|
||||
|
||||
// Use default shell for the platform
|
||||
let shell = get_default_shell();
|
||||
log::debug!("Using shell: {}", shell);
|
||||
let cmd = CommandBuilder::new(&shell);
|
||||
|
||||
log::debug!("Spawning shell process...");
|
||||
let child = pty_pair
|
||||
.slave
|
||||
.spawn_command(cmd)
|
||||
.context("Failed to spawn command")?;
|
||||
|
||||
let writer = pty_pair
|
||||
.master
|
||||
.take_writer()
|
||||
.context("Failed to get writer")?;
|
||||
|
||||
let reader = pty_pair
|
||||
.master
|
||||
.try_clone_reader()
|
||||
.context("Failed to get reader")?;
|
||||
|
||||
session.pid = child.process_id().unwrap_or(0) as u32;
|
||||
|
||||
// Create channels for input/output
|
||||
let (input_tx, input_rx) = mpsc::sync_channel::<Vec<u8>>(CHANNEL_BUFFER_SIZE);
|
||||
let (output_tx, output_rx) = mpsc::sync_channel::<Vec<u8>>(CHANNEL_BUFFER_SIZE);
|
||||
|
||||
// Spawn writer thread
|
||||
let terminal_id = open.terminal_id;
|
||||
let writer_thread = thread::spawn(move || {
|
||||
let mut writer = writer;
|
||||
while let Ok(data) = input_rx.recv() {
|
||||
if let Err(e) = writer.write_all(&data) {
|
||||
log::error!("Terminal {} write error: {}", terminal_id, e);
|
||||
break;
|
||||
}
|
||||
if let Err(e) = writer.flush() {
|
||||
log::error!("Terminal {} flush error: {}", terminal_id, e);
|
||||
}
|
||||
}
|
||||
log::debug!("Terminal {} writer thread exiting", terminal_id);
|
||||
});
|
||||
|
||||
// Spawn reader thread
|
||||
let terminal_id = open.terminal_id;
|
||||
let reader_thread = thread::spawn(move || {
|
||||
let mut reader = reader;
|
||||
let mut buf = vec![0u8; 4096];
|
||||
loop {
|
||||
match reader.read(&mut buf) {
|
||||
Ok(0) => {
|
||||
// EOF
|
||||
break;
|
||||
}
|
||||
Ok(n) => {
|
||||
let data = buf[..n].to_vec();
|
||||
// Try to send, if channel is full, drop the data
|
||||
match output_tx.try_send(data) {
|
||||
Ok(_) => {}
|
||||
Err(mpsc::TrySendError::Full(_)) => {
|
||||
log::debug!(
|
||||
"Terminal {} output channel full, dropping data",
|
||||
terminal_id
|
||||
);
|
||||
}
|
||||
Err(mpsc::TrySendError::Disconnected(_)) => {
|
||||
log::debug!("Terminal {} output channel disconnected", terminal_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => {
|
||||
// For non-blocking I/O, sleep briefly
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Terminal {} read error: {}", terminal_id, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
log::debug!("Terminal {} reader thread exiting", terminal_id);
|
||||
});
|
||||
|
||||
session.pty_pair = Some(pty_pair);
|
||||
session.child = Some(child);
|
||||
session.input_tx = Some(input_tx);
|
||||
session.output_rx = Some(output_rx);
|
||||
session.reader_thread = Some(reader_thread);
|
||||
session.writer_thread = Some(writer_thread);
|
||||
|
||||
let mut opened = TerminalOpened::new();
|
||||
opened.terminal_id = open.terminal_id;
|
||||
opened.success = true;
|
||||
opened.message = "Terminal opened".to_string();
|
||||
opened.pid = session.pid;
|
||||
// Return service_id for persistent sessions
|
||||
if self.is_persistent {
|
||||
opened.service_id = service.service_id.clone();
|
||||
}
|
||||
response.set_opened(opened);
|
||||
|
||||
log::info!(
|
||||
"Terminal {} opened successfully with PID {}",
|
||||
open.terminal_id,
|
||||
session.pid
|
||||
);
|
||||
|
||||
// Store the session
|
||||
service
|
||||
.sessions
|
||||
.insert(open.terminal_id, Arc::new(Mutex::new(session)));
|
||||
|
||||
Ok(Some(response))
|
||||
}
|
||||
|
||||
fn handle_resize(
|
||||
&self,
|
||||
session: Option<Arc<Mutex<TerminalSession>>>,
|
||||
resize: &ResizeTerminal,
|
||||
) -> Result<Option<TerminalResponse>> {
|
||||
if let Some(session_arc) = session {
|
||||
let mut session = session_arc.lock().unwrap();
|
||||
session.update_activity();
|
||||
session.rows = resize.rows as u16;
|
||||
session.cols = resize.cols as u16;
|
||||
|
||||
if let Some(pty_pair) = &session.pty_pair {
|
||||
pty_pair.master.resize(PtySize {
|
||||
rows: resize.rows as u16,
|
||||
cols: resize.cols as u16,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn handle_data(
|
||||
&self,
|
||||
session: Option<Arc<Mutex<TerminalSession>>>,
|
||||
data: &TerminalData,
|
||||
) -> Result<Option<TerminalResponse>> {
|
||||
if let Some(session_arc) = session {
|
||||
let mut session = session_arc.lock().unwrap();
|
||||
session.update_activity();
|
||||
if let Some(input_tx) = &session.input_tx {
|
||||
// Send data to writer thread
|
||||
if let Err(e) = input_tx.send(data.data.to_vec()) {
|
||||
log::error!(
|
||||
"Failed to send data to terminal {}: {}",
|
||||
data.terminal_id,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn handle_close(
|
||||
&self,
|
||||
service: &mut PersistentTerminalService,
|
||||
close: &CloseTerminal,
|
||||
) -> Result<Option<TerminalResponse>> {
|
||||
let mut response = TerminalResponse::new();
|
||||
|
||||
// Always close and remove the terminal
|
||||
if let Some(session_arc) = service.sessions.remove(&close.terminal_id) {
|
||||
let mut session = session_arc.lock().unwrap();
|
||||
let exit_code = if let Some(mut child) = session.child.take() {
|
||||
child.kill()?;
|
||||
add_to_reaper(child);
|
||||
-1 // -1 indicates forced termination
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let mut closed = TerminalClosed::new();
|
||||
closed.terminal_id = close.terminal_id;
|
||||
closed.exit_code = exit_code;
|
||||
response.set_closed(closed);
|
||||
Ok(Some(response))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_outputs(&self) -> Vec<TerminalResponse> {
|
||||
let service = match get_service(&self.service_id) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
|
||||
// Get session references with minimal service lock time
|
||||
let sessions: Vec<(i32, Arc<Mutex<TerminalSession>>)> = {
|
||||
let service = service.lock().unwrap();
|
||||
service
|
||||
.sessions
|
||||
.iter()
|
||||
.map(|(id, session)| (*id, session.clone()))
|
||||
.collect()
|
||||
};
|
||||
|
||||
let mut responses = Vec::new();
|
||||
let mut closed_terminals = Vec::new();
|
||||
|
||||
// Process each session with its own lock
|
||||
for (terminal_id, session_arc) in sessions {
|
||||
if let Ok(mut session) = session_arc.try_lock() {
|
||||
// Check if reader thread is still alive and we haven't sent closed message yet
|
||||
let mut should_send_closed = false;
|
||||
if !session.closed_message_sent {
|
||||
if let Some(thread) = &session.reader_thread {
|
||||
if thread.is_finished() {
|
||||
should_send_closed = true;
|
||||
session.closed_message_sent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read from output channel
|
||||
let mut has_activity = false;
|
||||
let mut received_data = Vec::new();
|
||||
if let Some(output_rx) = &session.output_rx {
|
||||
// Try to read all available data
|
||||
while let Ok(data) = output_rx.try_recv() {
|
||||
has_activity = true;
|
||||
received_data.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Update buffer after reading
|
||||
for data in &received_data {
|
||||
session.output_buffer.append(data);
|
||||
}
|
||||
|
||||
// Process received data for responses
|
||||
for data in received_data {
|
||||
let mut response = TerminalResponse::new();
|
||||
let mut terminal_data = TerminalData::new();
|
||||
terminal_data.terminal_id = terminal_id;
|
||||
|
||||
// Compress data if it exceeds threshold
|
||||
if data.len() > COMPRESS_THRESHOLD {
|
||||
let compressed = compress::compress(&data);
|
||||
if compressed.len() < data.len() {
|
||||
terminal_data.data = bytes::Bytes::from(compressed);
|
||||
terminal_data.compressed = true;
|
||||
} else {
|
||||
// Compression didn't help, send uncompressed
|
||||
terminal_data.data = bytes::Bytes::from(data);
|
||||
}
|
||||
} else {
|
||||
terminal_data.data = bytes::Bytes::from(data);
|
||||
}
|
||||
|
||||
response.set_data(terminal_data);
|
||||
responses.push(response);
|
||||
}
|
||||
|
||||
if has_activity {
|
||||
session.update_activity();
|
||||
}
|
||||
|
||||
if should_send_closed {
|
||||
closed_terminals.push(terminal_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up closed terminals (requires service lock briefly)
|
||||
if !closed_terminals.is_empty() {
|
||||
let mut sessions = service.lock().unwrap().sessions.clone();
|
||||
for terminal_id in closed_terminals {
|
||||
let mut exit_code = 0;
|
||||
|
||||
if !self.is_persistent {
|
||||
if let Some(session_arc) = sessions.remove(&terminal_id) {
|
||||
service.lock().unwrap().sessions.remove(&terminal_id);
|
||||
let mut session = session_arc.lock().unwrap();
|
||||
// Take the child and add to zombie reaper
|
||||
if let Some(mut child) = session.child.take() {
|
||||
// Try to get exit code if available
|
||||
if let Ok(Some(status)) = child.try_wait() {
|
||||
exit_code = status.exit_code() as i32;
|
||||
}
|
||||
add_to_reaper(child);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For persistent sessions, just clear the child reference
|
||||
if let Some(session_arc) = sessions.get(&terminal_id) {
|
||||
let mut session = session_arc.lock().unwrap();
|
||||
if let Some(mut child) = session.child.take() {
|
||||
// Try to get exit code if available
|
||||
if let Ok(Some(status)) = child.try_wait() {
|
||||
exit_code = status.exit_code() as i32;
|
||||
}
|
||||
add_to_reaper(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut response = TerminalResponse::new();
|
||||
let mut closed = TerminalClosed::new();
|
||||
closed.terminal_id = terminal_id;
|
||||
closed.exit_code = exit_code;
|
||||
response.set_closed(closed);
|
||||
responses.push(response);
|
||||
}
|
||||
}
|
||||
|
||||
responses
|
||||
}
|
||||
|
||||
/// Cleanup when connection drops
|
||||
pub fn on_disconnect(&self) {
|
||||
if !self.is_persistent {
|
||||
// Remove non-persistent service
|
||||
remove_service(&self.service_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ impl InvokeUiCM for SciterHandler {
|
||||
client.id,
|
||||
client.is_file_transfer,
|
||||
client.is_view_camera,
|
||||
client.is_terminal,
|
||||
client.port_forward.clone(),
|
||||
client.peer_id.clone(),
|
||||
client.name.clone(),
|
||||
|
||||
@@ -29,7 +29,7 @@ class Body: Reactor.Component
|
||||
};
|
||||
var right_style = show_chat ? "" : "display: none";
|
||||
var disconnected = c.disconnected;
|
||||
var show_elevation_btn = handler.can_elevate() && show_elevation && !c.is_file_transfer && c.port_forward.length == 0;
|
||||
var show_elevation_btn = handler.can_elevate() && show_elevation && !c.is_file_transfer && !c.is_view_camera && !c.is_terminal && c.port_forward.length == 0;
|
||||
var show_accept_btn = handler.get_option('approve-mode') != 'password';
|
||||
// below size:* is a workaround for Linux, it already set in css, but not work, shit sciter
|
||||
return <div .content style="size:*">
|
||||
@@ -48,8 +48,8 @@ class Body: Reactor.Component
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
{c.is_file_transfer || c.port_forward || disconnected ? "" : <div>{translate('Permissions')}</div>}
|
||||
{c.is_file_transfer || c.port_forward || disconnected ? "" : <div> <div .permissions>
|
||||
{c.is_file_transfer || c.is_terminal || c.port_forward || disconnected ? "" : <div>{translate('Permissions')}</div>}
|
||||
{c.is_file_transfer || c.is_terminal || c.port_forward || disconnected ? "" : <div> <div .permissions>
|
||||
<div class={!c.keyboard ? "disabled" : ""} title={translate('Enable keyboard/mouse')}><icon .keyboard /></div>
|
||||
<div class={!c.clipboard ? "disabled" : ""} title={translate('Enable clipboard')}><icon .clipboard /></div>
|
||||
<div class={!c.audio ? "disabled" : ""} title={translate('Enable audio')}><icon .audio /></div>
|
||||
@@ -60,6 +60,9 @@ class Body: Reactor.Component
|
||||
<div class={!c.block_input ? "disabled" : ""} title={translate('Enable blocking user input')} style={is_win ? "" : "display:none;"}><icon .block_input /></div>
|
||||
</div></div>
|
||||
}
|
||||
{c.is_file_transfer ? <div>{translate('Transfer file')}</div> : ""}
|
||||
{c.is_view_camera ? <div>{translate('View camera')}</div> : ""}
|
||||
{c.is_terminal ? <div>{translate('Terminal')}</div> : ""}
|
||||
{c.port_forward ? <div>Port Forwarding: {c.port_forward}</div> : ""}
|
||||
<div style="size:*"/>
|
||||
<div .outer_buttons>
|
||||
@@ -72,10 +75,10 @@ class Body: Reactor.Component
|
||||
{auth && !disconnected ? <button #disconnect .control .button>{translate('Disconnect')}</button> : "" }
|
||||
{auth && disconnected ? <button #close .control .button>{translate('Close')}</button> : "" }
|
||||
</div>
|
||||
{c.is_file_transfer || c.port_forward ? "" : <div .chaticon>{svg_chat}</div>}
|
||||
{c.is_file_transfer || c.is_terminal || c.port_forward ? "" : <div .chaticon>{svg_chat}</div>}
|
||||
</div>
|
||||
<div .right-panel style={right_style}>
|
||||
{c.is_file_transfer || c.port_forward ? "" : <ChatBox msgs={c.msgs} callback={callback} />}
|
||||
{c.is_file_transfer || c.is_terminal || c.port_forward ? "" : <ChatBox msgs={c.msgs} callback={callback} />}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
@@ -356,7 +359,7 @@ function bring_to_top(idx=-1) {
|
||||
}
|
||||
}
|
||||
|
||||
handler.addConnection = function(id, is_file_transfer, is_view_camera, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) {
|
||||
handler.addConnection = function(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) {
|
||||
stdout.println("new connection #" + id + ": " + peer_id);
|
||||
var conn;
|
||||
connections.map(function(c) {
|
||||
@@ -373,7 +376,7 @@ handler.addConnection = function(id, is_file_transfer, is_view_camera, port_forw
|
||||
});
|
||||
if (!name) name = "NA";
|
||||
conn = {
|
||||
id: id, is_file_transfer: is_file_transfer, peer_id: peer_id,
|
||||
id: id, is_file_transfer: is_file_transfer, is_view_camera: is_view_camera, is_terminal: is_terminal, peer_id: peer_id,
|
||||
port_forward: port_forward,
|
||||
name: name, authorized: authorized, time: new Date(), now: new Date(),
|
||||
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
|
||||
|
||||
@@ -313,7 +313,9 @@ class MyIdMenu: Reactor.Component {
|
||||
<menu.context #config-options>
|
||||
<li #enable-keyboard><span>{svg_checkmark}</span>{translate('Enable keyboard/mouse')}</li>
|
||||
<li #enable-clipboard><span>{svg_checkmark}</span>{translate('Enable clipboard')}</li>
|
||||
<li #enable-file-transfer><span>{svg_checkmark}</span>{translate('Enable file transfer')}</li>
|
||||
<li #enable-file-transfer><span>{svg_checkmark}</span>{translate('Enable file transfer')}</li>
|
||||
<li #enable-camera><span>{svg_checkmark}</span>{translate('Enable camera')}</li>
|
||||
<li #enable-terminal><span>{svg_checkmark}</span>{translate('Enable terminal')}</li>
|
||||
<li #enable-remote-restart><span>{svg_checkmark}</span>{translate('Enable remote restart')}</li>
|
||||
<li #enable-tunnel><span>{svg_checkmark}</span>{translate('Enable TCP tunneling')}</li>
|
||||
{is_win ? <li #enable-block-input><span>{svg_checkmark}</span>{translate('Enable blocking user input')}</li> : ""}
|
||||
|
||||
@@ -313,16 +313,10 @@ impl InvokeUiSession for SciterHandler {
|
||||
|
||||
fn on_connected(&self, conn_type: ConnType) {
|
||||
match conn_type {
|
||||
ConnType::RDP => {}
|
||||
ConnType::PORT_FORWARD => {}
|
||||
ConnType::FILE_TRANSFER => {}
|
||||
ConnType::VIEW_CAMERA => {}
|
||||
ConnType::DEFAULT_CONN => {
|
||||
crate::keyboard::client::start_grab_loop();
|
||||
}
|
||||
// Left empty code from compilation.
|
||||
// Please replace the code in the PR.
|
||||
ConnType::VIEW_CAMERA => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,6 +381,11 @@ impl InvokeUiSession for SciterHandler {
|
||||
fn handle_screenshot_resp(&self, _sid: String, msg: String) {
|
||||
self.call("screenshot", &make_args!(msg));
|
||||
}
|
||||
|
||||
fn handle_terminal_response(&self, _response: TerminalResponse) {
|
||||
// Terminal support is not implemented for Sciter UI
|
||||
// This is a stub implementation to satisfy the trait requirements
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SciterSession(Session<SciterHandler>);
|
||||
|
||||
@@ -48,6 +48,7 @@ pub struct Client {
|
||||
pub disconnected: bool,
|
||||
pub is_file_transfer: bool,
|
||||
pub is_view_camera: bool,
|
||||
pub is_terminal: bool,
|
||||
pub port_forward: String,
|
||||
pub name: String,
|
||||
pub peer_id: String,
|
||||
@@ -130,6 +131,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
id: i32,
|
||||
is_file_transfer: bool,
|
||||
is_view_camera: bool,
|
||||
is_terminal: bool,
|
||||
port_forward: String,
|
||||
peer_id: String,
|
||||
name: String,
|
||||
@@ -150,6 +152,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
disconnected: false,
|
||||
is_file_transfer,
|
||||
is_view_camera,
|
||||
is_terminal,
|
||||
port_forward,
|
||||
name: name.clone(),
|
||||
peer_id: peer_id.clone(),
|
||||
@@ -206,7 +209,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(_k, v)| !v.is_file_transfer)
|
||||
.filter(|(_k, v)| !v.is_file_transfer && !v.is_terminal)
|
||||
.next()
|
||||
.is_none()
|
||||
{
|
||||
@@ -405,9 +408,9 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
}
|
||||
Ok(Some(data)) => {
|
||||
match data {
|
||||
Data::Login{id, is_file_transfer, is_view_camera, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => {
|
||||
Data::Login{id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => {
|
||||
log::debug!("conn_id: {}", id);
|
||||
self.cm.add_connection(id, is_file_transfer, is_view_camera, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone());
|
||||
self.cm.add_connection(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone());
|
||||
self.conn_id = id;
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
@@ -676,6 +679,7 @@ pub async fn start_listen<T: InvokeUiCM>(
|
||||
id,
|
||||
is_file_transfer,
|
||||
is_view_camera,
|
||||
is_terminal,
|
||||
port_forward,
|
||||
peer_id,
|
||||
name,
|
||||
@@ -695,6 +699,7 @@ pub async fn start_listen<T: InvokeUiCM>(
|
||||
id,
|
||||
is_file_transfer,
|
||||
is_view_camera,
|
||||
is_terminal,
|
||||
port_forward,
|
||||
peer_id,
|
||||
name,
|
||||
|
||||
@@ -20,7 +20,7 @@ use uuid::Uuid;
|
||||
use hbb_common::fs;
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
config::{Config, LocalConfig, PeerConfig},
|
||||
config::{keys, Config, LocalConfig, PeerConfig},
|
||||
get_version_number, log,
|
||||
message_proto::*,
|
||||
rendezvous_proto::ConnType,
|
||||
@@ -191,10 +191,18 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
.eq(&ConnType::FILE_TRANSFER)
|
||||
}
|
||||
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.lc.read().unwrap().conn_type.eq(&ConnType::DEFAULT_CONN)
|
||||
}
|
||||
|
||||
pub fn is_view_camera(&self) -> bool {
|
||||
self.lc.read().unwrap().conn_type.eq(&ConnType::VIEW_CAMERA)
|
||||
}
|
||||
|
||||
pub fn is_terminal(&self) -> bool {
|
||||
self.lc.read().unwrap().conn_type.eq(&ConnType::TERMINAL)
|
||||
}
|
||||
|
||||
pub fn is_port_forward(&self) -> bool {
|
||||
let conn_type = self.lc.read().unwrap().conn_type;
|
||||
conn_type == ConnType::PORT_FORWARD || conn_type == ConnType::RDP
|
||||
@@ -341,7 +349,7 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
pub fn toggle_option(&self, name: String) {
|
||||
let msg = self.lc.write().unwrap().toggle_option(name.clone());
|
||||
#[cfg(all(target_os = "windows", not(feature = "flutter")))]
|
||||
if name == hbb_common::config::keys::OPTION_ENABLE_FILE_COPY_PASTE {
|
||||
if name == keys::OPTION_ENABLE_FILE_COPY_PASTE {
|
||||
self.send(Data::ToggleClipboardFile);
|
||||
}
|
||||
if let Some(msg) = msg {
|
||||
@@ -746,6 +754,57 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
self.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
// Terminal methods
|
||||
pub fn open_terminal(&self, terminal_id: i32, rows: u32, cols: u32) {
|
||||
let mut action = TerminalAction::new();
|
||||
action.set_open(OpenTerminal {
|
||||
terminal_id,
|
||||
rows,
|
||||
cols,
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_terminal_action(action);
|
||||
self.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
pub fn send_terminal_input(&self, terminal_id: i32, data: String) {
|
||||
let mut action = TerminalAction::new();
|
||||
action.set_data(TerminalData {
|
||||
terminal_id,
|
||||
data: bytes::Bytes::from(data.into_bytes()),
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_terminal_action(action);
|
||||
self.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
pub fn resize_terminal(&self, terminal_id: i32, rows: u32, cols: u32) {
|
||||
let mut action = TerminalAction::new();
|
||||
action.set_resize(ResizeTerminal {
|
||||
terminal_id,
|
||||
rows,
|
||||
cols,
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_terminal_action(action);
|
||||
self.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
pub fn close_terminal(&self, terminal_id: i32) {
|
||||
let mut action = TerminalAction::new();
|
||||
action.set_close(CloseTerminal {
|
||||
terminal_id,
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_terminal_action(action);
|
||||
self.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
|
||||
pub fn capture_displays(&self, add: Vec<i32>, sub: Vec<i32>, set: Vec<i32>) {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_capture_displays(CaptureDisplays {
|
||||
@@ -1488,7 +1547,7 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
self.read_remote_dir(remote_dir, show_hidden);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if !self.is_terminal() {
|
||||
self.msgbox(
|
||||
"success",
|
||||
"Successful",
|
||||
@@ -1603,6 +1662,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
||||
fn update_empty_dirs(&self, _res: ReadEmptyDirsResponse) {}
|
||||
fn printer_request(&self, id: i32, path: String);
|
||||
fn handle_screenshot_resp(&self, sid: String, msg: String);
|
||||
fn handle_terminal_response(&self, response: TerminalResponse);
|
||||
}
|
||||
|
||||
impl<T: InvokeUiSession> Deref for Session<T> {
|
||||
@@ -1663,7 +1723,7 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
self.on_error("No active console user logged on, please connect and logon first.");
|
||||
return;
|
||||
}
|
||||
} else if !self.is_port_forward() {
|
||||
} else if !self.is_port_forward() && !self.is_terminal() {
|
||||
if pi.displays.is_empty() {
|
||||
self.lc.write().unwrap().handle_peer_info(&pi);
|
||||
self.update_privacy_mode();
|
||||
@@ -1695,7 +1755,7 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
self.set_peer_info(&pi);
|
||||
if self.is_file_transfer() {
|
||||
self.close_success();
|
||||
} else if !self.is_port_forward() {
|
||||
} else if !self.is_port_forward() && !self.is_terminal() {
|
||||
self.msgbox(
|
||||
"success",
|
||||
"Successful",
|
||||
|
||||
Reference in New Issue
Block a user