feat: android clipboard, multi-formats (#9950)

* feat: android clipboard, multi-formats

Signed-off-by: fufesou <linlong1266@gmail.com>

* Chore

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unused code

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2024-11-18 15:43:41 +08:00
committed by GitHub
parent 0707e791e8
commit 8b710f62c8
61 changed files with 670 additions and 47 deletions

View File

@@ -71,8 +71,10 @@ use crate::{
ui_session_interface::{InvokeUiSession, Session},
};
#[cfg(not(target_os = "ios"))]
use crate::clipboard::CLIPBOARD_INTERVAL;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::clipboard::{check_clipboard, ClipboardSide, CLIPBOARD_INTERVAL};
use crate::clipboard::{check_clipboard, ClipboardSide};
#[cfg(not(feature = "flutter"))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::ui_session_interface::SessionPermissionConfig;
@@ -131,7 +133,7 @@ pub(crate) struct ClientClipboardContext {
/// Client of the remote desktop.
pub struct Client;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
struct TextClipboardState {
is_required: bool,
running: bool,
@@ -144,6 +146,10 @@ lazy_static::lazy_static! {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lazy_static::lazy_static! {
static ref ENIGO: Arc<Mutex<enigo::Enigo>> = Arc::new(Mutex::new(enigo::Enigo::new()));
}
#[cfg(not(target_os = "ios"))]
lazy_static::lazy_static! {
static ref TEXT_CLIPBOARD_STATE: Arc<Mutex<TextClipboardState>> = Arc::new(Mutex::new(TextClipboardState::new()));
}
@@ -648,12 +654,12 @@ impl Client {
#[inline]
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
pub fn set_is_text_clipboard_required(b: bool) {
TEXT_CLIPBOARD_STATE.lock().unwrap().is_required = b;
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
fn try_stop_clipboard() {
// There's a bug here.
// If session is closed by the peer, `has_sessions_running()` will always return true.
@@ -748,9 +754,41 @@ impl Client {
Some(rx_started)
}
#[cfg(target_os = "android")]
fn try_start_clipboard(_p: Option<()>) -> Option<UnboundedReceiver<()>> {
let mut clipboard_lock = TEXT_CLIPBOARD_STATE.lock().unwrap();
if clipboard_lock.running {
return None;
}
clipboard_lock.running = true;
log::info!("Start text clipboard loop");
std::thread::spawn(move || {
loop {
if !TEXT_CLIPBOARD_STATE.lock().unwrap().running {
break;
}
if !TEXT_CLIPBOARD_STATE.lock().unwrap().is_required {
std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL));
continue;
}
if let Some(msg) = crate::clipboard::get_clipboards_msg(true) {
crate::flutter::send_text_clipboard_msg(msg);
}
std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL));
}
log::info!("Stop text clipboard loop");
TEXT_CLIPBOARD_STATE.lock().unwrap().running = false;
});
None
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
impl TextClipboardState {
fn new() -> Self {
Self {

View File

@@ -8,9 +8,9 @@ use std::{
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::clipboard::{update_clipboard, ClipboardSide, CLIPBOARD_INTERVAL};
use crate::clipboard::{update_clipboard, ClipboardSide};
#[cfg(not(any(target_os = "ios")))]
use crate::{audio_service, ConnInner, CLIENT_SERVER};
use crate::{audio_service, clipboard::CLIPBOARD_INTERVAL, ConnInner, CLIENT_SERVER};
use crate::{
client::{
self, new_voice_call_request, Client, Data, Interface, MediaData, MediaSender,
@@ -302,7 +302,7 @@ impl<T: InvokeUiSession> Remote<T> {
.unwrap()
.set_disconnected(round);
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
if _set_disconnected_ok {
Client::try_stop_clipboard();
}
@@ -1177,7 +1177,7 @@ impl<T: InvokeUiSession> Remote<T> {
self.check_clipboard_file_context();
if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) {
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
let rx = Client::try_start_clipboard(None);
#[cfg(not(feature = "flutter"))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -1188,7 +1188,7 @@ impl<T: InvokeUiSession> Remote<T> {
},
));
// To make sure current text clipboard data is updated.
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
if let Some(mut rx) = rx {
timeout(CLIPBOARD_INTERVAL, rx.recv()).await.ok();
}
@@ -1209,6 +1209,11 @@ impl<T: InvokeUiSession> Remote<T> {
});
}
}
// to-do: Android, is `sync_init_clipboard` really needed?
// https://github.com/rustdesk/rustdesk/discussions/9010
#[cfg(target_os = "android")]
crate::flutter::update_text_clipboard_required();
// on connection established client
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
@@ -1240,7 +1245,7 @@ impl<T: InvokeUiSession> Remote<T> {
if !self.handler.lc.read().unwrap().disable_clipboard.v {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
update_clipboard(vec![cb], ClipboardSide::Client);
#[cfg(any(target_os = "android", target_os = "ios"))]
#[cfg(target_os = "ios")]
{
let content = if cb.compress {
hbb_common::compress::decompress(&cb.content)
@@ -1251,12 +1256,16 @@ impl<T: InvokeUiSession> Remote<T> {
self.handler.clipboard(content);
}
}
#[cfg(target_os = "android")]
crate::clipboard::handle_msg_clipboard(cb);
}
}
Some(message::Union::MultiClipboards(_mcb)) => {
if !self.handler.lc.read().unwrap().disable_clipboard.v {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
update_clipboard(_mcb.clipboards, ClipboardSide::Client);
#[cfg(target_os = "android")]
crate::clipboard::handle_msg_multi_clipboards(_mcb);
}
}
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
@@ -1421,14 +1430,14 @@ impl<T: InvokeUiSession> Remote<T> {
Ok(Permission::Keyboard) => {
*self.handler.server_keyboard_enabled.write().unwrap() = p.enabled;
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
crate::flutter::update_text_clipboard_required();
self.handler.set_permission("keyboard", p.enabled);
}
Ok(Permission::Clipboard) => {
*self.handler.server_clipboard_enabled.write().unwrap() = p.enabled;
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
crate::flutter::update_text_clipboard_required();
self.handler.set_permission("clipboard", p.enabled);
}

View File

@@ -1,4 +1,6 @@
#[cfg(not(target_os = "android"))]
use arboard::{ClipboardData, ClipboardFormat};
#[cfg(not(target_os = "android"))]
use clipboard_master::{ClipboardHandler, Master, Shutdown};
use hbb_common::{bail, log, message_proto::*, ResultType};
use std::{
@@ -16,6 +18,7 @@ const RUSTDESK_CLIPBOARD_OWNER_FORMAT: &'static str = "dyn.com.rustdesk.owner";
// Add special format for Excel XML Spreadsheet
const CLIPBOARD_FORMAT_EXCEL_XML_SPREADSHEET: &'static str = "XML Spreadsheet";
#[cfg(not(target_os = "android"))]
lazy_static::lazy_static! {
static ref ARBOARD_MTX: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
// cache the clipboard msg
@@ -27,9 +30,12 @@ lazy_static::lazy_static! {
static ref CLIPBOARD_CTX: Arc<Mutex<Option<ClipboardContext>>> = Arc::new(Mutex::new(None));
}
#[cfg(not(target_os = "android"))]
const CLIPBOARD_GET_MAX_RETRY: usize = 3;
#[cfg(not(target_os = "android"))]
const CLIPBOARD_GET_RETRY_INTERVAL_DUR: Duration = Duration::from_millis(33);
#[cfg(not(target_os = "android"))]
const SUPPORTED_FORMATS: &[ClipboardFormat] = &[
ClipboardFormat::Text,
ClipboardFormat::Html,
@@ -146,6 +152,7 @@ impl ClipboardContext {
}
}
#[cfg(not(target_os = "android"))]
pub fn check_clipboard(
ctx: &mut Option<ClipboardContext>,
side: ClipboardSide,
@@ -194,6 +201,7 @@ pub fn check_clipboard_cm() -> ResultType<MultiClipboards> {
}
}
#[cfg(not(target_os = "android"))]
fn update_clipboard_(multi_clipboards: Vec<Clipboard>, side: ClipboardSide) {
let mut to_update_data = proto::from_multi_clipbards(multi_clipboards);
if to_update_data.is_empty() {
@@ -224,17 +232,20 @@ fn update_clipboard_(multi_clipboards: Vec<Clipboard>, side: ClipboardSide) {
}
}
#[cfg(not(target_os = "android"))]
pub fn update_clipboard(multi_clipboards: Vec<Clipboard>, side: ClipboardSide) {
std::thread::spawn(move || {
update_clipboard_(multi_clipboards, side);
});
}
#[cfg(not(target_os = "android"))]
#[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))]
pub struct ClipboardContext {
inner: arboard::Clipboard,
}
#[cfg(not(target_os = "android"))]
#[cfg(not(any(all(target_os = "linux", feature = "unix-file-copy-paste"))))]
#[allow(unreachable_code)]
impl ClipboardContext {
@@ -337,10 +348,20 @@ impl ClipboardContext {
pub fn is_support_multi_clipboard(peer_version: &str, peer_platform: &str) -> bool {
use hbb_common::get_version_number;
get_version_number(peer_version) >= get_version_number("1.3.0")
&& !["", "Android", &whoami::Platform::Ios.to_string()].contains(&peer_platform)
if get_version_number(peer_version) < get_version_number("1.3.0") {
return false;
}
if ["", &whoami::Platform::Ios.to_string()].contains(&peer_platform) {
return false;
}
if "Android" == peer_platform && get_version_number(peer_version) < get_version_number("1.3.3")
{
return false;
}
true
}
#[cfg(not(target_os = "android"))]
pub fn get_current_clipboard_msg(
peer_version: &str,
peer_platform: &str,
@@ -406,6 +427,7 @@ impl std::fmt::Display for ClipboardSide {
}
}
#[cfg(not(target_os = "android"))]
pub fn start_clipbard_master_thread(
handler: impl ClipboardHandler + Send + 'static,
tx_start_res: Sender<(Option<Shutdown>, String)>,
@@ -437,6 +459,7 @@ pub fn start_clipbard_master_thread(
pub use proto::get_msg_if_not_support_multi_clip;
mod proto {
#[cfg(not(target_os = "android"))]
use arboard::ClipboardData;
use hbb_common::{
compress::{compress as compress_func, decompress},
@@ -459,6 +482,7 @@ mod proto {
}
}
#[cfg(not(target_os = "android"))]
fn image_to_proto(a: arboard::ImageData) -> Clipboard {
match &a {
arboard::ImageData::Rgba(rgba) => {
@@ -519,6 +543,7 @@ mod proto {
}
}
#[cfg(not(target_os = "android"))]
fn clipboard_data_to_proto(data: ClipboardData) -> Option<Clipboard> {
let d = match data {
ClipboardData::Text(s) => plain_to_proto(s, ClipboardFormat::Text),
@@ -531,6 +556,7 @@ mod proto {
Some(d)
}
#[cfg(not(target_os = "android"))]
pub fn create_multi_clipboards(vec_data: Vec<ClipboardData>) -> MultiClipboards {
MultiClipboards {
clipboards: vec_data
@@ -541,6 +567,7 @@ mod proto {
}
}
#[cfg(not(target_os = "android"))]
fn from_clipboard(clipboard: Clipboard) -> Option<ClipboardData> {
let data = if clipboard.compress {
decompress(&clipboard.content)
@@ -569,6 +596,7 @@ mod proto {
}
}
#[cfg(not(target_os = "android"))]
pub fn from_multi_clipbards(multi_clipboards: Vec<Clipboard>) -> Vec<ClipboardData> {
multi_clipboards
.into_iter()
@@ -597,3 +625,49 @@ mod proto {
})
}
}
#[cfg(target_os = "android")]
pub fn handle_msg_clipboard(mut cb: Clipboard) {
use hbb_common::protobuf::Message;
if cb.compress {
cb.content = bytes::Bytes::from(hbb_common::compress::decompress(&cb.content));
}
let multi_clips = MultiClipboards {
clipboards: vec![cb],
..Default::default()
};
if let Ok(bytes) = multi_clips.write_to_bytes() {
let _ = scrap::android::ffi::call_clipboard_manager_update_clipboard(&bytes);
}
}
#[cfg(target_os = "android")]
pub fn handle_msg_multi_clipboards(mut mcb: MultiClipboards) {
use hbb_common::protobuf::Message;
for cb in mcb.clipboards.iter_mut() {
if cb.compress {
cb.content = bytes::Bytes::from(hbb_common::compress::decompress(&cb.content));
}
}
if let Ok(bytes) = mcb.write_to_bytes() {
let _ = scrap::android::ffi::call_clipboard_manager_update_clipboard(&bytes);
}
}
#[cfg(target_os = "android")]
pub fn get_clipboards_msg(client: bool) -> Option<Message> {
let mut clipboards = scrap::android::ffi::get_clipboards(client)?;
let mut msg = Message::new();
for c in &mut clipboards.clipboards {
let compressed = hbb_common::compress::compress(&c.content);
let compress = compressed.len() < c.content.len();
if compress {
c.content = compressed.into();
}
c.compress = compress;
}
msg.set_multi_clipboards(clipboards);
Some(msg)
}

View File

@@ -1250,15 +1250,17 @@ fn try_send_close_event(event_stream: &Option<StreamSink<EventToUI>>) {
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
pub fn update_text_clipboard_required() {
let is_required = sessions::get_sessions()
.iter()
.any(|s| s.is_text_clipboard_required());
#[cfg(target_os = "android")]
let _ = scrap::android::ffi::call_clipboard_manager_enable_client_clipboard(is_required);
Client::set_is_text_clipboard_required(is_required);
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
pub fn send_text_clipboard_msg(msg: Message) {
for s in sessions::get_sessions() {
if s.is_text_clipboard_required() {
@@ -2051,7 +2053,7 @@ pub mod sessions {
}
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
pub fn has_sessions_running(conn_type: ConnType) -> bool {
SESSIONS.read().unwrap().iter().any(|((_, r#type), s)| {
*r#type == conn_type && s.session_handlers.read().unwrap().len() != 0

View File

@@ -274,7 +274,7 @@ pub fn session_toggle_option(session_id: SessionID, value: String) {
session.toggle_option(value.clone());
try_sync_peer_option(&session, &session_id, &value, None);
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
if sessions::get_session_by_session_id(&session_id).is_some() && value == "disable-clipboard" {
crate::flutter::update_text_clipboard_required();
}
@@ -817,6 +817,17 @@ pub fn main_show_option(_key: String) -> SyncReturn<bool> {
SyncReturn(false)
}
#[inline]
#[cfg(target_os = "android")]
fn enable_server_clipboard(keyboard_enabled: &str, clip_enabled: &str) {
use scrap::android::ffi::call_clipboard_manager_enable_service_clipboard;
let keyboard_enabled =
config::option2bool(config::keys::OPTION_ENABLE_KEYBOARD, &keyboard_enabled);
let clip_enabled = config::option2bool(config::keys::OPTION_ENABLE_CLIPBOARD, &clip_enabled);
crate::ui_cm_interface::switch_permission_all("clipboard".to_owned(), clip_enabled);
let _ = call_clipboard_manager_enable_service_clipboard(keyboard_enabled && clip_enabled);
}
pub fn main_set_option(key: String, value: String) {
#[cfg(target_os = "android")]
if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) {
@@ -824,6 +835,11 @@ pub fn main_set_option(key: String, value: String) {
config::keys::OPTION_ENABLE_KEYBOARD,
&value,
));
enable_server_clipboard(&value, &get_option(config::keys::OPTION_ENABLE_CLIPBOARD));
}
#[cfg(target_os = "android")]
if key.eq(config::keys::OPTION_ENABLE_CLIPBOARD) {
enable_server_clipboard(&get_option(config::keys::OPTION_ENABLE_KEYBOARD), &value);
}
if key.eq("custom-rendezvous-server") {
set_option(key, value.clone());

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "上传文件夹"),
("Upload files", "上传文件"),
("Clipboard is synchronized", "剪贴板已同步"),
("Update client clipboard", "更新客户端的粘贴板"),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Ordner hochladen"),
("Upload files", "Dateien hochladen"),
("Clipboard is synchronized", "Zwischenablage ist synchronisiert"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Subir carpeta"),
("Upload files", "Subir archivos"),
("Clipboard is synchronized", "Portapapeles sincronizado"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Mappa feltöltése"),
("Upload files", "Fájlok feltöltése"),
("Clipboard is synchronized", "A vágólap szinkronizálva van"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Cartella upload"),
("Upload files", "File upload"),
("Clipboard is synchronized", "Gli appunti sono sincronizzati"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "폴더 업로드"),
("Upload files", "파일 업로드"),
("Clipboard is synchronized", "클립보드가 동기화됨"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Augšupielādēt mapi"),
("Upload files", "Augšupielādēt failus"),
("Clipboard is synchronized", "Starpliktuve ir sinhronizēta"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Map uploaden"),
("Upload files", "Bestanden uploaden"),
("Clipboard is synchronized", "Klembord is gesynchroniseerd"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Wyślij folder"),
("Upload files", "Wyślij pliki"),
("Clipboard is synchronized", "Schowek jest zsynchronizowany"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Загрузить папку"),
("Upload files", "Загрузить файлы"),
("Clipboard is synchronized", "Буфер обмена синхронизирован"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "上傳資料夾"),
("Upload files", "上傳檔案"),
("Clipboard is synchronized", "剪貼簿已同步"),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -653,5 +653,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", ""),
("Upload files", ""),
("Clipboard is synchronized", ""),
("Update client clipboard", ""),
].iter().cloned().collect();
}

View File

@@ -45,7 +45,7 @@ mod custom_server;
mod lang;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
mod port_forward;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
mod clipboard;
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]

View File

@@ -32,7 +32,7 @@ use crate::ipc::Data;
pub mod audio_service;
cfg_if::cfg_if! {
if #[cfg(not(any(target_os = "android", target_os = "ios")))] {
if #[cfg(not(target_os = "ios"))] {
mod clipboard_service;
#[cfg(target_os = "linux")]
pub(crate) mod wayland;
@@ -42,17 +42,20 @@ pub mod uinput;
pub mod rdp_input;
#[cfg(target_os = "linux")]
pub mod dbus;
#[cfg(not(target_os = "android"))]
pub mod input_service;
} else {
mod clipboard_service {
pub const NAME: &'static str = "";
}
}
}
#[cfg(any(target_os = "android", target_os = "ios"))]
pub mod input_service {
pub const NAME_CURSOR: &'static str = "";
pub const NAME_POS: &'static str = "";
pub const NAME_WINDOW_FOCUS: &'static str = "";
}
}
pub const NAME_CURSOR: &'static str = "";
pub const NAME_POS: &'static str = "";
pub const NAME_WINDOW_FOCUS: &'static str = "";
}
mod connection;
@@ -99,10 +102,12 @@ pub fn new() -> ServerPtr {
};
server.add_service(Box::new(audio_service::new()));
#[cfg(not(target_os = "ios"))]
server.add_service(Box::new(display_service::new()));
{
server.add_service(Box::new(display_service::new()));
server.add_service(Box::new(clipboard_service::new()));
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
server.add_service(Box::new(clipboard_service::new()));
if !display_service::capture_cursor_embedded() {
server.add_service(Box::new(input_service::new_cursor()));
server.add_service(Box::new(input_service::new_pos()));

View File

@@ -1,11 +1,15 @@
use super::*;
pub use crate::clipboard::{
check_clipboard, ClipboardContext, ClipboardSide, CLIPBOARD_INTERVAL as INTERVAL,
CLIPBOARD_NAME as NAME,
};
#[cfg(not(target_os = "android"))]
pub use crate::clipboard::{check_clipboard, ClipboardContext, ClipboardSide};
pub use crate::clipboard::{CLIPBOARD_INTERVAL as INTERVAL, CLIPBOARD_NAME as NAME};
#[cfg(windows)]
use crate::ipc::{self, ClipboardFile, ClipboardNonFile, Data};
#[cfg(not(target_os = "android"))]
use clipboard_master::{CallbackResult, ClipboardHandler};
#[cfg(target_os = "android")]
use hbb_common::config::{keys, option2bool};
#[cfg(target_os = "android")]
use scrap::android::ffi::call_clipboard_manager_enable_service_clipboard;
use std::{
io,
sync::mpsc::{channel, RecvTimeoutError, Sender},
@@ -14,6 +18,7 @@ use std::{
#[cfg(windows)]
use tokio::runtime::Runtime;
#[cfg(not(target_os = "android"))]
struct Handler {
sp: EmptyExtraFieldService,
ctx: Option<ClipboardContext>,
@@ -25,11 +30,12 @@ struct Handler {
}
pub fn new() -> GenericService {
let svc = EmptyExtraFieldService::new(NAME.to_owned(), true);
let svc = EmptyExtraFieldService::new(NAME.to_owned(), false);
GenericService::run(&svc.clone(), run);
svc.sp
}
#[cfg(not(target_os = "android"))]
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
let (tx_cb_result, rx_cb_result) = channel();
let handler = Handler {
@@ -73,9 +79,9 @@ fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
Ok(())
}
#[cfg(not(target_os = "android"))]
impl ClipboardHandler for Handler {
fn on_clipboard_change(&mut self) -> CallbackResult {
self.sp.snapshot(|_sps| Ok(())).ok();
if self.sp.ok() {
if let Some(msg) = self.get_clipboard_msg() {
self.sp.send(msg);
@@ -92,6 +98,7 @@ impl ClipboardHandler for Handler {
}
}
#[cfg(not(target_os = "android"))]
impl Handler {
fn get_clipboard_msg(&mut self) -> Option<Message> {
#[cfg(target_os = "windows")]
@@ -216,3 +223,25 @@ impl Handler {
bail!("failed to get clipboard data from cm");
}
}
#[cfg(target_os = "android")]
fn is_clipboard_enabled() -> bool {
let keyboard_enabled = crate::ui_interface::get_option(keys::OPTION_ENABLE_KEYBOARD);
let keyboard_enabled = option2bool(keys::OPTION_ENABLE_KEYBOARD, &keyboard_enabled);
let clip_enabled = crate::ui_interface::get_option(keys::OPTION_ENABLE_CLIPBOARD);
let clip_enabled = option2bool(keys::OPTION_ENABLE_CLIPBOARD, &clip_enabled);
keyboard_enabled && clip_enabled
}
#[cfg(target_os = "android")]
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
let _res = call_clipboard_manager_enable_service_clipboard(is_clipboard_enabled());
while sp.ok() {
if let Some(msg) = crate::clipboard::get_clipboards_msg(false) {
sp.send(msg);
}
std::thread::sleep(Duration::from_millis(INTERVAL));
}
let _res = call_clipboard_manager_enable_service_clipboard(false);
Ok(())
}

View File

@@ -690,7 +690,7 @@ impl Connection {
}
}
Some(message::Union::MultiClipboards(_multi_clipboards)) => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
if let Some(msg_out) = crate::clipboard::get_msg_if_not_support_multi_clip(&conn.lr.version, &conn.lr.my_platform, _multi_clipboards) {
if let Err(err) = conn.stream.send(&msg_out).await {
conn.on_close(&err.to_string(), false).await;
@@ -2074,7 +2074,9 @@ impl Connection {
if self.clipboard {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
update_clipboard(vec![cb], ClipboardSide::Host);
#[cfg(all(feature = "flutter", target_os = "android"))]
// ios as the controlled side is actually not supported for now.
// The following code is only used to preserve the logic of handling text clipboard on mobile.
#[cfg(target_os = "ios")]
{
let content = if cb.compress {
hbb_common::compress::decompress(&cb.content)
@@ -2092,14 +2094,17 @@ impl Connection {
}
}
}
#[cfg(target_os = "android")]
crate::clipboard::handle_msg_clipboard(cb);
}
}
Some(message::Union::MultiClipboards(_mcb)) =>
{
Some(message::Union::MultiClipboards(_mcb)) => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.clipboard {
update_clipboard(_mcb.clipboards, ClipboardSide::Host);
}
#[cfg(target_os = "android")]
crate::clipboard::handle_msg_multi_clipboards(_mcb);
}
Some(message::Union::Cliprdr(_clip)) =>
{

View File

@@ -312,6 +312,17 @@ pub fn switch_permission(id: i32, name: String, enabled: bool) {
};
}
#[inline]
#[cfg(target_os = "android")]
pub fn switch_permission_all(name: String, enabled: bool) {
for (_, client) in CLIENTS.read().unwrap().iter() {
allow_err!(client.tx.send(Data::SwitchPermission {
name: name.clone(),
enabled
}));
}
}
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
#[inline]
pub fn get_clients_state() -> String {

View File

@@ -354,7 +354,7 @@ impl<T: InvokeUiSession> Session<T> {
self.lc.read().unwrap().is_privacy_mode_supported()
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[cfg(not(target_os = "ios"))]
pub fn is_text_clipboard_required(&self) -> bool {
*self.server_clipboard_enabled.read().unwrap()
&& *self.server_keyboard_enabled.read().unwrap()
@@ -526,10 +526,7 @@ impl<T: InvokeUiSession> Session<T> {
#[cfg(not(feature = "flutter"))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn is_xfce(&self) -> bool {
#[cfg(not(any(target_os = "ios")))]
return crate::platform::is_xfce();
#[cfg(any(target_os = "ios"))]
false
crate::platform::is_xfce()
}
pub fn remove_port_forward(&self, port: i32) {