use std::env;
use std::ffi::OsString;
use std::io;
use std::ops::Deref;
use std::os::unix::io::{IntoRawFd, RawFd};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use std::sync::Arc;
use nix::fcntl;
use EventQueue;
use imp::DisplayInner;
#[cfg(feature = "native_lib")]
use wayland_sys::client::wl_display;
#[derive(Debug)]
pub enum ConnectError {
NoWaylandLib,
XdgRuntimeDirNotSet,
NoCompositorListening,
InvalidName,
InvalidFd,
}
impl ::std::error::Error for ConnectError {
fn description(&self) -> &str {
match *self {
ConnectError::NoWaylandLib => "Could not find libwayland-client.so.",
ConnectError::XdgRuntimeDirNotSet => "XDG_RUNTIME_DIR is not set.",
ConnectError::NoCompositorListening => "Could not find a listening wayland compositor.",
ConnectError::InvalidName => "The wayland socket name is invalid.",
ConnectError::InvalidFd => "The FD provided in WAYLAND_SOCKET is invalid.",
}
}
}
impl ::std::fmt::Display for ConnectError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
f.write_str(::std::error::Error::description(self))
}
}
#[derive(Clone, Debug)]
pub struct ProtocolError {
pub code: u32,
pub object_id: u32,
pub object_interface: &'static str,
pub message: String,
}
impl ::std::error::Error for ProtocolError {
fn description(&self) -> &str {
"Wayland protocol error"
}
}
impl ::std::fmt::Display for ProtocolError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
write!(
f,
"Protocol error {} on object {}@{}: {}",
self.code, self.object_interface, self.object_id, self.message
)
}
}
#[derive(Clone)]
pub struct Display {
pub(crate) inner: Arc<DisplayInner>,
}
impl Display {
pub fn connect_to_env() -> Result<(Display, EventQueue), ConnectError> {
if let Ok(txt) = env::var("WAYLAND_SOCKET") {
let fd = txt.parse::<i32>().map_err(|_| ConnectError::InvalidFd)?;
let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD);
let result = flags
.map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC)
.and_then(|f| fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(f)));
match result {
Ok(_) => {
unsafe { Display::from_fd(fd) }
}
Err(_) => {
let _ = ::nix::unistd::close(fd);
return Err(ConnectError::InvalidFd);
}
}
} else {
let mut socket_path = env::var_os("XDG_RUNTIME_DIR")
.map(Into::<PathBuf>::into)
.ok_or(ConnectError::XdgRuntimeDirNotSet)?;
socket_path.push(env::var_os("WAYLAND_DISPLAY").ok_or(ConnectError::NoCompositorListening)?);
let socket = UnixStream::connect(socket_path).map_err(|_| ConnectError::NoCompositorListening)?;
unsafe { Display::from_fd(socket.into_raw_fd()) }
}
}
pub fn connect_to_name<S: Into<OsString>>(name: S) -> Result<(Display, EventQueue), ConnectError> {
let mut socket_path = env::var_os("XDG_RUNTIME_DIR")
.map(Into::<PathBuf>::into)
.ok_or(ConnectError::XdgRuntimeDirNotSet)?;
socket_path.push(name.into());
let socket = UnixStream::connect(socket_path).map_err(|_| ConnectError::NoCompositorListening)?;
unsafe { Display::from_fd(socket.into_raw_fd()) }
}
pub unsafe fn from_fd(fd: RawFd) -> Result<(Display, EventQueue), ConnectError> {
let (d_inner, evq_inner) = DisplayInner::from_fd(fd)?;
Ok((Display { inner: d_inner }, EventQueue::new(evq_inner)))
}
pub fn flush(&self) -> io::Result<()> {
self.inner.flush()
}
pub fn create_event_queue(&self) -> EventQueue {
let evq_inner = DisplayInner::create_event_queue(&self.inner);
EventQueue::new(evq_inner)
}
pub fn protocol_error(&self) -> Option<ProtocolError> {
self.inner.protocol_error()
}
#[cfg(feature = "native_lib")]
pub unsafe fn from_external_display(display_ptr: *mut wl_display) -> (Display, EventQueue) {
let (d_inner, evq_inner) = DisplayInner::from_external(display_ptr);
(Display { inner: d_inner }, EventQueue::new(evq_inner))
}
#[cfg(feature = "native_lib")]
pub fn get_display_ptr(&self) -> *mut wl_display {
self.inner.ptr()
}
}
impl Deref for Display {
type Target = ::protocol::wl_display::WlDisplay;
fn deref(&self) -> &::protocol::wl_display::WlDisplay {
self.inner.get_proxy()
}
}