diff options
-rw-r--r-- | src/win32/init.rs | 40 | ||||
-rw-r--r-- | src/win32/mod.rs | 29 | ||||
-rw-r--r-- | src/win32/monitor.rs | 59 |
3 files changed, 103 insertions, 25 deletions
diff --git a/src/win32/init.rs b/src/win32/init.rs index 383e3f4..8987d2e 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -8,8 +8,10 @@ use super::{event, ffi}; use super::{MonitorID, Window}; use {Event, Hints}; -/// Stores the list of all the windows. -/// Only available on callback thread. +/// Stores the current window and its events dispatcher. +/// +/// We only have one window per thread. We still store the HWND in case where we +/// receive an event for another window. local_data_key!(WINDOW: (ffi::HWND, Sender<Event>)) pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, @@ -19,11 +21,14 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, use std::mem; use std::os; - let title = title.to_string(); + // initializing variables to be sent to the task + let title = title.utf16_units().collect::<Vec<u16>>().append_one(0); // title to utf16 //let hints = hints.clone(); - let (tx, rx) = channel(); + // GetMessage must be called in the same thread as CreateWindow, + // so we create a new thread dedicated to this window. + // This is the only safe method. Using `nosend` wouldn't work for non-native runtime. TaskBuilder::new().native().spawn(proc() { // registering the window class let class_name: Vec<u16> = "Window Class".utf16_units().collect::<Vec<u16>>() @@ -57,7 +62,9 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, top: 0, bottom: dimensions.map(|(_, h)| h as ffi::LONG).unwrap_or(768), }; - // switching to fullscreen + // 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 monitor.is_some() { let monitor = monitor.as_ref().unwrap(); @@ -86,7 +93,7 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, } } - // computing the style and extended style + // computing the style and extended style of the window let (ex_style, style) = if monitor.is_some() { (ffi::WS_EX_APPWINDOW, ffi::WS_POPUP | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN) } else { @@ -94,13 +101,13 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, ffi::WS_OVERLAPPEDWINDOW | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN) }; - // adjusting + // adjusting the window coordinates using the style unsafe { ffi::AdjustWindowRectEx(&mut rect, style, 0, ex_style) }; // creating the window let handle = unsafe { let handle = ffi::CreateWindowExW(ex_style, class_name.as_ptr(), - title.as_slice().utf16_units().collect::<Vec<u16>>().append_one(0).as_ptr() as ffi::LPCWSTR, + title.as_ptr() as ffi::LPCWSTR, style | ffi::WS_VISIBLE | ffi::WS_CLIPSIBLINGS | ffi::WS_CLIPCHILDREN, if monitor.is_some() { 0 } else { ffi::CW_USEDEFAULT}, if monitor.is_some() { 0 } else { ffi::CW_USEDEFAULT}, @@ -123,7 +130,7 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, unsafe { ffi::SetForegroundWindow(handle) }; } - // adding it to WINDOWS_LIST + // filling the WINDOW task-local storage let events_receiver = { let (tx, rx) = channel(); WINDOW.replace(Some((handle, tx))); @@ -166,7 +173,7 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, } } - // creating the context + // creating the OpenGL context let context = { let ctxt = unsafe { ffi::wglCreateContext(hdc) }; if ctxt.is_null() { @@ -177,7 +184,7 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, ctxt }; - // loading opengl32 + // loading the opengl32 module let gl_library = { let name = "opengl32.dll".utf16_units().collect::<Vec<u16>>().append_one(0).as_ptr(); let lib = unsafe { ffi::LoadLibraryW(name) }; @@ -199,22 +206,24 @@ pub fn new_window(dimensions: Option<(uint, uint)>, title: &str, is_closed: AtomicBool::new(false), })); - // starting the events loop + // 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 = unsafe { mem::uninitialized() }; if unsafe { ffi::GetMessageW(&mut msg, ptr::mut_null(), 0, 0) } == 0 { - break + break; } unsafe { ffi::TranslateMessage(&msg) }; - unsafe { ffi::DispatchMessageW(&msg) }; + unsafe { ffi::DispatchMessageW(&msg) }; // calls `callback` (see below) } }); rx.recv() } +/// Checks that the window is the good one, and if so send the event to it. fn send_event(window: ffi::HWND, event: Event) { let stored = match WINDOW.get() { None => return, @@ -230,6 +239,9 @@ fn send_event(window: ffi::HWND, event: Event) { sender.send_opt(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. extern "stdcall" fn callback(window: ffi::HWND, msg: ffi::UINT, wparam: ffi::WPARAM, lparam: ffi::LPARAM) -> ffi::LRESULT { diff --git a/src/win32/mod.rs b/src/win32/mod.rs index 9d8723d..7558245 100644 --- a/src/win32/mod.rs +++ b/src/win32/mod.rs @@ -9,16 +9,32 @@ mod ffi; mod init; mod monitor; +/// The Win32 implementation of the main `Window` object. pub struct Window { + /// Main handle for the window. window: ffi::HWND, + + /// This represents a "draw context" for the surface of the window. hdc: ffi::HDC, + + /// OpenGL context. context: ffi::HGLRC, + + /// 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: ffi::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, } impl Window { + /// See the docs if the crate root file. pub fn new(dimensions: Option<(uint, uint)>, title: &str, hints: &Hints, monitor: Option<MonitorID>) -> Result<Window, String> @@ -26,11 +42,14 @@ impl Window { init::new_window(dimensions, title, hints, monitor) } + /// See the docs if the crate root file. pub fn is_closed(&self) -> bool { use std::sync::atomics::Relaxed; self.is_closed.load(Relaxed) } + /// See the docs if the crate root file. + /// /// Calls SetWindowText on the HWND. pub fn set_title(&self, text: &str) { unsafe { @@ -39,6 +58,7 @@ impl Window { } } + /// See the docs if the crate root file. pub fn get_position(&self) -> Option<(int, int)> { use std::mem; @@ -53,6 +73,7 @@ impl Window { Some((rect.left as int, rect.top as int)) } + /// See the docs if the crate root file. pub fn set_position(&self, x: uint, y: uint) { use libc; @@ -63,6 +84,7 @@ impl Window { } } + /// See the docs if the crate root file. pub fn get_inner_size(&self) -> Option<(uint, uint)> { use std::mem; let mut rect: ffi::RECT = unsafe { mem::uninitialized() }; @@ -77,6 +99,7 @@ impl Window { )) } + /// See the docs if the crate root file. pub fn get_outer_size(&self) -> Option<(uint, uint)> { use std::mem; let mut rect: ffi::RECT = unsafe { mem::uninitialized() }; @@ -91,6 +114,7 @@ impl Window { )) } + /// See the docs if the crate root file. pub fn set_inner_size(&self, x: uint, y: uint) { use libc; @@ -101,6 +125,7 @@ impl Window { } } + /// See the docs if the crate root file. // TODO: return iterator pub fn poll_events(&self) -> Vec<Event> { let mut events = Vec::new(); @@ -119,6 +144,7 @@ impl Window { events } + /// See the docs if the crate root file. // TODO: return iterator pub fn wait_events(&self) -> Vec<Event> { match self.events_receiver.recv_opt() { @@ -135,10 +161,12 @@ impl Window { } } + /// See the docs if the crate root file. pub unsafe fn make_current(&self) { ffi::wglMakeCurrent(self.hdc, self.context) } + /// See the docs if the crate root file. pub fn get_proc_address(&self, addr: &str) -> *const () { use std::c_str::ToCStr; @@ -151,6 +179,7 @@ impl Window { } } + /// See the docs if the crate root file. pub fn swap_buffers(&self) { unsafe { ffi::SwapBuffers(self.hdc); diff --git a/src/win32/monitor.rs b/src/win32/monitor.rs index d53577e..b8f5008 100644 --- a/src/win32/monitor.rs +++ b/src/win32/monitor.rs @@ -1,34 +1,61 @@ use super::ffi; +/// Win32 implementation of the main `MonitorID` object. pub struct MonitorID { + /// The system name of the monitor. name: [ffi::WCHAR, ..32], + + /// 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: ffi::DWORD, + + /// The position of the monitor in pixels on the desktop. + /// + /// A window that is positionned at these coordinates will overlap the monitor. position: (uint, uint), } +/// Win32 implementation of the main `get_available_monitors` function. pub fn get_available_monitors() -> Vec<MonitorID> { use std::{iter, mem, ptr}; + // return value let mut result = Vec::new(); + // enumerating the devices is done by querying device 0, then device 1, then device 2, etc. + // until the query function returns null for id in iter::count(0u, 1) { - let mut output: ffi::DISPLAY_DEVICEW = unsafe { mem::zeroed() }; - output.cb = mem::size_of::<ffi::DISPLAY_DEVICEW>() as ffi::DWORD; + // getting the DISPLAY_DEVICEW object of the current device + let output = { + let mut output: ffi::DISPLAY_DEVICEW = unsafe { mem::zeroed() }; + output.cb = mem::size_of::<ffi::DISPLAY_DEVICEW>() as ffi::DWORD; - if unsafe { ffi::EnumDisplayDevicesW(ptr::null(), id as ffi::DWORD, &mut output, 0) } == 0 { - break - } + if unsafe { ffi::EnumDisplayDevicesW(ptr::null(), + id as ffi::DWORD, &mut output, 0) } == 0 + { + // the device doesn't exist, which means we have finished enumerating + break; + } - if (output.StateFlags & ffi::DISPLAY_DEVICE_ACTIVE) == 0 || - (output.StateFlags & ffi::DISPLAY_DEVICE_MIRRORING_DRIVER) != 0 - { - continue - } + if (output.StateFlags & ffi::DISPLAY_DEVICE_ACTIVE) == 0 || + (output.StateFlags & ffi::DISPLAY_DEVICE_MIRRORING_DRIVER) != 0 + { + // the device is not active + // the Win32 api usually returns a lot of inactive devices + continue; + } + + output + }; + // computing the human-friendly name let readable_name = String::from_utf16_lossy(output.DeviceString.as_slice()); let readable_name = readable_name.as_slice().trim_right_chars(0 as char).to_string(); + // getting the position let position = unsafe { let mut dev: ffi::DEVMODE = mem::zeroed(); dev.dmSize = mem::size_of::<ffi::DEVMODE>() as ffi::WORD; @@ -36,13 +63,14 @@ pub fn get_available_monitors() -> Vec<MonitorID> { if ffi::EnumDisplaySettingsExW(output.DeviceName.as_ptr(), ffi::ENUM_CURRENT_SETTINGS, &mut dev, 0) == 0 { - continue + continue; } let point: &ffi::POINTL = mem::transmute(&dev.union1); (point.x as uint, point.y as uint) }; + // adding to the resulting list result.push(MonitorID { name: output.DeviceName, readable_name: readable_name, @@ -54,7 +82,11 @@ pub fn get_available_monitors() -> Vec<MonitorID> { 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().move_iter() { if (monitor.flags & ffi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 { return monitor @@ -65,14 +97,19 @@ pub fn get_primary_monitor() -> MonitorID { } impl MonitorID { + /// See the docs if the crate root file. pub fn get_name(&self) -> Option<String> { Some(self.readable_name.clone()) } + /// This is a Win32-only function for `MonitorID` that returns the system name of the device. pub fn get_system_name(&self) -> &[ffi::WCHAR] { self.name.as_slice() } + /// 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) -> (uint, uint) { self.position } |