diff --git a/.github/workflows/linux-test-build.yml b/.github/workflows/linux-test-build.yml new file mode 100644 index 000000000..7e7c407e8 --- /dev/null +++ b/.github/workflows/linux-test-build.yml @@ -0,0 +1,177 @@ +name: Linux Test Build + +on: + workflow_dispatch: + push: + branches: + - fix/wayland-ext-data-control + +env: + RUST_VERSION: "1.75" + FLUTTER_VERSION: "3.24.5" + VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" + +jobs: + generate-bridge: + uses: ./.github/workflows/bridge.yml + + build-linux-x86_64: + needs: [generate-bridge] + runs-on: ubuntu-22.04 + steps: + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@v6 + with: + script: | + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + + - name: Free Disk Space + uses: jlumbroso/free-disk-space@v1.3.1 + with: + tool-cache: false + android: true + dotnet: true + haskell: true + large-packages: false + docker-images: true + swap-storage: false + + - name: Checkout source code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Restore bridge files + uses: actions/download-artifact@v4 + with: + name: bridge-artifact + path: ./ + + - name: Install prerequisites + shell: bash + run: | + sudo apt-get update -y + sudo apt-get install -y \ + clang \ + cmake \ + curl \ + gcc \ + git \ + g++ \ + libayatana-appindicator3-dev \ + libasound2-dev \ + libclang-dev \ + libgstreamer1.0-dev \ + libgstreamer-plugins-base1.0-dev \ + libgtk-3-dev \ + libpam0g-dev \ + libpulse-dev \ + libssl-dev \ + libunwind-dev \ + libva-dev \ + libvdpau-dev \ + libxcb-randr0-dev \ + libxcb-shape0-dev \ + libxcb-xfixes0-dev \ + libxdo-dev \ + libxfixes-dev \ + nasm \ + ninja-build \ + pkg-config \ + python3 \ + rpm \ + unzip \ + wget \ + xz-utils + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: ${{ env.RUST_VERSION }} + targets: x86_64-unknown-linux-gnu + components: "rustfmt" + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: linux-test-ubuntu-22.04 + + - name: Disable rust bridge build + shell: bash + run: | + sed -i 's/\["cdylib", "staticlib", "rlib"\]/\["cdylib"\]/g' Cargo.toml + + - name: Setup vcpkg with GitHub Actions cache + uses: lukka/run-vcpkg@v11 + with: + vcpkgDirectory: /opt/artifacts/vcpkg + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + doNotCache: false + + - name: Install vcpkg dependencies + shell: bash + run: | + if ! $VCPKG_ROOT/vcpkg \ + install \ + --triplet x64-linux \ + --x-install-root="$VCPKG_ROOT/installed"; then + find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do + echo "$_1:" + echo "======" + cat "$_1" + echo "======" + echo "" + done + exit 1 + fi + head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-x64-linux-rel-out.log" || true + + - name: Install flutter + uses: subosito/flutter-action@v2.12.0 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + cache: true + + - name: Patch flutter + shell: bash + run: | + FLUTTER_DIR="$(dirname "$(dirname "$(which flutter)")")" + PATCH_FILE="${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff" + cp "$PATCH_FILE" "$FLUTTER_DIR" + pushd "$FLUTTER_DIR" + if git apply --check flutter_3.24.4_dropdown_menu_enableFilter.diff; then + git apply flutter_3.24.4_dropdown_menu_enableFilter.diff + else + echo "Skipping outdated flutter patch for Flutter ${{ env.FLUTTER_VERSION }}" + fi + popd + + - name: Build rustdesk core + shell: bash + run: | + cargo build --lib --features hwcodec,flutter,unix-file-copy-paste --release + + - name: Build linux bundle and package + shell: bash + run: | + export CARGO_INCREMENTAL=0 + export DEB_ARCH=amd64 + python3 ./build.py --flutter --skip-cargo + tar -C flutter/build/linux/x64/release -czf rustdesk-linux-x86_64-bundle.tar.gz bundle + deb_file=$(ls rustdesk-*.deb | head -n1) + cp "$deb_file" rustdesk-linux-x86_64.deb + ls -lh rustdesk-linux-x86_64-bundle.tar.gz rustdesk-linux-x86_64.deb + + - name: Upload linux bundle + uses: actions/upload-artifact@v4 + with: + name: rustdesk-linux-x86_64-bundle + path: rustdesk-linux-x86_64-bundle.tar.gz + + - name: Upload deb package + uses: actions/upload-artifact@v4 + with: + name: rustdesk-linux-x86_64-deb + path: rustdesk-linux-x86_64.deb diff --git a/Cargo.lock b/Cargo.lock index febfd6b17..e64631168 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1324,7 +1324,7 @@ dependencies = [ [[package]] name = "clipboard-master" version = "4.0.0-beta.6" -source = "git+https://github.com/rustdesk-org/clipboard-master#ddc39f00a6211959489ae683aa6ae6eedf03a809" +source = "git+https://github.com/night-hood/clipboard-master?branch=fix/ext-data-control-v1#c2e9f129cfd9179ace1a340d193aaa9556c2ed71" dependencies = [ "objc", "objc-foundation", @@ -2282,7 +2282,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -2694,7 +2694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -6673,7 +6673,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -7457,7 +7457,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -7514,7 +7514,7 @@ dependencies = [ "security-framework 3.5.1", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -9733,9 +9733,9 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd993de54a40a40fbe5601d9f1fbcaef0aebcc5fda447d7dc8f6dcbaae4f8953" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ "bitflags 2.9.1", "wayland-backend", @@ -10408,15 +10408,6 @@ dependencies = [ "windows-targets 0.53.5", ] -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-targets" version = "0.42.2" @@ -10838,16 +10829,15 @@ dependencies = [ [[package]] name = "wl-clipboard-rs" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de22eebb1d1e2bad2d970086e96da0e12cde0b411321e5b0f7b2a1f876aa26f" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" dependencies = [ "libc", "log", "os_pipe", - "rustix 0.38.34", - "tempfile", - "thiserror 1.0.61", + "rustix 1.1.2", + "thiserror 2.0.17", "tree_magic_mini", "wayland-backend", "wayland-client", diff --git a/Cargo.toml b/Cargo.toml index 3961e9d0b..ed90144fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,7 +98,7 @@ clipboard = { path = "libs/clipboard" } ctrlc = "3.2" # arboard = { version = "3.4", features = ["wayland-data-control"] } arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] } -clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" } +clipboard-master = { git = "https://github.com/night-hood/clipboard-master", branch = "fix/ext-data-control-v1" } portable-pty = { git = "https://github.com/rustdesk-org/wezterm", branch = "rustdesk/pty_based_0.8.1", package = "portable-pty" } system_shutdown = "4.0" diff --git a/src/client.rs b/src/client.rs index 72652776a..0dcb03dac 100644 --- a/src/client.rs +++ b/src/client.rs @@ -986,6 +986,7 @@ impl Client { std::thread::spawn(move || { let mut handler = ClientClipboardHandler { ctx: None, + last_sent_clipboard_sig: None, #[cfg(not(feature = "flutter"))] client_clip_ctx: _client_clip_ctx, }; @@ -1007,7 +1008,12 @@ impl Client { log::error!("Clipboard listener stopped with error: {}", err); break; } - Err(RecvTimeoutError::Timeout) => {} + Err(RecvTimeoutError::Timeout) => { + #[cfg(target_os = "linux")] + if !crate::platform::linux::is_x11() { + handler.check_clipboard(); + } + } Err(RecvTimeoutError::Disconnected) => { log::error!("Clipboard listener disconnected"); break; @@ -1070,12 +1076,25 @@ impl ClipboardState { #[cfg(not(any(target_os = "android", target_os = "ios")))] struct ClientClipboardHandler { ctx: Option, + last_sent_clipboard_sig: Option>, #[cfg(not(feature = "flutter"))] client_clip_ctx: Option, } #[cfg(not(any(target_os = "android", target_os = "ios")))] impl ClientClipboardHandler { + fn should_send_msg(&mut self, msg: &Message) -> bool { + use hbb_common::protobuf::Message as _; + let Ok(sig) = msg.write_to_bytes() else { + return true; + }; + if self.last_sent_clipboard_sig.as_ref() == Some(&sig) { + return false; + } + self.last_sent_clipboard_sig = Some(sig); + true + } + fn is_text_required(&self) -> bool { #[cfg(feature = "flutter")] { @@ -1132,7 +1151,7 @@ impl ClientClipboardHandler { } if let Some(msg) = check_clipboard(&mut self.ctx, ClipboardSide::Client, false) { - if self.is_text_required() { + if self.is_text_required() && self.should_send_msg(&msg) { self.send_msg(msg, false); } } diff --git a/src/server/clipboard_service.rs b/src/server/clipboard_service.rs index 1d2f0a3fb..e9168d106 100644 --- a/src/server/clipboard_service.rs +++ b/src/server/clipboard_service.rs @@ -26,6 +26,8 @@ use std::{ }; #[cfg(windows)] use tokio::runtime::Runtime; +#[cfg(not(target_os = "android"))] +use hbb_common::protobuf::Message as _; #[cfg(target_os = "android")] static CLIPBOARD_SERVICE_OK: AtomicBool = AtomicBool::new(false); @@ -33,6 +35,7 @@ static CLIPBOARD_SERVICE_OK: AtomicBool = AtomicBool::new(false); #[cfg(not(target_os = "android"))] struct Handler { ctx: Option, + last_clipboard_sig: Option>, #[cfg(target_os = "windows")] stream: Option>, #[cfg(target_os = "windows")] @@ -71,6 +74,7 @@ fn run(sp: EmptyExtraFieldService) -> ResultType<()> { clipboard_listener::subscribe(sp.name(), tx_cb_result)?; let mut handler = Handler { ctx, + last_clipboard_sig: None, #[cfg(target_os = "windows")] stream: None, #[cfg(target_os = "windows")] @@ -86,7 +90,9 @@ fn run(sp: EmptyExtraFieldService) -> ResultType<()> { continue; } if let Some(msg) = handler.get_clipboard_msg() { - sp.send(msg); + if handler.should_send_clipboard_msg(&msg) { + sp.send(msg); + } } } Ok(CallbackResult::Stop) => { @@ -96,7 +102,16 @@ fn run(sp: EmptyExtraFieldService) -> ResultType<()> { Ok(CallbackResult::StopWithError(err)) => { bail!("Clipboard listener stopped with error: {}", err); } - Err(RecvTimeoutError::Timeout) => {} + Err(RecvTimeoutError::Timeout) => { + #[cfg(target_os = "linux")] + if !crate::platform::linux::is_x11() && sp.name() == NAME { + if let Some(msg) = handler.get_clipboard_msg() { + if handler.should_send_clipboard_msg(&msg) { + sp.send(msg); + } + } + } + } Err(RecvTimeoutError::Disconnected) => { log::error!("Clipboard listener disconnected"); break; @@ -111,6 +126,17 @@ fn run(sp: EmptyExtraFieldService) -> ResultType<()> { #[cfg(not(target_os = "android"))] impl Handler { + fn should_send_clipboard_msg(&mut self, msg: &Message) -> bool { + let Ok(sig) = msg.write_to_bytes() else { + return true; + }; + if self.last_clipboard_sig.as_ref() == Some(&sig) { + return false; + } + self.last_clipboard_sig = Some(sig); + true + } + #[cfg(feature = "unix-file-copy-paste")] fn check_clipboard_file(&mut self) { if let Some(urls) = check_clipboard_files(&mut self.ctx, ClipboardSide::Host, false) {