From 8f44787ba356b9f0efbc33bdae88ae306dffb15b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?VenusGirl=E2=9D=A4?=
Date: Sat, 7 Dec 2024 11:24:32 +0900
Subject: [PATCH 1/3] Update README-KR.md (#10217)
---
docs/README-KR.md | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/docs/README-KR.md b/docs/README-KR.md
index af3c665244f3..b0a8b973e2ad 100644
--- a/docs/README-KR.md
+++ b/docs/README-KR.md
@@ -9,12 +9,12 @@
README를 모국어로 번역하기 위한 당신의 도움의 필요합니다.
-Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
+채팅하기: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
-Rust로 작성되었고, 설정없이 바로 사용할 수 있는 원격 데스트탑 소프트웨어입니다. 자신의 데이터를 완전히 컨트롤할 수 있고, 보안의 염려도 없습니다. 우리의 rendezvous/relay 서버를 사용해도, [스스로 설정](https://rustdesk.com/server)하는 것도, [스스로 rendezvous/relay 서버를 작성할 수도 있습니다](https://github.com/rustdesk/rustdesk-server-demo).
+Rust로 작성되었고, 설정없이 바로 사용할 수 있는 원격 데스트탑 소프트웨어입니다. 자신의 데이터를 완전히 컨트롤할 수 있고, 보안의 염려도 없습니다. 우리의 rendezvous/relay 서버를 사용해도, [직접 설정](https://rustdesk.com/server)하거나 [직접 rendezvous/relay 서버를 작성할 수도 있습니다](https://github.com/rustdesk/rustdesk-server-demo).
![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
@@ -43,9 +43,9 @@ RustDesk는 모든 기여를 환영합니다. 기여하고자 한다면 [`docs/C
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Linux/MacOS: vcpkg install libvpx libyuv opus aom
-- run `cargo run`
+- 실행 `cargo run`
-## [Build](https://rustdesk.com/docs/en/dev/build/)
+## [빌드](https://rustdesk.com/docs/en/dev/build/)
## Linux에서 빌드 순서
@@ -67,7 +67,7 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire
```
-### Install vcpkg
+### vcpkg 설치
```sh
git clone https://github.com/microsoft/vcpkg
@@ -79,7 +79,7 @@ export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus aom
```
-### Fix libvpx (For Fedora)
+### libvpx 수정 (For Fedora용)
```sh
cd vcpkg/buildtrees/libvpx/src
@@ -92,7 +92,7 @@ cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
cd
```
-### Build
+### 빌드
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
@@ -107,7 +107,7 @@ VCPKG_ROOT=$HOME/vcpkg cargo run
## Docker에 빌드하는 방법
-레포지토리를 클론하고, Docker 컨테이너 구성하는 것으로 시작합니다.
+리포지토리를 클론하고, Docker 컨테이너 구성하는 것으로 시작합니다.
```sh
git clone https://github.com/rustdesk/rustdesk
@@ -115,13 +115,13 @@ cd rustdesk
docker build -t "rustdesk-builder" .
```
-이후, 애플리케이션을 빌드할 필요가 있을 때마다, 이하의 커맨드를 실행합니다.
+이후, 애플리케이션을 빌드할 필요가 있을 때마다, 아래의의 명령을 실행합니다.
```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
```
-첫 빌드에서는 의존관계가 캐시될 때까지 시간이 걸릴 수 있습니다만, 이후의 빌드때는 빨라집니다. 더불어 빌드 커맨드에 다른 인수를 지정할 필요가 있다면, 커맨드 끝에 있는 `` 에 지정할 수 있습니다. 예를 들어 최적화된 출시 버전을 빌드하고 싶다면 이렇게 상기한 커맨드 뒤에 `--release` 를 붙여 실행합니다. 성공했다면 실행파일은 시스템 타겟 폴더에 담겨지고, 다음 커맨드로 실행할 수 있습니다.
+첫 빌드에서는 의존관계가 캐시될 때까지 시간이 걸릴 수 있습니다만, 이후의 빌드때는 빨라집니다. 더불어 빌드 명령에 다른 인수를 지정할 필요가 있다면, 명령 끝에 있는 `` 에 지정할 수 있습니다. 예를 들어 최적화된 출시 버전을 빌드하고 싶다면 이렇게 상기한 명령 뒤에 `--release` 를 붙여 실행합니다. 성공했다면 실행파일은 시스템 타겟 폴더에 담겨지고, 다음 명령으로 실행할 수 있습니다.
```sh
target/debug/rustdesk
@@ -133,9 +133,9 @@ target/debug/rustdesk
target/release/rustdesk
```
-커맨드를 RustDesk 리포지토리 루트에서 실행한다는 것을 확인해주세요. 그렇게 하지 않으면 애플리케이션이 필요한 리소스를 발견하지 못 할 가능성이 있습니다. 또한 `install`, `run` 같은 cargo 서브커맨드는 호스트가 아니라 컨테이너 프로그램을 설치, 실행을 위함이므로 현재 이 방법은 지원하지 않다는 점을 유념해주시길 바랍니다.
+명령을 RustDesk 리포지토리 루트에서 실행한다는 것을 확인해주세요. 그렇게 하지 않으면 애플리케이션이 필요한 리소스를 발견하지 못 할 가능성이 있습니다. 또한 `install`, `run` 같은 cargo 하위 명령은 호스트가 아니라 컨테이너 프로그램을 설치, 실행을 위함이므로 현재 이 방법은 지원하지 않다는 점을 유념해주시길 바랍니다.
-## File Structure
+## 파일 구조
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: 비디오 코덱, 설정, tcp/udp 랩퍼, protobuf, 파일 전송을 위한 fs 함수, 그 외 유틸리티 함수
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: 화면 캡처
@@ -143,12 +143,12 @@ target/release/rustdesk
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: 오디오, 클립보드, 입력, 비디오 서비스 그리고 네트워크 연결
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: 피어 접속 시작
-- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: [rustdesk-server](https://github.com/rustdesk/rustdesk-server)와 통신해서 리모트 다이렉트(TCP hole punching) 혹은 relayed 접속
+- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: [rustdesk-server](https://github.com/rustdesk/rustdesk-server)와 통신해서 리모트 다이렉트 (TCP hole punching) 혹은 relayed 접속
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: 플랫폼 고유의 코드
-- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for mobile
-- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript for Flutter web client
+- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: 모바일용 Flutter 코드
+- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Flutter 웹 클라이언트용 자바스크립트
-## Snapshot
+## 스냅샷
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)
From 3c838e7a9278df9e3f6293ef6aada14322e3a3c9 Mon Sep 17 00:00:00 2001
From: fufesou <13586388+fufesou@users.noreply.github.com>
Date: Sat, 7 Dec 2024 15:12:15 +0800
Subject: [PATCH 2/3] fix: Android, try sync clipboard on connecting (#10218)
* fix: Android, try sync clipboard on connecting
Signed-off-by: fufesou
* Android, clipboard, more clear skip check
Signed-off-by: fufesou
* comments
Signed-off-by: fufesou
* comment todo: Android clipboard listener, callback twice
Signed-off-by: fufesou
* Android, clipboard, remove listner
Signed-off-by: fufesou
---------
Signed-off-by: fufesou
---
.../flutter_hbb/FloatingWindowService.kt | 2 +-
.../com/carriez/flutter_hbb/MainActivity.kt | 14 ++---
.../com/carriez/flutter_hbb/MainService.kt | 2 +
.../carriez/flutter_hbb/RdClipboardManager.kt | 53 +++++--------------
flutter/lib/mobile/pages/remote_page.dart | 13 +++++
flutter/lib/mobile/pages/server_page.dart | 2 -
flutter/lib/models/server_model.dart | 15 ------
libs/scrap/src/android/ffi.rs | 8 ---
src/flutter_ffi.rs | 16 ------
src/server/clipboard_service.rs | 13 -----
src/server/connection.rs | 10 +++-
src/ui_cm_interface.rs | 11 ----
12 files changed, 41 insertions(+), 118 deletions(-)
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt
index c5da81c7c4db..42a1add7bebf 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt
@@ -306,7 +306,7 @@ class FloatingWindowService : Service(), View.OnTouchListener {
popupMenu.menu.add(0, idShowRustDesk, 0, translate("Show RustDesk"))
// For host side, clipboard sync
val idSyncClipboard = 1
- val isClipboardListenerEnabled = MainActivity.rdClipboardManager?.isListening ?: false
+ val isClipboardListenerEnabled = MainActivity.rdClipboardManager?.isCaptureStarted ?: false
if (isClipboardListenerEnabled) {
popupMenu.menu.add(0, idSyncClipboard, 0, translate("Update client clipboard"))
}
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
index 333ea9f14bd2..5c54c18fb821 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
@@ -103,7 +103,6 @@ class MainActivity : FlutterActivity() {
mainService?.let {
unbindService(serviceConnection)
}
- rdClipboardManager?.rustEnableServiceClipboard(false)
super.onDestroy()
}
@@ -221,6 +220,10 @@ class MainActivity : FlutterActivity() {
result.success(true)
}
+ "try_sync_clipboard" -> {
+ rdClipboardManager?.syncClipboard(true)
+ result.success(true)
+ }
GET_START_ON_BOOT_OPT -> {
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
result.success(prefs.getBoolean(KEY_START_ON_BOOT_OPT, false))
@@ -402,13 +405,4 @@ class MainActivity : FlutterActivity() {
super.onStart()
stopService(Intent(this, FloatingWindowService::class.java))
}
-
- // For client side
- // When swithing from other app to this app, try to sync clipboard.
- override fun onWindowFocusChanged(hasFocus: Boolean) {
- super.onWindowFocusChanged(hasFocus)
- if (hasFocus) {
- rdClipboardManager?.syncClipboard(true)
- }
- }
}
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
index 4c40e3349ed8..e9ec0975d1be 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
@@ -433,6 +433,7 @@ class MainService : Service() {
checkMediaPermission()
_isStart = true
FFI.setFrameRawEnable("video",true)
+ MainActivity.rdClipboardManager?.setCaptureStarted(_isStart)
return true
}
@@ -441,6 +442,7 @@ class MainService : Service() {
Log.d(logTag, "Stop Capture")
FFI.setFrameRawEnable("video",false)
_isStart = false
+ MainActivity.rdClipboardManager?.setCaptureStarted(_isStart)
// release video
if (reuseVirtualDisplay) {
// The virtual display video projection can be paused by calling `setSurface(null)`.
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt
index 0e098cb0831b..8c9d85028402 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/RdClipboardManager.kt
@@ -36,19 +36,19 @@ class RdClipboardManager(private val clipboardManager: ClipboardManager) {
// though the `lastUpdatedClipData` will be set to null once.
private var lastUpdatedClipData: ClipData? = null
private var isClientEnabled = true;
- private var _isListening = false;
- val isListening: Boolean
- get() = _isListening
+ private var _isCaptureStarted = false;
- fun checkPrimaryClip(isClient: Boolean, isSync: Boolean) {
+ val isCaptureStarted: Boolean
+ get() = _isCaptureStarted
+
+ fun checkPrimaryClip(isClient: Boolean) {
val clipData = clipboardManager.primaryClip
if (clipData != null && clipData.itemCount > 0) {
// Only handle the first item in the clipboard for now.
val clip = clipData.getItemAt(0)
- val isHostSync = !isClient && isSync
- // Ignore the `isClipboardDataEqual()` check if it's a host sync operation.
- // Because it's a action manually triggered by the user.
- if (!isHostSync) {
+ // Ignore the `isClipboardDataEqual()` check if it's a host operation.
+ // Because it's an action manually triggered by the user.
+ if (isClient) {
if (lastUpdatedClipData != null && isClipboardDataEqual(clipData, lastUpdatedClipData!!)) {
Log.d(logTag, "Clipboard data is the same as last update, ignore")
return
@@ -95,13 +95,6 @@ class RdClipboardManager(private val clipboardManager: ClipboardManager) {
}
}
- private val clipboardListener = object : ClipboardManager.OnPrimaryClipChangedListener {
- override fun onPrimaryClipChanged() {
- Log.d(logTag, "onPrimaryClipChanged")
- checkPrimaryClip(true, false)
- }
- }
-
private fun isSupportedMimeType(mimeType: String): Boolean {
return supportedMimeTypes.contains(mimeType)
}
@@ -136,43 +129,23 @@ class RdClipboardManager(private val clipboardManager: ClipboardManager) {
return true
}
- @Keep
- fun rustEnableServiceClipboard(enable: Boolean) {
- Log.d(logTag, "rustEnableServiceClipboard: enable: $enable, _isListening: $_isListening")
- if (enable) {
- if (!_isListening) {
- clipboardManager.addPrimaryClipChangedListener(clipboardListener)
- _isListening = true
- }
- } else {
- if (_isListening) {
- clipboardManager.removePrimaryClipChangedListener(clipboardListener)
- _isListening = false
- lastUpdatedClipData = null
- }
- }
+ fun setCaptureStarted(started: Boolean) {
+ _isCaptureStarted = started
}
@Keep
fun rustEnableClientClipboard(enable: Boolean) {
Log.d(logTag, "rustEnableClientClipboard: enable: $enable")
isClientEnabled = enable
- if (enable) {
- lastUpdatedClipData = clipboardManager.primaryClip
- } else {
- lastUpdatedClipData = null
- }
+ lastUpdatedClipData = null
}
fun syncClipboard(isClient: Boolean) {
- Log.d(logTag, "syncClipboard: isClient: $isClient, isClientEnabled: $isClientEnabled, _isListening: $_isListening")
+ Log.d(logTag, "syncClipboard: isClient: $isClient, isClientEnabled: $isClientEnabled")
if (isClient && !isClientEnabled) {
return
}
- if (!isClient && !_isListening) {
- return
- }
- checkPrimaryClip(isClient, true)
+ checkPrimaryClip(isClient)
}
@Keep
diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart
index fa7c35bb64e2..003640e05e1a 100644
--- a/flutter/lib/mobile/pages/remote_page.dart
+++ b/flutter/lib/mobile/pages/remote_page.dart
@@ -147,6 +147,19 @@ class _RemotePageState extends State with WidgetsBindingObserver {
gFFI.chatModel.onVoiceCallClosed("End connetion");
}
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ if (state == AppLifecycleState.resumed) {
+ trySyncClipboard();
+ }
+ }
+
+ // For client side
+ // When swithing from other app to this app, try to sync clipboard.
+ void trySyncClipboard() {
+ gFFI.invokeMethod("try_sync_clipboard");
+ }
+
@override
void didChangeMetrics() {
// If the soft keyboard is visible and the canvas has been changed(panned or scaled)
diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart
index db91e998b6ea..e9382be9f9f4 100644
--- a/flutter/lib/mobile/pages/server_page.dart
+++ b/flutter/lib/mobile/pages/server_page.dart
@@ -597,8 +597,6 @@ class _PermissionCheckerState extends State {
style: const TextStyle(color: MyTheme.darkGray),
))
]),
- PermissionRow(translate("Enable clipboard"), serverModel.clipboardOk,
- serverModel.toggleClipboard),
]));
}
}
diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart
index 7754672c0d0e..1d800ef69678 100644
--- a/flutter/lib/models/server_model.dart
+++ b/flutter/lib/models/server_model.dart
@@ -30,7 +30,6 @@ class ServerModel with ChangeNotifier {
bool _inputOk = false;
bool _audioOk = false;
bool _fileOk = false;
- bool _clipboardOk = false;
bool _showElevation = false;
bool hideCm = false;
int _connectStatus = 0; // Rendezvous Server status
@@ -60,8 +59,6 @@ class ServerModel with ChangeNotifier {
bool get fileOk => _fileOk;
- bool get clipboardOk => _clipboardOk;
-
bool get showElevation => _showElevation;
int get connectStatus => _connectStatus;
@@ -212,10 +209,6 @@ class ServerModel with ChangeNotifier {
_fileOk = fileOption != 'N';
}
- // clipboard
- final clipOption = await bind.mainGetOption(key: kOptionEnableClipboard);
- _clipboardOk = clipOption != 'N';
-
notifyListeners();
}
@@ -322,14 +315,6 @@ class ServerModel with ChangeNotifier {
notifyListeners();
}
- toggleClipboard() async {
- _clipboardOk = !_clipboardOk;
- bind.mainSetOption(
- key: kOptionEnableClipboard,
- value: _clipboardOk ? defaultOptionYes : 'N');
- notifyListeners();
- }
-
toggleInput() async {
if (clients.isNotEmpty) {
await showClientsMayNotBeChangedAlert(parent.target);
diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs
index ffa38a965d73..7433e6b090f9 100644
--- a/libs/scrap/src/android/ffi.rs
+++ b/libs/scrap/src/android/ffi.rs
@@ -371,14 +371,6 @@ pub fn call_clipboard_manager_update_clipboard(data: &[u8]) -> JniResult<()> {
}
}
-pub fn call_clipboard_manager_enable_service_clipboard(enable: bool) -> JniResult<()> {
- _call_clipboard_manager(
- "rustEnableServiceClipboard",
- "(Z)V",
- &[JValue::Bool(jboolean::from(enable))],
- )
-}
-
pub fn call_clipboard_manager_enable_client_clipboard(enable: bool) -> JniResult<()> {
_call_clipboard_manager(
"rustEnableClientClipboard",
diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs
index 088149a7e38b..7e3f39c83224 100644
--- a/src/flutter_ffi.rs
+++ b/src/flutter_ffi.rs
@@ -831,17 +831,6 @@ pub fn main_show_option(_key: String) -> SyncReturn {
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) {
@@ -849,11 +838,6 @@ 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());
diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs
index 401bb49336b4..bfba41c92a03 100644
--- a/src/server/clipboard_service.rs
+++ b/src/server/clipboard_service.rs
@@ -8,8 +8,6 @@ use crate::ipc::{self, ClipboardFile, ClipboardNonFile, Data};
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},
@@ -224,24 +222,13 @@ impl Handler {
}
}
-#[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(())
}
diff --git a/src/server/connection.rs b/src/server/connection.rs
index e1c564ce6b7a..28f653fdec31 100644
--- a/src/server/connection.rs
+++ b/src/server/connection.rs
@@ -1372,8 +1372,14 @@ impl Connection {
if !self.follow_remote_window {
noperms.push(NAME_WINDOW_FOCUS);
}
- if !self.clipboard_enabled()
- || !self.peer_keyboard_enabled()
+ // Do not consider the clipboard and keyboard permissions on Android.
+ // Because syncing the clipboard on Android is manually triggered by the user in the floating ball.
+ #[cfg(target_os = "android")]
+ let keyboard_clip_noperm = self.disable_keyboard || self.disable_clipboard;
+ #[cfg(not(target_os = "android"))]
+ let keyboard_clip_noperm =
+ !self.clipboard_enabled() || !self.peer_keyboard_enabled();
+ if keyboard_clip_noperm
|| crate::get_builtin_option(keys::OPTION_ONE_WAY_CLIPBOARD_REDIRECTION) == "Y"
{
noperms.push(super::clipboard_service::NAME);
diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs
index 2ff5e086287d..d2d5b2c83354 100644
--- a/src/ui_cm_interface.rs
+++ b/src/ui_cm_interface.rs
@@ -312,17 +312,6 @@ 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 {
From 1c17fddf519543d142af47f12f81eac20096a135 Mon Sep 17 00:00:00 2001
From: fufesou <13586388+fufesou@users.noreply.github.com>
Date: Sat, 7 Dec 2024 22:34:54 +0800
Subject: [PATCH 3/3] fix: android clipboard permission (#10223)
* fix: android clipboard permission
Signed-off-by: fufesou
* refact: Android, clipboard, floating ball
Call rust to check if clipboard is enabled.
Signed-off-by: fufesou
---------
Signed-off-by: fufesou
---
.../flutter_hbb/FloatingWindowService.kt | 4 +--
flutter/android/app/src/main/kotlin/ffi.kt | 1 +
flutter/lib/mobile/pages/server_page.dart | 2 ++
flutter/lib/models/server_model.dart | 15 +++++++++
src/flutter_ffi.rs | 26 ++++++++++++---
src/ipc.rs | 2 --
src/server.rs | 2 ++
src/server/clipboard_service.rs | 12 +++++++
src/server/connection.rs | 32 ++++++++-----------
src/ui_cm_interface.rs | 20 ++++++------
10 files changed, 80 insertions(+), 36 deletions(-)
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt
index 42a1add7bebf..696d536c62c3 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt
@@ -306,8 +306,8 @@ class FloatingWindowService : Service(), View.OnTouchListener {
popupMenu.menu.add(0, idShowRustDesk, 0, translate("Show RustDesk"))
// For host side, clipboard sync
val idSyncClipboard = 1
- val isClipboardListenerEnabled = MainActivity.rdClipboardManager?.isCaptureStarted ?: false
- if (isClipboardListenerEnabled) {
+ val isServiceSyncEnabled = (MainActivity.rdClipboardManager?.isCaptureStarted ?: false) && FFI.isServiceClipboardEnabled()
+ if (isServiceSyncEnabled) {
popupMenu.menu.add(0, idSyncClipboard, 0, translate("Update client clipboard"))
}
val idStopService = 2
diff --git a/flutter/android/app/src/main/kotlin/ffi.kt b/flutter/android/app/src/main/kotlin/ffi.kt
index 69b395ac2e9d..9f0f0216b727 100644
--- a/flutter/android/app/src/main/kotlin/ffi.kt
+++ b/flutter/android/app/src/main/kotlin/ffi.kt
@@ -24,4 +24,5 @@ object FFI {
external fun setCodecInfo(info: String)
external fun getLocalOption(key: String): String
external fun onClipboardUpdate(clips: ByteBuffer)
+ external fun isServiceClipboardEnabled(): Boolean
}
diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart
index e9382be9f9f4..db91e998b6ea 100644
--- a/flutter/lib/mobile/pages/server_page.dart
+++ b/flutter/lib/mobile/pages/server_page.dart
@@ -597,6 +597,8 @@ class _PermissionCheckerState extends State {
style: const TextStyle(color: MyTheme.darkGray),
))
]),
+ PermissionRow(translate("Enable clipboard"), serverModel.clipboardOk,
+ serverModel.toggleClipboard),
]));
}
}
diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart
index 1d800ef69678..8775764619ee 100644
--- a/flutter/lib/models/server_model.dart
+++ b/flutter/lib/models/server_model.dart
@@ -30,6 +30,7 @@ class ServerModel with ChangeNotifier {
bool _inputOk = false;
bool _audioOk = false;
bool _fileOk = false;
+ bool _clipboardOk = false;
bool _showElevation = false;
bool hideCm = false;
int _connectStatus = 0; // Rendezvous Server status
@@ -59,6 +60,8 @@ class ServerModel with ChangeNotifier {
bool get fileOk => _fileOk;
+ bool get clipboardOk => _clipboardOk;
+
bool get showElevation => _showElevation;
int get connectStatus => _connectStatus;
@@ -209,6 +212,10 @@ class ServerModel with ChangeNotifier {
_fileOk = fileOption != 'N';
}
+ // clipboard
+ final clipOption = await bind.mainGetOption(key: kOptionEnableClipboard);
+ _clipboardOk = clipOption != 'N';
+
notifyListeners();
}
@@ -315,6 +322,14 @@ class ServerModel with ChangeNotifier {
notifyListeners();
}
+ toggleClipboard() async {
+ _clipboardOk = !clipboardOk;
+ bind.mainSetOption(
+ key: kOptionEnableClipboard,
+ value: clipboardOk ? defaultOptionYes : 'N');
+ notifyListeners();
+ }
+
toggleInput() async {
if (clients.isNotEmpty) {
await showClientsMayNotBeChangedAlert(parent.target);
diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs
index 7e3f39c83224..0bb17c9036dc 100644
--- a/src/flutter_ffi.rs
+++ b/src/flutter_ffi.rs
@@ -834,11 +834,19 @@ pub fn main_show_option(_key: String) -> SyncReturn {
pub fn main_set_option(key: String, value: String) {
#[cfg(target_os = "android")]
if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) {
- crate::ui_cm_interface::notify_input_control(config::option2bool(
- config::keys::OPTION_ENABLE_KEYBOARD,
- &value,
- ));
+ crate::ui_cm_interface::switch_permission_all(
+ "keyboard".to_owned(),
+ config::option2bool(&key, &value),
+ );
+ }
+ #[cfg(target_os = "android")]
+ if key.eq(config::keys::OPTION_ENABLE_CLIPBOARD) {
+ crate::ui_cm_interface::switch_permission_all(
+ "clipboard".to_owned(),
+ config::option2bool(&key, &value),
+ );
}
+
if key.eq("custom-rendezvous-server") {
set_option(key, value.clone());
#[cfg(target_os = "android")]
@@ -2332,7 +2340,7 @@ pub mod server_side {
use jni::{
errors::{Error as JniError, Result as JniResult},
objects::{JClass, JObject, JString},
- sys::jstring,
+ sys::{jboolean, jstring},
JNIEnv,
};
@@ -2405,4 +2413,12 @@ pub mod server_side {
};
return env.new_string(res).unwrap_or_default().into_raw();
}
+
+ #[no_mangle]
+ pub unsafe extern "system" fn Java_ffi_FFI_isServiceClipboardEnabled(
+ env: JNIEnv,
+ _class: JClass,
+ ) -> jboolean {
+ jboolean::from(crate::server::is_clipboard_service_ok())
+ }
}
diff --git a/src/ipc.rs b/src/ipc.rs
index e3bcfac9a49f..5126aaf4e408 100644
--- a/src/ipc.rs
+++ b/src/ipc.rs
@@ -217,8 +217,6 @@ pub enum Data {
MouseMoveTime(i64),
Authorize,
Close,
- #[cfg(target_os = "android")]
- InputControl(bool),
#[cfg(windows)]
SAS,
UserSid(Option),
diff --git a/src/server.rs b/src/server.rs
index ed2c9f2fd541..ba1682f3d0f5 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -34,6 +34,8 @@ pub mod audio_service;
cfg_if::cfg_if! {
if #[cfg(not(target_os = "ios"))] {
mod clipboard_service;
+#[cfg(target_os = "android")]
+pub use clipboard_service::is_clipboard_service_ok;
#[cfg(target_os = "linux")]
pub(crate) mod wayland;
#[cfg(target_os = "linux")]
diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs
index bfba41c92a03..8ae482500550 100644
--- a/src/server/clipboard_service.rs
+++ b/src/server/clipboard_service.rs
@@ -8,6 +8,8 @@ use crate::ipc::{self, ClipboardFile, ClipboardNonFile, Data};
use clipboard_master::{CallbackResult, ClipboardHandler};
#[cfg(target_os = "android")]
use hbb_common::config::{keys, option2bool};
+#[cfg(target_os = "android")]
+use std::sync::atomic::{AtomicBool, Ordering};
use std::{
io,
sync::mpsc::{channel, RecvTimeoutError, Sender},
@@ -16,6 +18,9 @@ use std::{
#[cfg(windows)]
use tokio::runtime::Runtime;
+#[cfg(target_os = "android")]
+static CLIPBOARD_SERVICE_OK: AtomicBool = AtomicBool::new(false);
+
#[cfg(not(target_os = "android"))]
struct Handler {
sp: EmptyExtraFieldService,
@@ -27,6 +32,11 @@ struct Handler {
rt: Option,
}
+#[cfg(target_os = "android")]
+pub fn is_clipboard_service_ok() -> bool {
+ CLIPBOARD_SERVICE_OK.load(Ordering::SeqCst)
+}
+
pub fn new() -> GenericService {
let svc = EmptyExtraFieldService::new(NAME.to_owned(), false);
GenericService::run(&svc.clone(), run);
@@ -224,11 +234,13 @@ impl Handler {
#[cfg(target_os = "android")]
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
+ CLIPBOARD_SERVICE_OK.store(sp.ok(), Ordering::SeqCst);
while sp.ok() {
if let Some(msg) = crate::clipboard::get_clipboards_msg(false) {
sp.send(msg);
}
std::thread::sleep(Duration::from_millis(INTERVAL));
}
+ CLIPBOARD_SERVICE_OK.store(false, Ordering::SeqCst);
Ok(())
}
diff --git a/src/server/connection.rs b/src/server/connection.rs
index 28f653fdec31..153740c28a3d 100644
--- a/src/server/connection.rs
+++ b/src/server/connection.rs
@@ -463,11 +463,6 @@ impl Connection {
conn.on_close("connection manager", true).await;
break;
}
- #[cfg(target_os = "android")]
- ipc::Data::InputControl(v) => {
- conn.keyboard = v;
- conn.send_permission(Permission::Keyboard, v).await;
- }
ipc::Data::CmErr(e) => {
if e != "expected" {
// cm closed before connection
@@ -492,6 +487,9 @@ impl Connection {
conn.keyboard = enabled;
conn.send_permission(Permission::Keyboard, enabled).await;
if let Some(s) = conn.server.upgrade() {
+ s.write().unwrap().subscribe(
+ super::clipboard_service::NAME,
+ conn.inner.clone(), conn.can_sub_clipboard_service());
s.write().unwrap().subscribe(
NAME_CURSOR,
conn.inner.clone(), enabled || conn.show_remote_cursor);
@@ -502,7 +500,7 @@ impl Connection {
if let Some(s) = conn.server.upgrade() {
s.write().unwrap().subscribe(
super::clipboard_service::NAME,
- conn.inner.clone(), conn.clipboard_enabled() && conn.peer_keyboard_enabled());
+ conn.inner.clone(), conn.can_sub_clipboard_service());
}
} else if &name == "audio" {
conn.audio = enabled;
@@ -1372,16 +1370,7 @@ impl Connection {
if !self.follow_remote_window {
noperms.push(NAME_WINDOW_FOCUS);
}
- // Do not consider the clipboard and keyboard permissions on Android.
- // Because syncing the clipboard on Android is manually triggered by the user in the floating ball.
- #[cfg(target_os = "android")]
- let keyboard_clip_noperm = self.disable_keyboard || self.disable_clipboard;
- #[cfg(not(target_os = "android"))]
- let keyboard_clip_noperm =
- !self.clipboard_enabled() || !self.peer_keyboard_enabled();
- if keyboard_clip_noperm
- || crate::get_builtin_option(keys::OPTION_ONE_WAY_CLIPBOARD_REDIRECTION) == "Y"
- {
+ if !self.can_sub_clipboard_service() {
noperms.push(super::clipboard_service::NAME);
}
if !self.audio_enabled() {
@@ -1453,6 +1442,13 @@ impl Connection {
self.clipboard && !self.disable_clipboard
}
+ #[inline]
+ fn can_sub_clipboard_service(&self) -> bool {
+ self.clipboard_enabled()
+ && self.peer_keyboard_enabled()
+ && crate::get_builtin_option(keys::OPTION_ONE_WAY_CLIPBOARD_REDIRECTION) != "Y"
+ }
+
fn audio_enabled(&self) -> bool {
self.audio && !self.disable_audio
}
@@ -2930,7 +2926,7 @@ impl Connection {
s.write().unwrap().subscribe(
super::clipboard_service::NAME,
self.inner.clone(),
- self.clipboard_enabled() && self.peer_keyboard_enabled(),
+ self.can_sub_clipboard_service(),
);
}
}
@@ -2942,7 +2938,7 @@ impl Connection {
s.write().unwrap().subscribe(
super::clipboard_service::NAME,
self.inner.clone(),
- self.clipboard_enabled() && self.peer_keyboard_enabled(),
+ self.can_sub_clipboard_service(),
);
s.write().unwrap().subscribe(
NAME_CURSOR,
diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs
index d2d5b2c83354..a3373f8ccd72 100644
--- a/src/ui_cm_interface.rs
+++ b/src/ui_cm_interface.rs
@@ -280,15 +280,6 @@ pub fn close(id: i32) {
};
}
-#[inline]
-#[cfg(target_os = "android")]
-pub fn notify_input_control(v: bool) {
- for (_, mut client) in CLIENTS.write().unwrap().iter_mut() {
- client.keyboard = v;
- allow_err!(client.tx.send(Data::InputControl(v)));
- }
-}
-
#[inline]
pub fn remove(id: i32) {
CLIENTS.write().unwrap().remove(&id);
@@ -312,6 +303,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 {