mirror of
https://github.com/alacritty/alacritty.git
synced 2025-02-17 15:57:08 -05:00
173 lines
4.7 KiB
Rust
173 lines
4.7 KiB
Rust
//! 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 std::ffi::OsStr;
|
|
|
|
use super::{Load, Store};
|
|
|
|
/// 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) => {
|
|
match err.kind() {
|
|
io::ErrorKind::NotFound => {
|
|
write!(f, "Please install `xclip` to enable clipboard support")
|
|
},
|
|
_ => 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 = Command::new("xclip")
|
|
.args(&["-o", "-selection", "clipboard"])
|
|
.output()?;
|
|
|
|
Clipboard::process_xclip_output(output)
|
|
}
|
|
|
|
fn load_selection(&self) -> Result<String, Self::Err> {
|
|
let output = Command::new("xclip")
|
|
.args(&["-o"])
|
|
.output()?;
|
|
|
|
Clipboard::process_xclip_output(output)
|
|
}
|
|
}
|
|
|
|
impl Store for Clipboard {
|
|
/// Sets the primary clipboard contents
|
|
#[inline]
|
|
fn store_primary<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
|
where S: Into<String>
|
|
{
|
|
self.store(contents, &["-i", "-selection", "clipboard"])
|
|
}
|
|
|
|
/// Sets the secondary clipboard contents
|
|
#[inline]
|
|
fn store_selection<S>(&mut self, contents: S) -> Result<(), Self::Err>
|
|
where S: Into<String>
|
|
{
|
|
self.store(contents, &["-i"])
|
|
}
|
|
}
|
|
|
|
impl Clipboard {
|
|
fn process_xclip_output(output: Output) -> Result<String, Error> {
|
|
if output.status.success() {
|
|
String::from_utf8(output.stdout)
|
|
.map_err(::std::convert::From::from)
|
|
} else {
|
|
String::from_utf8(output.stderr)
|
|
.map_err(::std::convert::From::from)
|
|
}
|
|
}
|
|
|
|
fn store<C, S>(&mut self, contents: C, args: &[S]) -> Result<(), Error>
|
|
where C: Into<String>,
|
|
S: AsRef<OsStr>,
|
|
{
|
|
use std::io::Write;
|
|
use std::process::{Command, Stdio};
|
|
|
|
let contents = contents.into();
|
|
let mut child = Command::new("xclip")
|
|
.args(args)
|
|
.stdin(Stdio::piped())
|
|
.spawn()?;
|
|
|
|
if let Some(mut stdin) = child.stdin.as_mut() {
|
|
stdin.write_all(contents.as_bytes())?;
|
|
}
|
|
|
|
// Return error if didn't exit cleanly
|
|
let exit_status = child.wait()?;
|
|
if exit_status.success() {
|
|
Ok(())
|
|
} else {
|
|
Err(Error::Xclip("xclip returned non-zero exit code".into()))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::Clipboard;
|
|
use ::{Load, Store};
|
|
|
|
#[test]
|
|
fn clipboard_works() {
|
|
let mut clipboard = Clipboard::new().expect("create clipboard");
|
|
let arst = "arst";
|
|
let oien = "oien";
|
|
clipboard.store_primary(arst).expect("store selection");
|
|
clipboard.store_selection(oien).expect("store selection");
|
|
|
|
let selection = clipboard.load_selection().expect("load selection");
|
|
let primary = clipboard.load_primary().expect("load selection");
|
|
|
|
assert_eq!(arst, primary);
|
|
assert_eq!(oien, selection);
|
|
}
|
|
}
|