From fd203468296b0eea7f299a82a6eec2a238a5c952 Mon Sep 17 00:00:00 2001 From: Tomaka17 Date: Thu, 31 Jul 2014 20:55:30 +0200 Subject: [PATCH] Put GetMessage in the same thread as the window creation. Fixes #6 --- src/win32/init.rs | 355 +++++++++++++++++++++++++--------------------- src/win32/mod.rs | 32 ++--- 2 files changed, 201 insertions(+), 186 deletions(-) diff --git a/src/win32/init.rs b/src/win32/init.rs index 24c6798b..383e3f45 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -1,6 +1,8 @@ -use std::kinds::marker::NoSend; +extern crate native; + +use self::native::NativeTaskBuilder; +use std::task::TaskBuilder; use std::sync::atomics::AtomicBool; -use std::sync::Mutex; use std::ptr; use super::{event, ffi}; use super::{MonitorID, Window}; @@ -8,7 +10,7 @@ use {Event, Hints}; /// Stores the list of all the windows. /// Only available on callback thread. -local_data_key!(pub WINDOWS_LIST: Mutex)>>) +local_data_key!(WINDOW: (ffi::HWND, Sender)) pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, _hints: &Hints, monitor: Option) @@ -17,190 +19,215 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, use std::mem; use std::os; - // initializing WINDOWS_LIST if needed - // this is safe because WINDOWS_LIST is task-local - if WINDOWS_LIST.get().is_none() { - WINDOWS_LIST.replace(Some(Mutex::new(Vec::new()))); - } + let title = title.to_string(); + //let hints = hints.clone(); - // registering the window class - let class_name: Vec = "Window Class".utf16_units().collect::>() - .append_one(0); - - let class = ffi::WNDCLASSEX { - cbSize: mem::size_of::() as ffi::UINT, - style: ffi::CS_HREDRAW | ffi::CS_VREDRAW, - lpfnWndProc: callback, - cbClsExtra: 0, - cbWndExtra: 0, - hInstance: unsafe { ffi::GetModuleHandleW(ptr::null()) }, - hIcon: ptr::mut_null(), - hCursor: ptr::mut_null(), - hbrBackground: ptr::mut_null(), - lpszMenuName: ptr::null(), - lpszClassName: class_name.as_ptr(), - hIconSm: ptr::mut_null(), - }; + let (tx, rx) = channel(); - if unsafe { ffi::RegisterClassExW(&class) } == 0 { - use std::os; - return Err(format!("RegisterClassEx function failed: {}", - os::error_string(os::errno() as uint))) - } + TaskBuilder::new().native().spawn(proc() { + // registering the window class + let class_name: Vec = "Window Class".utf16_units().collect::>() + .append_one(0); + + let class = ffi::WNDCLASSEX { + cbSize: mem::size_of::() as ffi::UINT, + style: ffi::CS_HREDRAW | ffi::CS_VREDRAW, + lpfnWndProc: callback, + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: unsafe { ffi::GetModuleHandleW(ptr::null()) }, + hIcon: ptr::mut_null(), + hCursor: ptr::mut_null(), + hbrBackground: ptr::mut_null(), + lpszMenuName: ptr::null(), + lpszClassName: class_name.as_ptr(), + hIconSm: ptr::mut_null(), + }; - // building a RECT object with coordinates - let mut rect = ffi::RECT { - left: 0, right: dimensions.map(|(w, _)| w as ffi::LONG).unwrap_or(1024), - top: 0, bottom: dimensions.map(|(_, h)| h as ffi::LONG).unwrap_or(768), - }; - - // switching to fullscreen - if monitor.is_some() { - let monitor = monitor.as_ref().unwrap(); - - // adjusting the rect - { - let pos = monitor.get_position(); - rect.left += pos.val0() as ffi::LONG; - rect.right += pos.val0() as ffi::LONG; - rect.top += pos.val1() as ffi::LONG; - rect.bottom += pos.val1() as ffi::LONG; - } - - // changing device settings - let mut screen_settings: ffi::DEVMODE = unsafe { mem::zeroed() }; - screen_settings.dmSize = mem::size_of::() as ffi::WORD; - screen_settings.dmPelsWidth = 1024; - screen_settings.dmPelsHeight = 768; - screen_settings.dmBitsPerPel = 32; - screen_settings.dmFields = ffi::DM_BITSPERPEL | ffi::DM_PELSWIDTH | ffi::DM_PELSHEIGHT; - - let result = unsafe { ffi::ChangeDisplaySettingsExW(monitor.get_system_name().as_ptr(), - &mut screen_settings, ptr::mut_null(), ffi::CDS_FULLSCREEN, ptr::mut_null()) }; - if result != ffi::DISP_CHANGE_SUCCESSFUL { - return Err(format!("ChangeDisplaySettings failed: {}", result)) - } - } - - // computing the style and extended style - let (ex_style, style) = if monitor.is_some() { - (ffi::WS_EX_APPWINDOW, ffi::WS_POPUP | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN) - } else { - (ffi::WS_EX_APPWINDOW | ffi::WS_EX_WINDOWEDGE, - ffi::WS_OVERLAPPEDWINDOW | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN) - }; - - // adjusting - unsafe { ffi::AdjustWindowRectEx(&mut rect, style, 0, ex_style) }; - - // creating the window - let handle = unsafe { - let handle = ffi::CreateWindowExW(ex_style, class_name.as_ptr(), - title.utf16_units().collect::>().append_one(0).as_ptr() as ffi::LPCWSTR, - style | ffi::WS_VISIBLE | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN, - if monitor.is_some() { 0 } else { ffi::CW_USEDEFAULT}, - if monitor.is_some() { 0 } else { ffi::CW_USEDEFAULT}, - rect.right - rect.left, rect.bottom - rect.top, - ptr::mut_null(), ptr::mut_null(), ffi::GetModuleHandleW(ptr::null()), - ptr::mut_null()); - - if handle.is_null() { + if unsafe { ffi::RegisterClassExW(&class) } == 0 { use std::os; - return Err(format!("CreateWindowEx function failed: {}", - os::error_string(os::errno() as uint))) + tx.send(Err(format!("RegisterClassEx function failed: {}", + os::error_string(os::errno() as uint)))); + return; } - handle - }; + // building a RECT object with coordinates + let mut rect = ffi::RECT { + left: 0, right: dimensions.map(|(w, _)| w as ffi::LONG).unwrap_or(1024), + top: 0, bottom: dimensions.map(|(_, h)| h as ffi::LONG).unwrap_or(768), + }; - // calling SetForegroundWindow if fullscreen - if monitor.is_some() { - unsafe { ffi::SetForegroundWindow(handle) }; - } + // switching to fullscreen + if monitor.is_some() { + let monitor = monitor.as_ref().unwrap(); - // adding it to WINDOWS_LIST - let events_receiver = { - let (tx, rx) = channel(); - let list = WINDOWS_LIST.get().unwrap(); - let mut list = list.lock(); - list.push((handle, tx)); - rx - }; + // adjusting the rect + { + let pos = monitor.get_position(); + rect.left += pos.val0() as ffi::LONG; + rect.right += pos.val0() as ffi::LONG; + rect.top += pos.val1() as ffi::LONG; + rect.bottom += pos.val1() as ffi::LONG; + } - // Getting the HDC of the window - let hdc = { - let hdc = unsafe { ffi::GetDC(handle) }; - if hdc.is_null() { - return Err(format!("GetDC function failed: {}", - os::error_string(os::errno() as uint))) - } - hdc - }; + // changing device settings + let mut screen_settings: ffi::DEVMODE = unsafe { mem::zeroed() }; + screen_settings.dmSize = mem::size_of::() as ffi::WORD; + screen_settings.dmPelsWidth = 1024; + screen_settings.dmPelsHeight = 768; + screen_settings.dmBitsPerPel = 32; + screen_settings.dmFields = ffi::DM_BITSPERPEL | ffi::DM_PELSWIDTH | ffi::DM_PELSHEIGHT; - // getting the pixel format that we will use - // TODO: use something cleaner which uses hints - let pixel_format = { - let mut output: ffi::PIXELFORMATDESCRIPTOR = unsafe { mem::uninitialized() }; - - if unsafe { ffi::DescribePixelFormat(hdc, 1, - mem::size_of::() as ffi::UINT, &mut output) } == 0 - { - return Err(format!("DescribePixelFormat function failed: {}", - os::error_string(os::errno() as uint))) + let result = unsafe { ffi::ChangeDisplaySettingsExW(monitor.get_system_name().as_ptr(), + &mut screen_settings, ptr::mut_null(), ffi::CDS_FULLSCREEN, ptr::mut_null()) }; + if result != ffi::DISP_CHANGE_SUCCESSFUL { + tx.send(Err(format!("ChangeDisplaySettings failed: {}", result))); + return; + } } - output - }; + // computing the style and extended style + let (ex_style, style) = if monitor.is_some() { + (ffi::WS_EX_APPWINDOW, ffi::WS_POPUP | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN) + } else { + (ffi::WS_EX_APPWINDOW | ffi::WS_EX_WINDOWEDGE, + ffi::WS_OVERLAPPEDWINDOW | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN) + }; - // calling SetPixelFormat - unsafe { - if ffi::SetPixelFormat(hdc, 1, &pixel_format) == 0 { - return Err(format!("SetPixelFormat function failed: {}", - os::error_string(os::errno() as uint))) + // adjusting + unsafe { ffi::AdjustWindowRectEx(&mut rect, style, 0, ex_style) }; + + // creating the window + let handle = unsafe { + let handle = ffi::CreateWindowExW(ex_style, class_name.as_ptr(), + title.as_slice().utf16_units().collect::>().append_one(0).as_ptr() as ffi::LPCWSTR, + style | ffi::WS_VISIBLE | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN, + if monitor.is_some() { 0 } else { ffi::CW_USEDEFAULT}, + if monitor.is_some() { 0 } else { ffi::CW_USEDEFAULT}, + rect.right - rect.left, rect.bottom - rect.top, + ptr::mut_null(), ptr::mut_null(), ffi::GetModuleHandleW(ptr::null()), + ptr::mut_null()); + + if handle.is_null() { + use std::os; + tx.send(Err(format!("CreateWindowEx function failed: {}", + os::error_string(os::errno() as uint)))); + return; + } + + handle + }; + + // calling SetForegroundWindow if fullscreen + if monitor.is_some() { + unsafe { ffi::SetForegroundWindow(handle) }; } - } - // creating the context - let context = { - let ctxt = unsafe { ffi::wglCreateContext(hdc) }; - if ctxt.is_null() { - return Err(format!("wglCreateContext function failed: {}", - os::error_string(os::errno() as uint))) + // adding it to WINDOWS_LIST + let events_receiver = { + let (tx, rx) = channel(); + WINDOW.replace(Some((handle, tx))); + rx + }; + + // Getting the HDC of the window + let hdc = { + let hdc = unsafe { ffi::GetDC(handle) }; + if hdc.is_null() { + tx.send(Err(format!("GetDC function failed: {}", + os::error_string(os::errno() as uint)))); + return; + } + hdc + }; + + // getting the pixel format that we will use + // TODO: use something cleaner which uses hints + let pixel_format = { + let mut output: ffi::PIXELFORMATDESCRIPTOR = unsafe { mem::uninitialized() }; + + if unsafe { ffi::DescribePixelFormat(hdc, 1, + mem::size_of::() as ffi::UINT, &mut output) } == 0 + { + tx.send(Err(format!("DescribePixelFormat function failed: {}", + os::error_string(os::errno() as uint)))); + return; + } + + output + }; + + // calling SetPixelFormat + unsafe { + if ffi::SetPixelFormat(hdc, 1, &pixel_format) == 0 { + tx.send(Err(format!("SetPixelFormat function failed: {}", + os::error_string(os::errno() as uint)))); + return; + } } - ctxt - }; - // loading opengl32 - let gl_library = { - let name = "opengl32.dll".utf16_units().collect::>().append_one(0).as_ptr(); - let lib = unsafe { ffi::LoadLibraryW(name) }; - if lib.is_null() { - return Err(format!("LoadLibrary function failed: {}", - os::error_string(os::errno() as uint))) + // creating the context + let context = { + let ctxt = unsafe { ffi::wglCreateContext(hdc) }; + if ctxt.is_null() { + tx.send(Err(format!("wglCreateContext function failed: {}", + os::error_string(os::errno() as uint)))); + return; + } + ctxt + }; + + // loading opengl32 + let gl_library = { + let name = "opengl32.dll".utf16_units().collect::>().append_one(0).as_ptr(); + let lib = unsafe { ffi::LoadLibraryW(name) }; + if lib.is_null() { + tx.send(Err(format!("LoadLibrary function failed: {}", + os::error_string(os::errno() as uint)))); + return; + } + lib + }; + + // building the struct + tx.send(Ok(Window{ + window: handle, + hdc: hdc, + context: context, + gl_library: gl_library, + events_receiver: events_receiver, + is_closed: AtomicBool::new(false), + })); + + // starting the events loop + loop { + let mut msg = unsafe { mem::uninitialized() }; + + if unsafe { ffi::GetMessageW(&mut msg, ptr::mut_null(), 0, 0) } == 0 { + break + } + + unsafe { ffi::TranslateMessage(&msg) }; + unsafe { ffi::DispatchMessageW(&msg) }; } - lib - }; + }); - // building the struct - Ok(Window{ - window: handle, - hdc: hdc, - context: context, - gl_library: gl_library, - events_receiver: events_receiver, - is_closed: AtomicBool::new(false), - nosend: NoSend, - }) + rx.recv() } fn send_event(window: ffi::HWND, event: Event) { - let locked = WINDOWS_LIST.get().unwrap(); - let mut locked = locked.lock(); - locked.retain(|&(ref val, ref sender)| { - if val != &window { return true } + let stored = match WINDOW.get() { + None => return, + Some(v) => v + }; - sender.send_opt(event).is_ok() - }); + let &(ref win, ref sender) = stored.deref(); + + if win != &window { + return; + } + + sender.send_opt(event).ok(); // ignoring if closed } extern "stdcall" fn callback(window: ffi::HWND, msg: ffi::UINT, diff --git a/src/win32/mod.rs b/src/win32/mod.rs index 94c15909..9d8723d9 100644 --- a/src/win32/mod.rs +++ b/src/win32/mod.rs @@ -1,10 +1,8 @@ -use std::kinds::marker::NoSend; use std::sync::atomics::AtomicBool; use std::ptr; use {Event, Hints}; pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; -pub use self::init::WINDOWS_LIST; mod event; mod ffi; @@ -18,7 +16,6 @@ pub struct Window { gl_library: ffi::HMODULE, events_receiver: Receiver, is_closed: AtomicBool, - nosend: NoSend, } impl Window { @@ -106,19 +103,6 @@ impl Window { // TODO: return iterator pub fn poll_events(&self) -> Vec { - use std::mem; - - loop { - let mut msg = unsafe { mem::uninitialized() }; - - if unsafe { ffi::PeekMessageW(&mut msg, ptr::mut_null(), 0, 0, 0x1) } == 0 { - break - } - - unsafe { ffi::TranslateMessage(&msg) }; - unsafe { ffi::DispatchMessageW(&msg) }; - } - let mut events = Vec::new(); loop { match self.events_receiver.try_recv() { @@ -137,12 +121,16 @@ impl Window { // TODO: return iterator pub fn wait_events(&self) -> Vec { - loop { - unsafe { ffi::WaitMessage() }; - - let events = self.poll_events(); - if events.len() >= 1 { - return events + match self.events_receiver.recv_opt() { + Ok(ev) => { + let mut result = self.poll_events(); + result.insert(0, ev); + result + }, + Err(_) => { + use std::sync::atomics::Relaxed; + self.is_closed.store(true, Relaxed); + vec![] } } }