use std::mem;
use std::ptr::null_mut;
use std::sync::{Arc, Mutex};
use winapi::ctypes::c_int;
use winapi::shared::windef::*;
use winapi::um::winbase::*;
use winapi::um::winnt::*;
use winapi::um::winuser::*;
#[derive(Clone, Default)]
pub struct RunLoopHandle(Arc<Mutex<RunLoopState>>);
#[derive(Default)]
struct RunLoopState {
listeners: Vec<Listener>,
}
unsafe impl Send for Listener {}
struct Listener {
h: HANDLE,
callback: Box<dyn FnMut()>,
}
pub struct RunLoop {
handle: RunLoopHandle,
accel: HACCEL,
}
impl RunLoop {
pub fn new() -> RunLoop {
RunLoop {
handle: Default::default(),
accel: null_mut(),
}
}
pub fn get_handle(&self) -> RunLoopHandle {
self.handle.clone()
}
pub fn set_accel(&mut self, accel: &[ACCEL]) {
unsafe {
self.accel = CreateAcceleratorTableW(accel as *const _ as *mut _, accel.len() as c_int);
}
}
#[allow(clippy::absurd_extreme_comparisons)]
pub fn run(&mut self) {
unsafe {
loop {
let mut handles = Vec::new();
for listener in &self.handle.0.lock().unwrap().listeners {
handles.push(listener.h);
}
let len = handles.len() as u32;
let res =
MsgWaitForMultipleObjectsEx(len, handles.as_ptr(), INFINITE, QS_ALLEVENTS, 0);
if res >= WAIT_OBJECT_0 && res < WAIT_OBJECT_0 + len {
let ix = (res - WAIT_OBJECT_0) as usize;
(&mut self.handle.0.lock().unwrap().listeners[ix].callback)();
}
loop {
let mut msg = mem::MaybeUninit::uninit();
let res = PeekMessageW(msg.as_mut_ptr(), null_mut(), 0, 0, PM_NOREMOVE);
if res == 0 {
break;
}
let res = GetMessageW(msg.as_mut_ptr(), null_mut(), 0, 0);
if res <= 0 {
return;
}
let mut msg: MSG = msg.assume_init();
if self.accel.is_null()
|| TranslateAcceleratorW(msg.hwnd, self.accel, &mut msg) == 0
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
}
}
}
pub fn request_quit() {
unsafe {
PostQuitMessage(0);
}
}
impl RunLoopHandle {
pub unsafe fn add_handler<F>(&self, h: HANDLE, callback: F)
where
F: FnMut() + 'static,
{
let listener = Listener {
h,
callback: Box::new(callback),
};
self.0.lock().unwrap().listeners.push(listener);
}
}
impl Default for RunLoop {
fn default() -> Self {
RunLoop::new()
}
}