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 crate::{EventQueue, Proxy};
use crate::imp::DisplayInner;
#[cfg(feature = "use_system_lib")]
use wayland_sys::client::wl_display;
#[derive(Debug)]
pub enum ConnectError {
NoWaylandLib,
XdgRuntimeDirNotSet,
NoCompositorListening,
InvalidName,
InvalidFd,
}
impl ::std::error::Error for ConnectError {}
impl ::std::fmt::Display for ConnectError {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
match *self {
ConnectError::NoWaylandLib => f.write_str("Could not find libwayland-client.so."),
ConnectError::XdgRuntimeDirNotSet => f.write_str("XDG_RUNTIME_DIR is not set."),
ConnectError::NoCompositorListening => {
f.write_str("Could not find a listening wayland compositor.")
}
ConnectError::InvalidName => f.write_str("The wayland socket name is invalid."),
ConnectError::InvalidFd => f.write_str("The FD provided in WAYLAND_SOCKET is invalid."),
}
}
}
#[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, 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);
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, 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, ConnectError> {
Ok(Display { inner: DisplayInner::from_fd(fd)? })
}
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, self.clone())
}
pub fn protocol_error(&self) -> Option<ProtocolError> {
self.inner.protocol_error()
}
pub fn get_connection_fd(&self) -> ::std::os::unix::io::RawFd {
self.inner.get_connection_fd()
}
#[cfg(feature = "use_system_lib")]
pub unsafe fn from_external_display(display_ptr: *mut wl_display) -> Display {
Display { inner: DisplayInner::from_external(display_ptr) }
}
#[cfg(feature = "use_system_lib")]
pub fn get_display_ptr(&self) -> *mut wl_display {
self.inner.ptr()
}
}
impl Deref for Display {
type Target = Proxy<crate::protocol::wl_display::WlDisplay>;
fn deref(&self) -> &Proxy<crate::protocol::wl_display::WlDisplay> {
self.inner.get_proxy()
}
}