wl-client 0.2.0

Safe client-side libwayland wrapper
Documentation
use {
    crate::{
        Libwayland, Queue, proxy,
        test_protocol_helpers::{callback, get_root},
        test_protocols::core::{
            wl_callback::{WlCallback, WlCallbackEventHandler, WlCallbackRef},
            wl_display::WlDisplay,
            wl_string::{WlStringEventHandler, WlStringRef},
        },
        utils::{
            block_on::block_on,
            poller::{Poller, readable},
        },
    },
    run_on_drop::on_drop,
    std::{
        any::Any,
        cell::Cell,
        pin::pin,
        rc::Rc,
        sync::{
            Arc, Barrier,
            atomic::{AtomicBool, Ordering::Relaxed},
        },
        task::{Context, Wake, Waker},
        thread,
        time::Duration,
    },
};

#[test]
fn debug_queue() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    {
        let queue = format!("{:?}", queue);
        assert!(queue.contains("Queue"));
        assert!(queue.contains("name: \"queue name\""));
    }
    {
        let queue = format!("{:?}", *queue);
        assert!(queue.contains("Queue"));
        assert!(queue.contains("name: \"queue name\""));
    }
    {
        let lock = queue.lock_dispatch();
        let lock = format!("{:?}", lock);
        assert!(lock.contains("DispatchLock"));
    }
}

#[tokio::test]
async fn owned_watcher() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    let watcher = Arc::new(queue.create_watcher().unwrap());
    let poller = Poller::new(&watcher).unwrap();
    let _sync = queue.display::<WlDisplay>().sync();
    con.flush().unwrap();
    readable(&poller.data).await.unwrap();
}

#[tokio::test]
async fn borrowed_watcher() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    let borrowed = unsafe { con.borrow_foreign_queue(queue.wl_event_queue()) };
    let watcher = Arc::new(borrowed.create_watcher().unwrap());
    let poller = Poller::new(&watcher).unwrap();
    let _sync = queue.display::<WlDisplay>().sync();
    con.flush().unwrap();
    readable(&poller.data).await.unwrap();
}

#[test]
fn is_local() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    assert!(queue.is_non_local());
    assert!(!queue.is_local());
    let queue = con.create_local_queue(c"queue name");
    assert!(!queue.is_non_local());
    assert!(queue.is_local());
}

#[test]
fn dispatch() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    queue.dispatch_pending().unwrap();
}

#[test]
fn drop_during_dispatch() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_local_queue(c"queue name");
    let sync = queue.display::<WlDisplay>().sync();
    let n = Rc::new(Cell::new(false));
    let n2 = n.clone();
    let dec_on_drop = on_drop(move || n2.set(true));
    proxy::set_event_handler_local(
        &sync,
        Eh(Cell::new(Some(sync.clone())), n.clone(), dec_on_drop),
    );
    drop(sync);
    queue.dispatch_blocking().unwrap();
    assert!(n.get());

    struct Eh<T>(Cell<Option<WlCallback>>, Rc<Cell<bool>>, T);
    impl<T> WlCallbackEventHandler for Eh<T> {
        fn done(&self, _slf: &WlCallbackRef, _callback_data: u32) {
            drop(self.0.take());
            assert!(!self.1.get());
        }
    }
}

#[test]
fn drop_during_nested_dispatch() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_local_queue(c"queue name");
    let sync = queue.display::<WlDisplay>().sync();
    let n = Rc::new(Cell::new(false));
    let n2 = n.clone();
    let dec_on_drop = Box::new(on_drop(move || n2.set(true)));
    proxy::set_event_handler_local(
        &sync,
        Eh(
            Cell::new(Some(sync.clone())),
            n.clone(),
            dec_on_drop,
            queue.clone(),
            true,
        ),
    );
    drop(sync);
    queue.dispatch_blocking().unwrap();
    assert!(n.get());

    struct Eh(
        Cell<Option<WlCallback>>,
        Rc<Cell<bool>>,
        #[allow(dead_code)] Box<dyn Any>,
        Queue,
        bool,
    );
    impl WlCallbackEventHandler for Eh {
        fn done(&self, _slf: &WlCallbackRef, _callback_data: u32) {
            if self.4 {
                let sync = self.3.display::<WlDisplay>().sync();
                let n = Rc::new(Cell::new(false));
                let n2 = n.clone();
                let dec_on_drop = on_drop(move || n2.set(true));
                proxy::set_event_handler_local(
                    &sync,
                    Eh(
                        Cell::new(Some(sync.clone())),
                        n.clone(),
                        Box::new(dec_on_drop),
                        self.3.clone(),
                        false,
                    ),
                );
                drop(sync);
                self.3.dispatch_blocking().unwrap();
                assert!(!n.get());
            }
            drop(self.0.take());
            assert!(!self.1.get());
        }
    }
}

#[test]
fn drop_after() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_local_queue(c"queue name");
    let sync = queue.display::<WlDisplay>().sync();
    let n = Rc::new(Cell::new(false));
    let n2 = n.clone();
    let dec_on_drop = on_drop(move || n2.set(true));
    proxy::set_event_handler_local(&sync, Eh(dec_on_drop));
    queue.dispatch_blocking().unwrap();
    drop(sync);
    assert!(n.get());

    struct Eh<T>(T);
    impl<T> WlCallbackEventHandler for Eh<T> {}
}

#[test]
fn roundtrip() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_local_queue(c"queue name");
    let sync = queue.display::<WlDisplay>().sync();
    let n = Rc::new(Cell::new(false));
    let n2 = n.clone();
    proxy::set_event_handler_local(&sync, WlCallback::on_done(move |_, _| n2.set(true)));
    queue.dispatch_roundtrip_blocking().unwrap();
    assert!(n.get());
}

#[test]
fn borrowed_wait_for_events() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_local_queue(c"queue name");
    let sync = queue.display::<WlDisplay>().sync();
    let borrowed = unsafe { con.borrow_foreign_queue(queue.wl_event_queue()) };
    block_on(borrowed.wait_for_events()).unwrap();
    let n = Rc::new(Cell::new(false));
    let n2 = n.clone();
    proxy::set_event_handler_local(&sync, WlCallback::on_done(move |_, _| n2.set(true)));
    queue.dispatch_pending().unwrap();
    assert!(n.get());
}

#[test]
fn borrowed_queue_native() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_local_queue(c"queue name");
    let borrowed = unsafe { con.borrow_foreign_queue(queue.wl_event_queue()) };
    assert_eq!(borrowed.wl_event_queue(), Some(queue.wl_event_queue()));
    let borrowed = con.borrow_default_queue();
    assert_eq!(borrowed.wl_event_queue(), None);
}

#[test]
fn drow_queue_owner() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_local_queue(c"queue name");
    let sync = queue.display::<WlDisplay>().sync();
    let n = Rc::new(Cell::new(false));
    let n2 = n.clone();
    let on_drop = on_drop(move || n2.set(true));
    proxy::set_event_handler_local(&sync, callback(|| drop(on_drop)));
    drop(queue);
    assert!(n.get());
}

#[test]
fn partial_eq() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue1 = con.create_local_queue(c"queue name");
    let queue2 = con.create_local_queue(c"queue name");
    assert_eq!(*queue1, *queue1);
    assert_ne!(*queue1, *queue2);
    assert_eq!(*queue2, *queue2);
}

#[test]
fn run_locked() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    let queue2 = queue.clone();
    let lock = queue.lock_dispatch();
    let barrier1_1 = Arc::new(Barrier::new(2));
    let barrier1_2 = barrier1_1.clone();
    let barrier2_1 = Arc::new(Barrier::new(2));
    let barrier2_2 = barrier2_1.clone();
    let n = Arc::new(AtomicBool::new(false));
    let n2 = n.clone();
    let jh = thread::spawn(move || {
        barrier1_2.wait();
        queue2.run_locked(|| n2.store(true, Relaxed));
        barrier2_2.wait();
    });
    barrier1_1.wait();
    thread::sleep(Duration::from_millis(500));
    assert!(!n.load(Relaxed));
    drop(lock);
    barrier2_1.wait();
    assert!(n.load(Relaxed));
    jh.join().unwrap();
}

#[test]
fn dispatch_error() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    unsafe {
        lib.inject_error(con.wl_display().as_ptr());
    }
    assert!(queue.dispatch_pending().is_err());
}

#[test]
fn roundtrip_async() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    let fut = queue.dispatch_roundtrip_async();
    let mut fut = pin!(fut);

    struct W(AtomicBool);
    impl Wake for W {
        fn wake(self: Arc<Self>) {
            self.0.store(true, Relaxed);
        }
    }

    let w = Arc::new(W(AtomicBool::new(false)));
    let waker = Waker::from(w.clone());
    let mut ctx = Context::from_waker(&waker);

    assert!(fut.as_mut().poll(&mut ctx).is_pending());
    while fut.as_mut().poll(&mut ctx).is_pending() {
        thread::yield_now();
    }

    assert!(w.0.load(Relaxed));
}

#[test]
fn wrap_proxy() {
    let lib = Libwayland::open().unwrap();
    let con = lib.connect_to_default_display().unwrap();
    let queue = con.create_queue(c"queue name");
    let root = get_root(&queue);
    let root = queue.wrap_proxy(&*root);
    let abcd = root.echo("abcd");
    let eh = Arc::new(AtomicBool::new(false));
    proxy::set_event_handler(&abcd, Eh(eh.clone()));
    queue.dispatch_roundtrip_blocking().unwrap();
    assert!(eh.load(Relaxed));

    struct Eh(Arc<AtomicBool>);
    impl WlStringEventHandler for Eh {
        fn string(&self, _slf: &WlStringRef, string: &str) {
            assert_eq!(string, "abcd");
            self.0.store(true, Relaxed);
        }
    }
}

#[test]
#[should_panic(expected = "Trying to lock thread-local mutex in other thread")]
fn drop_queue_owner_off_thread() {
    let queue = thread::spawn(|| {
        let lib = Libwayland::open().unwrap();
        let con = lib.connect_to_default_display().unwrap();
        con.create_local_queue(c"queue name")
    })
    .join()
    .unwrap();
    drop(queue);
}