From 353567b216faf83a839d78ea9504459d47b1c5db Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 16 Feb 2015 11:46:18 +0100 Subject: [PATCH 1/6] Start splitting win32::init into multiple functions --- src/win32/init.rs | 739 +++++++++++++++++++++++----------------------- 1 file changed, 375 insertions(+), 364 deletions(-) diff --git a/src/win32/init.rs b/src/win32/init.rs index c3352b6d..c0d193ad 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -1,7 +1,10 @@ use std::sync::atomic::AtomicBool; use std::ptr; +use std::mem; +use std::os; use super::callback; use super::Window; +use super::MonitorID; use BuilderAttribs; use CreationError; use CreationError::OsError; @@ -23,9 +26,6 @@ unsafe impl Send for ContextHack {} pub fn new_window(builder: BuilderAttribs<'static>, builder_sharelists: Option) -> Result { - use std::mem; - use std::os; - // initializing variables to be sent to the task let title = builder.title.as_slice().utf16_units() .chain(Some(0).into_iter()).collect::>(); // title to utf16 @@ -36,368 +36,14 @@ pub fn new_window(builder: BuilderAttribs<'static>, builder_sharelists: Option = "Window Class".utf16_units().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: callback::callback, - cbClsExtra: 0, - cbWndExtra: 0, - hInstance: unsafe { kernel32::GetModuleHandleW(ptr::null()) }, - hIcon: ptr::null_mut(), - hCursor: ptr::null_mut(), - 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. - unsafe { user32::RegisterClassExW(&class) }; - - class_name - }; - - // 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(); - - // 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 = unsafe { 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 = unsafe { user32::ChangeDisplaySettingsExW(monitor.get_system_name().as_ptr(), - &mut screen_settings, ptr::null_mut(), winapi::CDS_FULLSCREEN, ptr::null_mut()) }; - - if result != winapi::DISP_CHANGE_SUCCESSFUL { - tx.send(Err(OsError(format!("ChangeDisplaySettings failed: {}", result)))); - return; - } - } - - // computing the style and extended style of the window - let (ex_style, style) = if builder.monitor.is_some() { - (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 - unsafe { user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style) }; - - // getting the address of wglCreateContextAttribsARB and the pixel format - // that we will use - let (extra_functions, pixel_format) = { - // creating a dummy invisible window for GL initialization - let dummy_window = unsafe { - let handle = user32::CreateWindowExW(ex_style, class_name.as_ptr(), - title.as_ptr() as winapi::LPCWSTR, - style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN, - winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT, - rect.right - rect.left, rect.bottom - rect.top, - ptr::null_mut(), ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()), - ptr::null_mut()); - - if handle.is_null() { - use std::os; - tx.send(Err(OsError(format!("CreateWindowEx function failed: {}", - os::error_string(os::errno() as usize))))); - return; - } - - handle - }; - - // getting the HDC of the dummy window - let dummy_hdc = { - let hdc = unsafe { user32::GetDC(dummy_window) }; - if hdc.is_null() { - tx.send(Err(OsError(format!("GetDC function failed: {}", - os::error_string(os::errno() as usize))))); - unsafe { user32::DestroyWindow(dummy_window); } - return; - } - hdc - }; - - // getting the pixel format that we will use - let pixel_format = { - // initializing a PIXELFORMATDESCRIPTOR that indicates what we want - let mut output: winapi::PIXELFORMATDESCRIPTOR = unsafe { mem::zeroed() }; - output.nSize = mem::size_of::() as winapi::WORD; - output.nVersion = 1; - output.dwFlags = winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_DOUBLEBUFFER | - winapi::PFD_SUPPORT_OPENGL | winapi::PFD_GENERIC_ACCELERATED; - output.iPixelType = winapi::PFD_TYPE_RGBA; - output.cColorBits = 24; - output.cAlphaBits = 8; - output.cAccumBits = 0; - output.cDepthBits = 24; - output.cStencilBits = 8; - output.cAuxBuffers = 0; - output.iLayerType = winapi::PFD_MAIN_PLANE; - - let pf_index = unsafe { gdi32::ChoosePixelFormat(dummy_hdc, &output) }; - - if pf_index == 0 { - tx.send(Err(OsError(format!("ChoosePixelFormat function failed: {}", - os::error_string(os::errno() as usize))))); - unsafe { user32::DestroyWindow(dummy_window); } - return; - } - - if unsafe { gdi32::DescribePixelFormat(dummy_hdc, pf_index, - mem::size_of::() as winapi::UINT, &mut output) } == 0 - { - tx.send(Err(OsError(format!("DescribePixelFormat function failed: {}", - os::error_string(os::errno() as usize))))); - unsafe { user32::DestroyWindow(dummy_window); } - return; - } - - output - }; - - // calling SetPixelFormat - unsafe { - if gdi32::SetPixelFormat(dummy_hdc, 1, &pixel_format) == 0 { - tx.send(Err(OsError(format!("SetPixelFormat function failed: {}", - os::error_string(os::errno() as usize))))); - user32::DestroyWindow(dummy_window); - return; - } - } - - // creating the dummy OpenGL context - let dummy_context = { - let ctxt = unsafe { gl::wgl::CreateContext(dummy_hdc as *const libc::c_void) }; - if ctxt.is_null() { - tx.send(Err(OsError(format!("wglCreateContext function failed: {}", - os::error_string(os::errno() as usize))))); - unsafe { user32::DestroyWindow(dummy_window); } - return; - } - ctxt - }; - - // making context current - unsafe { gl::wgl::MakeCurrent(dummy_hdc as *const libc::c_void, dummy_context); } - - // loading the extra WGL functions - let extra_functions = gl::wgl_extra::Wgl::load_with(|addr| { - use libc; - - let addr = CString::from_slice(addr.as_bytes()); - let addr = addr.as_slice_with_nul().as_ptr(); - - unsafe { - gl::wgl::GetProcAddress(addr) as *const libc::c_void - } - }); - - // removing current context - unsafe { gl::wgl::MakeCurrent(ptr::null(), ptr::null()); } - - // destroying the context and the window - unsafe { gl::wgl::DeleteContext(dummy_context); } - unsafe { user32::DestroyWindow(dummy_window); } - - // returning the address - (extra_functions, pixel_format) - }; - - // creating the real window this time - let real_window = unsafe { - 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 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, - if builder.monitor.is_some() { 0 } else { winapi::CW_USEDEFAULT }, - if builder.monitor.is_some() { 0 } else { 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() { - use std::os; - tx.send(Err(OsError(format!("CreateWindowEx function failed: {}", - os::error_string(os::errno() as usize))))); - return; - } - - handle - }; - - // getting the HDC of the window - let hdc = { - let hdc = unsafe { user32::GetDC(real_window) }; - if hdc.is_null() { - tx.send(Err(OsError(format!("GetDC function failed: {}", - os::error_string(os::errno() as usize))))); - unsafe { user32::DestroyWindow(real_window); } - return; - } - hdc - }; - - // calling SetPixelFormat - unsafe { - if gdi32::SetPixelFormat(hdc, 1, &pixel_format) == 0 { - tx.send(Err(OsError(format!("SetPixelFormat function failed: {}", - os::error_string(os::errno() as usize))))); - user32::DestroyWindow(real_window); - return; - } - } - - // creating the OpenGL context - let context = { - use libc; - - let mut attributes = Vec::new(); - - if builder.gl_version.is_some() { - let version = builder.gl_version.as_ref().unwrap(); - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); - attributes.push(version.0 as libc::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); - attributes.push(version.1 as libc::c_int); - } - - if builder.gl_debug { - attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); - attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); - } - - attributes.push(0); - - let ctxt = unsafe { - if extra_functions.CreateContextAttribsARB.is_loaded() { - let share = if let Some(c) = builder_sharelists { c } else { ptr::null_mut() }; - extra_functions.CreateContextAttribsARB(hdc as *const libc::c_void, - share as *const libc::c_void, - attributes.as_slice().as_ptr()) - - } else { - let ctxt = gl::wgl::CreateContext(hdc as *const libc::c_void); - if let Some(c) = builder_sharelists { - gl::wgl::ShareLists(c as *const libc::c_void, ctxt); - }; - ctxt - } - }; - - if ctxt.is_null() { - tx.send(Err(OsError(format!("OpenGL context creation failed: {}", - os::error_string(os::errno() as usize))))); - unsafe { user32::DestroyWindow(real_window); } - return; - } - - ctxt - }; - - // calling SetForegroundWindow if fullscreen - if builder.monitor.is_some() { - unsafe { user32::SetForegroundWindow(real_window) }; - } - - // filling the WINDOW task-local storage - let events_receiver = { - let (tx, rx) = channel(); - let mut tx = Some(tx); - callback::WINDOW.with(|window| { - (*window.borrow_mut()) = Some((real_window, tx.take().unwrap())); - }); - rx - }; - - // loading the opengl32 module - let gl_library = { - let name = "opengl32.dll".utf16_units().chain(Some(0).into_iter()) - .collect::>().as_ptr(); - let lib = unsafe { kernel32::LoadLibraryW(name) }; - if lib.is_null() { - tx.send(Err(OsError(format!("LoadLibrary function failed: {}", - os::error_string(os::errno() as usize))))); - unsafe { gl::wgl::DeleteContext(context); } - unsafe { user32::DestroyWindow(real_window); } - return; - } - lib - }; - - // handling vsync - if builder.vsync { - if extra_functions.SwapIntervalEXT.is_loaded() { - unsafe { gl::wgl::MakeCurrent(hdc as *const libc::c_void, context) }; - if unsafe { extra_functions.SwapIntervalEXT(1) } == 0 { - tx.send(Err(OsError(format!("wglSwapIntervalEXT failed")))); - unsafe { gl::wgl::DeleteContext(context); } - unsafe { user32::DestroyWindow(real_window); } - return; - } - - // it is important to remove the current context, otherwise you get very weird - // errors - unsafe { gl::wgl::MakeCurrent(ptr::null(), ptr::null()); } - } - } - - // building the struct - let window = Window{ - window: real_window, - hdc: hdc as winapi::HDC, - context: context as winapi::HGLRC, - gl_library: gl_library, - events_receiver: events_receiver, - is_closed: AtomicBool::new(false), - }; - // sending - tx.send(Ok(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 @@ -415,3 +61,368 @@ pub fn new_window(builder: BuilderAttribs<'static>, builder_sharelists: Option, builder: BuilderAttribs<'static>, builder_sharelists: Option) + -> Result +{ + let builder_sharelists = builder_sharelists.map(|s| s.0); + + // 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(); + switch_to_fullscreen(&mut rect, monitor); + } + + // computing the style and extended style of the window + let (ex_style, style) = if builder.monitor.is_some() { + (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 + unsafe { user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style) }; + + // getting the address of wglCreateContextAttribsARB and the pixel format + // that we will use + let (extra_functions, pixel_format) = { + // creating a dummy invisible window for GL initialization + let dummy_window = unsafe { + let handle = user32::CreateWindowExW(ex_style, class_name.as_ptr(), + title.as_ptr() as winapi::LPCWSTR, + style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN, + winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + ptr::null_mut(), ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()), + ptr::null_mut()); + + if handle.is_null() { + return Err(OsError(format!("CreateWindowEx function failed: {}", + os::error_string(os::errno() as usize)))); + } + + handle + }; + + // getting the HDC of the dummy window + let dummy_hdc = { + let hdc = unsafe { user32::GetDC(dummy_window) }; + if hdc.is_null() { + let err = Err(OsError(format!("GetDC function failed: {}", + os::error_string(os::errno() as usize)))); + unsafe { user32::DestroyWindow(dummy_window); } + return err; + } + hdc + }; + + // getting the pixel format that we will use + let pixel_format = { + // initializing a PIXELFORMATDESCRIPTOR that indicates what we want + let mut output: winapi::PIXELFORMATDESCRIPTOR = unsafe { mem::zeroed() }; + output.nSize = mem::size_of::() as winapi::WORD; + output.nVersion = 1; + output.dwFlags = winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_DOUBLEBUFFER | + winapi::PFD_SUPPORT_OPENGL | winapi::PFD_GENERIC_ACCELERATED; + output.iPixelType = winapi::PFD_TYPE_RGBA; + output.cColorBits = 24; + output.cAlphaBits = 8; + output.cAccumBits = 0; + output.cDepthBits = 24; + output.cStencilBits = 8; + output.cAuxBuffers = 0; + output.iLayerType = winapi::PFD_MAIN_PLANE; + + let pf_index = unsafe { gdi32::ChoosePixelFormat(dummy_hdc, &output) }; + + if pf_index == 0 { + let err = Err(OsError(format!("ChoosePixelFormat function failed: {}", + os::error_string(os::errno() as usize)))); + unsafe { user32::DestroyWindow(dummy_window); } + return err; + } + + if unsafe { gdi32::DescribePixelFormat(dummy_hdc, pf_index, + mem::size_of::() as winapi::UINT, &mut output) } == 0 + { + let err = Err(OsError(format!("DescribePixelFormat function failed: {}", + os::error_string(os::errno() as usize)))); + unsafe { user32::DestroyWindow(dummy_window); } + return err; + } + + output + }; + + // calling SetPixelFormat + unsafe { + if gdi32::SetPixelFormat(dummy_hdc, 1, &pixel_format) == 0 { + let err = Err(OsError(format!("SetPixelFormat function failed: {}", + os::error_string(os::errno() as usize)))); + user32::DestroyWindow(dummy_window); + return err; + } + } + + // creating the dummy OpenGL context + let dummy_context = { + let ctxt = unsafe { gl::wgl::CreateContext(dummy_hdc as *const libc::c_void) }; + if ctxt.is_null() { + let err = Err(OsError(format!("wglCreateContext function failed: {}", + os::error_string(os::errno() as usize)))); + unsafe { user32::DestroyWindow(dummy_window); } + return err; + } + ctxt + }; + + // making context current + unsafe { gl::wgl::MakeCurrent(dummy_hdc as *const libc::c_void, dummy_context); } + + // loading the extra WGL functions + let extra_functions = gl::wgl_extra::Wgl::load_with(|addr| { + use libc; + + let addr = CString::from_slice(addr.as_bytes()); + let addr = addr.as_slice_with_nul().as_ptr(); + + unsafe { + gl::wgl::GetProcAddress(addr) as *const libc::c_void + } + }); + + // removing current context + unsafe { gl::wgl::MakeCurrent(ptr::null(), ptr::null()); } + + // destroying the context and the window + unsafe { gl::wgl::DeleteContext(dummy_context); } + unsafe { user32::DestroyWindow(dummy_window); } + + // returning the address + (extra_functions, pixel_format) + }; + + // creating the real window this time + let real_window = unsafe { + 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 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, + if builder.monitor.is_some() { 0 } else { winapi::CW_USEDEFAULT }, + if builder.monitor.is_some() { 0 } else { 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: {}", + os::error_string(os::errno() as usize)))); + } + + handle + }; + + // getting the HDC of the window + let hdc = { + let hdc = unsafe { user32::GetDC(real_window) }; + if hdc.is_null() { + let err = Err(OsError(format!("GetDC function failed: {}", + os::error_string(os::errno() as usize)))); + unsafe { user32::DestroyWindow(real_window); } + return err; + } + hdc + }; + + // calling SetPixelFormat + unsafe { + if gdi32::SetPixelFormat(hdc, 1, &pixel_format) == 0 { + let err = Err(OsError(format!("SetPixelFormat function failed: {}", + os::error_string(os::errno() as usize)))); + user32::DestroyWindow(real_window); + return err; + } + } + + // creating the OpenGL context + let context = { + use libc; + + let mut attributes = Vec::new(); + + if builder.gl_version.is_some() { + let version = builder.gl_version.as_ref().unwrap(); + attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); + attributes.push(version.0 as libc::c_int); + attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); + attributes.push(version.1 as libc::c_int); + } + + if builder.gl_debug { + attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); + attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); + } + + attributes.push(0); + + let ctxt = unsafe { + if extra_functions.CreateContextAttribsARB.is_loaded() { + let share = if let Some(c) = builder_sharelists { c } else { ptr::null_mut() }; + extra_functions.CreateContextAttribsARB(hdc as *const libc::c_void, + share as *const libc::c_void, + attributes.as_slice().as_ptr()) + + } else { + let ctxt = gl::wgl::CreateContext(hdc as *const libc::c_void); + if let Some(c) = builder_sharelists { + gl::wgl::ShareLists(c as *const libc::c_void, ctxt); + }; + ctxt + } + }; + + if ctxt.is_null() { + let err = Err(OsError(format!("OpenGL context creation failed: {}", + os::error_string(os::errno() as usize)))); + unsafe { user32::DestroyWindow(real_window); } + return err; + } + + ctxt + }; + + // calling SetForegroundWindow if fullscreen + if builder.monitor.is_some() { + unsafe { user32::SetForegroundWindow(real_window) }; + } + + // filling the WINDOW task-local storage + let events_receiver = { + let (tx, rx) = channel(); + let mut tx = Some(tx); + callback::WINDOW.with(|window| { + (*window.borrow_mut()) = Some((real_window, tx.take().unwrap())); + }); + rx + }; + + // loading the opengl32 module + let gl_library = { + let name = "opengl32.dll".utf16_units().chain(Some(0).into_iter()) + .collect::>().as_ptr(); + let lib = unsafe { kernel32::LoadLibraryW(name) }; + if lib.is_null() { + let err = Err(OsError(format!("LoadLibrary function failed: {}", + os::error_string(os::errno() as usize)))); + unsafe { gl::wgl::DeleteContext(context); } + unsafe { user32::DestroyWindow(real_window); } + return err; + } + lib + }; + + // handling vsync + if builder.vsync { + if extra_functions.SwapIntervalEXT.is_loaded() { + unsafe { gl::wgl::MakeCurrent(hdc as *const libc::c_void, context) }; + if unsafe { extra_functions.SwapIntervalEXT(1) } == 0 { + unsafe { gl::wgl::DeleteContext(context); } + unsafe { user32::DestroyWindow(real_window); } + return Err(OsError(format!("wglSwapIntervalEXT failed"))); + } + + // it is important to remove the current context, otherwise you get very weird + // errors + unsafe { gl::wgl::MakeCurrent(ptr::null(), ptr::null()); } + } + } + + // building the struct + Ok(Window { + window: real_window, + hdc: hdc as winapi::HDC, + context: context as winapi::HGLRC, + gl_library: gl_library, + events_receiver: events_receiver, + is_closed: AtomicBool::new(false), + }) +} + +fn register_window_class() -> Vec { + let class_name: Vec = "Window Class".utf16_units().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: callback::callback, + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: unsafe { kernel32::GetModuleHandleW(ptr::null()) }, + hIcon: ptr::null_mut(), + hCursor: ptr::null_mut(), + 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. + unsafe { user32::RegisterClassExW(&class) }; + + class_name +} + +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 = unsafe { 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 = unsafe { user32::ChangeDisplaySettingsExW(monitor.get_system_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(()) +} From dfbf2adf4a8c70f73d96761bb274476952b91dd3 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 16 Feb 2015 13:58:43 +0100 Subject: [PATCH 2/6] Extract and add create_context function --- src/win32/init.rs | 73 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/src/win32/init.rs b/src/win32/init.rs index c0d193ad..fcb23f51 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -177,19 +177,10 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O } // creating the dummy OpenGL context - let dummy_context = { - let ctxt = unsafe { gl::wgl::CreateContext(dummy_hdc as *const libc::c_void) }; - if ctxt.is_null() { - let err = Err(OsError(format!("wglCreateContext function failed: {}", - os::error_string(os::errno() as usize)))); - unsafe { user32::DestroyWindow(dummy_window); } - return err; - } - ctxt - }; + let dummy_context = try!(create_context(None, dummy_hdc, None)); // making context current - unsafe { gl::wgl::MakeCurrent(dummy_hdc as *const libc::c_void, dummy_context); } + unsafe { gl::wgl::MakeCurrent(dummy_hdc as *const libc::c_void, dummy_context as *const libc::c_void); } // loading the extra WGL functions let extra_functions = gl::wgl_extra::Wgl::load_with(|addr| { @@ -207,7 +198,7 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O unsafe { gl::wgl::MakeCurrent(ptr::null(), ptr::null()); } // destroying the context and the window - unsafe { gl::wgl::DeleteContext(dummy_context); } + unsafe { gl::wgl::DeleteContext(dummy_context as *const libc::c_void); } unsafe { user32::DestroyWindow(dummy_window); } // returning the address @@ -426,3 +417,61 @@ fn switch_to_fullscreen(rect: &mut winapi::RECT, monitor: &MonitorID) -> Result< Ok(()) } + +fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'static>)>, + hdc: winapi::HDC, share: Option) + -> Result +{ + let share = share.unwrap_or(ptr::null_mut()); + + let ctxt = if let Some((extra_functions, builder)) = extra { + if extra_functions.CreateContextAttribsARB.is_loaded() { + let mut attributes = Vec::new(); + + if builder.gl_version.is_some() { + let version = builder.gl_version.as_ref().unwrap(); + attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); + attributes.push(version.0 as libc::c_int); + attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); + attributes.push(version.1 as libc::c_int); + } + + if builder.gl_debug { + attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); + attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); + } + + attributes.push(0); + + Some(unsafe { + extra_functions.CreateContextAttribsARB(hdc as *const libc::c_void, + share as *const libc::c_void, + attributes.as_slice().as_ptr()) + }) + } else { + None + } + } else { + None + }; + + let ctxt = match ctxt { + Some(ctxt) => ctxt, + None => { + unsafe { + let ctxt = gl::wgl::CreateContext(hdc as *const libc::c_void); + if !share.is_null() { + gl::wgl::ShareLists(share as *const libc::c_void, ctxt); + }; + ctxt + } + } + }; + + if ctxt.is_null() { + return Err(OsError(format!("OpenGL context creation failed: {}", + os::error_string(os::errno() as usize)))); + } + + Ok(ctxt as winapi::HGLRC) +} From 6bec85e0cc7251e9e12952ad38ef8aa24c772ee8 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 16 Feb 2015 15:36:32 +0100 Subject: [PATCH 3/6] Implement better handling for pixel formats --- src/lib.rs | 53 +++++++++++++++++++++++++++++- src/win32/init.rs | 82 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 110 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b251a7a3..1bd29bb3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -170,6 +170,21 @@ pub enum MouseCursor { RowResize, } +/// Describes a possible format. Unused. +#[allow(missing_docs)] +pub struct PixelFormat { + pub red_bits: u8, + pub green_bits: u8, + pub blue_bits: u8, + pub alpha_bits: u8, + pub depth_bits: u8, + pub stencil_bits: u8, + pub stereoscopy: bool, + pub double_buffer: bool, + pub multisampling: Option, + pub srgb: bool, +} + /// Attributes struct BuilderAttribs<'a> { #[allow(dead_code)] @@ -239,5 +254,41 @@ impl<'a> BuilderAttribs<'a> { (new_attribs, sharing) } -} + fn choose_pixel_format(&self, iter: I) -> (T, PixelFormat) + where I: Iterator, T: Clone + { + let mut current_result = None; + + // TODO: do this more properly + for (id, format) in iter { + if format.red_bits + format.green_bits + format.blue_bits < self.color_bits.unwrap_or(0) { + continue; + } + + if format.alpha_bits < self.alpha_bits.unwrap_or(0) { + continue; + } + + if format.depth_bits < self.depth_bits.unwrap_or(0) { + continue; + } + + if format.stencil_bits < self.stencil_bits.unwrap_or(0) { + continue; + } + + if !format.stereoscopy && self.stereoscopy { + continue; + } + + if self.multisampling.is_some() && format.multisampling.is_none() { + continue; + } + + current_result = Some((id, format)); + } + + current_result.expect("Could not find compliant pixel format") + } +} diff --git a/src/win32/init.rs b/src/win32/init.rs index fcb23f51..357b87db 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -8,6 +8,7 @@ use super::MonitorID; use BuilderAttribs; use CreationError; use CreationError::OsError; +use PixelFormat; use std::ffi::CString; use std::sync::mpsc::channel; @@ -130,31 +131,11 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O // getting the pixel format that we will use let pixel_format = { - // initializing a PIXELFORMATDESCRIPTOR that indicates what we want + let formats = enumerate_native_pixel_formats(dummy_hdc); + let (id, _) = builder.choose_pixel_format(formats.into_iter().map(|(a, b)| (b, a))); + let mut output: winapi::PIXELFORMATDESCRIPTOR = unsafe { mem::zeroed() }; - output.nSize = mem::size_of::() as winapi::WORD; - output.nVersion = 1; - output.dwFlags = winapi::PFD_DRAW_TO_WINDOW | winapi::PFD_DOUBLEBUFFER | - winapi::PFD_SUPPORT_OPENGL | winapi::PFD_GENERIC_ACCELERATED; - output.iPixelType = winapi::PFD_TYPE_RGBA; - output.cColorBits = 24; - output.cAlphaBits = 8; - output.cAccumBits = 0; - output.cDepthBits = 24; - output.cStencilBits = 8; - output.cAuxBuffers = 0; - output.iLayerType = winapi::PFD_MAIN_PLANE; - - let pf_index = unsafe { gdi32::ChoosePixelFormat(dummy_hdc, &output) }; - - if pf_index == 0 { - let err = Err(OsError(format!("ChoosePixelFormat function failed: {}", - os::error_string(os::errno() as usize)))); - unsafe { user32::DestroyWindow(dummy_window); } - return err; - } - - if unsafe { gdi32::DescribePixelFormat(dummy_hdc, pf_index, + if unsafe { gdi32::DescribePixelFormat(dummy_hdc, id, mem::size_of::() as winapi::UINT, &mut output) } == 0 { let err = Err(OsError(format!("DescribePixelFormat function failed: {}", @@ -475,3 +456,56 @@ fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'static>)> Ok(ctxt as winapi::HGLRC) } + +fn enumerate_native_pixel_formats(hdc: winapi::HDC) -> Vec<(PixelFormat, libc::c_int)> { + let size_of_pxfmtdescr = mem::size_of::() as u32; + let num = unsafe { gdi32::DescribePixelFormat(hdc, 1, size_of_pxfmtdescr, ptr::null_mut()) }; + + let mut result = Vec::new(); + + for index in (0 .. num) { + let mut output: winapi::PIXELFORMATDESCRIPTOR = unsafe { mem::zeroed() }; + + if unsafe { gdi32::DescribePixelFormat(hdc, index, size_of_pxfmtdescr, + &mut output) } == 0 + { + continue; + } + + if (output.dwFlags & winapi::PFD_DRAW_TO_WINDOW) == 0 { + continue; + } + if (output.dwFlags & winapi::PFD_SUPPORT_OPENGL) == 0 { + continue; + } + + if (output.dwFlags & winapi::PFD_GENERIC_ACCELERATED) == 0 && + (output.dwFlags & winapi::PFD_GENERIC_FORMAT) == 0 + { + continue; + } + + if output.iPixelType != winapi::PFD_TYPE_RGBA { + continue; + } + + result.push((PixelFormat { + red_bits: output.cRedBits, + green_bits: output.cGreenBits, + blue_bits: output.cBlueBits, + alpha_bits: output.cAlphaBits, + depth_bits: output.cDepthBits, + stencil_bits: output.cStencilBits, + stereoscopy: (output.dwFlags & winapi::PFD_STEREO) != 0, + double_buffer: (output.dwFlags & winapi::PFD_DOUBLEBUFFER) != 0, + multisampling: None, + srgb: false, + }, index)); + } + + result +} + +fn enumerate_arb_pixel_formats(extra: &gl::wgl_extra::Wgl, hdc: winapi::HDC) -> Vec { + unimplemented!() +} From d1bc85c011ae8328936ba7bef9cd8c463e040dd3 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 16 Feb 2015 15:42:00 +0100 Subject: [PATCH 4/6] Switch to create_context for the real context --- src/win32/init.rs | 54 +++++------------------------------------------ 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/src/win32/init.rs b/src/win32/init.rs index 357b87db..efb4405a 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -240,51 +240,7 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O } // creating the OpenGL context - let context = { - use libc; - - let mut attributes = Vec::new(); - - if builder.gl_version.is_some() { - let version = builder.gl_version.as_ref().unwrap(); - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); - attributes.push(version.0 as libc::c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); - attributes.push(version.1 as libc::c_int); - } - - if builder.gl_debug { - attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); - attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); - } - - attributes.push(0); - - let ctxt = unsafe { - if extra_functions.CreateContextAttribsARB.is_loaded() { - let share = if let Some(c) = builder_sharelists { c } else { ptr::null_mut() }; - extra_functions.CreateContextAttribsARB(hdc as *const libc::c_void, - share as *const libc::c_void, - attributes.as_slice().as_ptr()) - - } else { - let ctxt = gl::wgl::CreateContext(hdc as *const libc::c_void); - if let Some(c) = builder_sharelists { - gl::wgl::ShareLists(c as *const libc::c_void, ctxt); - }; - ctxt - } - }; - - if ctxt.is_null() { - let err = Err(OsError(format!("OpenGL context creation failed: {}", - os::error_string(os::errno() as usize)))); - unsafe { user32::DestroyWindow(real_window); } - return err; - } - - ctxt - }; + let context = try!(create_context(Some((&extra_functions, &builder)), hdc, builder_sharelists)); // calling SetForegroundWindow if fullscreen if builder.monitor.is_some() { @@ -309,7 +265,7 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O if lib.is_null() { let err = Err(OsError(format!("LoadLibrary function failed: {}", os::error_string(os::errno() as usize)))); - unsafe { gl::wgl::DeleteContext(context); } + unsafe { gl::wgl::DeleteContext(context as *const libc::c_void); } unsafe { user32::DestroyWindow(real_window); } return err; } @@ -319,9 +275,9 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O // handling vsync if builder.vsync { if extra_functions.SwapIntervalEXT.is_loaded() { - unsafe { gl::wgl::MakeCurrent(hdc as *const libc::c_void, context) }; + unsafe { gl::wgl::MakeCurrent(hdc as *const libc::c_void, context as *const libc::c_void) }; if unsafe { extra_functions.SwapIntervalEXT(1) } == 0 { - unsafe { gl::wgl::DeleteContext(context); } + unsafe { gl::wgl::DeleteContext(context as *const libc::c_void); } unsafe { user32::DestroyWindow(real_window); } return Err(OsError(format!("wglSwapIntervalEXT failed"))); } @@ -336,7 +292,7 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O Ok(Window { window: real_window, hdc: hdc as winapi::HDC, - context: context as winapi::HGLRC, + context: context, gl_library: gl_library, events_receiver: events_receiver, is_closed: AtomicBool::new(false), From 950fd58b77041f4c2e62be1e345df8554a447623 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 16 Feb 2015 15:55:26 +0100 Subject: [PATCH 5/6] Implement enumerate_arb_pixel_formats --- build.rs | 4 +++- src/win32/init.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/build.rs b/build.rs index 695d3979..a151502a 100644 --- a/build.rs +++ b/build.rs @@ -22,7 +22,9 @@ fn main() { khronos_api::WGL_XML, vec![ "WGL_ARB_create_context".to_string(), - "WGL_EXT_swap_control".to_string() + "WGL_EXT_swap_control".to_string(), + "WGL_ARB_pixel_format".to_string(), + "WGL_ARB_framebuffer_sRGB".to_string(), ], "1.0", "core", &mut file).unwrap(); } diff --git a/src/win32/init.rs b/src/win32/init.rs index efb4405a..9803e432 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -462,6 +462,52 @@ fn enumerate_native_pixel_formats(hdc: winapi::HDC) -> Vec<(PixelFormat, libc::c result } -fn enumerate_arb_pixel_formats(extra: &gl::wgl_extra::Wgl, hdc: winapi::HDC) -> Vec { - unimplemented!() +fn enumerate_arb_pixel_formats(extra: &gl::wgl_extra::Wgl, hdc: winapi::HDC) + -> Vec<(PixelFormat, libc::c_int)> +{ + let get_info = |index: u32, attrib: u32| { + let mut value = unsafe { mem::uninitialized() }; + unsafe { extra.GetPixelFormatAttribivARB(hdc as *const libc::c_void, index as libc::c_int, + 0, 1, [attrib as libc::c_int].as_ptr(), + &mut value) }; + value as u32 + }; + + // getting the number of formats + // the `1` is ignored + let num = get_info(1, gl::wgl_extra::NUMBER_PIXEL_FORMATS_ARB); + + let mut result = Vec::new(); + + for index in (0 .. num) { + if get_info(index, gl::wgl_extra::DRAW_TO_WINDOW_ARB) == 0 { + continue; + } + if get_info(index, gl::wgl_extra::SUPPORT_OPENGL_ARB) == 0 { + continue; + } + + if get_info(index, gl::wgl_extra::ACCELERATION_ARB) == gl::wgl_extra::NO_ACCELERATION_ARB { + continue; + } + + if get_info(index, gl::wgl_extra::PIXEL_TYPE_ARB) != gl::wgl_extra::TYPE_RGBA_ARB { + continue; + } + + result.push((PixelFormat { + red_bits: get_info(index, gl::wgl_extra::RED_BITS_ARB) as u8, + green_bits: get_info(index, gl::wgl_extra::GREEN_BITS_ARB) as u8, + blue_bits: get_info(index, gl::wgl_extra::BLUE_BITS_ARB) as u8, + alpha_bits: get_info(index, gl::wgl_extra::ALPHA_BITS_ARB) as u8, + depth_bits: get_info(index, gl::wgl_extra::DEPTH_BITS_ARB) as u8, + stencil_bits: get_info(index, gl::wgl_extra::STENCIL_BITS_ARB) as u8, + stereoscopy: get_info(index, gl::wgl_extra::STEREO_ARB) != 0, + double_buffer: get_info(index, gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0, + multisampling: None, // FIXME: + srgb: false, // FIXME: + }, index as libc::c_int)); + } + + result } From 1a33c9ce9fa5f932d71e2eadf5b7f4ac29b187ee Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 16 Feb 2015 15:57:17 +0100 Subject: [PATCH 6/6] Extract load_opengl32_dll --- src/win32/init.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/win32/init.rs b/src/win32/init.rs index 9803e432..90a6035b 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -258,19 +258,7 @@ fn init(title: Vec, builder: BuilderAttribs<'static>, builder_sharelists: O }; // loading the opengl32 module - let gl_library = { - let name = "opengl32.dll".utf16_units().chain(Some(0).into_iter()) - .collect::>().as_ptr(); - let lib = unsafe { kernel32::LoadLibraryW(name) }; - if lib.is_null() { - let err = Err(OsError(format!("LoadLibrary function failed: {}", - os::error_string(os::errno() as usize)))); - unsafe { gl::wgl::DeleteContext(context as *const libc::c_void); } - unsafe { user32::DestroyWindow(real_window); } - return err; - } - lib - }; + let gl_library = try!(load_opengl32_dll()); // handling vsync if builder.vsync { @@ -511,3 +499,17 @@ fn enumerate_arb_pixel_formats(extra: &gl::wgl_extra::Wgl, hdc: winapi::HDC) result } + +fn load_opengl32_dll() -> Result { + let name = "opengl32.dll".utf16_units().chain(Some(0).into_iter()) + .collect::>().as_ptr(); + + let lib = unsafe { kernel32::LoadLibraryW(name) }; + + if lib.is_null() { + return Err(OsError(format!("LoadLibrary function failed: {}", + os::error_string(os::errno() as usize)))); + } + + Ok(lib) +}