Fix clipboard for Linux, use xclip on this platform

This commit is contained in:
timvisee 2018-05-17 22:43:06 +02:00
parent d0cbf800f2
commit f11eabf868
No known key found for this signature in database
GPG key ID: 109CBA0BF74036C2
6 changed files with 79 additions and 16 deletions

1
Cargo.lock generated
View file

@ -358,7 +358,6 @@ dependencies = [
"derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"directories 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ffsend-api 0.0.1",
"fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -3,8 +3,6 @@ The first release used for gathering feedback on the application by selected
people.
Features:
- Allow unarchiving on download
- Use clipboard through `xclip` on Linux if available for persistence
- Write complete README
- Polish command outputs, make it consistent (format, color)
- Automated releases through CI

View file

@ -27,12 +27,10 @@ no-color = ["colored/no-color"]
[dependencies]
chrono = "0.4"
clap = "2.31"
clipboard = { version = "0.4", optional = true }
colored = "1.6"
derive_builder = "0.5"
directories = "0.10"
failure = "0.1"
failure_derive = "0.1"
ffsend-api = { version = "*", path = "../api" }
fs2 = "0.4"
lazy_static = "1.0"
@ -46,3 +44,6 @@ tar = { version = "0.4", optional = true }
tempfile = "3"
toml = "0.4"
version-compare = "0.0.6"
[target.'cfg(not(target_os = "linux"))'.dependencies]
clipboard = { version = "0.4", optional = true }

View file

@ -248,8 +248,10 @@ impl<'a> Upload<'a> {
// Copy the URL in the user's clipboard
#[cfg(feature = "clipboard")] {
if matcher_upload.copy() && set_clipboard(url.as_str().to_owned()).is_err() {
print_error_msg("failed to copy the URL to the clipboard");
if matcher_upload.copy() {
if let Err(err) = set_clipboard(url.as_str().to_owned()) {
print_error(err.context("failed to copy the URL to the clipboard, ignoring"));
}
}
}

View file

@ -3,9 +3,8 @@ extern crate chrono;
extern crate clap;
#[macro_use]
extern crate derive_builder;
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate failure;
extern crate ffsend_api;
#[cfg(feature = "history")]
#[macro_use]

View file

@ -1,4 +1,4 @@
#[cfg(feature = "clipboard")]
#[cfg(all(feature = "clipboard", not(target_os = "linux")))]
extern crate clipboard;
extern crate colored;
extern crate directories;
@ -7,8 +7,6 @@ extern crate open;
use std::borrow::Borrow;
use std::env::{current_exe, var_os};
#[cfg(feature = "clipboard")]
use std::error::Error as StdError;
use std::ffi::OsStr;
use std::fmt::{Debug, Display};
use std::io::{
@ -17,16 +15,22 @@ use std::io::{
stderr,
Write,
};
#[cfg(feature = "clipboard")]
use std::io::ErrorKind as IoErrorKind;
use std::path::Path;
#[cfg(feature = "history")]
use std::path::PathBuf;
use std::process::{exit, ExitStatus};
#[cfg(all(feature = "clipboard", target_os = "linux"))]
use std::process::{Command, Stdio};
use chrono::Duration;
use failure::{err_msg, Fail};
#[cfg(all(feature = "clipboard", not(target_os = "linux")))]
use failure::{Compat, Error};
use ffsend_api::url::Url;
use rpassword::prompt_password_stderr;
#[cfg(feature = "clipboard")]
#[cfg(all(feature = "clipboard", not(target_os = "linux")))]
use self::clipboard::{ClipboardContext, ClipboardProvider};
use self::colored::*;
#[cfg(feature = "history")]
@ -262,9 +266,69 @@ pub fn open_path(path: &str) -> Result<ExitStatus, IoError> {
/// Set the clipboard of the user to the given `content` string.
#[cfg(feature = "clipboard")]
pub fn set_clipboard(content: String) -> Result<(), Box<StdError>> {
let mut context: ClipboardContext = ClipboardProvider::new()?;
context.set_contents(content)
pub fn set_clipboard(content: String) -> Result<(), ClipboardError> {
#[cfg(not(target_os = "linux"))] {
ClipboardProvider::new()
.and_then(|mut context: ClipboardContext| context.set_contents(content))
.map_err(|err| format_err!("{}", err).compat())
.map_err(ClipboardError::Generic)
}
#[cfg(target_os = "linux")] {
// Open an xclip process
let mut process = match Command::new("xclip")
.arg("-sel")
.arg("clip")
.stdin(Stdio::piped())
.spawn()
{
Ok(process) => process,
Err(err) => return Err(match err.kind() {
IoErrorKind::NotFound => ClipboardError::NoXclip,
_ => ClipboardError::Xclip(err),
}),
};
// Write the contents to the xclip process
process.stdin.as_mut().unwrap()
.write_all(content.as_bytes())
.map_err(ClipboardError::Xclip)?;
// Wait for xclip to exit
let status = process.wait()
.map_err(ClipboardError::Xclip)?;
if !status.success() {
return Err(ClipboardError::XclipStatus(status.code().unwrap_or(0)));
}
Ok(())
}
}
#[cfg(feature = "clipboard")]
#[derive(Debug, Fail)]
pub enum ClipboardError {
/// A generic error occurred while setting the clipboard contents
#[cfg(not(target_os = "linux"))]
#[fail(display = "failed to access clipboard")]
Generic(#[cause] Compat<Error>),
/// Xclip is not installed on the system, which is required for clipboard support.
#[cfg(target_os = "linux")]
#[fail(display = "failed to access clipboard, xclip is not installed")]
NoXclip,
/// An error occurred while using xclip to set the clipboard contents.
/// This problem probably occurred when stargin the xclip process, or while piping the
/// clipboard contents to the process.
#[cfg(target_os = "linux")]
#[fail(display = "failed to access clipboard using xclip")]
Xclip(#[cause] IoError),
/// Xclip unexpectetly exited with a non-successful status code.
#[cfg(target_os = "linux")]
#[fail(display = "failed to use clipboard, xclip exited with status code {}", _0)]
XclipStatus(i32),
}
/// Check for an emtpy password in the given `password`.