Merge branch 'feat/x11/clipboard-file/init' into feat/osx/clipboard-file
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
commit
62563ad8a1
15
Cargo.toml
15
Cargo.toml
@ -32,6 +32,13 @@ linux_headless = ["pam" ]
|
|||||||
virtual_display_driver = ["virtual_display"]
|
virtual_display_driver = ["virtual_display"]
|
||||||
plugin_framework = []
|
plugin_framework = []
|
||||||
linux-pkg-config = ["magnum-opus/linux-pkg-config", "scrap/linux-pkg-config"]
|
linux-pkg-config = ["magnum-opus/linux-pkg-config", "scrap/linux-pkg-config"]
|
||||||
|
unix-file-copy-paste = [
|
||||||
|
"dep:x11-clipboard",
|
||||||
|
"dep:x11rb",
|
||||||
|
"dep:percent-encoding",
|
||||||
|
"dep:once_cell",
|
||||||
|
"clipboard/unix-file-copy-paste",
|
||||||
|
]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -132,10 +139,10 @@ dbus = "0.9"
|
|||||||
dbus-crossroads = "0.5"
|
dbus-crossroads = "0.5"
|
||||||
pam = { git="https://github.com/fufesou/pam", optional = true }
|
pam = { git="https://github.com/fufesou/pam", optional = true }
|
||||||
users = { version = "0.11" }
|
users = { version = "0.11" }
|
||||||
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch"}
|
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true}
|
||||||
x11rb = {version = "0.12", features = ["all-extensions"]}
|
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
|
||||||
percent-encoding = "2.3"
|
percent-encoding = {version = "2.3", optional = true}
|
||||||
once_cell = "1.18"
|
once_cell = {version = "1.18", optional = true}
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = "0.13"
|
android_logger = "0.13"
|
||||||
|
25
build.py
25
build.py
@ -24,18 +24,21 @@ else:
|
|||||||
flutter_build_dir_2 = f'flutter/{flutter_build_dir}'
|
flutter_build_dir_2 = f'flutter/{flutter_build_dir}'
|
||||||
skip_cargo = False
|
skip_cargo = False
|
||||||
|
|
||||||
|
|
||||||
def get_arch() -> str:
|
def get_arch() -> str:
|
||||||
custom_arch = os.environ.get("ARCH")
|
custom_arch = os.environ.get("ARCH")
|
||||||
if custom_arch is None:
|
if custom_arch is None:
|
||||||
return "amd64"
|
return "amd64"
|
||||||
return custom_arch
|
return custom_arch
|
||||||
|
|
||||||
|
|
||||||
def system2(cmd):
|
def system2(cmd):
|
||||||
err = os.system(cmd)
|
err = os.system(cmd)
|
||||||
if err != 0:
|
if err != 0:
|
||||||
print(f"Error occurred when executing: {cmd}. Exiting.")
|
print(f"Error occurred when executing: {cmd}. Exiting.")
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
with open("Cargo.toml", encoding="utf-8") as fh:
|
with open("Cargo.toml", encoding="utf-8") as fh:
|
||||||
for line in fh:
|
for line in fh:
|
||||||
@ -123,6 +126,11 @@ def make_parser():
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help='Build windows portable'
|
help='Build windows portable'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--unix-file-copy-paste',
|
||||||
|
action='store_true',
|
||||||
|
help='Build with unix file copy paste feature'
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--flatpak',
|
'--flatpak',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@ -185,6 +193,7 @@ def download_extract_features(features, res_dir):
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
proxy = ''
|
proxy = ''
|
||||||
|
|
||||||
def req(url):
|
def req(url):
|
||||||
if not proxy:
|
if not proxy:
|
||||||
return url
|
return url
|
||||||
@ -196,9 +205,9 @@ def download_extract_features(features, res_dir):
|
|||||||
|
|
||||||
for (feat, feat_info) in features.items():
|
for (feat, feat_info) in features.items():
|
||||||
includes = feat_info['include'] if 'include' in feat_info and feat_info['include'] else []
|
includes = feat_info['include'] if 'include' in feat_info and feat_info['include'] else []
|
||||||
includes = [ re.compile(p) for p in includes ]
|
includes = [re.compile(p) for p in includes]
|
||||||
excludes = feat_info['exclude'] if 'exclude' in feat_info and feat_info['exclude'] else []
|
excludes = feat_info['exclude'] if 'exclude' in feat_info and feat_info['exclude'] else []
|
||||||
excludes = [ re.compile(p) for p in excludes ]
|
excludes = [re.compile(p) for p in excludes]
|
||||||
|
|
||||||
print(f'{feat} download begin')
|
print(f'{feat} download begin')
|
||||||
download_filename = feat_info['zip_url'].split('/')[-1]
|
download_filename = feat_info['zip_url'].split('/')[-1]
|
||||||
@ -272,6 +281,8 @@ def get_features(args):
|
|||||||
features.append('flatpak')
|
features.append('flatpak')
|
||||||
if args.appimage:
|
if args.appimage:
|
||||||
features.append('appimage')
|
features.append('appimage')
|
||||||
|
if args.unix_file_copy_paste:
|
||||||
|
features.append('unix-file-copy-paste')
|
||||||
print("features:", features)
|
print("features:", features)
|
||||||
return features
|
return features
|
||||||
|
|
||||||
@ -350,6 +361,7 @@ def build_flutter_deb(version, features):
|
|||||||
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
|
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
|
||||||
os.chdir("..")
|
os.chdir("..")
|
||||||
|
|
||||||
|
|
||||||
def build_deb_from_folder(version, binary_folder):
|
def build_deb_from_folder(version, binary_folder):
|
||||||
os.chdir('flutter')
|
os.chdir('flutter')
|
||||||
system2('mkdir -p tmpdeb/usr/bin/')
|
system2('mkdir -p tmpdeb/usr/bin/')
|
||||||
@ -388,10 +400,12 @@ def build_deb_from_folder(version, binary_folder):
|
|||||||
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
|
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
|
||||||
os.chdir("..")
|
os.chdir("..")
|
||||||
|
|
||||||
|
|
||||||
def build_flutter_dmg(version, features):
|
def build_flutter_dmg(version, features):
|
||||||
if not skip_cargo:
|
if not skip_cargo:
|
||||||
# set minimum osx build target, now is 10.14, which is the same as the flutter xcode project
|
# set minimum osx build target, now is 10.14, which is the same as the flutter xcode project
|
||||||
system2(f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release')
|
system2(
|
||||||
|
f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release')
|
||||||
# copy dylib
|
# copy dylib
|
||||||
system2(
|
system2(
|
||||||
"cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib")
|
"cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib")
|
||||||
@ -557,7 +571,8 @@ def main():
|
|||||||
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/*
|
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/*
|
||||||
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app
|
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app
|
||||||
'''.format(pa))
|
'''.format(pa))
|
||||||
system2('create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version)
|
system2(
|
||||||
|
'create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version)
|
||||||
os.rename('RustDesk %s.dmg' %
|
os.rename('RustDesk %s.dmg' %
|
||||||
version, 'rustdesk-%s.dmg' % version)
|
version, 'rustdesk-%s.dmg' % version)
|
||||||
if pa:
|
if pa:
|
||||||
@ -577,7 +592,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
print('Not signed')
|
print('Not signed')
|
||||||
else:
|
else:
|
||||||
# buid deb package
|
# build deb package
|
||||||
system2(
|
system2(
|
||||||
'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb')
|
'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb')
|
||||||
system2('dpkg-deb -R rustdesk.deb tmpdeb')
|
system2('dpkg-deb -R rustdesk.deb tmpdeb')
|
||||||
|
@ -436,7 +436,9 @@ Future<List<TToggleMenu>> toolbarDisplayToggle(
|
|||||||
child: Text(translate('Mute'))));
|
child: Text(translate('Mute'))));
|
||||||
}
|
}
|
||||||
// file copy and paste
|
// file copy and paste
|
||||||
if (perms['file'] != false) {
|
if (perms['file'] != false &&
|
||||||
|
bind.mainHasFileClipboard() &&
|
||||||
|
pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard)) {
|
||||||
final option = 'enable-file-transfer';
|
final option = 'enable-file-transfer';
|
||||||
final value =
|
final value =
|
||||||
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
|
bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option);
|
||||||
|
@ -22,6 +22,7 @@ const String kPlatformAdditionsIsWayland = "is_wayland";
|
|||||||
const String kPlatformAdditionsHeadless = "headless";
|
const String kPlatformAdditionsHeadless = "headless";
|
||||||
const String kPlatformAdditionsIsInstalled = "is_installed";
|
const String kPlatformAdditionsIsInstalled = "is_installed";
|
||||||
const String kPlatformAdditionsVirtualDisplays = "virtual_displays";
|
const String kPlatformAdditionsVirtualDisplays = "virtual_displays";
|
||||||
|
const String kPlatformAdditionsHasFileClipboard = "has_file_clipboard";
|
||||||
|
|
||||||
const String kPeerPlatformWindows = "Windows";
|
const String kPeerPlatformWindows = "Windows";
|
||||||
const String kPeerPlatformLinux = "Linux";
|
const String kPeerPlatformLinux = "Linux";
|
||||||
|
@ -9,6 +9,21 @@ build = "build.rs"
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
unix-file-copy-paste = [
|
||||||
|
"dep:x11rb",
|
||||||
|
"dep:x11-clipboard",
|
||||||
|
"dep:rand",
|
||||||
|
"dep:fuser",
|
||||||
|
"dep:libc",
|
||||||
|
"dep:dashmap",
|
||||||
|
"dep:percent-encoding",
|
||||||
|
"dep:utf16string",
|
||||||
|
"dep:once_cell",
|
||||||
|
"dep:cacao"
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
@ -18,17 +33,18 @@ hbb_common = { path = "../hbb_common" }
|
|||||||
parking_lot = {version = "0.12"}
|
parking_lot = {version = "0.12"}
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||||
rand = {version = "0.8"}
|
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
|
||||||
fuser = {version = "0.13"}
|
rand = {version = "0.8", optional = true}
|
||||||
libc = {version = "0.2"}
|
fuser = {version = "0.13", optional = true}
|
||||||
dashmap = "5.5"
|
libc = {version = "0.2", optional = true}
|
||||||
percent-encoding = "2.3"
|
dashmap = {version ="5.5", optional = true}
|
||||||
utf16string = "0.2"
|
utf16string = {version = "0.2", optional = true}
|
||||||
once_cell = "1.18"
|
once_cell = {version = "1.18", optional = true}
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch"}
|
percent-encoding = {version ="2.3", optional = true}
|
||||||
x11rb = {version = "0.12", features = ["all-extensions"]}
|
x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true}
|
||||||
|
x11rb = {version = "0.12", features = ["all-extensions"], optional = true}
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
cacao = {git="https://github.com/clslaid/cacao", branch = "feat/set-file-urls"}
|
cacao = {git="https://github.com/clslaid/cacao", branch = "feat/set-file-urls", optional = true}
|
||||||
|
@ -47,6 +47,19 @@ impl ContextSend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// make sure the clipboard context is enabled.
|
||||||
|
pub fn make_sure_enabled() -> ResultType<()> {
|
||||||
|
let mut lock = CONTEXT_SEND.addr.lock().unwrap();
|
||||||
|
if lock.is_some() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctx = crate::create_cliprdr_context(true, false, CLIPBOARD_RESPONSE_WAIT_TIMEOUT_SECS)?;
|
||||||
|
*lock = Some(ctx);
|
||||||
|
log::info!("clipboard context for file transfer recreated.");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn proc<F: FnOnce(&mut Box<dyn CliprdrServiceContext>) -> ResultType<()>>(
|
pub fn proc<F: FnOnce(&mut Box<dyn CliprdrServiceContext>) -> ResultType<()>>(
|
||||||
f: F,
|
f: F,
|
||||||
) -> ResultType<()> {
|
) -> ResultType<()> {
|
||||||
|
@ -108,6 +108,7 @@ pub enum ClipboardFile {
|
|||||||
struct MsgChannel {
|
struct MsgChannel {
|
||||||
peer_id: String,
|
peer_id: String,
|
||||||
conn_id: i32,
|
conn_id: i32,
|
||||||
|
#[allow(dead_code)]
|
||||||
sender: UnboundedSender<ClipboardFile>,
|
sender: UnboundedSender<ClipboardFile>,
|
||||||
receiver: Arc<TokioMutex<UnboundedReceiver<ClipboardFile>>>,
|
receiver: Arc<TokioMutex<UnboundedReceiver<ClipboardFile>>>,
|
||||||
}
|
}
|
||||||
@ -193,6 +194,7 @@ pub fn get_rx_cliprdr_server(conn_id: i32) -> Arc<TokioMutex<UnboundedReceiver<C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste",))]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn send_data(conn_id: i32, data: ClipboardFile) {
|
fn send_data(conn_id: i32, data: ClipboardFile) {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@ -204,7 +206,7 @@ fn send_data(conn_id: i32, data: ClipboardFile) {
|
|||||||
send_data_to_channel(conn_id, data);
|
send_data_to_channel(conn_id, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste",))]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn send_data_to_channel(conn_id: i32, data: ClipboardFile) {
|
fn send_data_to_channel(conn_id: i32, data: ClipboardFile) {
|
||||||
// no need to handle result here
|
// no need to handle result here
|
||||||
@ -218,6 +220,7 @@ fn send_data_to_channel(conn_id: i32, data: ClipboardFile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unix-file-copy-paste")]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn send_data_to_all(data: ClipboardFile) {
|
fn send_data_to_all(data: ClipboardFile) {
|
||||||
// no need to handle result here
|
// no need to handle result here
|
||||||
|
@ -14,45 +14,53 @@ pub fn create_cliprdr_context(
|
|||||||
Ok(boxed)
|
Ok(boxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unix-file-copy-paste")]
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
/// use FUSE for file pasting on these platforms
|
/// use FUSE for file pasting on these platforms
|
||||||
pub mod fuse;
|
pub mod fuse;
|
||||||
|
#[cfg(feature = "unix-file-copy-paste")]
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
pub mod unix;
|
pub mod unix;
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
pub fn create_cliprdr_context(
|
pub fn create_cliprdr_context(
|
||||||
enable_files: bool,
|
_enable_files: bool,
|
||||||
_enable_others: bool,
|
_enable_others: bool,
|
||||||
response_wait_timeout_secs: u32,
|
_response_wait_timeout_secs: u32,
|
||||||
) -> crate::ResultType<Box<dyn crate::CliprdrServiceContext>> {
|
) -> crate::ResultType<Box<dyn crate::CliprdrServiceContext>> {
|
||||||
use std::{fs::Permissions, os::unix::prelude::PermissionsExt};
|
#[cfg(feature = "unix-file-copy-paste")]
|
||||||
|
{
|
||||||
|
use std::{fs::Permissions, os::unix::prelude::PermissionsExt};
|
||||||
|
|
||||||
use hbb_common::{config::APP_NAME, log};
|
use hbb_common::{config::APP_NAME, log};
|
||||||
|
|
||||||
if !enable_files {
|
if !_enable_files {
|
||||||
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeout = std::time::Duration::from_secs(_response_wait_timeout_secs as u64);
|
||||||
|
|
||||||
|
let app_name = APP_NAME.read().unwrap().clone();
|
||||||
|
|
||||||
|
let mnt_path = format!("/tmp/{}/{}", app_name, "cliprdr");
|
||||||
|
|
||||||
|
// this function must be called after the main IPC is up
|
||||||
|
std::fs::create_dir(&mnt_path).ok();
|
||||||
|
std::fs::set_permissions(&mnt_path, Permissions::from_mode(0o777)).ok();
|
||||||
|
|
||||||
|
log::info!("clear previously mounted cliprdr FUSE");
|
||||||
|
if let Err(e) = std::process::Command::new("umount").arg(&mnt_path).status() {
|
||||||
|
log::warn!("umount {:?} may fail: {:?}", mnt_path, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
let unix_ctx = unix::ClipboardContext::new(timeout, mnt_path.parse().unwrap())?;
|
||||||
|
log::debug!("start cliprdr FUSE");
|
||||||
|
unix_ctx.run().expect("failed to start cliprdr FUSE");
|
||||||
|
|
||||||
|
Ok(Box::new(unix_ctx) as Box<_>)
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeout = std::time::Duration::from_secs(response_wait_timeout_secs as u64);
|
#[cfg(not(feature = "unix-file-copy-paste"))]
|
||||||
|
return Ok(Box::new(DummyCliprdrContext {}) as Box<_>);
|
||||||
let app_name = APP_NAME.read().unwrap().clone();
|
|
||||||
|
|
||||||
let mnt_path = format!("/tmp/{}/{}", app_name, "cliprdr");
|
|
||||||
|
|
||||||
// this function must be called after the main IPC is up
|
|
||||||
std::fs::create_dir(&mnt_path).ok();
|
|
||||||
std::fs::set_permissions(&mnt_path, Permissions::from_mode(0o777)).ok();
|
|
||||||
|
|
||||||
log::info!("clear previously mounted cliprdr FUSE");
|
|
||||||
if let Err(e) = std::process::Command::new("umount").arg(&mnt_path).status() {
|
|
||||||
log::warn!("umount {:?} may fail: {:?}", mnt_path, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let unix_ctx = unix::ClipboardContext::new(timeout, mnt_path.parse().unwrap())?;
|
|
||||||
log::debug!("start cliprdr FUSE");
|
|
||||||
unix_ctx.run().expect("failed to start cliprdr FUSE");
|
|
||||||
|
|
||||||
Ok(Box::new(unix_ctx) as Box<_>)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DummyCliprdrContext {}
|
struct DummyCliprdrContext {}
|
||||||
@ -73,7 +81,8 @@ impl CliprdrServiceContext for DummyCliprdrContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unix-file-copy-paste")]
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
// begin of epoch used by microsoft
|
// begin of epoch used by microsoft
|
||||||
// 1601-01-01 00:00:00 + LDAP_EPOCH_DELTA*(100 ns) = 1970-01-01 00:00:00
|
// 1601-01-01 00:00:00 + LDAP_EPOCH_DELTA*(100 ns) = 1970-01-01 00:00:00
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
|
||||||
const LDAP_EPOCH_DELTA: u64 = 116444772610000000;
|
const LDAP_EPOCH_DELTA: u64 = 116444772610000000;
|
||||||
|
@ -24,7 +24,6 @@ use self::url::{encode_path_to_uri, parse_plain_uri_list};
|
|||||||
|
|
||||||
use super::fuse::FuseServer;
|
use super::fuse::FuseServer;
|
||||||
|
|
||||||
#[cfg(not(feature = "wayland"))]
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
/// clipboard implementation of x11
|
/// clipboard implementation of x11
|
||||||
pub mod x11;
|
pub mod x11;
|
||||||
@ -34,6 +33,7 @@ pub mod x11;
|
|||||||
pub mod ns_clipboard;
|
pub mod ns_clipboard;
|
||||||
|
|
||||||
pub mod local_file;
|
pub mod local_file;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub mod url;
|
pub mod url;
|
||||||
|
|
||||||
@ -68,7 +68,6 @@ fn add_remote_format(local_name: &str, remote_id: i32) {
|
|||||||
|
|
||||||
trait SysClipboard: Send + Sync {
|
trait SysClipboard: Send + Sync {
|
||||||
fn start(&self);
|
fn start(&self);
|
||||||
fn stop(&self);
|
|
||||||
|
|
||||||
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>;
|
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>;
|
||||||
fn get_file_list(&self) -> Vec<PathBuf>;
|
fn get_file_list(&self) -> Vec<PathBuf>;
|
||||||
@ -531,7 +530,7 @@ impl CliprdrServiceContext for ClipboardContext {
|
|||||||
if let Some(fuse_handle) = self.fuse_handle.lock().take() {
|
if let Some(fuse_handle) = self.fuse_handle.lock().take() {
|
||||||
fuse_handle.join();
|
fuse_handle.join();
|
||||||
}
|
}
|
||||||
self.clipboard.stop();
|
// we don't stop the clipboard, keep listening in case of restart
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
use std::{
|
use std::{collections::BTreeSet, path::PathBuf};
|
||||||
collections::BTreeSet,
|
|
||||||
path::PathBuf,
|
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use cacao::pasteboard::{Pasteboard, PasteboardName};
|
use cacao::pasteboard::{Pasteboard, PasteboardName};
|
||||||
use hbb_common::log;
|
use hbb_common::log;
|
||||||
@ -28,7 +24,6 @@ fn set_file_list(file_list: &[PathBuf]) -> Result<(), CliprdrError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct NsPasteboard {
|
pub struct NsPasteboard {
|
||||||
stopped: AtomicBool,
|
|
||||||
ignore_path: PathBuf,
|
ignore_path: PathBuf,
|
||||||
|
|
||||||
former_file_list: Mutex<Vec<PathBuf>>,
|
former_file_list: Mutex<Vec<PathBuf>>,
|
||||||
@ -37,16 +32,10 @@ pub struct NsPasteboard {
|
|||||||
impl NsPasteboard {
|
impl NsPasteboard {
|
||||||
pub fn new(ignore_path: &PathBuf) -> Result<Self, CliprdrError> {
|
pub fn new(ignore_path: &PathBuf) -> Result<Self, CliprdrError> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
stopped: AtomicBool::new(false),
|
|
||||||
ignore_path: ignore_path.to_owned(),
|
ignore_path: ignore_path.to_owned(),
|
||||||
former_file_list: Mutex::new(vec![]),
|
former_file_list: Mutex::new(vec![]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_stopped(&self) -> bool {
|
|
||||||
self.stopped.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SysClipboard for NsPasteboard {
|
impl SysClipboard for NsPasteboard {
|
||||||
@ -56,13 +45,11 @@ impl SysClipboard for NsPasteboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start(&self) {
|
fn start(&self) {
|
||||||
self.stopped.store(false, Ordering::Relaxed);
|
{
|
||||||
|
*self.former_file_list.lock() = vec![];
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if self.is_stopped() {
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let file_list = match wait_file_list() {
|
let file_list = match wait_file_list() {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => {
|
None => {
|
||||||
@ -104,10 +91,6 @@ impl SysClipboard for NsPasteboard {
|
|||||||
log::debug!("stop listening file related atoms on clipboard");
|
log::debug!("stop listening file related atoms on clipboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self) {
|
|
||||||
self.stopped.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_file_list(&self) -> Vec<PathBuf> {
|
fn get_file_list(&self) -> Vec<PathBuf> {
|
||||||
self.former_file_list.lock().clone()
|
self.former_file_list.lock().clone()
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
use std::{
|
use std::{collections::BTreeSet, path::PathBuf};
|
||||||
collections::BTreeSet,
|
|
||||||
path::PathBuf,
|
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use hbb_common::log;
|
use hbb_common::log;
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
@ -16,15 +12,11 @@ use super::{encode_path_to_uri, parse_plain_uri_list, SysClipboard};
|
|||||||
|
|
||||||
static X11_CLIPBOARD: OnceCell<Clipboard> = OnceCell::new();
|
static X11_CLIPBOARD: OnceCell<Clipboard> = OnceCell::new();
|
||||||
|
|
||||||
// this is tested on an Arch Linux with X11
|
|
||||||
const X11_CLIPBOARD_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(70);
|
|
||||||
|
|
||||||
fn get_clip() -> Result<&'static Clipboard, CliprdrError> {
|
fn get_clip() -> Result<&'static Clipboard, CliprdrError> {
|
||||||
X11_CLIPBOARD.get_or_try_init(|| Clipboard::new().map_err(|_| CliprdrError::CliprdrInit))
|
X11_CLIPBOARD.get_or_try_init(|| Clipboard::new().map_err(|_| CliprdrError::CliprdrInit))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct X11Clipboard {
|
pub struct X11Clipboard {
|
||||||
stop: AtomicBool,
|
|
||||||
ignore_path: PathBuf,
|
ignore_path: PathBuf,
|
||||||
text_uri_list: Atom,
|
text_uri_list: Atom,
|
||||||
gnome_copied_files: Atom,
|
gnome_copied_files: Atom,
|
||||||
@ -50,7 +42,6 @@ impl X11Clipboard {
|
|||||||
.map_err(|_| CliprdrError::CliprdrInit)?;
|
.map_err(|_| CliprdrError::CliprdrInit)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ignore_path: ignore_path.to_owned(),
|
ignore_path: ignore_path.to_owned(),
|
||||||
stop: AtomicBool::new(false),
|
|
||||||
text_uri_list,
|
text_uri_list,
|
||||||
gnome_copied_files,
|
gnome_copied_files,
|
||||||
nautilus_clipboard,
|
nautilus_clipboard,
|
||||||
@ -64,11 +55,18 @@ impl X11Clipboard {
|
|||||||
// NOTE:
|
// NOTE:
|
||||||
// # why not use `load_wait`
|
// # why not use `load_wait`
|
||||||
// load_wait is likely to wait forever, which is not what we want
|
// load_wait is likely to wait forever, which is not what we want
|
||||||
let res = get_clip()?.load(clip, target, prop, X11_CLIPBOARD_TIMEOUT);
|
let res = get_clip()?.load_wait(clip, target, prop);
|
||||||
match res {
|
match res {
|
||||||
Ok(res) => Ok(res),
|
Ok(res) => Ok(res),
|
||||||
Err(x11_clipboard::error::Error::UnexpectedType(_)) => Ok(vec![]),
|
Err(x11_clipboard::error::Error::UnexpectedType(_)) => Ok(vec![]),
|
||||||
Err(_) => Err(CliprdrError::ClipboardInternalError),
|
Err(x11_clipboard::error::Error::Timeout) => {
|
||||||
|
log::debug!("x11 clipboard get content timeout.");
|
||||||
|
Err(CliprdrError::ClipboardInternalError)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!("x11 clipboard get content fail: {:?}", e);
|
||||||
|
Err(CliprdrError::ClipboardInternalError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,22 +79,12 @@ impl X11Clipboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn wait_file_list(&self) -> Result<Option<Vec<PathBuf>>, CliprdrError> {
|
fn wait_file_list(&self) -> Result<Option<Vec<PathBuf>>, CliprdrError> {
|
||||||
if self.stop.load(Ordering::Relaxed) {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
let v = self.load(self.text_uri_list)?;
|
let v = self.load(self.text_uri_list)?;
|
||||||
let p = parse_plain_uri_list(v)?;
|
let p = parse_plain_uri_list(v)?;
|
||||||
Ok(Some(p))
|
Ok(Some(p))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11Clipboard {
|
|
||||||
#[inline]
|
|
||||||
fn is_stopped(&self) -> bool {
|
|
||||||
self.stop.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SysClipboard for X11Clipboard {
|
impl SysClipboard for X11Clipboard {
|
||||||
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> {
|
||||||
*self.former_file_list.lock() = paths.to_vec();
|
*self.former_file_list.lock() = paths.to_vec();
|
||||||
@ -114,19 +102,12 @@ impl SysClipboard for X11Clipboard {
|
|||||||
.map_err(|_| CliprdrError::ClipboardInternalError)
|
.map_err(|_| CliprdrError::ClipboardInternalError)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self) {
|
|
||||||
self.stop.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start(&self) {
|
fn start(&self) {
|
||||||
self.stop.store(false, Ordering::Relaxed);
|
{
|
||||||
|
// clear cached file list
|
||||||
|
*self.former_file_list.lock() = vec![];
|
||||||
|
}
|
||||||
loop {
|
loop {
|
||||||
if self.is_stopped() {
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sth = match self.wait_file_list() {
|
let sth = match self.wait_file_list() {
|
||||||
Ok(sth) => sth,
|
Ok(sth) => sth,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
from ast import parse
|
|
||||||
import os
|
import os
|
||||||
import optparse
|
import optparse
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
@ -47,7 +46,7 @@ def write_metadata(md5_table: dict, output_folder: str, exe: str):
|
|||||||
f.write((len(path)).to_bytes(length=length_count, byteorder='big'))
|
f.write((len(path)).to_bytes(length=length_count, byteorder='big'))
|
||||||
f.write(path)
|
f.write(path)
|
||||||
# data length & compressed data
|
# data length & compressed data
|
||||||
f.write((data_length).to_bytes(
|
f.write(data_length.to_bytes(
|
||||||
length=length_count, byteorder='big'))
|
length=length_count, byteorder='big'))
|
||||||
f.write(compressed_data)
|
f.write(compressed_data)
|
||||||
# md5 code
|
# md5 code
|
||||||
@ -65,6 +64,8 @@ def build_portable(output_folder: str):
|
|||||||
|
|
||||||
# Linux: python3 generate.py -f ../rustdesk-portable-packer/test -o . -e ./test/main.py
|
# Linux: python3 generate.py -f ../rustdesk-portable-packer/test -o . -e ./test/main.py
|
||||||
# Windows: python3 .\generate.py -f ..\rustdesk\flutter\build\windows\runner\Debug\ -o . -e ..\rustdesk\flutter\build\windows\runner\Debug\rustdesk.exe
|
# Windows: python3 .\generate.py -f ..\rustdesk\flutter\build\windows\runner\Debug\ -o . -e ..\rustdesk\flutter\build\windows\runner\Debug\rustdesk.exe
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = optparse.OptionParser()
|
parser = optparse.OptionParser()
|
||||||
parser.add_option("-f", "--folder", dest="folder",
|
parser.add_option("-f", "--folder", dest="folder",
|
||||||
|
102
res/lang.py
102
res/lang.py
@ -5,20 +5,22 @@ import glob
|
|||||||
import sys
|
import sys
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
|
|
||||||
def get_lang(lang):
|
def get_lang(lang):
|
||||||
out = {}
|
out = {}
|
||||||
for ln in open('./src/lang/%s.rs'%lang, encoding='utf8'):
|
for ln in open('./src/lang/%s.rs' % lang, encoding='utf8'):
|
||||||
ln = ln.strip()
|
ln = ln.strip()
|
||||||
if ln.startswith('("'):
|
if ln.startswith('("'):
|
||||||
k, v = line_split(ln)
|
k, v = line_split(ln)
|
||||||
out[k] = v
|
out[k] = v
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def line_split(line):
|
def line_split(line):
|
||||||
toks = line.split('", "')
|
toks = line.split('", "')
|
||||||
if len(toks) != 2:
|
if len(toks) != 2:
|
||||||
print(line)
|
print(line)
|
||||||
assert(0)
|
assert 0
|
||||||
# Replace fixed position.
|
# Replace fixed position.
|
||||||
# Because toks[1] may be v") or v"),
|
# Because toks[1] may be v") or v"),
|
||||||
k = toks[0][toks[0].find('"') + 1:]
|
k = toks[0][toks[0].find('"') + 1:]
|
||||||
@ -27,62 +29,62 @@ def line_split(line):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
expand()
|
expand()
|
||||||
elif sys.argv[1] == '1':
|
elif sys.argv[1] == '1':
|
||||||
to_csv()
|
to_csv()
|
||||||
else:
|
else:
|
||||||
to_rs(sys.argv[1])
|
to_rs(sys.argv[1])
|
||||||
|
|
||||||
|
|
||||||
def expand():
|
def expand():
|
||||||
for fn in glob.glob('./src/lang/*.rs'):
|
for fn in glob.glob('./src/lang/*.rs'):
|
||||||
lang = os.path.basename(fn)[:-3]
|
lang = os.path.basename(fn)[:-3]
|
||||||
if lang in ['en','template']: continue
|
if lang in ['en', 'template']: continue
|
||||||
print(lang)
|
print(lang)
|
||||||
dict = get_lang(lang)
|
dict = get_lang(lang)
|
||||||
fw = open("./src/lang/%s.rs"%lang, "wt", encoding='utf8')
|
fw = open("./src/lang/%s.rs" % lang, "wt", encoding='utf8')
|
||||||
for line in open('./src/lang/template.rs', encoding='utf8'):
|
for line in open('./src/lang/template.rs', encoding='utf8'):
|
||||||
line_strip = line.strip()
|
line_strip = line.strip()
|
||||||
if line_strip.startswith('("'):
|
if line_strip.startswith('("'):
|
||||||
k, v = line_split(line_strip)
|
k, v = line_split(line_strip)
|
||||||
if k in dict:
|
if k in dict:
|
||||||
# embraced with " to avoid empty v
|
# embraced with " to avoid empty v
|
||||||
line = line.replace('"%s"'%v, '"%s"'%dict[k])
|
line = line.replace('"%s"' % v, '"%s"' % dict[k])
|
||||||
else:
|
else:
|
||||||
line = line.replace(v, "")
|
line = line.replace(v, "")
|
||||||
fw.write(line)
|
fw.write(line)
|
||||||
else:
|
else:
|
||||||
fw.write(line)
|
fw.write(line)
|
||||||
fw.close()
|
fw.close()
|
||||||
|
|
||||||
|
|
||||||
def to_csv():
|
def to_csv():
|
||||||
for fn in glob.glob('./src/lang/*.rs'):
|
for fn in glob.glob('./src/lang/*.rs'):
|
||||||
lang = os.path.basename(fn)[:-3]
|
lang = os.path.basename(fn)[:-3]
|
||||||
csvfile = open('./src/lang/%s.csv'%lang, "wt", encoding='utf8')
|
csvfile = open('./src/lang/%s.csv' % lang, "wt", encoding='utf8')
|
||||||
csvwriter = csv.writer(csvfile)
|
csvwriter = csv.writer(csvfile)
|
||||||
for line in open(fn, encoding='utf8'):
|
for line in open(fn, encoding='utf8'):
|
||||||
line_strip = line.strip()
|
line_strip = line.strip()
|
||||||
if line_strip.startswith('("'):
|
if line_strip.startswith('("'):
|
||||||
k, v = line_split(line_strip)
|
k, v = line_split(line_strip)
|
||||||
csvwriter.writerow([k, v])
|
csvwriter.writerow([k, v])
|
||||||
csvfile.close()
|
csvfile.close()
|
||||||
|
|
||||||
|
|
||||||
def to_rs(lang):
|
def to_rs(lang):
|
||||||
csvfile = open('%s.csv'%lang, "rt", encoding='utf8')
|
csvfile = open('%s.csv' % lang, "rt", encoding='utf8')
|
||||||
fw = open("./src/lang/%s.rs"%lang, "wt", encoding='utf8')
|
fw = open("./src/lang/%s.rs" % lang, "wt", encoding='utf8')
|
||||||
fw.write('''lazy_static::lazy_static! {
|
fw.write('''lazy_static::lazy_static! {
|
||||||
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||||
[
|
[
|
||||||
''')
|
''')
|
||||||
for row in csv.reader(csvfile):
|
for row in csv.reader(csvfile):
|
||||||
fw.write(' ("%s", "%s"),\n'%(row[0].replace('"', '\"'), row[1].replace('"', '\"')))
|
fw.write(' ("%s", "%s"),\n' % (row[0].replace('"', '\"'), row[1].replace('"', '\"')))
|
||||||
fw.write(''' ].iter().cloned().collect();
|
fw.write(''' ].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
''')
|
''')
|
||||||
fw.close()
|
fw.close()
|
||||||
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
@ -317,6 +317,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
if stop {
|
if stop {
|
||||||
ContextSend::set_is_stopped();
|
ContextSend::set_is_stopped();
|
||||||
} else {
|
} else {
|
||||||
|
if let Err(e) = ContextSend::make_sure_enabled() {
|
||||||
|
log::error!("failed to restart clipboard context: {}", e);
|
||||||
|
};
|
||||||
log::debug!("Send system clipboard message to remote");
|
log::debug!("Send system clipboard message to remote");
|
||||||
let msg = crate::clipboard_file::clip_2_msg(clip);
|
let msg = crate::clipboard_file::clip_2_msg(clip);
|
||||||
allow_err!(peer.send(&msg).await);
|
allow_err!(peer.send(&msg).await);
|
||||||
@ -1704,7 +1707,13 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_clipboard_file_context(&self) {
|
fn check_clipboard_file_context(&self) {
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(
|
||||||
|
target_os = "windows",
|
||||||
|
all(
|
||||||
|
feature = "unix-file-copy-paste",
|
||||||
|
any(target_os = "linux", target_os = "macos")
|
||||||
|
)
|
||||||
|
))]
|
||||||
{
|
{
|
||||||
let enabled = *self.handler.server_file_transfer_enabled.read().unwrap()
|
let enabled = *self.handler.server_file_transfer_enabled.read().unwrap()
|
||||||
&& self.handler.lc.read().unwrap().enable_file_transfer.v;
|
&& self.handler.lc.read().unwrap().enable_file_transfer.v;
|
||||||
@ -1736,6 +1745,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
"Process clipboard message from server peer, stop: {}, is_stopping_allowed: {}, file_transfer_enabled: {}",
|
"Process clipboard message from server peer, stop: {}, is_stopping_allowed: {}, file_transfer_enabled: {}",
|
||||||
stop, is_stopping_allowed, file_transfer_enabled);
|
stop, is_stopping_allowed, file_transfer_enabled);
|
||||||
if !stop {
|
if !stop {
|
||||||
|
if let Err(e) = ContextSend::make_sure_enabled() {
|
||||||
|
log::error!("failed to restart clipboard context: {}", e);
|
||||||
|
};
|
||||||
let _ = ContextSend::proc(|context| -> ResultType<()> {
|
let _ = ContextSend::proc(|context| -> ResultType<()> {
|
||||||
context
|
context
|
||||||
.server_clip_file(self.client_conn_id, clip)
|
.server_clip_file(self.client_conn_id, clip)
|
||||||
|
@ -14,22 +14,22 @@ pub enum GrabState {
|
|||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
target_os = "android",
|
target_os = "android",
|
||||||
target_os = "ios",
|
target_os = "ios",
|
||||||
all(target_os = "linux", not(feature = "wayland"))
|
all(target_os = "linux", feature = "unix-file-copy-paste")
|
||||||
)))]
|
)))]
|
||||||
pub use arboard::Clipboard as ClipboardContext;
|
pub use arboard::Clipboard as ClipboardContext;
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))]
|
||||||
static X11_CLIPBOARD: once_cell::sync::OnceCell<x11_clipboard::Clipboard> =
|
static X11_CLIPBOARD: once_cell::sync::OnceCell<x11_clipboard::Clipboard> =
|
||||||
once_cell::sync::OnceCell::new();
|
once_cell::sync::OnceCell::new();
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))]
|
||||||
fn get_clipboard() -> Result<&'static x11_clipboard::Clipboard, String> {
|
fn get_clipboard() -> Result<&'static x11_clipboard::Clipboard, String> {
|
||||||
X11_CLIPBOARD
|
X11_CLIPBOARD
|
||||||
.get_or_try_init(|| x11_clipboard::Clipboard::new())
|
.get_or_try_init(|| x11_clipboard::Clipboard::new())
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))]
|
||||||
pub struct ClipboardContext {
|
pub struct ClipboardContext {
|
||||||
string_setter: x11rb::protocol::xproto::Atom,
|
string_setter: x11rb::protocol::xproto::Atom,
|
||||||
string_getter: x11rb::protocol::xproto::Atom,
|
string_getter: x11rb::protocol::xproto::Atom,
|
||||||
@ -39,7 +39,7 @@ pub struct ClipboardContext {
|
|||||||
prop: x11rb::protocol::xproto::Atom,
|
prop: x11rb::protocol::xproto::Atom,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))]
|
||||||
fn parse_plain_uri_list(v: Vec<u8>) -> Result<String, String> {
|
fn parse_plain_uri_list(v: Vec<u8>) -> Result<String, String> {
|
||||||
let text = String::from_utf8(v).map_err(|_| "ConversionFailure".to_owned())?;
|
let text = String::from_utf8(v).map_err(|_| "ConversionFailure".to_owned())?;
|
||||||
let mut list = String::new();
|
let mut list = String::new();
|
||||||
@ -56,7 +56,7 @@ fn parse_plain_uri_list(v: Vec<u8>) -> Result<String, String> {
|
|||||||
Ok(list)
|
Ok(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", not(feature = "wayland")))]
|
#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))]
|
||||||
impl ClipboardContext {
|
impl ClipboardContext {
|
||||||
pub fn new() -> Result<Self, String> {
|
pub fn new() -> Result<Self, String> {
|
||||||
let clipboard = get_clipboard()?;
|
let clipboard = get_clipboard()?;
|
||||||
@ -87,7 +87,7 @@ impl ClipboardContext {
|
|||||||
let clip = self.clip;
|
let clip = self.clip;
|
||||||
let prop = self.prop;
|
let prop = self.prop;
|
||||||
|
|
||||||
const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100);
|
const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(120);
|
||||||
|
|
||||||
let text_content = get_clipboard()?
|
let text_content = get_clipboard()?
|
||||||
.load(clip, self.string_getter, prop, TIMEOUT)
|
.load(clip, self.string_getter, prop, TIMEOUT)
|
||||||
|
@ -1725,6 +1725,17 @@ pub fn main_use_texture_render() -> SyncReturn<bool> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_has_file_clipboard() -> SyncReturn<bool> {
|
||||||
|
let ret = cfg!(any(
|
||||||
|
target_os = "windows",
|
||||||
|
all(
|
||||||
|
feature = "unix-file-copy-paste",
|
||||||
|
any(target_os = "linux", target_os = "macos")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
SyncReturn(ret)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cm_init() {
|
pub fn cm_init() {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
crate::flutter::connection_manager::cm_init();
|
crate::flutter::connection_manager::cm_init();
|
||||||
|
@ -570,7 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Big tiles", "Große Kacheln"),
|
("Big tiles", "Große Kacheln"),
|
||||||
("Small tiles", "Kleine Kacheln"),
|
("Small tiles", "Kleine Kacheln"),
|
||||||
("List", "Liste"),
|
("List", "Liste"),
|
||||||
("Virtual display", ""),
|
("Virtual display", "Virtueller Bildschirm"),
|
||||||
("Plug out all", ""),
|
("Plug out all", "Alle ausschalten"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -570,7 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Big tiles", "Icone grandi"),
|
("Big tiles", "Icone grandi"),
|
||||||
("Small tiles", "Icone piccole"),
|
("Small tiles", "Icone piccole"),
|
||||||
("List", "Elenco"),
|
("List", "Elenco"),
|
||||||
("Virtual display", ""),
|
("Virtual display", "Scehrmo virtuale"),
|
||||||
("Plug out all", ""),
|
("Plug out all", "Scollega tutto"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Chat", "Czat"),
|
("Chat", "Czat"),
|
||||||
("Total", "Łącznie"),
|
("Total", "Łącznie"),
|
||||||
("items", "elementów"),
|
("items", "elementów"),
|
||||||
("Selected", "Zaznaczonych"),
|
("Selected", "zaznaczonych"),
|
||||||
("Screen Capture", "Przechwytywanie ekranu"),
|
("Screen Capture", "Przechwytywanie ekranu"),
|
||||||
("Input Control", "Kontrola wejścia"),
|
("Input Control", "Kontrola wejścia"),
|
||||||
("Audio Capture", "Przechwytywanie dźwięku"),
|
("Audio Capture", "Przechwytywanie dźwięku"),
|
||||||
@ -564,13 +564,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("elevated_switch_display_msg", "Przełącz się na ekran główny, ponieważ wyświetlanie kilku ekranów nie jest obsługiwane przy podniesionych uprawnieniach."),
|
("elevated_switch_display_msg", "Przełącz się na ekran główny, ponieważ wyświetlanie kilku ekranów nie jest obsługiwane przy podniesionych uprawnieniach."),
|
||||||
("Open in new window", "Otwórz w nowym oknie"),
|
("Open in new window", "Otwórz w nowym oknie"),
|
||||||
("Show displays as individual windows", "Pokaż ekrany w osobnych oknach"),
|
("Show displays as individual windows", "Pokaż ekrany w osobnych oknach"),
|
||||||
("Use all my displays for the remote session", ""),
|
("Use all my displays for the remote session", "Użyj wszystkich moich ekranów do zdalnej sesji"),
|
||||||
("selinux_tip", ""),
|
("selinux_tip", "SELinux jest włączony na Twoim urządzeniu, co może przeszkodzić w uruchomieniu RustDesk po stronie kontrolowanej."),
|
||||||
("Change view", ""),
|
("Change view", "Zmień widok"),
|
||||||
("Big tiles", ""),
|
("Big tiles", "Duże kafelki"),
|
||||||
("Small tiles", ""),
|
("Small tiles", "Małe kafelki"),
|
||||||
("List", ""),
|
("List", "Lista"),
|
||||||
("Virtual display", ""),
|
("Virtual display", "Witualne ekrany"),
|
||||||
("Plug out all", ""),
|
("Plug out all", "Odłącz wszystko"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -570,7 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Big tiles", "Большие значки"),
|
("Big tiles", "Большие значки"),
|
||||||
("Small tiles", "Маленькие значки"),
|
("Small tiles", "Маленькие значки"),
|
||||||
("List", "Список"),
|
("List", "Список"),
|
||||||
("Virtual display", ""),
|
("Virtual display", "Виртуальный дисплей"),
|
||||||
("Plug out all", ""),
|
("Plug out all", "Отключить все"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
@ -1032,7 +1032,7 @@ impl Connection {
|
|||||||
pi.hostname = DEVICE_NAME.lock().unwrap().clone();
|
pi.hostname = DEVICE_NAME.lock().unwrap().clone();
|
||||||
pi.platform = "Android".into();
|
pi.platform = "Android".into();
|
||||||
}
|
}
|
||||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||||
let mut platform_additions = serde_json::Map::new();
|
let mut platform_additions = serde_json::Map::new();
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
@ -1062,7 +1062,18 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
#[cfg(any(
|
||||||
|
target_os = "windows",
|
||||||
|
all(
|
||||||
|
any(target_os = "linux", target_os = "macos"),
|
||||||
|
feature = "unix-file-copy-paste"
|
||||||
|
)
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
platform_additions.insert("has_file_clipboard".into(), json!(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
|
||||||
if !platform_additions.is_empty() {
|
if !platform_additions.is_empty() {
|
||||||
pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into());
|
pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into());
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ class Header: Reactor.Component {
|
|||||||
{!cursor_embedded && <li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li>}
|
{!cursor_embedded && <li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li>}
|
||||||
<li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li>
|
<li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li>
|
||||||
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
|
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
|
||||||
{((is_win && pi.platform == "Windows")||(is_linux && pi.platform == "Linux"))||(is_osx && pi.platform == "Mac OS") && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('Allow file copy and paste')}</li> : ""}
|
{(is_win && pi.platform == "Windows") && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('Allow file copy and paste')}</li> : ""}
|
||||||
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""}
|
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""}
|
||||||
{keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""}
|
{keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""}
|
||||||
{keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
|
{keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
|
||||||
|
@ -575,12 +575,13 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
log::debug!(
|
#[cfg(any(
|
||||||
"start_ipc enable context_send: {}",
|
target_os = "windows",
|
||||||
Config::get_option("enable-file-transfer").is_empty()
|
all(
|
||||||
);
|
any(target_os = "linux", target_os = "macos"),
|
||||||
|
feature = "unix-file-copy-paste"
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
),
|
||||||
|
))]
|
||||||
ContextSend::enable(Config::get_option("enable-file-transfer").is_empty());
|
ContextSend::enable(Config::get_option("enable-file-transfer").is_empty());
|
||||||
|
|
||||||
match ipc::new_listener("_cm").await {
|
match ipc::new_listener("_cm").await {
|
||||||
|
@ -1008,6 +1008,7 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
|||||||
#[cfg(not(feature = "flutter"))]
|
#[cfg(not(feature = "flutter"))]
|
||||||
let mut id = "".to_owned();
|
let mut id = "".to_owned();
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||||
|
#[allow(unused_mut, dead_code)]
|
||||||
let mut enable_file_transfer = "".to_owned();
|
let mut enable_file_transfer = "".to_owned();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -1030,7 +1031,13 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
|
|||||||
*OPTIONS.lock().unwrap() = v;
|
*OPTIONS.lock().unwrap() = v;
|
||||||
*OPTION_SYNCED.lock().unwrap() = true;
|
*OPTION_SYNCED.lock().unwrap() = true;
|
||||||
|
|
||||||
#[cfg(any(target_os="windows", target_os="linux", target_os = "macos"))]
|
#[cfg(any(
|
||||||
|
target_os = "windows",
|
||||||
|
all(
|
||||||
|
any(target_os="linux", target_os = "macos"),
|
||||||
|
feature = "unix-file-copy-paste"
|
||||||
|
)
|
||||||
|
))]
|
||||||
{
|
{
|
||||||
let b = OPTIONS.lock().unwrap().get("enable-file-transfer").map(|x| x.to_string()).unwrap_or_default();
|
let b = OPTIONS.lock().unwrap().get("enable-file-transfer").map(|x| x.to_string()).unwrap_or_default();
|
||||||
if b != enable_file_transfer {
|
if b != enable_file_transfer {
|
||||||
|
@ -1420,7 +1420,13 @@ impl<T: InvokeUiSession> Session<T> {
|
|||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
pub async fn io_loop<T: InvokeUiSession>(handler: Session<T>, round: u32) {
|
pub async fn io_loop<T: InvokeUiSession>(handler: Session<T>, round: u32) {
|
||||||
// It is ok to call this function multiple times.
|
// It is ok to call this function multiple times.
|
||||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
#[cfg(any(
|
||||||
|
target_os = "windows",
|
||||||
|
all(
|
||||||
|
any(target_os = "linux", target_os = "macos"),
|
||||||
|
feature = "unix-file-copy-paste"
|
||||||
|
)
|
||||||
|
))]
|
||||||
if !handler.is_file_transfer() && !handler.is_port_forward() {
|
if !handler.is_file_transfer() && !handler.is_port_forward() {
|
||||||
clipboard::ContextSend::enable(true);
|
clipboard::ContextSend::enable(true);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user