aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/win32
diff options
context:
space:
mode:
authorPierre Krieger <pierre.krieger1708@gmail.com>2015-04-24 09:51:23 +0200
committerPierre Krieger <pierre.krieger1708@gmail.com>2015-04-24 09:51:23 +0200
commit3ad7f9a58429b02b11b18f6a70ac011f698b6f4b (patch)
tree3061216335701a5faa8bd3c5c9ec0499a357be5a /src/api/win32
parentc1af76550f311e3da7a08d393b4ea9805cb61a7b (diff)
downloadglutin-3ad7f9a58429b02b11b18f6a70ac011f698b6f4b.tar.gz
glutin-3ad7f9a58429b02b11b18f6a70ac011f698b6f4b.zip
Create reorganization
Diffstat (limited to 'src/api/win32')
-rw-r--r--src/api/win32/callback.rs253
-rw-r--r--src/api/win32/event.rs181
-rw-r--r--src/api/win32/gl.rs12
-rw-r--r--src/api/win32/headless.rs40
-rw-r--r--src/api/win32/init.rs586
-rw-r--r--src/api/win32/make_current_guard.rs52
-rw-r--r--src/api/win32/mod.rs419
-rw-r--r--src/api/win32/monitor.rs180
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
+ }
+}