diff options
Diffstat (limited to 'src/api/win32')
| -rw-r--r-- | src/api/win32/callback.rs | 253 | ||||
| -rw-r--r-- | src/api/win32/event.rs | 181 | ||||
| -rw-r--r-- | src/api/win32/gl.rs | 12 | ||||
| -rw-r--r-- | src/api/win32/headless.rs | 40 | ||||
| -rw-r--r-- | src/api/win32/init.rs | 586 | ||||
| -rw-r--r-- | src/api/win32/make_current_guard.rs | 52 | ||||
| -rw-r--r-- | src/api/win32/mod.rs | 419 | ||||
| -rw-r--r-- | src/api/win32/monitor.rs | 180 | 
8 files changed, 1723 insertions, 0 deletions
diff --git a/src/api/win32/callback.rs b/src/api/win32/callback.rs new file mode 100644 index 0000000..e852eeb --- /dev/null +++ b/src/api/win32/callback.rs @@ -0,0 +1,253 @@ +use std::mem; +use std::ptr; +use std::cell::RefCell; +use std::sync::mpsc::Sender; +use std::sync::{Arc, Mutex}; + +use CursorState; +use Event; +use super::event; + +use user32; +use winapi; + +/// There's no parameters passed to the callback function, so it needs to get +/// its context (the HWND, the Sender for events, etc.) stashed in +/// a thread-local variable. +thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None)); + +pub struct ThreadLocalData { +    pub win: winapi::HWND, +    pub sender: Sender<Event>, +    pub cursor_state: Arc<Mutex<CursorState>> +} + +/// Checks that the window is the good one, and if so send the event to it. +fn send_event(input_window: winapi::HWND, event: Event) { +    CONTEXT_STASH.with(|context_stash| { +        let context_stash = context_stash.borrow(); +        let stored = match *context_stash { +            None => return, +            Some(ref v) => v +        }; + +        let &ThreadLocalData { ref win, ref sender, .. } = stored; + +        if win != &input_window { +            return; +        } + +        sender.send(event).ok();  // ignoring if closed +    }); +} + +/// This is the callback that is called by `DispatchMessage` in the events loop. +///  +/// Returning 0 tells the Win32 API that the message has been processed. +pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, +                                       wparam: winapi::WPARAM, lparam: winapi::LPARAM) +                                       -> winapi::LRESULT +{ +    match msg { +        winapi::WM_DESTROY => { +            use events::Event::Closed; + +            CONTEXT_STASH.with(|context_stash| { +                let context_stash = context_stash.borrow(); +                let stored = match *context_stash { +                    None => return, +                    Some(ref v) => v +                }; + +                let &ThreadLocalData { ref win, .. } = stored; + +                if win == &window { +                    user32::PostQuitMessage(0); +                } +            }); + +            send_event(window, Closed); +            0 +        }, + +        winapi::WM_ERASEBKGND => { +            1 +        }, + +        winapi::WM_SIZE => { +            use events::Event::Resized; +            let w = winapi::LOWORD(lparam as winapi::DWORD) as u32; +            let h = winapi::HIWORD(lparam as winapi::DWORD) as u32; +            send_event(window, Resized(w, h)); +            0 +        }, + +        winapi::WM_MOVE => { +            use events::Event::Moved; +            let x = winapi::LOWORD(lparam as winapi::DWORD) as i32; +            let y = winapi::HIWORD(lparam as winapi::DWORD) as i32; +            send_event(window, Moved(x, y)); +            0 +        }, + +        winapi::WM_CHAR => { +            use std::mem; +            use events::Event::ReceivedCharacter; +            let chr: char = mem::transmute(wparam as u32); +            send_event(window, ReceivedCharacter(chr)); +            0 +        }, + +        winapi::WM_MOUSEMOVE => { +            use events::Event::MouseMoved; + +            let x = winapi::GET_X_LPARAM(lparam) as i32; +            let y = winapi::GET_Y_LPARAM(lparam) as i32; + +            send_event(window, MouseMoved((x, y))); + +            0 +        }, + +        winapi::WM_MOUSEWHEEL => { +            use events::Event::MouseWheel; + +            let value = (wparam >> 16) as i16; +            let value = value as i32; + +            send_event(window, MouseWheel(value)); + +            0 +        }, + +        winapi::WM_KEYDOWN => { +            use events::Event::KeyboardInput; +            use events::ElementState::Pressed; +            let scancode = ((lparam >> 16) & 0xff) as u8; +            let vkey = event::vkeycode_to_element(wparam); +            send_event(window, KeyboardInput(Pressed, scancode, vkey)); +            0 +        }, + +        winapi::WM_KEYUP => { +            use events::Event::KeyboardInput; +            use events::ElementState::Released; +            let scancode = ((lparam >> 16) & 0xff) as u8; +            let vkey = event::vkeycode_to_element(wparam); +            send_event(window, KeyboardInput(Released, scancode, vkey)); +            0 +        }, + +        winapi::WM_LBUTTONDOWN => { +            use events::Event::MouseInput; +            use events::MouseButton::Left; +            use events::ElementState::Pressed; +            send_event(window, MouseInput(Pressed, Left)); +            0 +        }, + +        winapi::WM_LBUTTONUP => { +            use events::Event::MouseInput; +            use events::MouseButton::Left; +            use events::ElementState::Released; +            send_event(window, MouseInput(Released, Left)); +            0 +        }, + +        winapi::WM_RBUTTONDOWN => { +            use events::Event::MouseInput; +            use events::MouseButton::Right; +            use events::ElementState::Pressed; +            send_event(window, MouseInput(Pressed, Right)); +            0 +        }, + +        winapi::WM_RBUTTONUP => { +            use events::Event::MouseInput; +            use events::MouseButton::Right; +            use events::ElementState::Released; +            send_event(window, MouseInput(Released, Right)); +            0 +        }, + +        winapi::WM_MBUTTONDOWN => { +            use events::Event::MouseInput; +            use events::MouseButton::Middle; +            use events::ElementState::Pressed; +            send_event(window, MouseInput(Pressed, Middle)); +            0 +        }, + +        winapi::WM_MBUTTONUP => { +            use events::Event::MouseInput; +            use events::MouseButton::Middle; +            use events::ElementState::Released; +            send_event(window, MouseInput(Released, Middle)); +            0 +        }, + +        winapi::WM_INPUT => { +            let mut data: winapi::RAWINPUT = mem::uninitialized(); +            let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT; +            user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT, +                                    mem::transmute(&mut data), &mut data_size, +                                    mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT); + +            if data.header.dwType == winapi::RIM_TYPEMOUSE { +                let _x = data.mouse.lLastX;  // FIXME: this is not always the relative movement +                let _y = data.mouse.lLastY; +                // TODO:  +                //send_event(window, Event::MouseRawMovement { x: x, y: y }); + +                0 + +            } else { +                user32::DefWindowProcW(window, msg, wparam, lparam) +            } +        }, + +        winapi::WM_SETFOCUS => { +            use events::Event::Focused; +            send_event(window, Focused(true)); +            0 +        }, + +        winapi::WM_KILLFOCUS => { +            use events::Event::Focused; +            send_event(window, Focused(false)); +            0 +        }, + +        winapi::WM_SETCURSOR => { +            CONTEXT_STASH.with(|context_stash| { +                let cstash = context_stash.borrow(); +                let cstash = cstash.as_ref(); +                // there's a very bizarre borrow checker bug +                // possibly related to rust-lang/rust/#23338 +                let cursor_state = if let Some(cstash) = cstash {  +                    if let Ok(cursor_state) = cstash.cursor_state.lock() { +                        match *cursor_state { +                            CursorState::Normal => { +                                user32::SetCursor(user32::LoadCursorW( +                                        ptr::null_mut(), +                                        winapi::IDC_ARROW)); +                            }, +                            CursorState::Grab | CursorState::Hide => { +                                user32::SetCursor(ptr::null_mut()); +                            } +                        } +                    } +                } else { +                    return +                }; + +//                let &ThreadLocalData { ref cursor_state, .. } = stored; +            }); +            0 +        }, + +        _ => { +            user32::DefWindowProcW(window, msg, wparam, lparam) +        } +    } +} diff --git a/src/api/win32/event.rs b/src/api/win32/event.rs new file mode 100644 index 0000000..4c8f4f3 --- /dev/null +++ b/src/api/win32/event.rs @@ -0,0 +1,181 @@ +use events::VirtualKeyCode; +use winapi; + +pub fn vkeycode_to_element(code: winapi::WPARAM) -> Option<VirtualKeyCode> { +    match code { +        //winapi::VK_LBUTTON => Some(VirtualKeyCode::Lbutton), +        //winapi::VK_RBUTTON => Some(VirtualKeyCode::Rbutton), +        //winapi::VK_CANCEL => Some(VirtualKeyCode::Cancel), +        //winapi::VK_MBUTTON => Some(VirtualKeyCode::Mbutton), +        //winapi::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1), +        //winapi::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2), +        winapi::VK_BACK => Some(VirtualKeyCode::Back), +        winapi::VK_TAB => Some(VirtualKeyCode::Tab), +        //winapi::VK_CLEAR => Some(VirtualKeyCode::Clear), +        winapi::VK_RETURN => Some(VirtualKeyCode::Return), +        //winapi::VK_SHIFT => Some(VirtualKeyCode::Shift), +        //winapi::VK_CONTROL => Some(VirtualKeyCode::Control), +        //winapi::VK_MENU => Some(VirtualKeyCode::Menu), +        winapi::VK_PAUSE => Some(VirtualKeyCode::Pause), +        winapi::VK_CAPITAL => Some(VirtualKeyCode::Capital), +        winapi::VK_KANA => Some(VirtualKeyCode::Kana), +        //winapi::VK_HANGUEL => Some(VirtualKeyCode::Hanguel), +        //winapi::VK_HANGUL => Some(VirtualKeyCode::Hangul), +        //winapi::VK_JUNJA => Some(VirtualKeyCode::Junja), +        //winapi::VK_FINAL => Some(VirtualKeyCode::Final), +        //winapi::VK_HANJA => Some(VirtualKeyCode::Hanja), +        winapi::VK_KANJI => Some(VirtualKeyCode::Kanji), +        winapi::VK_ESCAPE => Some(VirtualKeyCode::Escape), +        winapi::VK_CONVERT => Some(VirtualKeyCode::Convert), +        //winapi::VK_NONCONVERT => Some(VirtualKeyCode::Nonconvert), +        //winapi::VK_ACCEPT => Some(VirtualKeyCode::Accept), +        //winapi::VK_MODECHANGE => Some(VirtualKeyCode::Modechange), +        winapi::VK_SPACE => Some(VirtualKeyCode::Space), +        winapi::VK_PRIOR => Some(VirtualKeyCode::PageUp), +        winapi::VK_NEXT => Some(VirtualKeyCode::PageDown), +        winapi::VK_END => Some(VirtualKeyCode::End), +        winapi::VK_HOME => Some(VirtualKeyCode::Home), +        winapi::VK_LEFT => Some(VirtualKeyCode::Left), +        winapi::VK_UP => Some(VirtualKeyCode::Up), +        winapi::VK_RIGHT => Some(VirtualKeyCode::Right), +        winapi::VK_DOWN => Some(VirtualKeyCode::Down), +        //winapi::VK_SELECT => Some(VirtualKeyCode::Select), +        //winapi::VK_PRINT => Some(VirtualKeyCode::Print), +        //winapi::VK_EXECUTE => Some(VirtualKeyCode::Execute), +        winapi::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot), +        winapi::VK_INSERT => Some(VirtualKeyCode::Insert), +        winapi::VK_DELETE => Some(VirtualKeyCode::Delete), +        //winapi::VK_HELP => Some(VirtualKeyCode::Help), +        0x30 => Some(VirtualKeyCode::Key0), +        0x31 => Some(VirtualKeyCode::Key1), +        0x32 => Some(VirtualKeyCode::Key2), +        0x33 => Some(VirtualKeyCode::Key3), +        0x34 => Some(VirtualKeyCode::Key4), +        0x35 => Some(VirtualKeyCode::Key5), +        0x36 => Some(VirtualKeyCode::Key6), +        0x37 => Some(VirtualKeyCode::Key7), +        0x38 => Some(VirtualKeyCode::Key8), +        0x39 => Some(VirtualKeyCode::Key9), +        0x41 => Some(VirtualKeyCode::A), +        0x42 => Some(VirtualKeyCode::B), +        0x43 => Some(VirtualKeyCode::C), +        0x44 => Some(VirtualKeyCode::D), +        0x45 => Some(VirtualKeyCode::E), +        0x46 => Some(VirtualKeyCode::F), +        0x47 => Some(VirtualKeyCode::G), +        0x48 => Some(VirtualKeyCode::H), +        0x49 => Some(VirtualKeyCode::I), +        0x4A => Some(VirtualKeyCode::J), +        0x4B => Some(VirtualKeyCode::K), +        0x4C => Some(VirtualKeyCode::L), +        0x4D => Some(VirtualKeyCode::M), +        0x4E => Some(VirtualKeyCode::N), +        0x4F => Some(VirtualKeyCode::O), +        0x50 => Some(VirtualKeyCode::P), +        0x51 => Some(VirtualKeyCode::Q), +        0x52 => Some(VirtualKeyCode::R), +        0x53 => Some(VirtualKeyCode::S), +        0x54 => Some(VirtualKeyCode::T), +        0x55 => Some(VirtualKeyCode::U), +        0x56 => Some(VirtualKeyCode::V), +        0x57 => Some(VirtualKeyCode::W), +        0x58 => Some(VirtualKeyCode::X), +        0x59 => Some(VirtualKeyCode::Y), +        0x5A => Some(VirtualKeyCode::Z), +        //winapi::VK_LWIN => Some(VirtualKeyCode::Lwin), +        //winapi::VK_RWIN => Some(VirtualKeyCode::Rwin), +        winapi::VK_APPS => Some(VirtualKeyCode::Apps), +        winapi::VK_SLEEP => Some(VirtualKeyCode::Sleep), +        winapi::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0), +        winapi::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1), +        winapi::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2), +        winapi::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3), +        winapi::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4), +        winapi::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5), +        winapi::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6), +        winapi::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7), +        winapi::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8), +        winapi::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9), +        winapi::VK_MULTIPLY => Some(VirtualKeyCode::Multiply), +        winapi::VK_ADD => Some(VirtualKeyCode::Add), +        //winapi::VK_SEPARATOR => Some(VirtualKeyCode::Separator), +        winapi::VK_SUBTRACT => Some(VirtualKeyCode::Subtract), +        winapi::VK_DECIMAL => Some(VirtualKeyCode::Decimal), +        winapi::VK_DIVIDE => Some(VirtualKeyCode::Divide), +        winapi::VK_F1 => Some(VirtualKeyCode::F1), +        winapi::VK_F2 => Some(VirtualKeyCode::F2), +        winapi::VK_F3 => Some(VirtualKeyCode::F3), +        winapi::VK_F4 => Some(VirtualKeyCode::F4), +        winapi::VK_F5 => Some(VirtualKeyCode::F5), +        winapi::VK_F6 => Some(VirtualKeyCode::F6), +        winapi::VK_F7 => Some(VirtualKeyCode::F7), +        winapi::VK_F8 => Some(VirtualKeyCode::F8), +        winapi::VK_F9 => Some(VirtualKeyCode::F9), +        winapi::VK_F10 => Some(VirtualKeyCode::F10), +        winapi::VK_F11 => Some(VirtualKeyCode::F11), +        winapi::VK_F12 => Some(VirtualKeyCode::F12), +        winapi::VK_F13 => Some(VirtualKeyCode::F13), +        winapi::VK_F14 => Some(VirtualKeyCode::F14), +        winapi::VK_F15 => Some(VirtualKeyCode::F15), +        /*winapi::VK_F16 => Some(VirtualKeyCode::F16), +        winapi::VK_F17 => Some(VirtualKeyCode::F17), +        winapi::VK_F18 => Some(VirtualKeyCode::F18), +        winapi::VK_F19 => Some(VirtualKeyCode::F19), +        winapi::VK_F20 => Some(VirtualKeyCode::F20), +        winapi::VK_F21 => Some(VirtualKeyCode::F21), +        winapi::VK_F22 => Some(VirtualKeyCode::F22), +        winapi::VK_F23 => Some(VirtualKeyCode::F23), +        winapi::VK_F24 => Some(VirtualKeyCode::F24),*/ +        winapi::VK_NUMLOCK => Some(VirtualKeyCode::Numlock), +        winapi::VK_SCROLL => Some(VirtualKeyCode::Scroll), +        /*winapi::VK_LSHIFT => Some(VirtualKeyCode::Lshift), +        winapi::VK_RSHIFT => Some(VirtualKeyCode::Rshift), +        winapi::VK_LCONTROL => Some(VirtualKeyCode::Lcontrol), +        winapi::VK_RCONTROL => Some(VirtualKeyCode::Rcontrol), +        winapi::VK_LMENU => Some(VirtualKeyCode::Lmenu), +        winapi::VK_RMENU => Some(VirtualKeyCode::Rmenu), +        winapi::VK_BROWSER_BACK => Some(VirtualKeyCode::Browser_back), +        winapi::VK_BROWSER_FORWARD => Some(VirtualKeyCode::Browser_forward), +        winapi::VK_BROWSER_REFRESH => Some(VirtualKeyCode::Browser_refresh), +        winapi::VK_BROWSER_STOP => Some(VirtualKeyCode::Browser_stop), +        winapi::VK_BROWSER_SEARCH => Some(VirtualKeyCode::Browser_search), +        winapi::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::Browser_favorites), +        winapi::VK_BROWSER_HOME => Some(VirtualKeyCode::Browser_home), +        winapi::VK_VOLUME_MUTE => Some(VirtualKeyCode::Volume_mute), +        winapi::VK_VOLUME_DOWN => Some(VirtualKeyCode::Volume_down), +        winapi::VK_VOLUME_UP => Some(VirtualKeyCode::Volume_up), +        winapi::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::Media_next_track), +        winapi::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::Media_prev_track), +        winapi::VK_MEDIA_STOP => Some(VirtualKeyCode::Media_stop), +        winapi::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::Media_play_pause), +        winapi::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Launch_mail), +        winapi::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::Launch_media_select), +        winapi::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1), +        winapi::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2), +        winapi::VK_OEM_1 => Some(VirtualKeyCode::Oem_1), +        winapi::VK_OEM_PLUS => Some(VirtualKeyCode::Oem_plus), +        winapi::VK_OEM_COMMA => Some(VirtualKeyCode::Oem_comma), +        winapi::VK_OEM_MINUS => Some(VirtualKeyCode::Oem_minus), +        winapi::VK_OEM_PERIOD => Some(VirtualKeyCode::Oem_period), +        winapi::VK_OEM_2 => Some(VirtualKeyCode::Oem_2), +        winapi::VK_OEM_3 => Some(VirtualKeyCode::Oem_3), +        winapi::VK_OEM_4 => Some(VirtualKeyCode::Oem_4), +        winapi::VK_OEM_5 => Some(VirtualKeyCode::Oem_5), +        winapi::VK_OEM_6 => Some(VirtualKeyCode::Oem_6), +        winapi::VK_OEM_7 => Some(VirtualKeyCode::Oem_7), +        winapi::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), +        winapi::VK_OEM_102 => Some(VirtualKeyCode::Oem_102), +        winapi::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey), +        winapi::VK_PACKET => Some(VirtualKeyCode::Packet), +        winapi::VK_ATTN => Some(VirtualKeyCode::Attn), +        winapi::VK_CRSEL => Some(VirtualKeyCode::Crsel), +        winapi::VK_EXSEL => Some(VirtualKeyCode::Exsel), +        winapi::VK_EREOF => Some(VirtualKeyCode::Ereof), +        winapi::VK_PLAY => Some(VirtualKeyCode::Play), +        winapi::VK_ZOOM => Some(VirtualKeyCode::Zoom), +        winapi::VK_NONAME => Some(VirtualKeyCode::Noname), +        winapi::VK_PA1 => Some(VirtualKeyCode::Pa1), +        winapi::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/ +        _ => None +    } +} diff --git a/src/api/win32/gl.rs b/src/api/win32/gl.rs new file mode 100644 index 0000000..1354d95 --- /dev/null +++ b/src/api/win32/gl.rs @@ -0,0 +1,12 @@ +/// WGL bindings +pub mod wgl { +    include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs")); +} + +/// Functions that are not necessarly always available +pub mod wgl_extra { +    include!(concat!(env!("OUT_DIR"), "/wgl_extra_bindings.rs")); +} + +#[link(name = "opengl32")] +extern {} diff --git a/src/api/win32/headless.rs b/src/api/win32/headless.rs new file mode 100644 index 0000000..6189360 --- /dev/null +++ b/src/api/win32/headless.rs @@ -0,0 +1,40 @@ +use super::Window; +use super::init; + +use Api; +use BuilderAttribs; +use CreationError; + +/// +pub struct HeadlessContext(Window); + +impl HeadlessContext { +    /// See the docs in the crate root file. +    pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> { +        let (builder, _) = builder.extract_non_static(); +        init::new_window(builder, None).map(|w| HeadlessContext(w)) +    } + +    /// See the docs in the crate root file. +    pub unsafe fn make_current(&self) { +        self.0.make_current() +    } + +    /// See the docs in the crate root file. +    pub fn is_current(&self) -> bool { +        self.0.is_current() +    } + +    /// See the docs in the crate root file. +    pub fn get_proc_address(&self, addr: &str) -> *const () { +        self.0.get_proc_address(addr) +    } + +    /// See the docs in the crate root file. +    pub fn get_api(&self) -> Api { +        Api::OpenGl +    } + +    pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { +    } +} diff --git a/src/api/win32/init.rs b/src/api/win32/init.rs new file mode 100644 index 0000000..5cdd6b8 --- /dev/null +++ b/src/api/win32/init.rs @@ -0,0 +1,586 @@ +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex}; +use std::io; +use std::ptr; +use std::mem; +use std::thread; + +use super::callback; +use super::Window; +use super::MonitorID; +use super::ContextWrapper; +use super::WindowWrapper; +use super::make_current_guard::CurrentContextGuard; + +use Api; +use BuilderAttribs; +use CreationError; +use CreationError::OsError; +use CursorState; +use GlRequest; +use PixelFormat; + +use std::ffi::{CStr, CString, OsStr}; +use std::os::windows::ffi::OsStrExt; +use std::sync::mpsc::channel; + +use libc; +use super::gl; +use winapi; +use kernel32; +use user32; +use gdi32; + +/// Work-around the fact that HGLRC doesn't implement Send +pub struct ContextHack(pub winapi::HGLRC); +unsafe impl Send for ContextHack {} + +pub fn new_window(builder: BuilderAttribs<'static>, builder_sharelists: Option<ContextHack>) +                  -> Result<Window, CreationError> +{ +    // initializing variables to be sent to the task + +    let title = OsStr::new(&builder.title).encode_wide().chain(Some(0).into_iter()) +                                          .collect::<Vec<_>>(); + +    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<u16>, builder: BuilderAttribs<'static>, +               builder_sharelists: Option<ContextHack>) -> Result<Window, CreationError> +{ +    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(); +        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() { +        (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); + +    // the first step is to create a dummy window and a dummy context which we will use +    // to load the pointers to some functions in the OpenGL driver in `extra_functions` +    let extra_functions = { +        // creating a dummy invisible window +        let dummy_window = { +            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: {}", +                                           format!("{}", io::Error::last_os_error())))); +            } + +            let hdc = user32::GetDC(handle); +            if hdc.is_null() { +                let err = Err(OsError(format!("GetDC function failed: {}", +                                              format!("{}", io::Error::last_os_error())))); +                return err; +            } + +            WindowWrapper(handle, hdc) +        }; + +        // getting the pixel format that we will use and setting it +        { +            let formats = enumerate_native_pixel_formats(&dummy_window); +            let id = try!(choose_dummy_pixel_format(formats.into_iter())); +            try!(set_pixel_format(&dummy_window, id)); +        } + +        // creating the dummy OpenGL context and making it current +        let dummy_context = try!(create_context(None, &dummy_window, None)); +        let current_context = try!(CurrentContextGuard::make_current(&dummy_window, +                                                                     &dummy_context)); + +        // loading the extra WGL functions +        gl::wgl_extra::Wgl::load_with(|addr| { +            use libc; + +            let addr = CString::new(addr.as_bytes()).unwrap(); +            let addr = addr.as_ptr(); + +            gl::wgl::GetProcAddress(addr) as *const libc::c_void +        }) +    }; + +    // 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) +    }; + +    // calling SetPixelFormat +    let pixel_format = { +        let formats = if extra_functions.GetPixelFormatAttribivARB.is_loaded() { +            enumerate_arb_pixel_formats(&extra_functions, &real_window) +        } else { +            enumerate_native_pixel_formats(&real_window) +        }; + +        let (id, f) = try!(builder.choose_pixel_format(formats.into_iter().map(|(a, b)| (b, a)))); +        try!(set_pixel_format(&real_window, id)); +        f +    }; + +    // creating the OpenGL context +    let context = try!(create_context(Some((&extra_functions, &builder)), &real_window, +                                      builder_sharelists)); + +    // 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 +    }; + +    // loading the opengl32 module +    let gl_library = try!(load_opengl32_dll()); + +    // handling vsync +    if builder.vsync { +        if extra_functions.SwapIntervalEXT.is_loaded() { +            let _guard = try!(CurrentContextGuard::make_current(&real_window, &context)); + +            if extra_functions.SwapIntervalEXT(1) == 0 { +                return Err(OsError(format!("wglSwapIntervalEXT failed"))); +            } +        } +    } + +    // building the struct +    Ok(Window { +        window: real_window, +        context: context, +        gl_library: gl_library, +        events_receiver: events_receiver, +        is_closed: AtomicBool::new(false), +        cursor_state: cursor_state, +        pixel_format: pixel_format, +    }) +} + +unsafe fn register_window_class() -> Vec<u16> { +    let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter()) +                                               .collect::<Vec<_>>(); +     +    let class = winapi::WNDCLASSEXW { +        cbSize: mem::size_of::<winapi::WNDCLASSEXW>() 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::<winapi::DEVMODEW>() 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(()) +} + +unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'static>)>, +                         hdc: &WindowWrapper, share: Option<winapi::HGLRC>) +                         -> Result<ContextWrapper, CreationError> +{ +    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(); + +            match builder.gl_version { +                GlRequest::Latest => {}, +                GlRequest::Specific(Api::OpenGl, (major, minor)) => { +                    attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); +                    attributes.push(major as libc::c_int); +                    attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); +                    attributes.push(minor as libc::c_int); +                }, +                GlRequest::Specific(Api::OpenGlEs, (major, minor)) => { +                    if is_extension_supported(extra_functions, hdc, +                                              "WGL_EXT_create_context_es2_profile") +                    { +                        attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int); +                        attributes.push(gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as libc::c_int); +                    } else { +                        return Err(CreationError::NotSupported); +                    } + +                    attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); +                    attributes.push(major as libc::c_int); +                    attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); +                    attributes.push(minor as libc::c_int); +                }, +                GlRequest::Specific(_, _) => return Err(CreationError::NotSupported), +                GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { +                    attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int); +                    attributes.push(major as libc::c_int); +                    attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int); +                    attributes.push(minor 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(extra_functions.CreateContextAttribsARB(hdc.1 as *const libc::c_void, +                                                         share as *const libc::c_void, +                                                         attributes.as_ptr())) + +        } else { +            None +        } +    } else { +        None +    }; + +    let ctxt = match ctxt { +        Some(ctxt) => ctxt, +        None => { +            let ctxt = gl::wgl::CreateContext(hdc.1 as *const libc::c_void); +            if !ctxt.is_null() && !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: {}", +                           format!("{}", io::Error::last_os_error())))); +    } + +    Ok(ContextWrapper(ctxt as winapi::HGLRC)) +} + +unsafe fn enumerate_native_pixel_formats(hdc: &WindowWrapper) -> Vec<(PixelFormat, libc::c_int)> { +    let size_of_pxfmtdescr = mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u32; +    let num = gdi32::DescribePixelFormat(hdc.1, 1, size_of_pxfmtdescr, ptr::null_mut()); + +    let mut result = Vec::new(); + +    for index in (0 .. num) { +        let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed(); +         +        if gdi32::DescribePixelFormat(hdc.1, 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.iPixelType != winapi::PFD_TYPE_RGBA { +            continue; +        } + +        result.push((PixelFormat { +            hardware_accelerated: (output.dwFlags & winapi::PFD_GENERIC_FORMAT) == 0, +            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 +} + +unsafe fn enumerate_arb_pixel_formats(extra: &gl::wgl_extra::Wgl, hdc: &WindowWrapper) +                                      -> Vec<(PixelFormat, libc::c_int)> +{ +    let get_info = |index: u32, attrib: u32| { +        let mut value = mem::uninitialized(); +        extra.GetPixelFormatAttribivARB(hdc.1 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 { +            hardware_accelerated: true, +            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: { +                if is_extension_supported(extra, hdc, "WGL_ARB_multisample") { +                    match get_info(index, gl::wgl_extra::SAMPLES_ARB) { +                        0 => None, +                        a => Some(a as u16), +                    } +                } else { +                    None +                } +            }, +            srgb: if is_extension_supported(extra, hdc, "WGL_ARB_framebuffer_sRGB") { +                get_info(index, gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 +            } else if is_extension_supported(extra, hdc, "WGL_EXT_framebuffer_sRGB") { +                get_info(index, gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0 +            } else { +                false +            }, +        }, index as libc::c_int)); +    } + +    result +} + +unsafe fn set_pixel_format(hdc: &WindowWrapper, id: libc::c_int) -> Result<(), CreationError> { +    let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed(); + +    if gdi32::DescribePixelFormat(hdc.1, id, mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() +                                  as winapi::UINT, &mut output) == 0 +    { +        return Err(OsError(format!("DescribePixelFormat function failed: {}", +                                   format!("{}", io::Error::last_os_error())))); +    } + +    if gdi32::SetPixelFormat(hdc.1, id, &output) == 0 { +        return Err(OsError(format!("SetPixelFormat function failed: {}", +                                   format!("{}", io::Error::last_os_error())))); +    } + +    Ok(()) +} + +unsafe fn load_opengl32_dll() -> Result<winapi::HMODULE, CreationError> { +    let name = OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter()) +                                         .collect::<Vec<_>>(); + +    let lib = kernel32::LoadLibraryW(name.as_ptr()); + +    if lib.is_null() { +        return Err(OsError(format!("LoadLibrary function failed: {}", +                                    format!("{}", io::Error::last_os_error())))); +    } + +    Ok(lib) +} + +unsafe fn is_extension_supported(extra: &gl::wgl_extra::Wgl, hdc: &WindowWrapper, +                                 extension: &str) -> bool +{ +    let extensions = if extra.GetExtensionsStringARB.is_loaded() { +        let data = extra.GetExtensionsStringARB(hdc.1 as *const _); +        let data = CStr::from_ptr(data).to_bytes().to_vec(); +        String::from_utf8(data).unwrap() + +    } else if extra.GetExtensionsStringEXT.is_loaded() { +        let data = extra.GetExtensionsStringEXT(); +        let data = CStr::from_ptr(data).to_bytes().to_vec(); +        String::from_utf8(data).unwrap() + +    } else { +        return false; +    }; + +    extensions.split(" ").find(|&e| e == extension).is_some() +} + +fn choose_dummy_pixel_format<I>(iter: I) -> Result<libc::c_int, CreationError> +                                where I: Iterator<Item=(PixelFormat, libc::c_int)> +{ +    let mut backup_id = None; + +    for (format, id) in iter { +        if backup_id.is_none() { +            backup_id = Some(id); +        } + +        if format.hardware_accelerated { +            return Ok(id); +        } +    } + +    backup_id.ok_or(CreationError::NotSupported) +} diff --git a/src/api/win32/make_current_guard.rs b/src/api/win32/make_current_guard.rs new file mode 100644 index 0000000..8983899 --- /dev/null +++ b/src/api/win32/make_current_guard.rs @@ -0,0 +1,52 @@ +use std::marker::PhantomData; +use std::io; + +use libc; +use winapi; +use CreationError; + +use super::gl; +use super::ContextWrapper; +use super::WindowWrapper; + +/// A guard for when you want to make the context current. Destroying the guard restores the +/// previously-current context. +pub struct CurrentContextGuard<'a, 'b> { +    previous_hdc: winapi::HDC, +    previous_hglrc: winapi::HGLRC, +    marker1: PhantomData<&'a ()>, +    marker2: PhantomData<&'b ()>, +} + +impl<'a, 'b> CurrentContextGuard<'a, 'b> { +    pub unsafe fn make_current(window: &'a WindowWrapper, context: &'b ContextWrapper) +                               -> Result<CurrentContextGuard<'a, 'b>, CreationError> +    { +        let previous_hdc = gl::wgl::GetCurrentDC() as winapi::HDC; +        let previous_hglrc = gl::wgl::GetCurrentContext() as winapi::HGLRC; + +        let result = gl::wgl::MakeCurrent(window.1 as *const libc::c_void, +                                          context.0 as *const libc::c_void); + +        if result == 0 { +            return Err(CreationError::OsError(format!("wglMakeCurrent function failed: {}", +                                                      format!("{}", io::Error::last_os_error())))); +        } + +        Ok(CurrentContextGuard { +            previous_hdc: previous_hdc, +            previous_hglrc: previous_hglrc, +            marker1: PhantomData, +            marker2: PhantomData, +        }) +    } +} + +impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> { +    fn drop(&mut self) { +        unsafe { +            gl::wgl::MakeCurrent(self.previous_hdc as *const libc::c_void, +                                 self.previous_hglrc as *const libc::c_void); +        } +    } +} diff --git a/src/api/win32/mod.rs b/src/api/win32/mod.rs new file mode 100644 index 0000000..41fb6aa --- /dev/null +++ b/src/api/win32/mod.rs @@ -0,0 +1,419 @@ +#![cfg(target_os = "windows")] + +use std::sync::atomic::AtomicBool; +use std::mem; +use std::ptr; +use std::ffi::CString; +use std::ffi::OsStr; +use std::os::windows::ffi::OsStrExt; +use std::sync::{ +    Arc, +    Mutex +}; +use std::sync::mpsc::Receiver; +use libc; +use {CreationError, Event, MouseCursor}; +use CursorState; + +use PixelFormat; +use BuilderAttribs; + +pub use self::headless::HeadlessContext; +pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; + +use winapi; +use user32; +use kernel32; +use gdi32; + +mod callback; +mod event; +mod gl; +mod headless; +mod init; +mod make_current_guard; +mod monitor; + +/// The Win32 implementation of the main `Window` object. +pub struct Window { +    /// Main handle for the window. +    window: WindowWrapper, + +    /// OpenGL context. +    context: ContextWrapper, + +    /// Binded to `opengl32.dll`. +    /// +    /// `wglGetProcAddress` returns null for GL 1.1 functions because they are +    ///  already defined by the system. This module contains them. +    gl_library: winapi::HMODULE, + +    /// Receiver for the events dispatched by the window callback. +    events_receiver: Receiver<Event>, + +    /// True if a `Closed` event has been received. +    is_closed: AtomicBool, + +    /// The current cursor state. +    cursor_state: Arc<Mutex<CursorState>>, + +    /// The pixel format that has been used to create this window. +    pixel_format: PixelFormat, +} + +unsafe impl Send for Window {} +unsafe impl Sync for Window {} + +/// A simple wrapper that destroys the context when it is destroyed. +// FIXME: remove `pub` (https://github.com/rust-lang/rust/issues/23585) +#[doc(hidden)] +pub struct ContextWrapper(pub winapi::HGLRC); + +impl Drop for ContextWrapper { +    fn drop(&mut self) { +        unsafe { +            gl::wgl::DeleteContext(self.0 as *const libc::c_void); +        } +    } +} + +/// A simple wrapper that destroys the window when it is destroyed. +// FIXME: remove `pub` (https://github.com/rust-lang/rust/issues/23585) +#[doc(hidden)] +pub struct WindowWrapper(pub winapi::HWND, pub winapi::HDC); + +impl Drop for WindowWrapper { +    fn drop(&mut self) { +        unsafe { +            user32::DestroyWindow(self.0); +        } +    } +} + +#[derive(Clone)] +pub struct WindowProxy; + +impl WindowProxy { +    pub fn wakeup_event_loop(&self) { +        unimplemented!() +    } +} + +impl Window { +    /// See the docs in the crate root file. +    pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { +        let (builder, sharing) = builder.extract_non_static(); +        let sharing = sharing.map(|w| init::ContextHack(w.context.0)); +        init::new_window(builder, sharing) +    } + +    /// See the docs in the crate root file. +    pub fn is_closed(&self) -> bool { +        use std::sync::atomic::Ordering::Relaxed; +        self.is_closed.load(Relaxed) +    } + +    /// See the docs in the crate root file. +    /// +    /// Calls SetWindowText on the HWND. +    pub fn set_title(&self, text: &str) { +        let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter()) +                                   .collect::<Vec<_>>(); + +        unsafe { +            user32::SetWindowTextW(self.window.0, text.as_ptr() as winapi::LPCWSTR); +        } +    } + +    pub fn show(&self) { +        unsafe { +            user32::ShowWindow(self.window.0, winapi::SW_SHOW); +        } +    } + +    pub fn hide(&self) { +        unsafe { +            user32::ShowWindow(self.window.0, winapi::SW_HIDE); +        } +    } + +    /// See the docs in the crate root file. +    pub fn get_position(&self) -> Option<(i32, i32)> { +        use std::mem; + +        let mut placement: winapi::WINDOWPLACEMENT = unsafe { mem::zeroed() }; +        placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT; + +        if unsafe { user32::GetWindowPlacement(self.window.0, &mut placement) } == 0 { +            return None +        } + +        let ref rect = placement.rcNormalPosition; +        Some((rect.left as i32, rect.top as i32)) +    } + +    /// See the docs in the crate root file. +    pub fn set_position(&self, x: i32, y: i32) { +        use libc; + +        unsafe { +            user32::SetWindowPos(self.window.0, ptr::null_mut(), x as libc::c_int, y as libc::c_int, +                0, 0, winapi::SWP_NOZORDER | winapi::SWP_NOSIZE); +            user32::UpdateWindow(self.window.0); +        } +    } + +    /// See the docs in the crate root file. +    pub fn get_inner_size(&self) -> Option<(u32, u32)> { +        let mut rect: winapi::RECT = unsafe { mem::uninitialized() }; + +        if unsafe { user32::GetClientRect(self.window.0, &mut rect) } == 0 { +            return None +        } + +        Some(( +            (rect.right - rect.left) as u32, +            (rect.bottom - rect.top) as u32 +        )) +    } + +    /// See the docs in the crate root file. +    pub fn get_outer_size(&self) -> Option<(u32, u32)> { +        let mut rect: winapi::RECT = unsafe { mem::uninitialized() }; + +        if unsafe { user32::GetWindowRect(self.window.0, &mut rect) } == 0 { +            return None +        } + +        Some(( +            (rect.right - rect.left) as u32, +            (rect.bottom - rect.top) as u32 +        )) +    } + +    /// See the docs in the crate root file. +    pub fn set_inner_size(&self, x: u32, y: u32) { +        use libc; + +        unsafe { +            user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, x as libc::c_int, +                y as libc::c_int, winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION); +            user32::UpdateWindow(self.window.0); +        } +    } + +    pub fn create_window_proxy(&self) -> WindowProxy { +        WindowProxy +    } + +    /// See the docs in the crate root file. +    pub fn poll_events(&self) -> PollEventsIterator { +        PollEventsIterator { +            window: self, +        } +    } + +    /// See the docs in the crate root file. +    pub fn wait_events(&self) -> WaitEventsIterator { +        WaitEventsIterator { +            window: self, +        } +    } + +    /// See the docs in the crate root file. +    pub unsafe fn make_current(&self) { +        // TODO: check return value +        gl::wgl::MakeCurrent(self.window.1 as *const libc::c_void, +                             self.context.0 as *const libc::c_void); +    } + +    /// See the docs in the crate root file. +    pub fn is_current(&self) -> bool { +        unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const libc::c_void } +    } + +    /// See the docs in the crate root file. +    pub fn get_proc_address(&self, addr: &str) -> *const () { +        let addr = CString::new(addr.as_bytes()).unwrap(); +        let addr = addr.as_ptr(); + +        unsafe { +            let p = gl::wgl::GetProcAddress(addr) as *const (); +            if !p.is_null() { return p; } +            kernel32::GetProcAddress(self.gl_library, addr) as *const () +        } +    } + +    /// See the docs in the crate root file. +    pub fn swap_buffers(&self) { +        unsafe { +            gdi32::SwapBuffers(self.window.1); +        } +    } + +    pub fn platform_display(&self) -> *mut libc::c_void { +        unimplemented!() +    } + +    pub fn platform_window(&self) -> *mut libc::c_void { +        self.window.0 as *mut libc::c_void +    } + +    /// See the docs in the crate root file. +    pub fn get_api(&self) -> ::Api { +        ::Api::OpenGl +    } + +    pub fn get_pixel_format(&self) -> PixelFormat { +        self.pixel_format.clone() +    } + +    pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { +    } + +    pub fn set_cursor(&self, _cursor: MouseCursor) { +        unimplemented!() +    } + +    pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> { +        let mut current_state = self.cursor_state.lock().unwrap(); + +        let foreground_thread_id = unsafe { user32::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) }; +        let current_thread_id = unsafe { kernel32::GetCurrentThreadId() }; + +        unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 1) }; + +        let res = match (state, *current_state) { +            (CursorState::Normal, CursorState::Normal) => Ok(()), +            (CursorState::Hide, CursorState::Hide) => Ok(()), +            (CursorState::Grab, CursorState::Grab) => Ok(()), + +            (CursorState::Hide, CursorState::Normal) => { +                unsafe { +                    user32::SetCursor(ptr::null_mut()); +                    *current_state = CursorState::Hide; +                    Ok(()) +                } +            }, + +            (CursorState::Normal, CursorState::Hide) => { +                unsafe { +                    user32::SetCursor(user32::LoadCursorW(ptr::null_mut(), winapi::IDC_ARROW)); +                    *current_state = CursorState::Normal; +                    Ok(()) +                } +            }, + +            (CursorState::Grab, CursorState::Normal) => { +                unsafe { +                    user32::SetCursor(ptr::null_mut()); +                    let mut rect = mem::uninitialized(); +                    if user32::GetClientRect(self.window.0, &mut rect) == 0 { +                        return Err(format!("GetWindowRect failed")); +                    } +                    user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.left)); +                    user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.right)); +                    if user32::ClipCursor(&rect) == 0 { +                        return Err(format!("ClipCursor failed")); +                    } +                    *current_state = CursorState::Grab; +                    Ok(()) +                } +            }, + +            (CursorState::Normal, CursorState::Grab) => { +                unsafe { +                    user32::SetCursor(user32::LoadCursorW(ptr::null_mut(), winapi::IDC_ARROW)); +                    if user32::ClipCursor(ptr::null()) == 0 { +                        return Err(format!("ClipCursor failed")); +                    } +                    *current_state = CursorState::Normal; +                    Ok(()) +                } +            }, + +            _ => unimplemented!(), +        }; + +        unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 0) }; + +        res +    } + +    pub fn hidpi_factor(&self) -> f32 { +        1.0 +    } + +    pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> { +        let mut point = winapi::POINT { +            x: x, +            y: y, +        }; + +        unsafe { +            if user32::ClientToScreen(self.window.0, &mut point) == 0 { +                return Err(()); +            } + +            if user32::SetCursorPos(point.x, point.y) == 0 { +                return Err(()); +            } +        } + +        Ok(()) +    } +} + +pub struct PollEventsIterator<'a> { +    window: &'a Window, +} + +impl<'a> Iterator for PollEventsIterator<'a> { +    type Item = Event; + +    fn next(&mut self) -> Option<Event> { +        use events::Event::Closed; + +        match self.window.events_receiver.try_recv() { +            Ok(Closed) => { +                use std::sync::atomic::Ordering::Relaxed; +                self.window.is_closed.store(true, Relaxed); +                Some(Closed) +            }, +            Ok(ev) => Some(ev), +            Err(_) => None +        } +    } +} + +pub struct WaitEventsIterator<'a> { +    window: &'a Window, +} + +impl<'a> Iterator for WaitEventsIterator<'a> { +    type Item = Event; + +    fn next(&mut self) -> Option<Event> { +        use events::Event::Closed; + +        match self.window.events_receiver.recv() { +            Ok(Closed) => { +                use std::sync::atomic::Ordering::Relaxed; +                self.window.is_closed.store(true, Relaxed); +                Some(Closed) +            }, +            Ok(ev) => Some(ev), +            Err(_) => None +        } +    } +} + +impl Drop for Window { +    fn drop(&mut self) { +        unsafe { +            // we don't call MakeCurrent(0, 0) because we are not sure that the context +            // is still the current one +            user32::PostMessageW(self.window.0, winapi::WM_DESTROY, 0, 0); +        } +    } +} diff --git a/src/api/win32/monitor.rs b/src/api/win32/monitor.rs new file mode 100644 index 0000000..4b592cb --- /dev/null +++ b/src/api/win32/monitor.rs @@ -0,0 +1,180 @@ +use winapi; +use user32; + +use std::collections::VecDeque; +use std::mem; + +use native_monitor::NativeMonitorId; + +/// Win32 implementation of the main `MonitorID` object. +pub struct MonitorID { +    /// The system name of the adapter. +    adapter_name: [winapi::WCHAR; 32], + +    /// The system name of the monitor. +    monitor_name: String, + +    /// Name to give to the user. +    readable_name: String, + +    /// See the `StateFlags` element here: +    /// http://msdn.microsoft.com/en-us/library/dd183569(v=vs.85).aspx +    flags: winapi::DWORD, + +    /// True if this is the primary monitor. +    primary: bool, + +    /// The position of the monitor in pixels on the desktop. +    /// +    /// A window that is positionned at these coordinates will overlap the monitor. +    position: (u32, u32), + +    /// The current resolution in pixels on the monitor. +    dimensions: (u32, u32), +} + +struct DeviceEnumerator { +    parent_device: *const winapi::WCHAR, +    current_index: u32, +} + +impl DeviceEnumerator { +    fn adapters() -> DeviceEnumerator { +        use std::ptr; +        DeviceEnumerator { +            parent_device: ptr::null(), +            current_index: 0 +        } +    } + +    fn monitors(adapter_name: *const winapi::WCHAR) -> DeviceEnumerator { +        DeviceEnumerator { +            parent_device: adapter_name, +            current_index: 0 +        } +    } +} + +impl Iterator for DeviceEnumerator { +    type Item = winapi::DISPLAY_DEVICEW; +    fn next(&mut self) -> Option<winapi::DISPLAY_DEVICEW> { +        use std::mem; +        loop { +            let mut output: winapi::DISPLAY_DEVICEW = unsafe { mem::zeroed() }; +            output.cb = mem::size_of::<winapi::DISPLAY_DEVICEW>() as winapi::DWORD; + +            if unsafe { user32::EnumDisplayDevicesW(self.parent_device, +                self.current_index as winapi::DWORD, &mut output, 0) } == 0 +            { +                // the device doesn't exist, which means we have finished enumerating +                break; +            } +            self.current_index += 1; + +            if  (output.StateFlags & winapi::DISPLAY_DEVICE_ACTIVE) == 0 || +                (output.StateFlags & winapi::DISPLAY_DEVICE_MIRRORING_DRIVER) != 0 +            { +                // the device is not active +                // the Win32 api usually returns a lot of inactive devices +                continue; +            } + +            return Some(output); +        } +        None +    } +} + +fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String { +    String::from_utf16_lossy(wchar) +        .trim_right_matches(0 as char) +        .to_string() +} + +/// Win32 implementation of the main `get_available_monitors` function. +pub fn get_available_monitors() -> VecDeque<MonitorID> { +    // return value +    let mut result = VecDeque::new(); + +    for adapter in DeviceEnumerator::adapters() { +        // getting the position +        let (position, dimensions) = unsafe { +            let mut dev: winapi::DEVMODEW = mem::zeroed(); +            dev.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD; + +            if user32::EnumDisplaySettingsExW(adapter.DeviceName.as_ptr(),  +                winapi::ENUM_CURRENT_SETTINGS, +                &mut dev, 0) == 0 +            { +                continue; +            } + +            let point: &winapi::POINTL = mem::transmute(&dev.union1); +            let position = (point.x as u32, point.y as u32); + +            let dimensions = (dev.dmPelsWidth as u32, dev.dmPelsHeight as u32); + +            (position, dimensions) +        }; + +        for (num, monitor) in DeviceEnumerator::monitors(adapter.DeviceName.as_ptr()).enumerate() { +            // adding to the resulting list +            result.push_back(MonitorID { +                adapter_name: adapter.DeviceName, +                monitor_name: wchar_as_string(&monitor.DeviceName), +                readable_name: wchar_as_string(&monitor.DeviceString), +                flags: monitor.StateFlags, +                primary: (adapter.StateFlags & winapi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 && +                         num == 0, +                position: position, +                dimensions: dimensions, +            }); +        } +    } +    result +} + +/// Win32 implementation of the main `get_primary_monitor` function. +pub fn get_primary_monitor() -> MonitorID { +    // we simply get all available monitors and return the one with the `PRIMARY_DEVICE` flag +    // TODO: it is possible to query the win32 API for the primary monitor, this should be done +    //  instead +    for monitor in get_available_monitors().into_iter() { +        if monitor.primary { +            return monitor; +        } +    } + +    panic!("Failed to find the primary monitor") +} + +impl MonitorID { +    /// See the docs if the crate root file. +    pub fn get_name(&self) -> Option<String> { +        Some(self.readable_name.clone()) +    } + +    /// See the docs of the crate root file. +    pub fn get_native_identifier(&self) -> NativeMonitorId { +        NativeMonitorId::Name(self.monitor_name.clone()) +    } + +    /// See the docs if the crate root file. +    pub fn get_dimensions(&self) -> (u32, u32) { +        // TODO: retreive the dimensions every time this is called +        self.dimensions +    } + +    /// This is a Win32-only function for `MonitorID` that returns the system name of the adapter +    /// device. +    pub fn get_adapter_name(&self) -> &[winapi::WCHAR] { +        &self.adapter_name +    } + +    /// This is a Win32-only function for `MonitorID` that returns the position of the +    ///  monitor on the desktop. +    /// A window that is positionned at these coordinates will overlap the monitor. +    pub fn get_position(&self) -> (u32, u32) { +        self.position +    } +}  | 
