tiny-xlib 0.2.5

A tiny Xlib wrapper for Rust
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0 OR Zlib

// Copyright 2023 John Nunley
//
// Licensed under the Apache License, Version 2.0, the MIT License, and
// the Zlib license. You may not use this software except in compliance
// with at least one of these licenses. You should have received a copy
// of these licenses with this software. You may also find them at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//     https://opensource.org/licenses/MIT
//     https://opensource.org/licenses/Zlib
//
// Unless required by applicable law or agreed to in writing, software
// distributed under these licenses is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the licenses for the specific language governing permissions and
// limitations under the licenses.

use as_raw_xcb_connection::AsRawXcbConnection;
use std::ffi::CString;
use std::sync::{Arc, Mutex};
use tiny_xlib::Display;

#[test]
fn smoke() {
    tracing_subscriber::fmt::try_init().ok();

    let display = Display::new(None).unwrap();
    let _conn = unsafe {
        x11rb::xcb_ffi::XCBConnection::from_raw_xcb_connection(
            display.as_raw_xcb_connection().cast(),
            false,
        )
        .unwrap()
    };
}

#[test]
fn error_handling() {
    tracing_subscriber::fmt::try_init().ok();
    let display = Display::new(None).unwrap();

    // Add a handler for the error.
    let error = Arc::new(Mutex::new(None));
    let key = tiny_xlib::register_error_handler({
        let error = error.clone();
        Box::new(move |_, event| {
            error.lock().unwrap().replace(event.clone());
            true
        })
    })
    .unwrap();

    // Create a GC with a bum window.
    trigger_error(&display);

    // The error should be set.
    tiny_xlib::unregister_error_handler(key);
    let error = error.lock().unwrap().take().unwrap();
    assert_eq!(error.error_code(), x11_dl::xlib::BadDrawable as _);
    assert_eq!(error.minor_code(), 0);

    // Eat coverage.
    let _ = format!("{:?}", error);
    let _ = format!("{:?}", key.clone());
}

#[test]
fn display_should_be_able_to_fail_creating() {
    tracing_subscriber::fmt::try_init().ok();
    let res = Display::new(Some(&CString::new("not-a-real-display").unwrap()));
    assert!(res.is_err());
}

#[test]
fn remove_and_re_insert() {
    tracing_subscriber::fmt::try_init().ok();

    let bad_flag = Arc::new(Mutex::new(false));
    let good_flag = Arc::new(Mutex::new(false));

    let key = tiny_xlib::register_error_handler({
        let bad_flag = bad_flag.clone();
        Box::new(move |_, _| {
            *bad_flag.lock().unwrap() = true;
            true
        })
    })
    .unwrap();

    // Remove the error handler.
    tiny_xlib::unregister_error_handler(key);

    // Nothing should happen.
    let display = Display::new(None).unwrap();
    trigger_error(&display);

    // The error should not be set.
    assert!(!*bad_flag.lock().unwrap());

    // Push the error handler back on with a different flag.
    let _key = tiny_xlib::register_error_handler({
        let good_flag = good_flag.clone();
        Box::new(move |_, _| {
            *good_flag.lock().unwrap() = true;
            true
        })
    })
    .unwrap();

    // Create a GC with a bum window.
    trigger_error(&display);

    // The error should be set.
    assert!(!*bad_flag.lock().unwrap());
    assert!(*good_flag.lock().unwrap());
}

/// Trigger an error by creating a bad drawable.
fn trigger_error(display: &Display) {
    tracing_subscriber::fmt::try_init().ok();

    let xlib = x11_dl::xlib::Xlib::open().unwrap();
    unsafe {
        (xlib.XCreateGC)(display.as_ptr().cast(), 0x1337, 0, std::ptr::null_mut());
        (xlib.XSync)(display.as_ptr().cast(), 0);
    }
}