use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex}; use std::io; use std::ptr; use std::mem; use std::thread; use libc; use super::callback; use super::Window; use super::MonitorID; use super::WindowWrapper; use super::Context; use Api; use BuilderAttribs; use CreationError; use CreationError::OsError; use CursorState; use GlRequest; use std::ffi::{CString, OsStr}; use std::os::windows::ffi::OsStrExt; use std::sync::mpsc::channel; use winapi; use kernel32; use dwmapi; use user32; use api::wgl; use api::wgl::Context as WglContext; use api::egl; use api::egl::Context as EglContext; pub enum RawContext { Egl(egl::ffi::egl::types::EGLContext), Wgl(winapi::HGLRC), } unsafe impl Send for RawContext {} unsafe impl Sync for RawContext {} pub fn new_window(builder: BuilderAttribs<'static>, builder_sharelists: Option) -> Result { // initializing variables to be sent to the task let title = OsStr::new(&builder.title).encode_wide().chain(Some(0).into_iter()) .collect::>(); let (tx, rx) = channel(); // `GetMessage` must be called in the same thread as CreateWindow, so we create a new thread // dedicated to this window. thread::spawn(move || { unsafe { // creating and sending the `Window` match init(title, builder, builder_sharelists) { Ok(w) => tx.send(Ok(w)).ok(), Err(e) => { tx.send(Err(e)).ok(); return; } }; // now that the `Window` struct is initialized, the main `Window::new()` function will // return and this events loop will run in parallel loop { let mut msg = mem::uninitialized(); if user32::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 { break; } user32::TranslateMessage(&msg); user32::DispatchMessageW(&msg); // calls `callback` (see the callback module) } } }); rx.recv().unwrap() } unsafe fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: Option) -> Result { // registering the window class let class_name = register_window_class(); // building a RECT object with coordinates let mut rect = winapi::RECT { left: 0, right: builder.dimensions.unwrap_or((1024, 768)).0 as winapi::LONG, top: 0, bottom: builder.dimensions.unwrap_or((1024, 768)).1 as winapi::LONG, }; // switching to fullscreen if necessary // this means adjusting the window's position so that it overlaps the right monitor, // and change the monitor's resolution if necessary if builder.monitor.is_some() { let monitor = builder.monitor.as_ref().unwrap(); try!(switch_to_fullscreen(&mut rect, monitor)); } // computing the style and extended style of the window let (ex_style, style) = if builder.monitor.is_some() || builder.decorations == false { (winapi::WS_EX_APPWINDOW, winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN) } else { (winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE, winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN) }; // adjusting the window coordinates using the style user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style); // creating the real window this time, by using the functions in `extra_functions` let real_window = { let (width, height) = if builder.monitor.is_some() || builder.dimensions.is_some() { (Some(rect.right - rect.left), Some(rect.bottom - rect.top)) } else { (None, None) }; let (x, y) = if builder.monitor.is_some() { (Some(rect.left), Some(rect.top)) } else { (None, None) }; let style = if !builder.visible || builder.headless { style } else { style | winapi::WS_VISIBLE }; let handle = user32::CreateWindowExW(ex_style, class_name.as_ptr(), title.as_ptr() as winapi::LPCWSTR, style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN, x.unwrap_or(winapi::CW_USEDEFAULT), y.unwrap_or(winapi::CW_USEDEFAULT), width.unwrap_or(winapi::CW_USEDEFAULT), height.unwrap_or(winapi::CW_USEDEFAULT), ptr::null_mut(), ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()), ptr::null_mut()); if handle.is_null() { return Err(OsError(format!("CreateWindowEx function failed: {}", format!("{}", io::Error::last_os_error())))); } let hdc = user32::GetDC(handle); if hdc.is_null() { return Err(OsError(format!("GetDC function failed: {}", format!("{}", io::Error::last_os_error())))); } WindowWrapper(handle, hdc) }; // creating the OpenGL context let context = match builder.gl_version { GlRequest::Specific(Api::OpenGlEs, (major, minor)) => { // trying to load EGL from the ATI drivers // TODO: use LoadLibraryA instead let dll_name = if cfg!(target_pointer_width = "64") { "atio6axx.dll" } else { "atioglxx.dll" }; let dll_name = OsStr::new(dll_name).encode_wide().chain(Some(0).into_iter()) .collect::>(); let dll = unsafe { kernel32::LoadLibraryW(dll_name.as_ptr()) }; if !dll.is_null() { let egl = ::api::egl::ffi::egl::Egl::load_with(|name| { let name = CString::new(name).unwrap(); unsafe { kernel32::GetProcAddress(dll, name.as_ptr()) as *const libc::c_void } }); if let Ok(c) = EglContext::new(egl, &builder, Some(ptr::null()), real_window.0) { Context::Egl(c) } else { let builder_sharelists = match builder_sharelists { None => None, Some(RawContext::Wgl(c)) => Some(c), _ => unimplemented!() }; try!(WglContext::new(&builder, real_window.0, builder_sharelists) .map(Context::Wgl)) } } else { // falling back to WGL, which is always available let builder_sharelists = match builder_sharelists { None => None, Some(RawContext::Wgl(c)) => Some(c), _ => unimplemented!() }; try!(WglContext::new(&builder, real_window.0, builder_sharelists) .map(Context::Wgl)) } }, _ => { let builder_sharelists = match builder_sharelists { None => None, Some(RawContext::Wgl(c)) => Some(c), _ => unimplemented!() }; try!(WglContext::new(&builder, real_window.0, builder_sharelists).map(Context::Wgl)) } }; // making the window transparent if builder.transparent { let bb = winapi::DWM_BLURBEHIND { dwFlags: 0x1, // FIXME: DWM_BB_ENABLE; fEnable: 1, hRgnBlur: ptr::null_mut(), fTransitionOnMaximized: 0, }; unsafe { dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb); } } // calling SetForegroundWindow if fullscreen if builder.monitor.is_some() { user32::SetForegroundWindow(real_window.0); } // Creating a mutex to track the current cursor state let cursor_state = Arc::new(Mutex::new(CursorState::Normal)); // filling the CONTEXT_STASH task-local storage so that we can start receiving events let events_receiver = { let (tx, rx) = channel(); let mut tx = Some(tx); callback::CONTEXT_STASH.with(|context_stash| { let data = callback::ThreadLocalData { win: real_window.0, sender: tx.take().unwrap(), cursor_state: cursor_state.clone() }; (*context_stash.borrow_mut()) = Some(data); }); rx }; // building the struct Ok(Window { window: real_window, context: context, events_receiver: events_receiver, cursor_state: cursor_state, }) } unsafe fn register_window_class() -> Vec { let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter()) .collect::>(); let class = winapi::WNDCLASSEXW { cbSize: mem::size_of::() as winapi::UINT, style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC, lpfnWndProc: Some(callback::callback), cbClsExtra: 0, cbWndExtra: 0, hInstance: kernel32::GetModuleHandleW(ptr::null()), hIcon: ptr::null_mut(), hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly hbrBackground: ptr::null_mut(), lpszMenuName: ptr::null(), lpszClassName: class_name.as_ptr(), hIconSm: ptr::null_mut(), }; // We ignore errors because registering the same window class twice would trigger // an error, and because errors here are detected during CreateWindowEx anyway. // Also since there is no weird element in the struct, there is no reason for this // call to fail. user32::RegisterClassExW(&class); class_name } unsafe fn switch_to_fullscreen(rect: &mut winapi::RECT, monitor: &MonitorID) -> Result<(), CreationError> { // adjusting the rect { let pos = monitor.get_position(); rect.left += pos.0 as winapi::LONG; rect.right += pos.0 as winapi::LONG; rect.top += pos.1 as winapi::LONG; rect.bottom += pos.1 as winapi::LONG; } // changing device settings let mut screen_settings: winapi::DEVMODEW = mem::zeroed(); screen_settings.dmSize = mem::size_of::() as winapi::WORD; screen_settings.dmPelsWidth = (rect.right - rect.left) as winapi::DWORD; screen_settings.dmPelsHeight = (rect.bottom - rect.top) as winapi::DWORD; screen_settings.dmBitsPerPel = 32; // TODO: ? screen_settings.dmFields = winapi::DM_BITSPERPEL | winapi::DM_PELSWIDTH | winapi::DM_PELSHEIGHT; let result = user32::ChangeDisplaySettingsExW(monitor.get_adapter_name().as_ptr(), &mut screen_settings, ptr::null_mut(), winapi::CDS_FULLSCREEN, ptr::null_mut()); if result != winapi::DISP_CHANGE_SUCCESSFUL { return Err(OsError(format!("ChangeDisplaySettings failed: {}", result))); } Ok(()) }