aboutsummaryrefslogtreecommitdiff
path: root/copypasta/src/x11.rs
blob: 45fa48250d54f77be2049bbb6cfeaadfa210f657 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! X11 Clipboard implementation
//!
//! Note that the x11 implementation is really crap right now - we just depend
//! on xclip being on the user's path. If x11 pasting doesn't work, it's
//! probably because xclip is unavailable. There's currently no non-GPL x11
//! clipboard library for Rust. Until then, we have this hack.
//!
//! FIXME: Implement actual X11 clipboard API using the ICCCM reference
//!        https://tronche.com/gui/x/icccm/
use std::io;
use std::process::{Output, Command};
use std::string::FromUtf8Error;

use super::Load;

/// The x11 clipboard
pub struct Clipboard;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Xclip(String),
    Utf8(FromUtf8Error),
}

impl ::std::error::Error for Error {
    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            Error::Io(ref err) => Some(err),
            Error::Utf8(ref err) => Some(err),
            _ => None,
        }
    }

    fn description(&self) -> &str {
        match *self {
            Error::Io(..) => "error calling xclip",
            Error::Xclip(..) => "error reported by xclip",
            Error::Utf8(..) => "clipboard contents not utf8",
        }
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Error::Io(ref err) => write!(f, "error calling xclip: {}", err),
            Error::Xclip(ref s) => write!(f, "error from xclip: {}", s),
            Error::Utf8(ref err) => write!(f, "error parsing xclip output: {}", err),
        }
    }
}

impl From<io::Error> for Error {
    fn from(val: io::Error) -> Error {
        Error::Io(val)
    }
}

impl From<FromUtf8Error> for Error {
    fn from(val: FromUtf8Error) -> Error {
        Error::Utf8(val)
    }
}

impl Load for Clipboard {
    type Err = Error;

    fn new() -> Result<Self, Error> {
        Ok(Clipboard)
    }

    fn load_primary(&self) -> Result<String, Self::Err> {
        let output = try!(Command::new("xclip")
            .args(&["-o", "-selection", "clipboard"])
            .output());

        Clipboard::process_xclip_output(output)
    }

    fn load_selection(&self) -> Result<String, Self::Err> {
        let output = try!(Command::new("xclip")
            .args(&["-o"])
            .output());

        Clipboard::process_xclip_output(output)
    }
}

impl Clipboard {
    fn process_xclip_output(output: Output) -> Result<String, Error> {
        if output.status.success() {
            Ok(try!(String::from_utf8(output.stdout)))
        } else {
            Ok(try!(String::from_utf8(output.stderr)))
        }
    }
}