#![allow(deprecated)]
use crate::ecs::world::*;
use crate::process_singleton::*;
use crate::win_message_handler::*;
use crate::win_style::*;
use crate::winproc::*;
use async_executor::*;
use std::cell::RefCell;
use std::future::*;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::*;
use std::thread;
use std::time::Instant;
use tracing::{debug, info, trace};
use windows::core::*;
use windows::Win32::Foundation::*;
use windows::Win32::Graphics::Dwm::*;
use windows::Win32::System::Com::*;
use windows::Win32::UI::WindowsAndMessaging::*;
static DEBUG_LOOP_COUNT: AtomicU64 = AtomicU64::new(0);
static DEBUG_VSYNC_COUNT: AtomicU64 = AtomicU64::new(0);
static DEBUG_OTHER_MSG_COUNT: AtomicU64 = AtomicU64::new(0);
static DEBUG_NO_MSG_COUNT: AtomicU64 = AtomicU64::new(0);
pub(crate) static VSYNC_TICK_COUNT: AtomicU64 = AtomicU64::new(0);
pub(crate) static LAST_VSYNC_TICK: AtomicU64 = AtomicU64::new(0);
#[cfg(debug_assertions)]
pub(crate) static DEBUG_WNDPROC_TICK_COUNT: AtomicU64 = AtomicU64::new(0);
#[cfg(debug_assertions)]
pub(crate) static DEBUG_RUN_TICK_COUNT: AtomicU64 = AtomicU64::new(0);
const WM_VSYNC: u32 = WM_USER + 1;
pub(crate) const WM_LAST_WINDOW_DESTROYED: u32 = WM_USER + 2;
#[derive(Clone)]
pub struct WinThreadMgr(Arc<WinThreadMgrInner>);
impl WinThreadMgr {
pub fn new() -> Result<Self> {
let inner = Arc::new(WinThreadMgrInner::new()?);
Ok(Self(inner))
}
}
impl Deref for WinThreadMgr {
type Target = Arc<WinThreadMgrInner>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug)]
pub struct WinThreadMgrInner {
executor_normal: Executor<'static>,
world: Rc<RefCell<EcsWorld>>,
message_window: HWND,
vsync_thread_stop: Arc<AtomicBool>,
vsync_thread_handle: Option<thread::JoinHandle<()>>,
}
impl WinThreadMgrInner {
pub fn instance(&self) -> HINSTANCE {
let singleton = WinProcessSingleton::get_or_init();
singleton.instance()
}
pub fn world(&self) -> Rc<RefCell<EcsWorld>> {
Rc::clone(&self.world)
}
fn new() -> Result<Self> {
unsafe {
CoInitializeEx(None, COINIT_MULTITHREADED).ok()?;
}
let singleton = WinProcessSingleton::get_or_init();
let message_window = unsafe {
CreateWindowExW(
WINDOW_EX_STYLE::default(),
singleton.window_class_name(),
w!("wintf Message Window"),
WS_OVERLAPPED,
0,
0,
0,
0,
Some(HWND_MESSAGE),
None,
None,
None,
)?
};
let world = Rc::new(RefCell::new(EcsWorld::new()));
world.borrow_mut().set_message_window(message_window);
crate::ecs::set_ecs_world(Rc::downgrade(&world));
let vsync_thread_stop = Arc::new(AtomicBool::new(false));
let vsync_thread_handle =
spawn_vsync_thread(message_window, Arc::clone(&vsync_thread_stop));
let rc = WinThreadMgrInner {
executor_normal: Executor::new(),
world,
message_window,
vsync_thread_stop,
vsync_thread_handle: Some(vsync_thread_handle),
};
let _ = rc.instance();
Ok(rc)
}
pub fn create_window<S1>(
&self,
handler: Arc<dyn BaseWinMessageHandler>,
window_name: S1,
style: WinStyle,
) -> Result<HWND>
where
S1: Into<HSTRING>,
{
let singleton = WinProcessSingleton::get_or_init();
let window_name_hstring: HSTRING = window_name.into();
let boxed_ptr = handler.into_boxed_ptr();
unsafe {
debug!("Window creation...");
let rc = CreateWindowExW(
style.ex_style,
singleton.window_class_name(),
&window_name_hstring,
style.style,
style.x,
style.y,
style.width,
style.height,
style.parent,
None,
None,
Some(boxed_ptr),
);
debug!(hwnd = ?rc, "Window created");
rc
}
}
pub fn show_window(&self, hwnd: HWND) -> Result<()> {
unsafe {
let _ = ShowWindow(hwnd, SW_SHOW);
}
Ok(())
}
pub fn spawn_normal<T: Send + 'static>(
&self,
fut: impl Future<Output = T> + Send + 'static,
) -> Task<T> {
self.executor_normal.spawn(fut)
}
pub fn run(&self) -> Result<()> {
let mut msg = MSG::default();
let mut last_stats_time = Instant::now();
unsafe {
loop {
DEBUG_LOOP_COUNT.fetch_add(1, Ordering::Relaxed);
let now = Instant::now();
if now.duration_since(last_stats_time).as_secs() >= 1 {
let loop_count = DEBUG_LOOP_COUNT.swap(0, Ordering::Relaxed);
let vsync_count = DEBUG_VSYNC_COUNT.swap(0, Ordering::Relaxed);
let other_msg_count = DEBUG_OTHER_MSG_COUNT.swap(0, Ordering::Relaxed);
let no_msg_count = DEBUG_NO_MSG_COUNT.swap(0, Ordering::Relaxed);
#[cfg(debug_assertions)]
{
let wndproc_tick = DEBUG_WNDPROC_TICK_COUNT.swap(0, Ordering::Relaxed);
let run_tick = DEBUG_RUN_TICK_COUNT.swap(0, Ordering::Relaxed);
debug!(
loop_count,
vsync_count,
other_msg_count,
no_msg_count,
wndproc_tick,
run_tick,
"Message loop stats"
);
}
#[cfg(not(debug_assertions))]
trace!(
loop_count,
vsync_count,
other_msg_count,
no_msg_count,
"Message loop stats"
);
last_stats_time = now;
}
if PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
if msg.message == WM_QUIT {
break;
}
if msg.message == WM_VSYNC {
DEBUG_VSYNC_COUNT.fetch_add(1, Ordering::Relaxed);
let ticked = {
let mut world = self.world.borrow_mut();
world.try_tick_on_vsync()
};
crate::ecs::window::flush_window_pos_commands();
#[cfg(debug_assertions)]
if ticked {
DEBUG_RUN_TICK_COUNT.fetch_add(1, Ordering::Relaxed);
}
continue;
}
if msg.message == WM_LAST_WINDOW_DESTROYED {
info!("WM_LAST_WINDOW_DESTROYED received, calling PostQuitMessage(0)");
PostQuitMessage(0);
continue;
}
DEBUG_OTHER_MSG_COUNT.fetch_add(1, Ordering::Relaxed);
if msg.message >= 0x0400 && msg.message <= 0x040F {
trace!(
msg = format_args!("0x{:04X}", msg.message),
hwnd = ?msg.hwnd,
"WM_USER range message"
);
}
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
continue;
}
DEBUG_NO_MSG_COUNT.fetch_add(1, Ordering::Relaxed);
if self.try_tick_normal() {
continue;
}
let _ = WaitMessage();
}
}
Ok(())
}
fn try_tick_normal(&self) -> bool {
self.executor_normal.try_tick()
}
}
impl Drop for WinThreadMgrInner {
fn drop(&mut self) {
self.vsync_thread_stop.store(true, Ordering::Relaxed);
if let Some(handle) = self.vsync_thread_handle.take() {
let _ = handle.join();
}
unsafe {
let _ = DestroyWindow(self.message_window);
}
}
}
fn spawn_vsync_thread(message_window: HWND, stop_flag: Arc<AtomicBool>) -> thread::JoinHandle<()> {
let message_window_ptr = message_window.0 as isize;
thread::spawn(move || {
let message_window = HWND(message_window_ptr as *mut _);
loop {
if stop_flag.load(Ordering::Relaxed) {
break;
}
unsafe {
if DwmFlush().is_ok() {
VSYNC_TICK_COUNT.fetch_add(1, Ordering::Relaxed);
let _ = PostMessageW(Some(message_window), WM_VSYNC, WPARAM(0), LPARAM(0));
} else {
thread::sleep(std::time::Duration::from_millis(15)); }
}
}
})
}