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
use fltk_sys::fl;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::{any, marker, mem, os::raw};

/// Sends a custom message
/// # Safety
/// The type must be Send and Sync safe
pub unsafe fn awake_msg<T>(msg: T) {
    fl::Fl_awake_msg(Box::into_raw(Box::from(msg)) as *mut raw::c_void);
}

#[allow(clippy::missing_safety_doc)]
/**
    Receives a custom message
    ```rust,no_run
    use fltk::{prelude::*, *};
    if let Some(msg) = unsafe { app::thread_msg::<i32>() } { /* do something */ }
    ```
    # Safety
    The type must correspond to the received message
*/
pub unsafe fn thread_msg<T>() -> Option<T> {
    let msg = fl::Fl_thread_msg();
    if msg.is_null() {
        None
    } else {
        let msg = Box::from_raw(msg as *const _ as *mut T);
        Some(*msg)
    }
}

#[repr(C)]
struct Message<T: Send + Sync> {
    hash: u64,
    sz: usize,
    msg: T,
}

/// Creates a sender struct
#[derive(Debug, Clone, Copy)]
pub struct Sender<T: Send + Sync> {
    data: marker::PhantomData<T>,
    hash: u64,
    sz: usize,
}

impl<T: Send + Sync> Sender<T> {
    /// Sends a message
    pub fn send(&self, val: T) {
        let msg = Message {
            hash: self.hash,
            sz: self.sz,
            msg: val,
        };
        unsafe { awake_msg(msg) }
    }
}

/// Creates a receiver struct
#[derive(Debug, Clone, Copy)]
pub struct Receiver<T: Send + Sync> {
    data: marker::PhantomData<T>,
    hash: u64,
    sz: usize,
}

impl<T: Send + Sync> Receiver<T> {
    /// Receives a message
    pub fn recv(&self) -> Option<T> {
        let data: Option<Message<T>> = unsafe { thread_msg() };
        data.and_then(|data| {
            if data.sz == self.sz && data.hash == self.hash {
                Some(data.msg)
            } else {
                None
            }
        })
    }
}

/// Creates a channel returning a Sender and Receiver structs (mpsc)
// The implementation could really use generic statics
pub fn channel<T: Send + Sync>() -> (Sender<T>, Receiver<T>) {
    let msg_sz = mem::size_of::<T>();
    let type_name = any::type_name::<T>();
    let mut hasher = DefaultHasher::new();
    type_name.hash(&mut hasher);
    let type_hash = hasher.finish();

    let s = Sender {
        data: marker::PhantomData,
        hash: type_hash,
        sz: msg_sz,
    };
    let r = Receiver {
        data: marker::PhantomData,
        hash: type_hash,
        sz: msg_sz,
    };
    (s, r)
}