2017-06-27 05:21:13 +10:00
|
|
|
|
#![cfg(target_os = "windows")]
|
|
|
|
|
|
|
|
|
|
use std::ffi::OsStr;
|
|
|
|
|
use std::io;
|
|
|
|
|
use std::mem;
|
|
|
|
|
use std::os::raw;
|
|
|
|
|
use std::os::windows::ffi::OsStrExt;
|
|
|
|
|
use std::ptr;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use std::sync::Mutex;
|
|
|
|
|
use std::sync::mpsc::channel;
|
2018-04-13 03:12:15 +10:00
|
|
|
|
use std::cell::Cell;
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-25 06:20:40 +10:00
|
|
|
|
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
|
2017-06-27 05:21:13 +10:00
|
|
|
|
use platform::platform::EventsLoop;
|
|
|
|
|
use platform::platform::PlatformSpecificWindowBuilderAttributes;
|
Windows: Implement DeviceEvents (#482)
Fixes #467
All variants other than Text have been implemented. While Text can
be implemented using ToUnicode, that doesn't play nice with dead
keys, IME, etc.
Most of the mouse DeviceEvents were already implemented, but due
to the flags that were used when registering for raw input events,
they only worked when the window was in the foreground.
This is also a step forward for #338, as DeviceIds are no longer
useless on Windows. On DeviceEvents, the DeviceId contains that
device's handle. While that handle could ostensibly be used by
developers to query device information, my actual reason for
choosing it is because it's simply a very easy way to handle this.
As a fun bonus, this enabled me to create this method:
DevideIdExt::get_persistent_identifier() -> Option<String>
Using this gives you a unique identifier for the device that
persists across replugs/reboots/etc., so it's ideal for something
like device-specific configuration.
There's a notable caveat to the new DeviceIds, which is that the
value will always be 0 for a WindowEvent. There doesn't seem to be
any straightforward way around this limitation.
I was concerned that multi-window applications would receive n
copies of every DeviceEvent, but Windows only sends them to one
window per application.
Lastly, there's a chance that these additions will cause
antivirus/etc. software to detect winit applications as keyloggers.
I don't know how likely that is to actually happen to people, but
if it does become an issue, the raw input code is neatly
sequestered and would be easy to make optional during compilation.
2018-04-29 02:42:33 +10:00
|
|
|
|
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
2017-06-27 05:21:13 +10:00
|
|
|
|
use platform::platform::WindowId;
|
|
|
|
|
|
|
|
|
|
use CreationError;
|
|
|
|
|
use CursorState;
|
|
|
|
|
use MouseCursor;
|
|
|
|
|
use WindowAttributes;
|
2017-08-29 11:08:12 +10:00
|
|
|
|
use MonitorId as RootMonitorId;
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
use winapi::shared::minwindef::{UINT, DWORD, BOOL};
|
2017-12-25 00:46:47 +11:00
|
|
|
|
use winapi::shared::windef::{HWND, HDC, RECT, POINT};
|
2018-04-13 03:12:15 +10:00
|
|
|
|
use winapi::um::{winuser, dwmapi, libloaderapi, processthreadsapi};
|
|
|
|
|
use winapi::um::winnt::{LPCWSTR, LONG, HRESULT};
|
|
|
|
|
use winapi::um::combaseapi;
|
|
|
|
|
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
|
|
|
|
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
|
|
|
|
/// The Win32 implementation of the main `Window` object.
|
|
|
|
|
pub struct Window {
|
|
|
|
|
/// Main handle for the window.
|
|
|
|
|
window: WindowWrapper,
|
|
|
|
|
|
|
|
|
|
/// The current window state.
|
|
|
|
|
window_state: Arc<Mutex<events_loop::WindowState>>,
|
2018-04-13 03:12:15 +10:00
|
|
|
|
|
|
|
|
|
// The events loop proxy.
|
|
|
|
|
events_loop_proxy: events_loop::EventsLoopProxy,
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe impl Send for Window {}
|
|
|
|
|
unsafe impl Sync for Window {}
|
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
|
2018-04-25 06:20:40 +10:00
|
|
|
|
// The idea here is that we use the AdjustWindowRectEx function to calculate how much additional
|
|
|
|
|
// non-client area gets added due to the styles we passed. To make the math simple,
|
|
|
|
|
// we ask for a zero client rectangle, so that the resulting window is all non-client.
|
|
|
|
|
// And then we pass in the empty rectangle represented by the dot in the middle,
|
|
|
|
|
// and the AdjustWindowRectEx expands the rectangle in all dimensions.
|
|
|
|
|
// We see that it added ten pixels to the left, right, and bottom,
|
2018-04-13 03:12:15 +10:00
|
|
|
|
// and it added fifty pixels to the top.
|
2018-04-25 06:20:40 +10:00
|
|
|
|
// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.
|
2018-04-13 03:12:15 +10:00
|
|
|
|
unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> BOOL {
|
|
|
|
|
let mut rc: RECT = mem::zeroed();
|
|
|
|
|
|
|
|
|
|
winuser::SetRectEmpty(&mut rc);
|
|
|
|
|
|
|
|
|
|
let frc = winuser::AdjustWindowRectEx(&mut rc, style, 0, ex_style);
|
|
|
|
|
if frc != 0 {
|
|
|
|
|
prc.left -= rc.left;
|
|
|
|
|
prc.top -= rc.top;
|
|
|
|
|
prc.right -= rc.right;
|
|
|
|
|
prc.bottom -= rc.bottom;
|
|
|
|
|
}
|
2018-04-25 06:20:40 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
frc
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-27 05:21:13 +10:00
|
|
|
|
impl Window {
|
|
|
|
|
pub fn new(events_loop: &EventsLoop, w_attr: &WindowAttributes,
|
|
|
|
|
pl_attr: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
|
|
|
|
|
{
|
|
|
|
|
let mut w_attr = Some(w_attr.clone());
|
|
|
|
|
let mut pl_attr = Some(pl_attr.clone());
|
2017-12-22 23:50:46 +11:00
|
|
|
|
|
2017-06-27 05:21:13 +10:00
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
let proxy = events_loop.create_proxy();
|
|
|
|
|
|
2017-06-27 05:21:13 +10:00
|
|
|
|
events_loop.execute_in_thread(move |inserter| {
|
|
|
|
|
// We dispatch an `init` function because of code style.
|
2018-04-13 03:12:15 +10:00
|
|
|
|
let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter, proxy.clone()) };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
let _ = tx.send(win);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
rx.recv().unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_title(&self, text: &str) {
|
|
|
|
|
unsafe {
|
|
|
|
|
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::SetWindowTextW(self.window.0, text.as_ptr() as LPCWSTR);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn show(&self) {
|
|
|
|
|
unsafe {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::ShowWindow(self.window.0, winuser::SW_SHOW);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn hide(&self) {
|
|
|
|
|
unsafe {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::ShowWindow(self.window.0, winuser::SW_HIDE);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// See the docs in the crate root file.
|
|
|
|
|
pub fn get_position(&self) -> Option<(i32, i32)> {
|
Windows: Position fixes (#479)
* Remove executable flag from os/macos.rs
This was causing me some grief while working on Windows, and it
doesn't belong here to begin with.
* Windows: get_position returns screen coordinates instead of workspace coordinates
Previously, get_position used GetWindowPlacement. As per the
documentation of WINDOWSTRUCT, the returned coordinates are in
workspace space, meaning they're relative to the taskbar. It's
also explicitly remarked that these coordinates should only be
used in conjunction with SetWindowPlacement, as mixing them with
functions expecting screen coordinates can cause unpleasantness.
Since our set_position (correctly) uses SetWindowPos, this meant
that passing the return of get_position to set_position would
cause the window to move.
We now use GetWindowRect, which returns screen coordinates. This
gives us both better consistency within the Windows backend and
across platforms.
Note that this only makes a difference if the taskbar is visible.
With the taskbar hidden, the values are exactly the same as before.
* Windows: Moved event position values are consistent with get_position
The old Moved values had two problems:
* They were obtained by casting a WORD (u16) straight to an i32.
This meant wrap-around would never be interpreted as negative,
thus negative positions (which are ubiquitous when using multiple
monitors) would result in positions around u16::MAX.
* WM_MOVE supplies client area positions, not window positions.
Switching to handling WM_WINDOWPOSCHANGED solves both of these
problems.
* Better documentation for Moved and Resized
2018-04-27 10:09:33 +10:00
|
|
|
|
let mut rect: RECT = unsafe { mem::uninitialized() };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
Windows: Position fixes (#479)
* Remove executable flag from os/macos.rs
This was causing me some grief while working on Windows, and it
doesn't belong here to begin with.
* Windows: get_position returns screen coordinates instead of workspace coordinates
Previously, get_position used GetWindowPlacement. As per the
documentation of WINDOWSTRUCT, the returned coordinates are in
workspace space, meaning they're relative to the taskbar. It's
also explicitly remarked that these coordinates should only be
used in conjunction with SetWindowPlacement, as mixing them with
functions expecting screen coordinates can cause unpleasantness.
Since our set_position (correctly) uses SetWindowPos, this meant
that passing the return of get_position to set_position would
cause the window to move.
We now use GetWindowRect, which returns screen coordinates. This
gives us both better consistency within the Windows backend and
across platforms.
Note that this only makes a difference if the taskbar is visible.
With the taskbar hidden, the values are exactly the same as before.
* Windows: Moved event position values are consistent with get_position
The old Moved values had two problems:
* They were obtained by casting a WORD (u16) straight to an i32.
This meant wrap-around would never be interpreted as negative,
thus negative positions (which are ubiquitous when using multiple
monitors) would result in positions around u16::MAX.
* WM_MOVE supplies client area positions, not window positions.
Switching to handling WM_WINDOWPOSCHANGED solves both of these
problems.
* Better documentation for Moved and Resized
2018-04-27 10:09:33 +10:00
|
|
|
|
if unsafe { winuser::GetWindowRect(self.window.0, &mut rect) } != 0 {
|
|
|
|
|
Some((rect.left as i32, rect.top as i32))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 11:40:30 +10:00
|
|
|
|
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
|
|
|
|
use std::mem;
|
|
|
|
|
|
Windows: Position fixes (#479)
* Remove executable flag from os/macos.rs
This was causing me some grief while working on Windows, and it
doesn't belong here to begin with.
* Windows: get_position returns screen coordinates instead of workspace coordinates
Previously, get_position used GetWindowPlacement. As per the
documentation of WINDOWSTRUCT, the returned coordinates are in
workspace space, meaning they're relative to the taskbar. It's
also explicitly remarked that these coordinates should only be
used in conjunction with SetWindowPlacement, as mixing them with
functions expecting screen coordinates can cause unpleasantness.
Since our set_position (correctly) uses SetWindowPos, this meant
that passing the return of get_position to set_position would
cause the window to move.
We now use GetWindowRect, which returns screen coordinates. This
gives us both better consistency within the Windows backend and
across platforms.
Note that this only makes a difference if the taskbar is visible.
With the taskbar hidden, the values are exactly the same as before.
* Windows: Moved event position values are consistent with get_position
The old Moved values had two problems:
* They were obtained by casting a WORD (u16) straight to an i32.
This meant wrap-around would never be interpreted as negative,
thus negative positions (which are ubiquitous when using multiple
monitors) would result in positions around u16::MAX.
* WM_MOVE supplies client area positions, not window positions.
Switching to handling WM_WINDOWPOSCHANGED solves both of these
problems.
* Better documentation for Moved and Resized
2018-04-27 10:09:33 +10:00
|
|
|
|
let mut position: POINT = unsafe { mem::zeroed() };
|
|
|
|
|
if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
|
2018-04-17 11:40:30 +10:00
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some((position.x, position.y))
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-27 05:21:13 +10:00
|
|
|
|
/// See the docs in the crate root file.
|
|
|
|
|
pub fn set_position(&self, x: i32, y: i32) {
|
|
|
|
|
unsafe {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::SetWindowPos(self.window.0, ptr::null_mut(), x as raw::c_int, y as raw::c_int,
|
|
|
|
|
0, 0, winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE);
|
|
|
|
|
winuser::UpdateWindow(self.window.0);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// See the docs in the crate root file.
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let mut rect: RECT = unsafe { mem::uninitialized() };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
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)> {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let mut rect: RECT = unsafe { mem::uninitialized() };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
if unsafe { winuser::GetWindowRect(self.window.0, &mut rect) } == 0 {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
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) {
|
|
|
|
|
unsafe {
|
|
|
|
|
// Calculate the outer size based upon the specified inner size
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let mut rect = RECT { top: 0, left: 0, bottom: y as LONG, right: x as LONG };
|
|
|
|
|
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
|
|
|
|
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
|
|
|
|
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
|
|
|
|
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
|
|
|
|
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
|
|
|
|
winuser::UpdateWindow(self.window.0);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-23 20:35:35 +11:00
|
|
|
|
/// See the docs in the crate root file.
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
|
|
|
|
let mut window_state = self.window_state.lock().unwrap();
|
|
|
|
|
window_state.attributes.min_dimensions = dimensions;
|
|
|
|
|
|
|
|
|
|
// Make windows re-check the window size bounds.
|
|
|
|
|
if let Some(inner_size) = self.get_inner_size() {
|
|
|
|
|
unsafe {
|
|
|
|
|
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
|
|
|
|
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
|
|
|
|
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
|
|
|
|
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
|
|
|
|
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
|
|
|
|
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
|
|
|
|
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
|
|
|
|
|
|
|
|
|
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// See the docs in the crate root file.
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
|
|
|
|
let mut window_state = self.window_state.lock().unwrap();
|
|
|
|
|
window_state.attributes.max_dimensions = dimensions;
|
|
|
|
|
|
|
|
|
|
// Make windows re-check the window size bounds.
|
|
|
|
|
if let Some(inner_size) = self.get_inner_size() {
|
|
|
|
|
unsafe {
|
|
|
|
|
let mut rect = RECT { top: 0, left: 0, bottom: inner_size.1 as LONG, right: inner_size.0 as LONG };
|
|
|
|
|
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
|
|
|
|
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
|
|
|
|
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
|
|
|
|
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
|
|
|
|
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
|
|
|
|
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
|
|
|
|
|
|
|
|
|
winuser::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOREPOSITION | winuser::SWP_NOMOVE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-27 05:21:13 +10:00
|
|
|
|
// TODO: remove
|
|
|
|
|
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
|
|
|
|
panic!() // Deprecated function ; we don't care anymore
|
|
|
|
|
}
|
|
|
|
|
// TODO: remove
|
|
|
|
|
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
2017-07-14 05:14:32 +10:00
|
|
|
|
self.window.0 as *mut ::libc::c_void
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the `hwnd` of this window.
|
|
|
|
|
#[inline]
|
2017-12-25 00:46:47 +11:00
|
|
|
|
pub fn hwnd(&self) -> HWND {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
self.window.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_cursor(&self, cursor: MouseCursor) {
|
|
|
|
|
let cursor_id = match cursor {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
MouseCursor::Arrow | MouseCursor::Default => winuser::IDC_ARROW,
|
|
|
|
|
MouseCursor::Hand => winuser::IDC_HAND,
|
|
|
|
|
MouseCursor::Crosshair => winuser::IDC_CROSS,
|
|
|
|
|
MouseCursor::Text | MouseCursor::VerticalText => winuser::IDC_IBEAM,
|
|
|
|
|
MouseCursor::NotAllowed | MouseCursor::NoDrop => winuser::IDC_NO,
|
2018-04-14 03:53:01 +10:00
|
|
|
|
MouseCursor::Grab | MouseCursor::Grabbing |
|
|
|
|
|
MouseCursor::Move | MouseCursor::AllScroll => winuser::IDC_SIZEALL,
|
|
|
|
|
MouseCursor::EResize | MouseCursor::WResize |
|
2017-12-25 00:46:47 +11:00
|
|
|
|
MouseCursor::EwResize | MouseCursor::ColResize => winuser::IDC_SIZEWE,
|
2018-04-14 03:53:01 +10:00
|
|
|
|
MouseCursor::NResize | MouseCursor::SResize |
|
2017-12-25 00:46:47 +11:00
|
|
|
|
MouseCursor::NsResize | MouseCursor::RowResize => winuser::IDC_SIZENS,
|
2018-04-14 03:53:01 +10:00
|
|
|
|
MouseCursor::NeResize | MouseCursor::SwResize |
|
|
|
|
|
MouseCursor::NeswResize => winuser::IDC_SIZENESW,
|
|
|
|
|
MouseCursor::NwResize | MouseCursor::SeResize |
|
|
|
|
|
MouseCursor::NwseResize => winuser::IDC_SIZENWSE,
|
|
|
|
|
MouseCursor::Wait => winuser::IDC_WAIT,
|
|
|
|
|
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
2017-12-25 00:46:47 +11:00
|
|
|
|
MouseCursor::Help => winuser::IDC_HELP,
|
|
|
|
|
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
2017-06-27 05:21:13 +10:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut cur = self.window_state.lock().unwrap();
|
|
|
|
|
cur.cursor = cursor_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: it should be possible to rework this function by using the `execute_in_thread` method
|
|
|
|
|
// of the events loop.
|
|
|
|
|
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
|
|
|
|
let mut current_state = self.window_state.lock().unwrap();
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let foreground_thread_id = unsafe { winuser::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) };
|
|
|
|
|
let current_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
unsafe { winuser::AttachThreadInput(foreground_thread_id, current_thread_id, 1) };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
|
|
|
|
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();
|
2017-12-25 00:46:47 +11:00
|
|
|
|
if winuser::GetClientRect(self.window.0, &mut rect) == 0 {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
return Err(format!("GetWindowRect failed"));
|
|
|
|
|
}
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::ClientToScreen(self.window.0, mem::transmute(&mut rect.left));
|
|
|
|
|
winuser::ClientToScreen(self.window.0, mem::transmute(&mut rect.right));
|
|
|
|
|
if winuser::ClipCursor(&rect) == 0 {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
return Err(format!("ClipCursor failed"));
|
|
|
|
|
}
|
|
|
|
|
current_state.cursor_state = CursorState::Grab;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
(CursorState::Normal, CursorState::Grab) => {
|
|
|
|
|
unsafe {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
if winuser::ClipCursor(ptr::null()) == 0 {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
return Err(format!("ClipCursor failed"));
|
|
|
|
|
}
|
|
|
|
|
current_state.cursor_state = CursorState::Normal;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
_ => unimplemented!(),
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
unsafe { winuser::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
|
|
|
|
res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn hidpi_factor(&self) -> f32 {
|
|
|
|
|
1.0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let mut point = POINT {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
x: x,
|
|
|
|
|
y: y,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsafe {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
if winuser::ClientToScreen(self.window.0, &mut point) == 0 {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
return Err(());
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
if winuser::SetCursorPos(point.x, point.y) == 0 {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
return Err(());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn id(&self) -> WindowId {
|
|
|
|
|
WindowId(self.window.0)
|
|
|
|
|
}
|
2017-08-28 10:43:34 +10:00
|
|
|
|
|
|
|
|
|
#[inline]
|
2018-04-13 03:12:15 +10:00
|
|
|
|
pub fn set_maximized(&self, maximized: bool) {
|
|
|
|
|
let mut window_state = self.window_state.lock().unwrap();
|
|
|
|
|
|
2018-04-25 06:20:40 +10:00
|
|
|
|
window_state.attributes.maximized = maximized;
|
2018-04-13 03:12:15 +10:00
|
|
|
|
// we only maximized if we are not in fullscreen
|
|
|
|
|
if window_state.attributes.fullscreen.is_some() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-04-25 06:20:40 +10:00
|
|
|
|
|
|
|
|
|
let window = self.window.clone();
|
2018-04-13 03:12:15 +10:00
|
|
|
|
unsafe {
|
|
|
|
|
// And because ShowWindow will resize the window
|
|
|
|
|
// We call it in the main thread
|
|
|
|
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
|
|
|
|
winuser::ShowWindow(
|
|
|
|
|
window.0,
|
|
|
|
|
if maximized {
|
|
|
|
|
winuser::SW_MAXIMIZE
|
|
|
|
|
} else {
|
|
|
|
|
winuser::SW_RESTORE
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn set_fullscreen_style(&self) -> (LONG, LONG) {
|
|
|
|
|
let mut window_state = self.window_state.lock().unwrap();
|
|
|
|
|
|
|
|
|
|
if window_state.attributes.fullscreen.is_none() || window_state.saved_window_info.is_none() {
|
|
|
|
|
let mut rect: RECT = mem::zeroed();
|
|
|
|
|
|
|
|
|
|
winuser::GetWindowRect(self.window.0, &mut rect);
|
|
|
|
|
|
|
|
|
|
window_state.saved_window_info = Some(events_loop::SavedWindowInfo {
|
|
|
|
|
style: winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE),
|
|
|
|
|
ex_style: winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE),
|
|
|
|
|
rect,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We sync the system maximized state here, it will be used when restoring
|
|
|
|
|
let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed();
|
|
|
|
|
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as u32;
|
|
|
|
|
winuser::GetWindowPlacement(self.window.0, &mut placement);
|
|
|
|
|
window_state.attributes.maximized =
|
|
|
|
|
placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32);
|
|
|
|
|
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
|
|
|
|
|
|
|
|
|
(saved_window_info.style, saved_window_info.ex_style)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn restore_saved_window(&self) {
|
|
|
|
|
let window_state = self.window_state.lock().unwrap();
|
|
|
|
|
// Reset original window style and size. The multiple window size/moves
|
|
|
|
|
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
|
|
|
|
// repainted. Better-looking methods welcome.
|
|
|
|
|
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
|
|
|
|
|
|
|
|
|
let rect = saved_window_info.rect.clone();
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
let (style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
|
|
|
|
|
|
|
|
|
let maximized = window_state.attributes.maximized;
|
|
|
|
|
|
|
|
|
|
// On restore, resize to the previous saved rect size.
|
|
|
|
|
// And because SetWindowPos will resize the window
|
|
|
|
|
// We call it in the main thread
|
|
|
|
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
|
|
|
|
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
|
|
|
|
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
|
|
|
|
|
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
|
window.0,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
rect.left,
|
|
|
|
|
rect.top,
|
|
|
|
|
rect.right - rect.left,
|
|
|
|
|
rect.bottom - rect.top,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE
|
|
|
|
|
| winuser::SWP_FRAMECHANGED,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// if it was set to maximized when it were fullscreened, we restore it as well
|
|
|
|
|
winuser::ShowWindow(
|
|
|
|
|
window.0,
|
|
|
|
|
if maximized {
|
|
|
|
|
winuser::SW_MAXIMIZE
|
|
|
|
|
} else {
|
|
|
|
|
winuser::SW_RESTORE
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
mark_fullscreen(window.0, false);
|
|
|
|
|
});
|
2017-08-28 10:43:34 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2018-04-13 03:12:15 +10:00
|
|
|
|
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
|
|
|
|
unsafe {
|
|
|
|
|
match &monitor {
|
|
|
|
|
&Some(RootMonitorId { ref inner }) => {
|
|
|
|
|
let pos = inner.get_position();
|
|
|
|
|
let dim = inner.get_dimensions();
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
|
|
|
|
|
let (style, ex_style) = self.set_fullscreen_style();
|
|
|
|
|
|
|
|
|
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
|
|
|
|
winuser::SetWindowLongW(
|
|
|
|
|
window.0,
|
|
|
|
|
winuser::GWL_STYLE,
|
|
|
|
|
((style as DWORD) & !(winuser::WS_CAPTION | winuser::WS_THICKFRAME))
|
|
|
|
|
as LONG,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
winuser::SetWindowLongW(
|
|
|
|
|
window.0,
|
|
|
|
|
winuser::GWL_EXSTYLE,
|
|
|
|
|
((ex_style as DWORD)
|
|
|
|
|
& !(winuser::WS_EX_DLGMODALFRAME | winuser::WS_EX_WINDOWEDGE
|
|
|
|
|
| winuser::WS_EX_CLIENTEDGE
|
|
|
|
|
| winuser::WS_EX_STATICEDGE))
|
|
|
|
|
as LONG,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
|
window.0,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
pos.0,
|
|
|
|
|
pos.1,
|
|
|
|
|
dim.0 as i32,
|
|
|
|
|
dim.1 as i32,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
|
|
|
|
| winuser::SWP_NOACTIVATE
|
|
|
|
|
| winuser::SWP_FRAMECHANGED,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
mark_fullscreen(window.0, true);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
&None => {
|
|
|
|
|
self.restore_saved_window();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut window_state = self.window_state.lock().unwrap();
|
|
|
|
|
window_state.attributes.fullscreen = monitor;
|
2017-09-07 18:33:46 +10:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-22 23:50:46 +11:00
|
|
|
|
#[inline]
|
2018-04-13 03:12:15 +10:00
|
|
|
|
pub fn set_decorations(&self, decorations: bool) {
|
|
|
|
|
if let Ok(mut window_state) = self.window_state.lock() {
|
|
|
|
|
if window_state.attributes.decorations == decorations {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let style_flags = (winuser::WS_CAPTION | winuser::WS_THICKFRAME) as LONG;
|
|
|
|
|
let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG;
|
|
|
|
|
|
|
|
|
|
// if we are in fullscreen mode, we only change the saved window info
|
|
|
|
|
if window_state.attributes.fullscreen.is_some() {
|
|
|
|
|
{
|
|
|
|
|
let mut saved = window_state.saved_window_info.as_mut().unwrap();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
unjust_window_rect(&mut saved.rect, saved.style as _, saved.ex_style as _);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if decorations {
|
|
|
|
|
saved.style = saved.style | style_flags;
|
|
|
|
|
saved.ex_style = saved.ex_style | ex_style_flags;
|
|
|
|
|
} else {
|
|
|
|
|
saved.style = saved.style & !style_flags;
|
|
|
|
|
saved.ex_style = saved.ex_style & !ex_style_flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
winuser::AdjustWindowRectEx(
|
|
|
|
|
&mut saved.rect,
|
|
|
|
|
saved.style as _,
|
|
|
|
|
0,
|
|
|
|
|
saved.ex_style as _,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window_state.attributes.decorations = decorations;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
let mut rect: RECT = mem::zeroed();
|
|
|
|
|
winuser::GetWindowRect(self.window.0, &mut rect);
|
|
|
|
|
|
|
|
|
|
let mut style = winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE);
|
|
|
|
|
let mut ex_style = winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE);
|
|
|
|
|
unjust_window_rect(&mut rect, style as _, ex_style as _);
|
|
|
|
|
|
|
|
|
|
if decorations {
|
|
|
|
|
style = style | style_flags;
|
|
|
|
|
ex_style = ex_style | ex_style_flags;
|
|
|
|
|
} else {
|
|
|
|
|
style = style & !style_flags;
|
|
|
|
|
ex_style = ex_style & !ex_style_flags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
|
|
|
|
|
self.events_loop_proxy.execute_in_thread(move |_| {
|
|
|
|
|
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
|
|
|
|
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
|
|
|
|
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
|
|
|
|
|
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
|
window.0,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
rect.left,
|
|
|
|
|
rect.top,
|
|
|
|
|
rect.right - rect.left,
|
|
|
|
|
rect.bottom - rect.top,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
|
|
|
|
| winuser::SWP_NOACTIVATE
|
|
|
|
|
| winuser::SWP_FRAMECHANGED,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window_state.attributes.decorations = decorations;
|
|
|
|
|
}
|
2017-12-22 23:50:46 +11:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-07 18:33:46 +10:00
|
|
|
|
#[inline]
|
|
|
|
|
pub fn get_current_monitor(&self) -> RootMonitorId {
|
2018-04-13 03:12:15 +10:00
|
|
|
|
RootMonitorId {
|
|
|
|
|
inner: EventsLoop::get_current_monitor(self.window.0),
|
|
|
|
|
}
|
2017-08-28 10:43:34 +10:00
|
|
|
|
}
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for Window {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
2018-04-25 06:20:40 +10:00
|
|
|
|
// The window must be destroyed from the same thread that created it, so we send a
|
|
|
|
|
// custom message to be handled by our callback to do the actual work.
|
|
|
|
|
winuser::PostMessageW(self.window.0, *DESTROY_MSG_ID, 0, 0);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-26 05:30:16 +11:00
|
|
|
|
/// A simple non-owning wrapper around a window.
|
2017-06-27 05:21:13 +10:00
|
|
|
|
#[doc(hidden)]
|
2018-04-13 03:12:15 +10:00
|
|
|
|
#[derive(Clone)]
|
2017-12-25 00:46:47 +11:00
|
|
|
|
pub struct WindowWrapper(HWND, HDC);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
// Send is not implemented for HWND and HDC, we have to wrap it and implement it manually.
|
|
|
|
|
// For more info see:
|
|
|
|
|
// https://github.com/retep998/winapi-rs/issues/360
|
|
|
|
|
// https://github.com/retep998/winapi-rs/issues/396
|
|
|
|
|
unsafe impl Send for WindowWrapper {}
|
|
|
|
|
|
2018-04-14 02:51:29 +10:00
|
|
|
|
pub unsafe fn adjust_size(
|
|
|
|
|
(x, y): (u32, u32), style: DWORD, ex_style: DWORD,
|
|
|
|
|
) -> (LONG, LONG) {
|
|
|
|
|
let mut rect = RECT { left: 0, right: x as LONG, top: 0, bottom: y as LONG };
|
|
|
|
|
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
|
|
|
|
(rect.right - rect.left, rect.bottom - rect.top)
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-27 05:21:13 +10:00
|
|
|
|
unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
2018-04-13 03:12:15 +10:00
|
|
|
|
inserter: events_loop::Inserter, events_loop_proxy: events_loop::EventsLoopProxy) -> Result<Window, CreationError> {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
// registering the window class
|
|
|
|
|
let class_name = register_window_class();
|
|
|
|
|
|
|
|
|
|
// building a RECT object with coordinates
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let mut rect = RECT {
|
|
|
|
|
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as LONG,
|
|
|
|
|
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as LONG,
|
2017-06-27 05:21:13 +10:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// computing the style and extended style of the window
|
2018-04-13 03:12:15 +10:00
|
|
|
|
let (ex_style, style) = if !window.decorations {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
(winuser::WS_EX_APPWINDOW,
|
2017-06-27 05:21:13 +10:00
|
|
|
|
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
|
|
|
|
if pl_attribs.parent.is_some() {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
else {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::WS_POPUP | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
} else {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
(winuser::WS_EX_APPWINDOW | winuser::WS_EX_WINDOWEDGE,
|
|
|
|
|
winuser::WS_OVERLAPPEDWINDOW | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN)
|
2017-06-27 05:21:13 +10:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// adjusting the window coordinates using the style
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
|
|
|
|
// creating the real window this time, by using the functions in `extra_functions`
|
|
|
|
|
let real_window = {
|
2018-04-13 03:12:15 +10:00
|
|
|
|
let (width, height) = if window.dimensions.is_some() {
|
2018-03-23 20:35:35 +11:00
|
|
|
|
let min_dimensions = window.min_dimensions
|
2018-04-14 02:51:29 +10:00
|
|
|
|
.map(|d| adjust_size(d, style, ex_style))
|
2018-03-23 20:35:35 +11:00
|
|
|
|
.unwrap_or((0, 0));
|
|
|
|
|
let max_dimensions = window.max_dimensions
|
2018-04-14 02:51:29 +10:00
|
|
|
|
.map(|d| adjust_size(d, style, ex_style))
|
2018-03-23 20:35:35 +11:00
|
|
|
|
.unwrap_or((raw::c_int::max_value(), raw::c_int::max_value()));
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
Some((rect.right - rect.left).min(max_dimensions.0).max(min_dimensions.0)),
|
|
|
|
|
Some((rect.bottom - rect.top).min(max_dimensions.1).max(min_dimensions.1))
|
|
|
|
|
)
|
2017-06-27 05:21:13 +10:00
|
|
|
|
} else {
|
|
|
|
|
(None, None)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut style = if !window.visible {
|
|
|
|
|
style
|
|
|
|
|
} else {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
style | winuser::WS_VISIBLE
|
2017-06-27 05:21:13 +10:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if pl_attribs.parent.is_some() {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
style |= winuser::WS_CHILD;
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let handle = winuser::CreateWindowExW(ex_style | winuser::WS_EX_ACCEPTFILES,
|
2017-06-27 05:21:13 +10:00
|
|
|
|
class_name.as_ptr(),
|
2017-12-25 00:46:47 +11:00
|
|
|
|
title.as_ptr() as LPCWSTR,
|
|
|
|
|
style | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN,
|
2018-04-13 03:12:15 +10:00
|
|
|
|
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
|
2017-12-25 00:46:47 +11:00
|
|
|
|
width.unwrap_or(winuser::CW_USEDEFAULT), height.unwrap_or(winuser::CW_USEDEFAULT),
|
2017-06-27 05:21:13 +10:00
|
|
|
|
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
2017-12-25 00:46:47 +11:00
|
|
|
|
ptr::null_mut(), libloaderapi::GetModuleHandleW(ptr::null()),
|
2017-06-27 05:21:13 +10:00
|
|
|
|
ptr::null_mut());
|
|
|
|
|
|
|
|
|
|
if handle.is_null() {
|
|
|
|
|
return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}",
|
|
|
|
|
format!("{}", io::Error::last_os_error()))));
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let hdc = winuser::GetDC(handle);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
if hdc.is_null() {
|
|
|
|
|
return Err(CreationError::OsError(format!("GetDC function failed: {}",
|
|
|
|
|
format!("{}", io::Error::last_os_error()))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WindowWrapper(handle, hdc)
|
|
|
|
|
};
|
|
|
|
|
|
Windows: Implement DeviceEvents (#482)
Fixes #467
All variants other than Text have been implemented. While Text can
be implemented using ToUnicode, that doesn't play nice with dead
keys, IME, etc.
Most of the mouse DeviceEvents were already implemented, but due
to the flags that were used when registering for raw input events,
they only worked when the window was in the foreground.
This is also a step forward for #338, as DeviceIds are no longer
useless on Windows. On DeviceEvents, the DeviceId contains that
device's handle. While that handle could ostensibly be used by
developers to query device information, my actual reason for
choosing it is because it's simply a very easy way to handle this.
As a fun bonus, this enabled me to create this method:
DevideIdExt::get_persistent_identifier() -> Option<String>
Using this gives you a unique identifier for the device that
persists across replugs/reboots/etc., so it's ideal for something
like device-specific configuration.
There's a notable caveat to the new DeviceIds, which is that the
value will always be 0 for a WindowEvent. There doesn't seem to be
any straightforward way around this limitation.
I was concerned that multi-window applications would receive n
copies of every DeviceEvent, but Windows only sends them to one
window per application.
Lastly, there's a chance that these additions will cause
antivirus/etc. software to detect winit applications as keyloggers.
I don't know how likely that is to actually happen to people, but
if it does become an issue, the raw input code is neatly
sequestered and would be easy to make optional during compilation.
2018-04-29 02:42:33 +10:00
|
|
|
|
// Set up raw input
|
|
|
|
|
register_all_mice_and_keyboards_for_raw_input(real_window.0);
|
2017-07-05 12:32:59 +10:00
|
|
|
|
|
2018-04-06 05:25:37 +10:00
|
|
|
|
// Register for touch events if applicable
|
|
|
|
|
{
|
|
|
|
|
let digitizer = winuser::GetSystemMetrics( winuser::SM_DIGITIZER ) as u32;
|
|
|
|
|
if digitizer & winuser::NID_READY != 0 {
|
|
|
|
|
winuser::RegisterTouchWindow( real_window.0, winuser::TWF_WANTPALM );
|
|
|
|
|
}
|
|
|
|
|
}
|
Windows: Position fixes (#479)
* Remove executable flag from os/macos.rs
This was causing me some grief while working on Windows, and it
doesn't belong here to begin with.
* Windows: get_position returns screen coordinates instead of workspace coordinates
Previously, get_position used GetWindowPlacement. As per the
documentation of WINDOWSTRUCT, the returned coordinates are in
workspace space, meaning they're relative to the taskbar. It's
also explicitly remarked that these coordinates should only be
used in conjunction with SetWindowPlacement, as mixing them with
functions expecting screen coordinates can cause unpleasantness.
Since our set_position (correctly) uses SetWindowPos, this meant
that passing the return of get_position to set_position would
cause the window to move.
We now use GetWindowRect, which returns screen coordinates. This
gives us both better consistency within the Windows backend and
across platforms.
Note that this only makes a difference if the taskbar is visible.
With the taskbar hidden, the values are exactly the same as before.
* Windows: Moved event position values are consistent with get_position
The old Moved values had two problems:
* They were obtained by casting a WORD (u16) straight to an i32.
This meant wrap-around would never be interpreted as negative,
thus negative positions (which are ubiquitous when using multiple
monitors) would result in positions around u16::MAX.
* WM_MOVE supplies client area positions, not window positions.
Switching to handling WM_WINDOWPOSCHANGED solves both of these
problems.
* Better documentation for Moved and Resized
2018-04-27 10:09:33 +10:00
|
|
|
|
|
2017-06-27 05:21:13 +10:00
|
|
|
|
// Creating a mutex to track the current window state
|
|
|
|
|
let window_state = Arc::new(Mutex::new(events_loop::WindowState {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
cursor: winuser::IDC_ARROW, // use arrow by default
|
2017-06-27 05:21:13 +10:00
|
|
|
|
cursor_state: CursorState::Normal,
|
|
|
|
|
attributes: window.clone(),
|
|
|
|
|
mouse_in_window: false,
|
2018-04-13 03:12:15 +10:00
|
|
|
|
saved_window_info: None,
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// making the window transparent
|
|
|
|
|
if window.transparent {
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let bb = dwmapi::DWM_BLURBEHIND {
|
2017-06-27 05:21:13 +10:00
|
|
|
|
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
|
|
|
|
fEnable: 1,
|
|
|
|
|
hRgnBlur: ptr::null_mut(),
|
|
|
|
|
fTransitionOnMaximized: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
|
|
|
|
}
|
2018-04-25 06:20:40 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
let win = Window {
|
|
|
|
|
window: real_window,
|
|
|
|
|
window_state: window_state,
|
|
|
|
|
events_loop_proxy
|
|
|
|
|
};
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
win.set_maximized(window.maximized);
|
|
|
|
|
if let Some(_) = window.fullscreen {
|
|
|
|
|
win.set_fullscreen(window.fullscreen);
|
|
|
|
|
force_window_active(win.window.0);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
inserter.insert(win.window.0, win.window_state.clone());
|
2018-04-25 06:20:40 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
Ok(win)
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn register_window_class() -> Vec<u16> {
|
|
|
|
|
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
2017-12-25 00:46:47 +11:00
|
|
|
|
let class = winuser::WNDCLASSEXW {
|
|
|
|
|
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
|
|
|
|
|
style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC,
|
2017-06-27 05:21:13 +10:00
|
|
|
|
lpfnWndProc: Some(events_loop::callback),
|
|
|
|
|
cbClsExtra: 0,
|
|
|
|
|
cbWndExtra: 0,
|
2017-12-25 00:46:47 +11:00
|
|
|
|
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
|
2017-06-27 05:21:13 +10:00
|
|
|
|
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.
|
2017-12-25 00:46:47 +11:00
|
|
|
|
winuser::RegisterClassExW(&class);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
|
|
|
|
class_name
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
|
|
|
|
|
struct ComInitialized(*mut ());
|
|
|
|
|
impl Drop for ComInitialized {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe { combaseapi::CoUninitialize() };
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|
2018-04-13 03:12:15 +10:00
|
|
|
|
}
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-25 06:20:40 +10:00
|
|
|
|
thread_local!{
|
2018-04-13 03:12:15 +10:00
|
|
|
|
static COM_INITIALIZED: ComInitialized = {
|
|
|
|
|
unsafe {
|
|
|
|
|
combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED);
|
|
|
|
|
ComInitialized(ptr::null_mut())
|
|
|
|
|
}
|
|
|
|
|
};
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
static TASKBAR_LIST: Cell<*mut taskbar::ITaskbarList2> = Cell::new(ptr::null_mut());
|
|
|
|
|
}
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
pub fn com_initialized() {
|
|
|
|
|
COM_INITIALIZED.with(|_| {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: remove these when they get added to winapi
|
|
|
|
|
// https://github.com/retep998/winapi-rs/pull/592
|
|
|
|
|
#[allow(non_upper_case_globals)]
|
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
mod taskbar {
|
|
|
|
|
use super::{IUnknown,IUnknownVtbl,HRESULT, HWND,BOOL};
|
|
|
|
|
DEFINE_GUID!{CLSID_TaskbarList,
|
|
|
|
|
0x56fdf344, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90}
|
|
|
|
|
|
|
|
|
|
RIDL!(#[uuid(0x56fdf342, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90)]
|
|
|
|
|
interface ITaskbarList(ITaskbarListVtbl): IUnknown(IUnknownVtbl) {
|
|
|
|
|
fn HrInit() -> HRESULT,
|
|
|
|
|
fn AddTab(
|
|
|
|
|
hwnd: HWND,
|
|
|
|
|
) -> HRESULT,
|
|
|
|
|
fn DeleteTab(
|
|
|
|
|
hwnd: HWND,
|
|
|
|
|
) -> HRESULT,
|
|
|
|
|
fn ActivateTab(
|
|
|
|
|
hwnd: HWND,
|
|
|
|
|
) -> HRESULT,
|
|
|
|
|
fn SetActiveAlt(
|
|
|
|
|
hwnd: HWND,
|
|
|
|
|
) -> HRESULT,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
RIDL!(#[uuid(0x602d4995, 0xb13a, 0x429b, 0xa6, 0x6e, 0x19, 0x35, 0xe4, 0x4f, 0x43, 0x17)]
|
|
|
|
|
interface ITaskbarList2(ITaskbarList2Vtbl): ITaskbarList(ITaskbarListVtbl) {
|
|
|
|
|
fn MarkFullscreenWindow(
|
|
|
|
|
hwnd: HWND,
|
|
|
|
|
fFullscreen: BOOL,
|
2018-04-25 06:20:40 +10:00
|
|
|
|
) -> HRESULT,
|
2018-04-13 03:12:15 +10:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reference Implementation:
|
|
|
|
|
// https://github.com/chromium/chromium/blob/f18e79d901f56154f80eea1e2218544285e62623/ui/views/win/fullscreen_handler.cc
|
|
|
|
|
//
|
|
|
|
|
// As per MSDN marking the window as fullscreen should ensure that the
|
|
|
|
|
// taskbar is moved to the bottom of the Z-order when the fullscreen window
|
|
|
|
|
// is activated. If the window is not fullscreen, the Shell falls back to
|
|
|
|
|
// heuristics to determine how the window should be treated, which means
|
|
|
|
|
// that it could still consider the window as fullscreen. :(
|
|
|
|
|
unsafe fn mark_fullscreen(handle: HWND, fullscreen: bool) {
|
|
|
|
|
com_initialized();
|
|
|
|
|
|
|
|
|
|
TASKBAR_LIST.with(|task_bar_list_ptr| {
|
|
|
|
|
let mut task_bar_list = task_bar_list_ptr.get();
|
|
|
|
|
|
|
|
|
|
if task_bar_list == ptr::null_mut() {
|
|
|
|
|
use winapi::shared::winerror::S_OK;
|
|
|
|
|
use winapi::Interface;
|
|
|
|
|
|
|
|
|
|
let hr = combaseapi::CoCreateInstance(
|
|
|
|
|
&taskbar::CLSID_TaskbarList,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
combaseapi::CLSCTX_ALL,
|
|
|
|
|
&taskbar::ITaskbarList2::uuidof(),
|
|
|
|
|
&mut task_bar_list as *mut _ as *mut _,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if hr != S_OK || (*task_bar_list).HrInit() != S_OK {
|
|
|
|
|
// In some old windows, the taskbar object could not be created, we just ignore it
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
task_bar_list_ptr.set(task_bar_list)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task_bar_list = task_bar_list_ptr.get();
|
|
|
|
|
(*task_bar_list).MarkFullscreenWindow(handle, if fullscreen { 1 } else { 0 });
|
|
|
|
|
})
|
|
|
|
|
}
|
2017-06-27 05:21:13 +10:00
|
|
|
|
|
2018-04-13 03:12:15 +10:00
|
|
|
|
unsafe fn force_window_active(handle: HWND) {
|
|
|
|
|
// In some situation, calling SetForegroundWindow could not bring up the window,
|
|
|
|
|
// This is a little hack which can "steal" the foreground window permission
|
|
|
|
|
// We only call this function in the window creation, so it should be fine.
|
|
|
|
|
// See : https://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open
|
|
|
|
|
let alt_sc = winuser::MapVirtualKeyW(winuser::VK_MENU as _, winuser::MAPVK_VK_TO_VSC);
|
|
|
|
|
|
|
|
|
|
let mut inputs: [winuser::INPUT; 2] = mem::zeroed();
|
|
|
|
|
inputs[0].type_ = winuser::INPUT_KEYBOARD;
|
|
|
|
|
inputs[0].u.ki_mut().wVk = winuser::VK_LMENU as _;
|
|
|
|
|
inputs[0].u.ki_mut().wScan = alt_sc as _;
|
|
|
|
|
inputs[0].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY;
|
|
|
|
|
|
|
|
|
|
inputs[1].type_ = winuser::INPUT_KEYBOARD;
|
|
|
|
|
inputs[1].u.ki_mut().wVk = winuser::VK_LMENU as _;
|
|
|
|
|
inputs[1].u.ki_mut().wScan = alt_sc as _;
|
|
|
|
|
inputs[1].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY | winuser::KEYEVENTF_KEYUP;
|
|
|
|
|
|
|
|
|
|
// Simulate a key press and release
|
|
|
|
|
winuser::SendInput(
|
|
|
|
|
inputs.len() as _,
|
|
|
|
|
inputs.as_mut_ptr(),
|
|
|
|
|
mem::size_of::<winuser::INPUT>() as _,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
winuser::SetForegroundWindow(handle);
|
2017-06-27 05:21:13 +10:00
|
|
|
|
}
|