feat(sharing): Switch to arboard crate

This commit is contained in:
Henrik Friedrichsen
2024-03-04 11:58:23 +01:00
parent e22b711d9c
commit 159bfc4d9c
7 changed files with 226 additions and 225 deletions

View File

@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Instructions for installation with winget
### Changed
- Switch to `arboard` for clipboard access as it is still maintained
### Fixed
- Crash on Android (Termux) due to unknown user runtime directory

226
Cargo.lock generated
View File

@@ -167,6 +167,26 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "arboard"
version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2041f1943049c7978768d84e6d0fd95de98b76d6c4727b09e78ec253d29fa58"
dependencies = [
"clipboard-win",
"core-graphics",
"image",
"log",
"objc",
"objc-foundation",
"objc_id",
"parking_lot 0.12.1",
"thiserror",
"windows-sys 0.48.0",
"wl-clipboard-rs",
"x11rb",
]
[[package]]
name = "async-broadcast"
version = "0.5.1"
@@ -491,6 +511,12 @@ version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
[[package]]
name = "bytemuck"
version = "1.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f"
[[package]]
name = "byteorder"
version = "1.5.0"
@@ -621,26 +647,19 @@ dependencies = [
]
[[package]]
name = "clipboard"
version = "0.5.0"
name = "clipboard-win"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7"
checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297"
dependencies = [
"clipboard-win",
"objc",
"objc-foundation",
"objc_id",
"x11-clipboard",
"error-code",
]
[[package]]
name = "clipboard-win"
version = "2.2.0"
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b"
dependencies = [
"winapi",
]
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
@@ -711,6 +730,30 @@ version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "core-graphics"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"core-graphics-types",
"foreign-types 0.5.0",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
"libc",
]
[[package]]
name = "coreaudio-rs"
version = "0.10.0"
@@ -764,6 +807,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.12"
@@ -1167,6 +1219,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "error-code"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
[[package]]
name = "event-listener"
version = "2.5.3"
@@ -1241,6 +1299,15 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fdeflate"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
dependencies = [
"simd-adler32",
]
[[package]]
name = "fern"
version = "0.6.2"
@@ -1256,6 +1323,16 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -1268,7 +1345,28 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
"foreign-types-shared 0.1.1",
]
[[package]]
name = "foreign-types"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared 0.3.1",
]
[[package]]
name = "foreign-types-macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
]
[[package]]
@@ -1277,6 +1375,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
@@ -1413,6 +1517,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "getrandom"
version = "0.2.12"
@@ -1660,6 +1774,20 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "image"
version = "0.24.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-traits",
"png",
"tiff",
]
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -1750,6 +1878,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jpeg-decoder"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
[[package]]
name = "js-sys"
version = "0.3.68"
@@ -2103,6 +2237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
"simd-adler32",
]
[[package]]
@@ -2139,9 +2274,9 @@ dependencies = [
name = "ncspot"
version = "1.0.0"
dependencies = [
"arboard",
"chrono",
"clap",
"clipboard",
"crossbeam-channel",
"cursive",
"cursive_buffered_backend",
@@ -2173,7 +2308,6 @@ dependencies = [
"toml",
"unicode-width",
"url",
"wl-clipboard-rs",
"zbus 4.1.2",
]
@@ -2581,7 +2715,7 @@ checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [
"bitflags 2.4.2",
"cfg-if",
"foreign-types",
"foreign-types 0.3.2",
"libc",
"once_cell",
"openssl-macros",
@@ -2801,6 +2935,19 @@ dependencies = [
"dirs-next 1.0.2",
]
[[package]]
name = "png"
version = "0.17.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "polling"
version = "2.8.0"
@@ -3501,6 +3648,12 @@ dependencies = [
"libc",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "slab"
version = "0.4.9"
@@ -3742,6 +3895,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "tiff"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]]
name = "time"
version = "0.3.34"
@@ -4280,6 +4444,12 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "weezl"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
name = "winapi"
version = "0.3.9"
@@ -4529,23 +4699,21 @@ dependencies = [
]
[[package]]
name = "x11-clipboard"
version = "0.3.3"
name = "x11rb"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea"
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
dependencies = [
"xcb",
"gethostname",
"rustix 0.38.31",
"x11rb-protocol",
]
[[package]]
name = "xcb"
version = "0.8.2"
name = "x11rb-protocol"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de"
dependencies = [
"libc",
"log",
]
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
[[package]]
name = "xdg-home"

View File

@@ -38,7 +38,7 @@ codegen-units = 16
[dependencies]
chrono = "0.4"
clap = "4.5.1"
clipboard = {version = "0.5", optional = true}
arboard = {version = "3.3", optional = true}
crossbeam-channel = "0.5"
zbus = {version = "4.1.2", default-features = false, features = ["tokio"], optional = true}
fern = "0.6"
@@ -68,9 +68,6 @@ unicode-width = "0.1.9"
url = "2.5"
cursive_buffered_backend = "0.6.1"
[target.'cfg(target_os = "linux")'.dependencies]
wl-clipboard-rs = {version = "0.8", optional = true}
[target.'cfg(unix)'.dependencies]
signal-hook = "0.3.0"
@@ -102,8 +99,8 @@ pancurses_backend = ["cursive/pancurses-backend", "pancurses/win32"]
portaudio_backend = ["librespot-playback/portaudio-backend"]
pulseaudio_backend = ["librespot-playback/pulseaudio-backend"]
rodio_backend = ["librespot-playback/rodio-backend"]
share_clipboard = ["clipboard", "wl-clipboard-rs"] # Share a link to the system clipboard
share_selection = ["clipboard", "wl-clipboard-rs"] # Use the primary selection for sharing - linux and bsd only
share_clipboard = ["arboard", "arboard/wayland-data-control"] # Share a link to the system clipboard
share_selection = ["arboard", "arboard/wayland-data-control"] # Use the primary selection for sharing - linux and bsd only
termion_backend = ["cursive/termion-backend"]
[package.metadata.deb]

View File

@@ -1,194 +1,26 @@
#![cfg(feature = "share_clipboard")]
use std::env;
#[cfg(all(feature = "wl-clipboard-rs", target_os = "linux"))]
use {
std::io::Read,
wl_clipboard_rs::{
copy,
copy::{Options, Source},
paste,
paste::{get_contents, Error, Seat},
},
};
use arboard::Clipboard;
use std::error::Error;
#[cfg(feature = "share_selection")]
use clipboard::{x11_clipboard, x11_clipboard::X11ClipboardContext};
#[cfg(all(
feature = "share_selection",
all(feature = "wl-clipboard-rs", target_os = "linux")
))]
use wl_clipboard_rs::utils::{is_primary_selection_supported, PrimarySelectionCheckError};
use arboard::{GetExtLinux, LinuxClipboardKind, SetExtLinux};
#[cfg(not(feature = "share_selection"))]
use clipboard::ClipboardContext;
use clipboard::ClipboardProvider;
pub fn read_share() -> Result<String, Box<dyn Error>> {
let mut ctx = Clipboard::new()?;
fn is_wayland() -> bool {
fn session_type() -> String {
env::var("XDG_SESSION_TYPE").unwrap_or_default()
}
fn wl_display() -> String {
env::var("WAYLAND_DISPLAY").unwrap_or_default()
}
fn gdk_backend() -> String {
env::var("GDK_BACKEND").unwrap_or_default()
}
fn current_desktop() -> String {
env::var("XDG_CURRENT_DESKTOP").unwrap_or_default()
}
current_desktop() != "GNOME"
&& (session_type().as_str() == "wayland"
|| !wl_display().is_empty()
|| gdk_backend() == "wayland")
#[cfg(feature = "share_selection")]
return Ok(ctx.get().clipboard(LinuxClipboardKind::Primary).text()?);
#[cfg(not(feature = "share_selection"))]
return Ok(ctx.get_text()?);
}
#[cfg(not(feature = "share_selection"))]
pub fn read_share() -> Option<String> {
if is_wayland() {
#[allow(unused_mut, unused_assignments)]
let mut string = None;
#[cfg(all(feature = "wl-clipboard-rs", target_os = "linux"))]
{
//use wayland clipboard
let result = get_contents(
paste::ClipboardType::Regular,
Seat::Unspecified,
paste::MimeType::Text,
);
match result {
Ok((mut pipe, _)) => {
let mut contents = vec![];
pipe.read_to_end(&mut contents).ok();
string = Some(String::from_utf8_lossy(&contents).to_string())
}
Err(Error::NoSeats) | Err(Error::ClipboardEmpty) | Err(Error::NoMimeType) => {
//The clipboard is empty or doesn't contain text, nothing to worry about.
string = None
}
Err(err) => {
eprintln!("{err}");
string = None
}
}
}
string
} else {
//use x11 clipboard
ClipboardProvider::new()
.and_then(|mut ctx: ClipboardContext| ctx.get_contents())
.ok()
}
}
pub fn write_share(url: String) -> Result<(), Box<dyn Error>> {
let mut ctx = Clipboard::new()?;
#[cfg(feature = "share_selection")]
pub fn read_share() -> Option<String> {
if is_wayland() {
#[allow(unused_mut, unused_assignments)]
let mut string = None;
#[cfg(all(feature = "wl-clipboard-rs", target_os = "linux"))]
{
//use wayland clipboard
string = match is_primary_selection_supported() {
Ok(_supported) => {
let result = get_contents(
paste::ClipboardType::Primary,
Seat::Unspecified,
paste::MimeType::Text,
);
match result {
Ok((mut pipe, _)) => {
let mut contents = vec![];
pipe.read_to_end(&mut contents).ok();
Some(String::from_utf8_lossy(&contents).to_string())
}
Err(Error::NoSeats)
| Err(Error::ClipboardEmpty)
| Err(Error::NoMimeType) => {
//The clipboard is empty or doesn't contain text, nothing to worry about.
None
}
Err(err) => {
eprintln!("{}", err);
None
}
}
}
Err(PrimarySelectionCheckError::NoSeats) => {
// Impossible to give a definitive result. Primary selection may or may not be
// supported.
#[cfg(feature = "share_selection")]
return Ok(ctx.set().clipboard(LinuxClipboardKind::Primary).text(url)?);
// The required protocol (data-control version 2) is there, but there are no seats.
// Unfortunately, at least one seat is needed to check for the primary clipboard support.
None
}
Err(PrimarySelectionCheckError::MissingProtocol { .. }) => {
// The data-control protocol (required for wl-clipboard-rs operation) is not
// supported by the compositor.
None
}
Err(err) => {
eprintln!("{}", err);
None
}
}
}
string
} else {
//use x11 clipboard
ClipboardProvider::new()
.and_then(|mut ctx: X11ClipboardContext<x11_clipboard::Primary>| ctx.get_contents())
.ok()
}
}
#[cfg(not(feature = "share_selection"))]
pub fn write_share(url: String) -> Option<()> {
if is_wayland() {
#[allow(unused_mut, unused_assignments)]
let mut option = None;
#[cfg(all(feature = "wl-clipboard-rs", target_os = "linux"))]
{
//use wayland clipboard
let opts = Options::new();
option = opts
.copy(
Source::Bytes(url.into_bytes().into()),
copy::MimeType::Autodetect,
)
.ok()
}
option
} else {
//use x11 clipboard
ClipboardProvider::new()
.and_then(|mut ctx: ClipboardContext| ctx.set_contents(url))
.ok()
}
}
#[cfg(feature = "share_selection")]
pub fn write_share(url: String) -> Option<()> {
if is_wayland() {
#[allow(unused_mut, unused_assignments)]
let mut option = None;
#[cfg(all(feature = "wl-clipboard-rs", target_os = "linux"))]
{
//use wayland clipboard
let mut opts = Options::new();
opts.clipboard(copy::ClipboardType::Primary);
option = opts
.copy(
Source::Bytes(url.into_bytes().into()),
copy::MimeType::Autodetect,
)
.ok()
}
option
} else {
//use x11 clipboard
ClipboardProvider::new()
.and_then(|mut ctx: X11ClipboardContext<x11_clipboard::Primary>| ctx.set_contents(url))
.ok()
}
#[cfg(not(feature = "share_selection"))]
return Ok(ctx.set_text(url)?);
}

View File

@@ -305,7 +305,7 @@ impl ContextMenu {
}
#[cfg(feature = "share_clipboard")]
ContextMenuAction::ShareUrl(url) => {
write_share(url.to_string());
write_share(url.to_string()).ok();
}
ContextMenuAction::AddToPlaylist(track) => {
let dialog =

View File

@@ -247,7 +247,7 @@ impl ViewExt for CoverView {
.and_then(|t| t.as_listitem().share_url());
if let Some(url) = url {
crate::sharing::write_share(url);
crate::sharing::write_share(url).ok();
}
return Ok(CommandResult::Consumed(None));

View File

@@ -565,7 +565,7 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
};
if let Some(url) = url {
write_share(url);
write_share(url).ok();
}
return Ok(CommandResult::Consumed(None));
@@ -708,9 +708,9 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
let url = match source {
InsertSource::Input(url) => Some(url.clone()),
#[cfg(feature = "share_clipboard")]
InsertSource::Clipboard => {
read_share().and_then(crate::spotify_url::SpotifyUrl::from_url)
}
InsertSource::Clipboard => read_share()
.ok()
.and_then(crate::spotify_url::SpotifyUrl::from_url),
};
let spotify = self.queue.get_spotify();