Move the win32 implementation to platform/windows
This commit is contained in:
parent
a32aaee83e
commit
751b0a7ed7
7 changed files with 357 additions and 395 deletions
379
src/platform/windows/callback.rs
Normal file
379
src/platform/windows/callback.rs
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
use CursorState;
|
||||
use Event;
|
||||
use super::event;
|
||||
use super::WindowState;
|
||||
|
||||
use user32;
|
||||
use shell32;
|
||||
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 window_state: Arc<Mutex<WindowState>>,
|
||||
pub mouse_in_window: bool
|
||||
}
|
||||
|
||||
/// Equivalent to the windows api [MINMAXINFO](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632605%28v=vs.85%29.aspx)
|
||||
/// struct. Used because winapi-rs doesn't have this declared.
|
||||
#[repr(C)]
|
||||
#[allow(dead_code)]
|
||||
struct MinMaxInfo {
|
||||
reserved: winapi::POINT, // Do not use/change
|
||||
max_size: winapi::POINT,
|
||||
max_position: winapi::POINT,
|
||||
min_track: winapi::POINT,
|
||||
max_track: winapi::POINT
|
||||
}
|
||||
|
||||
/// 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.
|
||||
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
|
||||
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
|
||||
},
|
||||
|
||||
// Prevents default windows menu hotkeys playing unwanted
|
||||
// "ding" sounds. Alternatively could check for WM_SYSCOMMAND
|
||||
// with wparam being SC_KEYMENU, but this may prevent some
|
||||
// other unwanted default hotkeys as well.
|
||||
winapi::WM_SYSCHAR => {
|
||||
0
|
||||
}
|
||||
|
||||
winapi::WM_MOUSEMOVE => {
|
||||
use events::Event::MouseMoved;
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
context_stash.mouse_in_window = true;
|
||||
}
|
||||
});
|
||||
|
||||
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_MOUSELEAVE => {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
context_stash.mouse_in_window = false;
|
||||
}
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MOUSEWHEEL => {
|
||||
use events::Event::MouseWheel;
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::TouchPhase;
|
||||
|
||||
let value = (wparam >> 16) as i16;
|
||||
let value = value as i32;
|
||||
let value = value as f32 / winapi::WHEEL_DELTA as f32;
|
||||
|
||||
send_event(window, MouseWheel(LineDelta(0.0, value), TouchPhase::Moved));
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
|
||||
use events::Event::KeyboardInput;
|
||||
use events::ElementState::Pressed;
|
||||
if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
send_event(window, KeyboardInput(Pressed, scancode, vkey));
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
|
||||
use events::Event::KeyboardInput;
|
||||
use events::ElementState::Released;
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
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_XBUTTONDOWN => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Other;
|
||||
use events::ElementState::Pressed;
|
||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
|
||||
send_event(window, MouseInput(Pressed, Other(xbutton as u8)));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_XBUTTONUP => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Other;
|
||||
use events::ElementState::Released;
|
||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
|
||||
send_event(window, MouseInput(Released, Other(xbutton as u8)));
|
||||
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 => {
|
||||
let call_def_window_proc = CONTEXT_STASH.with(|context_stash| {
|
||||
let cstash = context_stash.borrow();
|
||||
let mut call_def_window_proc = false;
|
||||
if let Some(cstash) = cstash.as_ref() {
|
||||
if let Ok(window_state) = cstash.window_state.lock() {
|
||||
if cstash.mouse_in_window {
|
||||
match window_state.cursor_state {
|
||||
CursorState::Normal => {
|
||||
user32::SetCursor(user32::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
window_state.cursor));
|
||||
},
|
||||
CursorState::Grab | CursorState::Hide => {
|
||||
user32::SetCursor(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
call_def_window_proc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_def_window_proc
|
||||
});
|
||||
|
||||
if call_def_window_proc {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
winapi::WM_DROPFILES => {
|
||||
use events::Event::DroppedFile;
|
||||
|
||||
let hdrop = wparam as winapi::HDROP;
|
||||
let mut pathbuf: [u16; winapi::MAX_PATH] = mem::uninitialized();
|
||||
let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
||||
|
||||
for i in 0..num_drops {
|
||||
let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
|
||||
winapi::MAX_PATH as u32) as usize;
|
||||
if nch > 0 {
|
||||
send_event(window, DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into()));
|
||||
}
|
||||
}
|
||||
|
||||
shell32::DragFinish(hdrop);
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_GETMINMAXINFO => {
|
||||
let mmi = lparam as *mut MinMaxInfo;
|
||||
//(*mmi).max_position = winapi::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
|
||||
//(*mmi).max_size = winapi::POINT { x: .., y: .. }; // The dimensions of the primary monitor.
|
||||
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
match context_stash.borrow().as_ref() {
|
||||
Some(cstash) => {
|
||||
let window_state = cstash.window_state.lock().unwrap();
|
||||
|
||||
match window_state.attributes.min_dimensions {
|
||||
Some((width, height)) => {
|
||||
(*mmi).min_track = winapi::POINT { x: width as i32, y: height as i32 };
|
||||
},
|
||||
None => { }
|
||||
}
|
||||
|
||||
match window_state.attributes.max_dimensions {
|
||||
Some((width, height)) => {
|
||||
(*mmi).max_track = winapi::POINT { x: width as i32, y: height as i32 };
|
||||
},
|
||||
None => { }
|
||||
}
|
||||
},
|
||||
None => { }
|
||||
}
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
x if x == *super::WAKEUP_MSG_ID => {
|
||||
use events::Event::Awakened;
|
||||
send_event(window, Awakened);
|
||||
0
|
||||
},
|
||||
|
||||
_ => {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
}
|
||||
}
|
||||
190
src/platform/windows/event.rs
Normal file
190
src/platform/windows/event.rs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
use events::VirtualKeyCode;
|
||||
use winapi;
|
||||
use user32;
|
||||
use ScanCode;
|
||||
|
||||
const MAPVK_VSC_TO_VK_EX: u32 = 3;
|
||||
|
||||
pub fn vkeycode_to_element(wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
|
||||
let scancode = ((lparam >> 16) & 0xff) as u8;
|
||||
let extended = (lparam & 0x01000000) != 0;
|
||||
let vk = match wparam as i32 {
|
||||
winapi::VK_SHIFT => unsafe { user32::MapVirtualKeyA(scancode as u32, MAPVK_VSC_TO_VK_EX) as i32 },
|
||||
winapi::VK_CONTROL => if extended { winapi::VK_RCONTROL } else { winapi::VK_LCONTROL },
|
||||
winapi::VK_MENU => if extended { winapi::VK_RMENU } else { winapi::VK_LMENU },
|
||||
other => other
|
||||
};
|
||||
(scancode, match vk {
|
||||
//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_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_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::NoConvert),
|
||||
//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_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),
|
||||
winapi::VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward),
|
||||
winapi::VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh),
|
||||
winapi::VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop),
|
||||
winapi::VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch),
|
||||
winapi::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites),
|
||||
winapi::VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome),
|
||||
winapi::VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute),
|
||||
winapi::VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown),
|
||||
winapi::VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp),
|
||||
winapi::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack),
|
||||
winapi::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack),
|
||||
winapi::VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop),
|
||||
winapi::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause),
|
||||
winapi::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail),
|
||||
winapi::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect),
|
||||
/*winapi::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1),
|
||||
winapi::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/
|
||||
winapi::VK_OEM_PLUS => Some(VirtualKeyCode::Equals),
|
||||
winapi::VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
|
||||
winapi::VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
|
||||
winapi::VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
|
||||
/*winapi::VK_OEM_1 => Some(VirtualKeyCode::Oem_1),
|
||||
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::OEM102),
|
||||
/*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
|
||||
})
|
||||
}
|
||||
245
src/platform/windows/init.rs
Normal file
245
src/platform/windows/init.rs
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use std::thread;
|
||||
|
||||
use super::callback;
|
||||
use super::WindowState;
|
||||
use super::Window;
|
||||
use super::MonitorId;
|
||||
use super::WindowWrapper;
|
||||
|
||||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use CursorState;
|
||||
use WindowAttributes;
|
||||
|
||||
use std::ffi::{OsStr};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use winapi;
|
||||
use kernel32;
|
||||
use dwmapi;
|
||||
use user32;
|
||||
|
||||
pub fn new_window(window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||
let window = window.clone();
|
||||
|
||||
// initializing variables to be sent to the task
|
||||
|
||||
let title = OsStr::new(&window.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, &window) {
|
||||
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>, window: &WindowAttributes) -> Result<Window, CreationError> {
|
||||
// registering the window class
|
||||
let class_name = register_window_class();
|
||||
|
||||
// building a RECT object with coordinates
|
||||
let mut rect = winapi::RECT {
|
||||
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as winapi::LONG,
|
||||
top: 0, bottom: window.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 window.monitor.is_some() {
|
||||
let monitor = window.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 window.monitor.is_some() || window.decorations == false {
|
||||
(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);
|
||||
|
||||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (width, height) = if window.monitor.is_some() || window.dimensions.is_some() {
|
||||
(Some(rect.right - rect.left), Some(rect.bottom - rect.top))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let (x, y) = if window.monitor.is_some() {
|
||||
(Some(rect.left), Some(rect.top))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let style = if !window.visible {
|
||||
style
|
||||
} else {
|
||||
style | winapi::WS_VISIBLE
|
||||
};
|
||||
|
||||
let handle = user32::CreateWindowExW(ex_style | winapi::WS_EX_ACCEPTFILES,
|
||||
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)
|
||||
};
|
||||
|
||||
// making the window transparent
|
||||
if window.transparent {
|
||||
let bb = winapi::DWM_BLURBEHIND {
|
||||
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
||||
fEnable: 1,
|
||||
hRgnBlur: ptr::null_mut(),
|
||||
fTransitionOnMaximized: 0,
|
||||
};
|
||||
|
||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||
}
|
||||
|
||||
// calling SetForegroundWindow if fullscreen
|
||||
if window.monitor.is_some() {
|
||||
user32::SetForegroundWindow(real_window.0);
|
||||
}
|
||||
|
||||
// Creating a mutex to track the current window state
|
||||
let window_state = Arc::new(Mutex::new(WindowState {
|
||||
cursor: winapi::IDC_ARROW, // use arrow by default
|
||||
cursor_state: CursorState::Normal,
|
||||
attributes: window.clone()
|
||||
}));
|
||||
|
||||
// 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(),
|
||||
window_state: window_state.clone(),
|
||||
mouse_in_window: false
|
||||
};
|
||||
(*context_stash.borrow_mut()) = Some(data);
|
||||
});
|
||||
rx
|
||||
};
|
||||
|
||||
// building the struct
|
||||
Ok(Window {
|
||||
window: real_window,
|
||||
events_receiver: events_receiver,
|
||||
window_state: window_state,
|
||||
})
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
|
@ -1,44 +1,384 @@
|
|||
#![cfg(target_os = "windows")]
|
||||
|
||||
pub use api::win32;
|
||||
pub use api::win32::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use api::win32::{WindowProxy, PollEventsIterator, WaitEventsIterator};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
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 CreationError;
|
||||
use WindowAttributes;
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
||||
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
|
||||
use winapi;
|
||||
use user32;
|
||||
use kernel32;
|
||||
|
||||
mod callback;
|
||||
mod event;
|
||||
mod init;
|
||||
mod monitor;
|
||||
|
||||
lazy_static! {
|
||||
static ref WAKEUP_MSG_ID: u32 = unsafe { user32::RegisterWindowMessageA("Glutin::EventID".as_ptr() as *const i8) };
|
||||
}
|
||||
|
||||
/// Cursor
|
||||
pub type Cursor = *const winapi::wchar_t;
|
||||
|
||||
/// Contains information about states and the window for the callback.
|
||||
#[derive(Clone)]
|
||||
pub struct WindowState {
|
||||
pub cursor: Cursor,
|
||||
pub cursor_state: CursorState,
|
||||
pub attributes: WindowAttributes
|
||||
}
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
pub struct Window(win32::Window);
|
||||
pub struct Window {
|
||||
/// Main handle for the window.
|
||||
window: WindowWrapper,
|
||||
|
||||
/// Receiver for the events dispatched by the window callback.
|
||||
events_receiver: Receiver<Event>,
|
||||
|
||||
/// The current window state.
|
||||
window_state: Arc<Mutex<WindowState>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
/// 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 {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
user32::DestroyWindow(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy {
|
||||
hwnd: winapi::HWND,
|
||||
}
|
||||
|
||||
unsafe impl Send for WindowProxy {}
|
||||
unsafe impl Sync for WindowProxy {}
|
||||
|
||||
impl WindowProxy {
|
||||
#[inline]
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
unsafe {
|
||||
user32::PostMessageA(self.hwnd, *WAKEUP_MSG_ID, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn new(window: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
win32::Window::new(window).map(Window)
|
||||
init::new_window(window)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Window {
|
||||
type Target = win32::Window;
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &win32::Window {
|
||||
&self.0
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
user32::ShowWindow(self.window.0, winapi::SW_SHOW);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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 {
|
||||
// Calculate the outer size based upon the specified inner size
|
||||
let mut rect = winapi::RECT { top: 0, left: 0, bottom: y as winapi::LONG, right: x as winapi::LONG };
|
||||
let dw_style = user32::GetWindowLongA(self.window.0, winapi::GWL_STYLE) as winapi::DWORD;
|
||||
let b_menu = !user32::GetMenu(self.window.0).is_null() as winapi::BOOL;
|
||||
let dw_style_ex = user32::GetWindowLongA(self.window.0, winapi::GWL_EXSTYLE) as winapi::DWORD;
|
||||
user32::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||
let outer_x = (rect.right - rect.left).abs() as libc::c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as libc::c_int;
|
||||
|
||||
user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||
winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION | winapi::SWP_NOMOVE);
|
||||
user32::UpdateWindow(self.window.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy { hwnd: self.window.0 }
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
#[inline]
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
// What should this return on win32?
|
||||
// It could be GetDC(NULL), but that requires a ReleaseDC()
|
||||
// to avoid leaking the DC.
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
self.window.0 as *mut libc::c_void
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
let cursor_id = match _cursor {
|
||||
MouseCursor::Arrow | MouseCursor::Default => winapi::IDC_ARROW,
|
||||
MouseCursor::Hand => winapi::IDC_HAND,
|
||||
MouseCursor::Crosshair => winapi::IDC_CROSS,
|
||||
MouseCursor::Text | MouseCursor::VerticalText => winapi::IDC_IBEAM,
|
||||
MouseCursor::NotAllowed | MouseCursor::NoDrop => winapi::IDC_NO,
|
||||
MouseCursor::EResize => winapi::IDC_SIZEWE,
|
||||
MouseCursor::NResize => winapi::IDC_SIZENS,
|
||||
MouseCursor::WResize => winapi::IDC_SIZEWE,
|
||||
MouseCursor::SResize => winapi::IDC_SIZENS,
|
||||
MouseCursor::EwResize | MouseCursor::ColResize => winapi::IDC_SIZEWE,
|
||||
MouseCursor::NsResize | MouseCursor::RowResize => winapi::IDC_SIZENS,
|
||||
MouseCursor::Wait | MouseCursor::Progress => winapi::IDC_WAIT,
|
||||
MouseCursor::Help => winapi::IDC_HELP,
|
||||
_ => winapi::IDC_ARROW, // use arrow for the missing cases.
|
||||
};
|
||||
|
||||
let mut cur = self.window_state.lock().unwrap();
|
||||
cur.cursor = cursor_id;
|
||||
}
|
||||
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let mut current_state = self.window_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.cursor_state) {
|
||||
(CursorState::Normal, CursorState::Normal) => Ok(()),
|
||||
(CursorState::Hide, CursorState::Hide) => Ok(()),
|
||||
(CursorState::Grab, CursorState::Grab) => Ok(()),
|
||||
|
||||
(CursorState::Hide, CursorState::Normal) => {
|
||||
current_state.cursor_state = CursorState::Hide;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Hide) => {
|
||||
current_state.cursor_state = CursorState::Normal;
|
||||
Ok(())
|
||||
},
|
||||
|
||||
(CursorState::Grab, CursorState::Normal) | (CursorState::Grab, CursorState::Hide) => {
|
||||
unsafe {
|
||||
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.cursor_state = CursorState::Grab;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Grab) => {
|
||||
unsafe {
|
||||
if user32::ClipCursor(ptr::null()) == 0 {
|
||||
return Err(format!("ClipCursor failed"));
|
||||
}
|
||||
current_state.cursor_state = CursorState::Normal;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Window {
|
||||
pub struct PollEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut win32::Window {
|
||||
&mut self.0
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
self.window.events_receiver.try_recv().ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
self.window.events_receiver.recv().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
#[inline]
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
186
src/platform/windows/monitor.rs
Normal file
186
src/platform/windows/monitor.rs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
use winapi;
|
||||
use user32;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
/// Win32 implementation of the main `MonitorId` object.
|
||||
#[derive(Clone)]
|
||||
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.
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.readable_name.clone())
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Name(self.monitor_name.clone())
|
||||
}
|
||||
|
||||
/// See the docs if the crate root file.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
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.
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (u32, u32) {
|
||||
self.position
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue