diff options
Diffstat (limited to 'src')
| -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      }  | 
