Add support for xsel clipboard as well, dynamically select at runtime
Fixes timvisee/ffsend#76
This commit is contained in:
parent
c2dfd3e174
commit
982027f33c
5 changed files with 158 additions and 19 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -557,6 +557,7 @@ dependencies = [
|
|||
"pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"qr2term 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quale 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rpassword 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1246,6 +1247,14 @@ dependencies = [
|
|||
"checked_int_cast 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quale"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.5.2"
|
||||
|
@ -2280,6 +2289,7 @@ dependencies = [
|
|||
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
|
||||
"checksum qr2term 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "358175087ab058962f8f4457864ece0db9685ca5a8938385b5c9e279aa781349"
|
||||
"checksum qrcode 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3553d8614cd59cede2a00db69bc96ea72dfdd92c041600f06b100f6f2699b26"
|
||||
"checksum quale 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c46811c513cc64cceb23adc27892b3b3470c1790de117610221b97071de8bc8c"
|
||||
"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8"
|
||||
"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
|
||||
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -50,7 +50,17 @@ name = "ffsend"
|
|||
path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
default = ["archive", "clipboard", "history", "infer-command", "qrcode", "send2", "send3", "urlshorten"]
|
||||
default = [
|
||||
"archive",
|
||||
"clipboard",
|
||||
"history",
|
||||
"infer-command",
|
||||
"qrcode",
|
||||
"quale",
|
||||
"send2",
|
||||
"send3",
|
||||
"urlshorten"
|
||||
]
|
||||
|
||||
# Compile with file archiving support
|
||||
archive = ["tar"]
|
||||
|
@ -92,14 +102,15 @@ openssl-probe = "0.1"
|
|||
pbr = "1"
|
||||
prettytable-rs = "0.8"
|
||||
qr2term = { version = "0.1", optional = true }
|
||||
quale = { version = "1.0", optional = true }
|
||||
rpassword = "3.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
tar = { version = "0.4", optional = true }
|
||||
tempfile = "3"
|
||||
toml = "0.5"
|
||||
version-compare = "0.0.6"
|
||||
urlshortener = { version = "0.10", default-features = false, optional = true }
|
||||
version-compare = "0.0.6"
|
||||
|
||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||
clipboard = { version = "0.5", optional = true }
|
||||
|
|
|
@ -490,6 +490,13 @@ empty.
|
|||
| `FFSEND_QUIET` | `--quiet` | Log quiet information |
|
||||
| `FFSEND_VERBOSE` | `--verbose` | Log verbose information |
|
||||
|
||||
Some environment variables may be set at compile time to tweak some defaults.
|
||||
|
||||
| Variable | Description |
|
||||
| :----------- | :------------------------------------- |
|
||||
| `XCLIP_PATH` | Set fixed `xclip` binary path on Linux |
|
||||
| `XSEL_PATH` | Set fixed `xsel` binary path on Linux |
|
||||
|
||||
At this time, no configuration or _dotfile_ file support is available.
|
||||
This will be something added in a later release.
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ use prettytable::{format::FormatBuilder, Cell, Row, Table};
|
|||
use crate::client::to_duration;
|
||||
use crate::cmd::matcher::{debug::DebugMatcher, main::MainMatcher, Matcher};
|
||||
use crate::error::ActionError;
|
||||
#[cfg(all(feature = "clipboard", target_os = "linux"))]
|
||||
use crate::util::ClipboardType;
|
||||
use crate::util::{api_version_list, features_list, format_bool, format_duration};
|
||||
|
||||
/// A file debug action.
|
||||
|
@ -100,11 +102,7 @@ impl<'a> Debug<'a> {
|
|||
#[cfg(all(feature = "clipboard", target_os = "linux"))]
|
||||
table.add_row(Row::new(vec![
|
||||
Cell::new("Clipboard:"),
|
||||
Cell::new(
|
||||
&option_env!("XCLIP_PATH")
|
||||
.map(|path| format!("xclip ({})", path))
|
||||
.unwrap_or_else(|| "xclip".into()),
|
||||
),
|
||||
Cell::new(&format!("{}", ClipboardType::select())),
|
||||
]));
|
||||
|
||||
// Show whether quiet is used
|
||||
|
|
137
src/util.rs
137
src/util.rs
|
@ -4,12 +4,16 @@ extern crate colored;
|
|||
extern crate directories;
|
||||
extern crate fs2;
|
||||
extern crate open;
|
||||
#[cfg(all(feature = "clipboard", target_os = "linux"))]
|
||||
extern crate quale;
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::env::{self, current_exe, var_os};
|
||||
use std::ffi::OsStr;
|
||||
#[cfg(all(feature = "clipboard", target_os = "linux"))]
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Display};
|
||||
#[cfg(all(target_os = "linux", feature = "clipboard"))]
|
||||
#[cfg(all(feature = "clipboard", target_os = "linux"))]
|
||||
use std::io::ErrorKind as IoErrorKind;
|
||||
use std::io::{stderr, stdin, Error as IoError, Write};
|
||||
use std::path::Path;
|
||||
|
@ -284,11 +288,81 @@ pub fn open_path(path: &str) -> Result<ExitStatus, IoError> {
|
|||
open::that(path)
|
||||
}
|
||||
|
||||
/// Set the clipboard of the user to the given `content` string.
|
||||
/// Clipboard management enum.
|
||||
///
|
||||
/// Defines which method of setting the clipboard is used.
|
||||
/// Invoke `ClipboardType::select()` to select the best variant to use determined at runtime.
|
||||
///
|
||||
/// Usually, the `Native` variant is used. However, on Linux system a different variant will be
|
||||
/// selected which will call a system binary to set the clipboard. This must be done because the
|
||||
/// native clipboard interface only has a lifetime of the application. This means that the
|
||||
/// clipboard is instantly cleared as soon as this application quits, which is always immediately.
|
||||
/// This limitation is due to security reasons as defined by X11. The alternative binaries we set
|
||||
/// the clipboard with spawn a daemon in the background to keep the clipboad alive until it's
|
||||
/// flushed.
|
||||
#[cfg(feature = "clipboard")]
|
||||
pub fn set_clipboard(content: String) -> Result<(), ClipboardError> {
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum ClipboardType {
|
||||
/// Native operating system clipboard.
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
Native,
|
||||
|
||||
/// Manage clipboard through `xclip` on Linux.
|
||||
///
|
||||
/// May contain a binary path if specified at compile time through the `XCLIP_PATH` variable.
|
||||
#[cfg(target_os = "linux")]
|
||||
Xclip(Option<String>),
|
||||
|
||||
/// Manage clipboard through `xsel` on Linux.
|
||||
///
|
||||
/// May contain a binary path if specified at compile time through the `XSEL_PATH` variable.
|
||||
#[cfg(target_os = "linux")]
|
||||
Xsel(Option<String>),
|
||||
}
|
||||
|
||||
#[cfg(feature = "clipboard")]
|
||||
impl ClipboardType {
|
||||
/// Select the clipboard type to use, depending on the runtime system.
|
||||
pub fn select() -> Self {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
ClipboardType::Native
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Some(path) = option_env!("XCLIP_PATH") {
|
||||
ClipboardType::Xclip(Some(path.to_owned()))
|
||||
} else if let Some(path) = option_env!("XSEL_PATH") {
|
||||
ClipboardType::Xsel(Some(path.to_owned()))
|
||||
} else if quale::which("xclip").is_some() {
|
||||
ClipboardType::Xclip(None)
|
||||
} else if quale::which("xsel").is_some() {
|
||||
ClipboardType::Xsel(None)
|
||||
} else {
|
||||
// TODO: should we error here instead, as no clipboard binary was found?
|
||||
ClipboardType::Xclip(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set clipboar contents through the selected clipboard type.
|
||||
pub fn set(&self, content: String) -> Result<(), ClipboardError> {
|
||||
match self {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
ClipboardType::Native => Self::native_set(content),
|
||||
#[cfg(target_os = "linux")]
|
||||
ClipboardType::Xclip(path) => Self::xclip_set(path.clone(), content),
|
||||
#[cfg(target_os = "linux")]
|
||||
ClipboardType::Xsel(path) => Self::xsel_set(path.clone(), content),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the clipboard through a native interface.
|
||||
///
|
||||
/// This is used on non-Linux systems.
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn native_set(content: String) -> Result<(), ClipboardError> {
|
||||
ClipboardProvider::new()
|
||||
.and_then(|mut context: ClipboardContext| context.set_contents(content))
|
||||
.map_err(|err| format_err!("{}", err).compat())
|
||||
|
@ -296,14 +370,27 @@ pub fn set_clipboard(content: String) -> Result<(), ClipboardError> {
|
|||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// Open an xclip process
|
||||
let mut process = match Command::new(option_env!("XCLIP_PATH").unwrap_or("xclip"))
|
||||
.arg("-sel")
|
||||
.arg("clip")
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
fn xclip_set(path: Option<String>, content: String) -> Result<(), ClipboardError> {
|
||||
Self::sys_cmd_set(
|
||||
Command::new(path.unwrap_or_else(|| "xclip".into()))
|
||||
.arg("-sel")
|
||||
.arg("clip"),
|
||||
content,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn xsel_set(path: Option<String>, content: String) -> Result<(), ClipboardError> {
|
||||
Self::sys_cmd_set(
|
||||
Command::new(path.unwrap_or_else(|| "xsel".into())).arg("--clipboard"),
|
||||
content,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn sys_cmd_set(command: &mut Command, content: String) -> Result<(), ClipboardError> {
|
||||
// Spawn the command process for setting the clipboard
|
||||
let mut process = match command.stdin(Stdio::piped()).spawn() {
|
||||
Ok(process) => process,
|
||||
Err(err) => {
|
||||
return Err(match err.kind() {
|
||||
|
@ -331,6 +418,32 @@ pub fn set_clipboard(content: String) -> Result<(), ClipboardError> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "clipboard")]
|
||||
impl fmt::Display for ClipboardType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
ClipboardType::Native => write!(f, "native"),
|
||||
#[cfg(target_os = "linux")]
|
||||
ClipboardType::Xclip(path) => match path {
|
||||
None => write!(f, "xclip"),
|
||||
Some(path) => write!(f, "xclip ({})", path),
|
||||
},
|
||||
#[cfg(target_os = "linux")]
|
||||
ClipboardType::Xsel(path) => match path {
|
||||
None => write!(f, "xsel"),
|
||||
Some(path) => write!(f, "xsel ({})", path),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the clipboard of the user to the given `content` string.
|
||||
#[cfg(feature = "clipboard")]
|
||||
pub fn set_clipboard(content: String) -> Result<(), ClipboardError> {
|
||||
ClipboardType::select().set(content)
|
||||
}
|
||||
|
||||
#[cfg(feature = "clipboard")]
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ClipboardError {
|
||||
|
|
Loading…
Add table
Reference in a new issue