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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::io;
use std::os::unix::io::RawFd;
use std::sync::Arc;

use crate::protocol::wl_display::WlDisplay;
use wayland_sys::client::*;

use crate::{ConnectError, Proxy};

use super::{EventQueueInner, ProxyInner};

pub(crate) struct DisplayInner {
    proxy: Proxy<WlDisplay>,
    display: Arc<DisplayGuard>,
}

pub(crate) struct DisplayGuard {
    ptr: *mut wl_display,
    external: bool,
}

unsafe impl Send for DisplayInner {}
unsafe impl Sync for DisplayInner {}

unsafe fn make_display(ptr: *mut wl_display) -> Result<Arc<DisplayInner>, ConnectError> {
    if ptr.is_null() {
        return Err(ConnectError::NoCompositorListening);
    }

    let mut inner = DisplayInner {
        proxy: Proxy::from_c_ptr(ptr as *mut _),
        display: Arc::new(DisplayGuard { ptr, external: false }),
    };

    inner.proxy.inner.display = Some(Arc::downgrade(&inner.display));

    Ok(Arc::new(inner))
}

impl DisplayInner {
    pub unsafe fn from_fd(fd: RawFd) -> Result<Arc<DisplayInner>, ConnectError> {
        if !::wayland_sys::client::is_lib_available() {
            return Err(ConnectError::NoWaylandLib);
        }

        let display_ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_connect_to_fd, fd);

        make_display(display_ptr)
    }

    pub(crate) fn get_connection_fd(&self) -> ::std::os::unix::io::RawFd {
        unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_get_fd, self.ptr()) }
    }

    pub(crate) fn ptr(&self) -> *mut wl_display {
        self.display.ptr
    }

    pub(crate) fn flush(&self) -> io::Result<()> {
        let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_flush, self.ptr()) };
        if ret >= 0 {
            Ok(())
        } else {
            Err(io::Error::last_os_error())
        }
    }

    pub(crate) fn create_event_queue(me: &Arc<DisplayInner>) -> EventQueueInner {
        unsafe {
            let ptr = ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_create_queue, me.ptr());
            EventQueueInner::new(me.clone(), ptr)
        }
    }

    pub(crate) fn get_proxy(&self) -> &Proxy<WlDisplay> {
        &self.proxy
    }

    pub(crate) fn protocol_error(&self) -> Option<crate::ProtocolError> {
        let ret = unsafe { ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_get_error, self.ptr()) };
        if ret == ::nix::errno::Errno::EPROTO as i32 {
            let mut interface = ::std::ptr::null_mut();
            let mut id = 0;
            let code = unsafe {
                ffi_dispatch!(
                    WAYLAND_CLIENT_HANDLE,
                    wl_display_get_protocol_error,
                    self.ptr(),
                    &mut interface,
                    &mut id
                )
            };
            let interface_name = if !interface.is_null() {
                unsafe { ::std::ffi::CStr::from_ptr((*interface).name) }
                    .to_str()
                    .unwrap_or("<unknown>")
            } else {
                "<unknown>"
            };
            Some(crate::ProtocolError {
                code,
                object_id: id,
                object_interface: interface_name,
                message: String::new(),
            })
        } else {
            None
        }
    }

    pub(crate) unsafe fn from_external(display_ptr: *mut wl_display) -> Arc<DisplayInner> {
        Arc::new(DisplayInner {
            proxy: Proxy::wrap(ProxyInner::from_external_display(display_ptr as *mut _)),
            display: Arc::new(DisplayGuard { ptr: display_ptr, external: true }),
        })
    }
}

impl Drop for DisplayGuard {
    fn drop(&mut self) {
        if !self.external {
            // disconnect only if we are owning this display
            unsafe {
                ffi_dispatch!(WAYLAND_CLIENT_HANDLE, wl_display_disconnect, self.ptr);
            }
        }
    }
}