2017-06-26 21:21:13 +02:00
|
|
|
#![cfg(target_os = "windows")]
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
use parking_lot::Mutex;
|
2019-08-14 07:57:16 -04:00
|
|
|
use raw_window_handle::{windows::WindowsHandle, RawWindowHandle};
|
2019-06-21 11:33:15 -04:00
|
|
|
use std::{
|
|
|
|
|
cell::Cell,
|
|
|
|
|
ffi::OsStr,
|
|
|
|
|
io, mem,
|
|
|
|
|
os::windows::ffi::OsStrExt,
|
|
|
|
|
ptr,
|
|
|
|
|
sync::{mpsc::channel, Arc},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use winapi::{
|
|
|
|
|
ctypes::c_int,
|
|
|
|
|
shared::{
|
|
|
|
|
minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM},
|
|
|
|
|
windef::{HWND, POINT, RECT},
|
|
|
|
|
},
|
|
|
|
|
um::{
|
|
|
|
|
combaseapi, dwmapi, libloaderapi,
|
|
|
|
|
objbase::COINIT_APARTMENTTHREADED,
|
|
|
|
|
ole2,
|
|
|
|
|
oleidl::LPDROPTARGET,
|
|
|
|
|
shobjidl_core::{CLSID_TaskbarList, ITaskbarList2},
|
|
|
|
|
wingdi::{CreateRectRgn, DeleteObject},
|
|
|
|
|
winnt::{LONG, LPCWSTR},
|
|
|
|
|
winuser,
|
|
|
|
|
},
|
|
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::{
|
|
|
|
|
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
|
|
|
|
|
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
|
|
|
|
monitor::MonitorHandle as RootMonitorHandle,
|
|
|
|
|
platform_impl::platform::{
|
|
|
|
|
dpi::{dpi_to_scale_factor, hwnd_dpi},
|
|
|
|
|
drop_handler::FileDropHandler,
|
|
|
|
|
event_loop::{
|
|
|
|
|
self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID,
|
|
|
|
|
REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID,
|
|
|
|
|
},
|
|
|
|
|
icon::{self, IconType, WinIcon},
|
|
|
|
|
monitor,
|
|
|
|
|
raw_input::register_all_mice_and_keyboards_for_raw_input,
|
|
|
|
|
util,
|
|
|
|
|
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
|
|
|
|
PlatformSpecificWindowBuilderAttributes, WindowId,
|
|
|
|
|
},
|
2019-07-29 21:16:14 +03:00
|
|
|
window::{CursorIcon, Fullscreen, Icon, WindowAttributes},
|
2018-06-14 19:42:18 -04:00
|
|
|
};
|
2018-07-04 10:15:19 +10:00
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
/// The Win32 implementation of the main `Window` object.
|
|
|
|
|
pub struct Window {
|
|
|
|
|
/// Main handle for the window.
|
|
|
|
|
window: WindowWrapper,
|
|
|
|
|
|
|
|
|
|
/// The current window state.
|
2018-07-27 23:34:08 +01:00
|
|
|
window_state: Arc<Mutex<WindowState>>,
|
2018-05-07 17:36:21 -04:00
|
|
|
|
2018-04-13 01:12:15 +08:00
|
|
|
// The events loop proxy.
|
2019-02-05 10:30:33 -05:00
|
|
|
thread_executor: event_loop::EventLoopThreadExecutor,
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Window {
|
2019-02-05 10:30:33 -05:00
|
|
|
pub fn new<T: 'static>(
|
|
|
|
|
event_loop: &EventLoopWindowTarget<T>,
|
2018-05-07 17:36:21 -04:00
|
|
|
w_attr: WindowAttributes,
|
|
|
|
|
pl_attr: PlatformSpecificWindowBuilderAttributes,
|
2019-05-29 21:29:54 -04:00
|
|
|
) -> Result<Window, RootOsError> {
|
2019-02-05 10:30:33 -05:00
|
|
|
// We dispatch an `init` function because of code style.
|
|
|
|
|
// First person to remove the need for cloning here gets a cookie!
|
|
|
|
|
//
|
|
|
|
|
// done. you owe me -- ossi
|
|
|
|
|
unsafe {
|
|
|
|
|
init(w_attr, pl_attr, event_loop).map(|win| {
|
|
|
|
|
let file_drop_handler = {
|
|
|
|
|
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK};
|
|
|
|
|
|
|
|
|
|
let ole_init_result = ole2::OleInitialize(ptr::null_mut());
|
|
|
|
|
// It is ok if the initialize result is `S_FALSE` because it might happen that
|
|
|
|
|
// multiple windows are created on the same thread.
|
|
|
|
|
if ole_init_result == OLE_E_WRONGCOMPOBJ {
|
|
|
|
|
panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`");
|
|
|
|
|
} else if ole_init_result == RPC_E_CHANGED_MODE {
|
|
|
|
|
panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let file_drop_runner = event_loop.runner_shared.clone();
|
|
|
|
|
let file_drop_handler = FileDropHandler::new(
|
|
|
|
|
win.window.0,
|
2019-06-21 11:33:15 -04:00
|
|
|
Box::new(move |event| {
|
|
|
|
|
if let Ok(e) = event.map_nonuser_event() {
|
|
|
|
|
file_drop_runner.send_event(e)
|
|
|
|
|
}
|
|
|
|
|
}),
|
2019-02-05 10:30:33 -05:00
|
|
|
);
|
2019-06-21 11:33:15 -04:00
|
|
|
let handler_interface_ptr =
|
|
|
|
|
&mut (*file_drop_handler.data).interface as LPDROPTARGET;
|
2019-02-05 10:30:33 -05:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
assert_eq!(
|
|
|
|
|
ole2::RegisterDragDrop(win.window.0, handler_interface_ptr),
|
|
|
|
|
S_OK
|
|
|
|
|
);
|
2019-02-05 10:30:33 -05:00
|
|
|
file_drop_handler
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let subclass_input = event_loop::SubclassInput {
|
|
|
|
|
window_state: win.window_state.clone(),
|
|
|
|
|
event_loop_runner: event_loop.runner_shared.clone(),
|
|
|
|
|
file_drop_handler,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
event_loop::subclass_window(win.window.0, subclass_input);
|
|
|
|
|
win
|
|
|
|
|
})
|
|
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_title(&self, text: &str) {
|
2018-05-07 17:36:21 -04:00
|
|
|
let text = OsStr::new(text)
|
|
|
|
|
.encode_wide()
|
|
|
|
|
.chain(Some(0).into_iter())
|
|
|
|
|
.collect::<Vec<_>>();
|
2017-06-26 21:21:13 +02:00
|
|
|
unsafe {
|
2017-12-24 15:46:47 +02:00
|
|
|
winuser::SetWindowTextW(self.window.0, text.as_ptr() as LPCWSTR);
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_visible(&self, visible: bool) {
|
|
|
|
|
match visible {
|
2019-06-21 11:33:15 -04:00
|
|
|
true => unsafe {
|
|
|
|
|
winuser::ShowWindow(self.window.0, winuser::SW_SHOW);
|
|
|
|
|
},
|
|
|
|
|
false => unsafe {
|
|
|
|
|
winuser::ShowWindow(self.window.0, winuser::SW_HIDE);
|
|
|
|
|
},
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
#[inline]
|
|
|
|
|
pub fn request_redraw(&self) {
|
|
|
|
|
unsafe {
|
|
|
|
|
if self.thread_executor.trigger_newevents_on_redraw() {
|
|
|
|
|
winuser::RedrawWindow(
|
|
|
|
|
self.window.0,
|
|
|
|
|
ptr::null(),
|
|
|
|
|
ptr::null_mut(),
|
2019-06-21 11:33:15 -04:00
|
|
|
winuser::RDW_INTERNALPAINT,
|
2019-02-05 10:30:33 -05:00
|
|
|
);
|
|
|
|
|
} else {
|
2019-07-04 16:14:15 -04:00
|
|
|
let mut window_state = self.window_state.lock();
|
|
|
|
|
if !window_state.queued_out_of_band_redraw {
|
|
|
|
|
window_state.queued_out_of_band_redraw = true;
|
|
|
|
|
winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0);
|
|
|
|
|
}
|
2019-02-05 10:30:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub(crate) fn outer_position_physical(&self) -> (i32, i32) {
|
2018-06-14 19:42:18 -04:00
|
|
|
util::get_window_rect(self.window.0)
|
|
|
|
|
.map(|rect| (rect.left as i32, rect.top as i32))
|
2019-05-29 21:29:54 -04:00
|
|
|
.unwrap()
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
|
|
|
|
|
let physical_position = self.outer_position_physical();
|
|
|
|
|
let dpi_factor = self.hidpi_factor();
|
2019-06-21 11:33:15 -04:00
|
|
|
Ok(LogicalPosition::from_physical(
|
|
|
|
|
physical_position,
|
|
|
|
|
dpi_factor,
|
|
|
|
|
))
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2018-04-16 21:40:30 -04:00
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub(crate) fn inner_position_physical(&self) -> (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-26 20:09:33 -04:00
|
|
|
let mut position: POINT = unsafe { mem::zeroed() };
|
|
|
|
|
if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
|
2019-05-29 21:29:54 -04:00
|
|
|
panic!("Unexpected ClientToScreen failure: please report this error to https://github.com/rust-windowing/winit")
|
2018-04-16 21:40:30 -04:00
|
|
|
}
|
2019-05-29 21:29:54 -04:00
|
|
|
(position.x, position.y)
|
2018-04-16 21:40:30 -04:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
|
|
|
|
|
let physical_position = self.inner_position_physical();
|
|
|
|
|
let dpi_factor = self.hidpi_factor();
|
2019-06-21 11:33:15 -04:00
|
|
|
Ok(LogicalPosition::from_physical(
|
|
|
|
|
physical_position,
|
|
|
|
|
dpi_factor,
|
|
|
|
|
))
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
|
2017-06-26 21:21:13 +02:00
|
|
|
unsafe {
|
2018-06-14 19:42:18 -04:00
|
|
|
winuser::SetWindowPos(
|
|
|
|
|
self.window.0,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
x as c_int,
|
|
|
|
|
y as c_int,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE,
|
|
|
|
|
);
|
2017-12-24 15:46:47 +02:00
|
|
|
winuser::UpdateWindow(self.window.0);
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_outer_position(&self, logical_position: LogicalPosition) {
|
|
|
|
|
let dpi_factor = self.hidpi_factor();
|
2018-06-14 19:42:18 -04:00
|
|
|
let (x, y) = logical_position.to_physical(dpi_factor).into();
|
2019-08-26 22:07:15 -04:00
|
|
|
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
|
|
|
|
f.set(WindowFlags::MAXIMIZED, false)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
self.set_position_physical(x, y);
|
|
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
|
2019-06-29 00:07:36 +02:00
|
|
|
let mut rect: RECT = unsafe { mem::zeroed() };
|
2017-12-24 15:46:47 +02:00
|
|
|
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
|
2019-05-29 21:29:54 -04:00
|
|
|
panic!("Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit")
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2019-05-29 21:29:54 -04:00
|
|
|
(
|
2017-06-26 21:21:13 +02:00
|
|
|
(rect.right - rect.left) as u32,
|
2018-06-14 19:42:18 -04:00
|
|
|
(rect.bottom - rect.top) as u32,
|
2019-05-29 21:29:54 -04:00
|
|
|
)
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn inner_size(&self) -> LogicalSize {
|
|
|
|
|
let physical_size = self.inner_size_physical();
|
|
|
|
|
let dpi_factor = self.hidpi_factor();
|
|
|
|
|
LogicalSize::from_physical(physical_size, dpi_factor)
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub(crate) fn outer_size_physical(&self) -> (u32, u32) {
|
2018-06-14 19:42:18 -04:00
|
|
|
util::get_window_rect(self.window.0)
|
2019-06-21 11:33:15 -04:00
|
|
|
.map(|rect| {
|
|
|
|
|
(
|
|
|
|
|
(rect.right - rect.left) as u32,
|
|
|
|
|
(rect.bottom - rect.top) as u32,
|
|
|
|
|
)
|
|
|
|
|
})
|
2019-05-29 21:29:54 -04:00
|
|
|
.unwrap()
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn outer_size(&self) -> LogicalSize {
|
|
|
|
|
let physical_size = self.outer_size_physical();
|
|
|
|
|
let dpi_factor = self.hidpi_factor();
|
|
|
|
|
LogicalSize::from_physical(physical_size, dpi_factor)
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
pub(crate) fn set_inner_size_physical(&self, x: u32, y: u32) {
|
2017-06-26 21:21:13 +02:00
|
|
|
unsafe {
|
2019-02-04 11:52:00 -05:00
|
|
|
let rect = util::adjust_window_rect(
|
|
|
|
|
self.window.0,
|
|
|
|
|
RECT {
|
|
|
|
|
top: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
bottom: y as LONG,
|
|
|
|
|
right: x as LONG,
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
.expect("adjust_window_rect failed");
|
2019-02-04 11:52:00 -05:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let outer_x = (rect.right - rect.left).abs() as c_int;
|
|
|
|
|
let outer_y = (rect.top - rect.bottom).abs() as c_int;
|
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
|
self.window.0,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
outer_x,
|
|
|
|
|
outer_y,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS
|
2019-06-21 11:33:15 -04:00
|
|
|
| winuser::SWP_NOZORDER
|
|
|
|
|
| winuser::SWP_NOREPOSITION
|
2019-06-26 00:04:49 -04:00
|
|
|
| winuser::SWP_NOMOVE
|
|
|
|
|
| winuser::SWP_NOACTIVATE,
|
2018-06-14 19:42:18 -04:00
|
|
|
);
|
2017-12-24 15:46:47 +02:00
|
|
|
winuser::UpdateWindow(self.window.0);
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-23 05:35:35 -04:00
|
|
|
#[inline]
|
2018-06-14 19:42:18 -04:00
|
|
|
pub fn set_inner_size(&self, logical_size: LogicalSize) {
|
2019-05-29 21:29:54 -04:00
|
|
|
let dpi_factor = self.hidpi_factor();
|
2018-06-14 19:42:18 -04:00
|
|
|
let (width, height) = logical_size.to_physical(dpi_factor).into();
|
2019-08-26 22:07:15 -04:00
|
|
|
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
|
|
|
|
f.set(WindowFlags::MAXIMIZED, false)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
self.set_inner_size_physical(width, height);
|
|
|
|
|
}
|
2018-03-23 05:35:35 -04:00
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
|
2019-02-05 10:30:33 -05:00
|
|
|
self.window_state.lock().min_size = dimensions.map(Into::into);
|
2018-03-23 05:35:35 -04:00
|
|
|
// Make windows re-check the window size bounds.
|
2019-05-29 21:29:54 -04:00
|
|
|
let (width, height) = self.inner_size_physical();
|
|
|
|
|
self.set_inner_size_physical(width, height);
|
2018-03-23 05:35:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_min_inner_size(&self, logical_size: Option<LogicalSize>) {
|
2018-06-14 19:42:18 -04:00
|
|
|
let physical_size = logical_size.map(|logical_size| {
|
2019-05-29 21:29:54 -04:00
|
|
|
let dpi_factor = self.hidpi_factor();
|
2018-06-14 19:42:18 -04:00
|
|
|
logical_size.to_physical(dpi_factor).into()
|
|
|
|
|
});
|
2019-05-29 21:29:54 -04:00
|
|
|
self.set_min_inner_size_physical(physical_size);
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2018-03-23 05:35:35 -04:00
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
|
2019-02-05 10:30:33 -05:00
|
|
|
self.window_state.lock().max_size = dimensions.map(Into::into);
|
2018-03-23 05:35:35 -04:00
|
|
|
// Make windows re-check the window size bounds.
|
2019-05-29 21:29:54 -04:00
|
|
|
let (width, height) = self.inner_size_physical();
|
|
|
|
|
self.set_inner_size_physical(width, height);
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_max_inner_size(&self, logical_size: Option<LogicalSize>) {
|
2018-06-14 19:42:18 -04:00
|
|
|
let physical_size = logical_size.map(|logical_size| {
|
2019-05-29 21:29:54 -04:00
|
|
|
let dpi_factor = self.hidpi_factor();
|
2018-06-14 19:42:18 -04:00
|
|
|
logical_size.to_physical(dpi_factor).into()
|
|
|
|
|
});
|
2019-05-29 21:29:54 -04:00
|
|
|
self.set_max_inner_size_physical(physical_size);
|
2018-03-23 05:35:35 -04:00
|
|
|
}
|
2018-06-12 11:58:18 -04:00
|
|
|
|
2018-06-11 16:47:50 -06:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_resizable(&self, resizable: bool) {
|
2019-02-04 11:52:00 -05:00
|
|
|
let window = self.window.clone();
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2018-06-14 19:42:18 -04:00
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2019-07-29 21:16:14 +03:00
|
|
|
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
2019-06-21 11:33:15 -04:00
|
|
|
f.set(WindowFlags::RESIZABLE, resizable)
|
|
|
|
|
});
|
2019-02-04 11:52:00 -05:00
|
|
|
});
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the `hwnd` of this window.
|
|
|
|
|
#[inline]
|
2017-12-24 15:46:47 +02:00
|
|
|
pub fn hwnd(&self) -> HWND {
|
2017-06-26 21:21:13 +02:00
|
|
|
self.window.0
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-14 07:57:16 -04:00
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
|
|
|
|
let handle = WindowsHandle {
|
|
|
|
|
hwnd: self.window.0 as *mut _,
|
|
|
|
|
..WindowsHandle::empty()
|
|
|
|
|
};
|
|
|
|
|
RawWindowHandle::Windows(handle)
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
2019-02-05 10:30:33 -05:00
|
|
|
self.window_state.lock().mouse.cursor = cursor;
|
|
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
2019-06-21 11:33:15 -04:00
|
|
|
let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor());
|
2018-09-22 21:03:38 -04:00
|
|
|
winuser::SetCursor(cursor);
|
|
|
|
|
});
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-18 12:32:18 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
|
2018-06-18 12:32:18 -04:00
|
|
|
let window = self.window.clone();
|
2018-07-27 23:34:08 +01:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2018-06-18 12:32:18 -04:00
|
|
|
let (tx, rx) = channel();
|
2019-02-04 11:52:00 -05:00
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2019-06-21 11:33:15 -04:00
|
|
|
let result = window_state
|
|
|
|
|
.lock()
|
|
|
|
|
.mouse
|
2019-02-04 11:52:00 -05:00
|
|
|
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab))
|
2019-05-29 21:29:54 -04:00
|
|
|
.map_err(|e| ExternalError::Os(os_error!(e)));
|
2018-06-18 12:32:18 -04:00
|
|
|
let _ = tx.send(result);
|
|
|
|
|
});
|
|
|
|
|
rx.recv().unwrap()
|
|
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2018-06-18 12:32:18 -04:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_cursor_visible(&self, visible: bool) {
|
2019-02-04 11:52:00 -05:00
|
|
|
let window = self.window.clone();
|
2018-07-27 23:34:08 +01:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2019-02-04 11:52:00 -05:00
|
|
|
let (tx, rx) = channel();
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2019-06-21 11:33:15 -04:00
|
|
|
let result = window_state
|
|
|
|
|
.lock()
|
|
|
|
|
.mouse
|
2019-05-29 21:29:54 -04:00
|
|
|
.set_cursor_flags(window.0, |f| f.set(CursorFlags::HIDDEN, !visible))
|
2019-02-04 11:52:00 -05:00
|
|
|
.map_err(|e| e.to_string());
|
|
|
|
|
let _ = tx.send(result);
|
2018-05-19 12:02:57 -04:00
|
|
|
});
|
2019-02-04 11:52:00 -05:00
|
|
|
rx.recv().unwrap().ok();
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn hidpi_factor(&self) -> f64 {
|
2019-02-05 10:30:33 -05:00
|
|
|
self.window_state.lock().dpi_factor
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
|
2018-06-14 19:42:18 -04:00
|
|
|
let mut point = POINT { x, y };
|
2017-06-26 21:21:13 +02:00
|
|
|
unsafe {
|
2017-12-24 15:46:47 +02:00
|
|
|
if winuser::ClientToScreen(self.window.0, &mut point) == 0 {
|
2019-05-29 21:29:54 -04:00
|
|
|
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2017-12-24 15:46:47 +02:00
|
|
|
if winuser::SetCursorPos(point.x, point.y) == 0 {
|
2019-05-29 21:29:54 -04:00
|
|
|
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
#[inline]
|
2019-06-21 11:33:15 -04:00
|
|
|
pub fn set_cursor_position(
|
|
|
|
|
&self,
|
|
|
|
|
logical_position: LogicalPosition,
|
|
|
|
|
) -> Result<(), ExternalError> {
|
2019-05-29 21:29:54 -04:00
|
|
|
let dpi_factor = self.hidpi_factor();
|
2018-06-14 19:42:18 -04:00
|
|
|
let (x, y) = logical_position.to_physical(dpi_factor).into();
|
|
|
|
|
self.set_cursor_position_physical(x, y)
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn id(&self) -> WindowId {
|
|
|
|
|
WindowId(self.window.0)
|
|
|
|
|
}
|
2017-08-28 01:43:34 +01:00
|
|
|
|
|
|
|
|
#[inline]
|
2018-04-13 01:12:15 +08:00
|
|
|
pub fn set_maximized(&self, maximized: bool) {
|
|
|
|
|
let window = self.window.clone();
|
2018-06-18 12:32:18 -04:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2018-04-13 01:12:15 +08:00
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2019-07-29 21:16:14 +03:00
|
|
|
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
2019-06-21 11:33:15 -04:00
|
|
|
f.set(WindowFlags::MAXIMIZED, maximized)
|
|
|
|
|
});
|
2018-04-13 01:12:15 +08:00
|
|
|
});
|
2017-08-28 01:43:34 +01:00
|
|
|
}
|
|
|
|
|
|
2019-04-26 03:09:32 +10:00
|
|
|
#[inline]
|
2019-07-29 21:16:14 +03:00
|
|
|
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
2019-04-26 03:09:32 +10:00
|
|
|
let window_state = self.window_state.lock();
|
|
|
|
|
window_state.fullscreen.clone()
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-28 01:43:34 +01:00
|
|
|
#[inline]
|
2019-07-29 21:16:14 +03:00
|
|
|
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2018-04-13 01:12:15 +08:00
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
let mut window_state_lock = window_state.lock();
|
|
|
|
|
let old_fullscreen = window_state_lock.fullscreen.clone();
|
|
|
|
|
if window_state_lock.fullscreen == fullscreen {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
window_state_lock.fullscreen = fullscreen.clone();
|
|
|
|
|
drop(window_state_lock);
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
let mut window_state_lock = window_state.lock();
|
|
|
|
|
|
|
|
|
|
// Save window bounds before entering fullscreen
|
|
|
|
|
match (&old_fullscreen, &fullscreen) {
|
|
|
|
|
(&None, &Some(_)) => {
|
|
|
|
|
let client_rect = util::get_client_rect(window.0).unwrap();
|
|
|
|
|
window_state_lock.saved_window = Some(SavedWindow {
|
|
|
|
|
client_rect,
|
|
|
|
|
dpi_factor: window_state_lock.dpi_factor,
|
2018-04-13 01:12:15 +08:00
|
|
|
});
|
2019-06-24 12:14:55 -04:00
|
|
|
}
|
2019-07-29 21:16:14 +03:00
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Change video mode if we're transitioning to or from exclusive
|
|
|
|
|
// fullscreen
|
|
|
|
|
match (&old_fullscreen, &fullscreen) {
|
|
|
|
|
(&None, &Some(Fullscreen::Exclusive(ref video_mode)))
|
|
|
|
|
| (
|
|
|
|
|
&Some(Fullscreen::Borderless(_)),
|
|
|
|
|
&Some(Fullscreen::Exclusive(ref video_mode)),
|
|
|
|
|
)
|
|
|
|
|
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Exclusive(ref video_mode))) =>
|
|
|
|
|
{
|
|
|
|
|
let monitor = video_mode.monitor();
|
|
|
|
|
|
|
|
|
|
let mut display_name = OsStr::new(&monitor.inner.native_identifier())
|
|
|
|
|
.encode_wide()
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
// `encode_wide` does not add a null-terminator but
|
|
|
|
|
// `ChangeDisplaySettingsExW` requires a null-terminated
|
|
|
|
|
// string, so add it
|
|
|
|
|
display_name.push(0);
|
|
|
|
|
|
|
|
|
|
let mut native_video_mode = video_mode.video_mode.native_video_mode.clone();
|
|
|
|
|
|
|
|
|
|
let res = unsafe {
|
|
|
|
|
winuser::ChangeDisplaySettingsExW(
|
|
|
|
|
display_name.as_ptr(),
|
|
|
|
|
&mut native_video_mode,
|
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
|
winuser::CDS_FULLSCREEN,
|
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS);
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_BADMODE);
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_BADPARAM);
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_FAILED);
|
|
|
|
|
assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL);
|
|
|
|
|
}
|
|
|
|
|
(&Some(Fullscreen::Exclusive(_)), &None)
|
|
|
|
|
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => {
|
|
|
|
|
let res = unsafe {
|
|
|
|
|
winuser::ChangeDisplaySettingsExW(
|
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
|
winuser::CDS_FULLSCREEN,
|
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS);
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_BADMODE);
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_BADPARAM);
|
|
|
|
|
debug_assert!(res != winuser::DISP_CHANGE_FAILED);
|
|
|
|
|
assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL);
|
|
|
|
|
}
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
// There are some scenarios where calling `ChangeDisplaySettingsExW` takes long
|
|
|
|
|
// enough to execute that the DWM thinks our program has frozen and takes over
|
|
|
|
|
// our program's window. When that happens, the `SetWindowPos` call below gets
|
|
|
|
|
// eaten and the window doesn't get set to the proper fullscreen position.
|
|
|
|
|
//
|
|
|
|
|
// Calling `PeekMessageW` here notifies Windows that our process is still running
|
|
|
|
|
// fine, taking control back from the DWM and ensuring that the `SetWindowPos` call
|
|
|
|
|
// below goes through.
|
|
|
|
|
let mut msg = mem::zeroed();
|
|
|
|
|
winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update window style
|
|
|
|
|
WindowState::set_window_flags(window_state_lock, window.0, |f| {
|
|
|
|
|
f.set(WindowFlags::MARKER_FULLSCREEN, fullscreen.is_some())
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update window bounds
|
|
|
|
|
match &fullscreen {
|
|
|
|
|
Some(fullscreen) => {
|
|
|
|
|
let monitor = match fullscreen {
|
|
|
|
|
Fullscreen::Exclusive(ref video_mode) => video_mode.monitor(),
|
|
|
|
|
Fullscreen::Borderless(ref monitor) => monitor.clone(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let position: (i32, i32) = monitor.position().into();
|
|
|
|
|
let size: (u32, u32) = monitor.size().into();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
|
window.0,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
position.0,
|
|
|
|
|
position.1,
|
|
|
|
|
size.0 as i32,
|
|
|
|
|
size.1 as i32,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER,
|
|
|
|
|
);
|
|
|
|
|
winuser::UpdateWindow(window.0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
let mut window_state_lock = window_state.lock();
|
|
|
|
|
if let Some(SavedWindow {
|
|
|
|
|
client_rect,
|
|
|
|
|
dpi_factor,
|
|
|
|
|
}) = window_state_lock.saved_window.take()
|
|
|
|
|
{
|
|
|
|
|
window_state_lock.dpi_factor = dpi_factor;
|
|
|
|
|
drop(window_state_lock);
|
2019-09-19 11:48:20 -04:00
|
|
|
let client_rect = util::adjust_window_rect(window.0, client_rect).unwrap();
|
2019-07-29 21:16:14 +03:00
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
winuser::SetWindowPos(
|
2019-02-04 11:52:00 -05:00
|
|
|
window.0,
|
2019-07-29 21:16:14 +03:00
|
|
|
ptr::null_mut(),
|
|
|
|
|
client_rect.left,
|
|
|
|
|
client_rect.top,
|
|
|
|
|
client_rect.right - client_rect.left,
|
|
|
|
|
client_rect.bottom - client_rect.top,
|
|
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER,
|
2019-02-04 11:52:00 -05:00
|
|
|
);
|
2019-07-29 21:16:14 +03:00
|
|
|
winuser::UpdateWindow(window.0);
|
2019-02-04 11:52:00 -05:00
|
|
|
}
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
2019-06-24 12:14:55 -04:00
|
|
|
}
|
2018-04-13 01:12:15 +08:00
|
|
|
}
|
2019-07-29 21:16:14 +03:00
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
taskbar_mark_fullscreen(window.0, fullscreen.is_some());
|
|
|
|
|
}
|
|
|
|
|
});
|
2017-09-07 09:33:46 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-22 07:50:46 -05:00
|
|
|
#[inline]
|
2018-04-13 01:12:15 +08:00
|
|
|
pub fn set_decorations(&self, decorations: bool) {
|
2019-02-04 11:52:00 -05:00
|
|
|
let window = self.window.clone();
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2018-06-14 19:42:18 -04:00
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2019-07-29 21:16:14 +03:00
|
|
|
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
2019-06-21 11:33:15 -04:00
|
|
|
f.set(WindowFlags::DECORATIONS, decorations)
|
|
|
|
|
});
|
2019-02-04 11:52:00 -05:00
|
|
|
});
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_always_on_top(&self, always_on_top: bool) {
|
2019-02-04 11:52:00 -05:00
|
|
|
let window = self.window.clone();
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2019-07-29 21:16:14 +03:00
|
|
|
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
2019-06-21 11:33:15 -04:00
|
|
|
f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top)
|
|
|
|
|
});
|
2019-02-04 11:52:00 -05:00
|
|
|
});
|
2018-05-20 10:24:05 -04:00
|
|
|
}
|
|
|
|
|
|
2017-09-07 09:33:46 +01:00
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn current_monitor(&self) -> RootMonitorHandle {
|
2019-02-05 10:30:33 -05:00
|
|
|
RootMonitorHandle {
|
2019-05-29 21:29:54 -04:00
|
|
|
inner: monitor::current_monitor(self.window.0),
|
2018-04-13 01:12:15 +08:00
|
|
|
}
|
2017-08-28 01:43:34 +01:00
|
|
|
}
|
2018-05-07 17:36:21 -04:00
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_window_icon(&self, mut window_icon: Option<Icon>) {
|
|
|
|
|
let window_icon = window_icon
|
|
|
|
|
.take()
|
|
|
|
|
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_SMALL`"));
|
|
|
|
|
if let Some(ref window_icon) = window_icon {
|
|
|
|
|
window_icon.set_for_window(self.window.0, IconType::Small);
|
|
|
|
|
} else {
|
|
|
|
|
icon::unset_for_window(self.window.0, IconType::Small);
|
|
|
|
|
}
|
2019-02-05 10:30:33 -05:00
|
|
|
self.window_state.lock().window_icon = window_icon;
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_taskbar_icon(&self, mut taskbar_icon: Option<Icon>) {
|
|
|
|
|
let taskbar_icon = taskbar_icon
|
|
|
|
|
.take()
|
|
|
|
|
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_BIG`"));
|
|
|
|
|
if let Some(ref taskbar_icon) = taskbar_icon {
|
|
|
|
|
taskbar_icon.set_for_window(self.window.0, IconType::Big);
|
|
|
|
|
} else {
|
|
|
|
|
icon::unset_for_window(self.window.0, IconType::Big);
|
|
|
|
|
}
|
2019-02-05 10:30:33 -05:00
|
|
|
self.window_state.lock().taskbar_icon = taskbar_icon;
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
2018-05-17 21:28:30 -04:00
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_ime_position(&self, _logical_spot: LogicalPosition) {
|
2018-05-17 21:28:30 -04:00
|
|
|
unimplemented!();
|
|
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for Window {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
2018-04-24 16:20:40 -04: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-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-25 21:30:16 +03:00
|
|
|
/// A simple non-owning wrapper around a window.
|
2017-06-26 21:21:13 +02:00
|
|
|
#[doc(hidden)]
|
2018-04-13 01:12:15 +08:00
|
|
|
#[derive(Clone)]
|
2018-08-02 19:03:15 +02:00
|
|
|
pub struct WindowWrapper(HWND);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2018-07-27 23:34:08 +01:00
|
|
|
// Send and Sync are not implemented for HWND and HDC, we have to wrap it and implement them manually.
|
2018-04-13 01:12:15 +08:00
|
|
|
// For more info see:
|
|
|
|
|
// https://github.com/retep998/winapi-rs/issues/360
|
|
|
|
|
// https://github.com/retep998/winapi-rs/issues/396
|
2018-07-27 23:34:08 +01:00
|
|
|
unsafe impl Sync for WindowWrapper {}
|
2018-04-13 01:12:15 +08:00
|
|
|
unsafe impl Send for WindowWrapper {}
|
|
|
|
|
|
2018-07-27 23:34:08 +01:00
|
|
|
pub unsafe fn adjust_size(
|
|
|
|
|
physical_size: PhysicalSize,
|
|
|
|
|
style: DWORD,
|
|
|
|
|
ex_style: DWORD,
|
|
|
|
|
) -> (LONG, LONG) {
|
2018-06-14 19:42:18 -04:00
|
|
|
let (width, height): (u32, u32) = physical_size.into();
|
2018-07-27 23:34:08 +01:00
|
|
|
let mut rect = RECT {
|
|
|
|
|
left: 0,
|
|
|
|
|
right: width as LONG,
|
|
|
|
|
top: 0,
|
|
|
|
|
bottom: height as LONG,
|
|
|
|
|
};
|
2018-04-13 11:51:29 -05:00
|
|
|
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
|
|
|
|
(rect.right - rect.left, rect.bottom - rect.top)
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
unsafe fn init<T: 'static>(
|
2018-06-14 19:42:18 -04:00
|
|
|
mut attributes: WindowAttributes,
|
2019-05-29 21:29:54 -04:00
|
|
|
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
2019-02-05 10:30:33 -05:00
|
|
|
event_loop: &EventLoopWindowTarget<T>,
|
2019-05-29 21:29:54 -04:00
|
|
|
) -> Result<Window, RootOsError> {
|
2018-06-14 19:42:18 -04:00
|
|
|
let title = OsStr::new(&attributes.title)
|
2018-05-07 17:36:21 -04:00
|
|
|
.encode_wide()
|
|
|
|
|
.chain(Some(0).into_iter())
|
2017-06-26 21:21:13 +02:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
2018-05-07 17:36:21 -04:00
|
|
|
let window_icon = {
|
2019-06-21 11:33:15 -04:00
|
|
|
let icon = attributes.window_icon.take().map(WinIcon::from_icon);
|
2019-05-29 21:29:54 -04:00
|
|
|
if let Some(icon) = icon {
|
|
|
|
|
Some(icon.map_err(|e| os_error!(e))?)
|
2018-05-07 17:36:21 -04:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let taskbar_icon = {
|
2019-06-21 11:33:15 -04:00
|
|
|
let icon = attributes.window_icon.take().map(WinIcon::from_icon);
|
2019-05-29 21:29:54 -04:00
|
|
|
if let Some(icon) = icon {
|
|
|
|
|
Some(icon.map_err(|e| os_error!(e))?)
|
2018-05-07 17:36:21 -04:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
// registering the window class
|
2018-05-07 17:36:21 -04:00
|
|
|
let class_name = register_window_class(&window_icon, &taskbar_icon);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2018-07-01 11:01:46 -04:00
|
|
|
let guessed_dpi_factor = {
|
2019-05-29 21:29:54 -04:00
|
|
|
let monitors = monitor::available_monitors();
|
2018-07-01 11:01:46 -04:00
|
|
|
let dpi_factor = if !monitors.is_empty() {
|
2019-05-29 21:29:54 -04:00
|
|
|
let mut dpi_factor = Some(monitors[0].hidpi_factor());
|
2018-07-01 11:01:46 -04:00
|
|
|
for monitor in &monitors {
|
2019-05-29 21:29:54 -04:00
|
|
|
if Some(monitor.hidpi_factor()) != dpi_factor {
|
2018-07-01 11:01:46 -04:00
|
|
|
dpi_factor = None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dpi_factor
|
|
|
|
|
} else {
|
2019-06-21 11:33:15 -04:00
|
|
|
return Err(os_error!(io::Error::new(
|
|
|
|
|
io::ErrorKind::NotFound,
|
|
|
|
|
"No monitors were detected."
|
|
|
|
|
)));
|
2018-07-01 11:01:46 -04:00
|
|
|
};
|
|
|
|
|
dpi_factor.unwrap_or_else(|| {
|
|
|
|
|
util::get_cursor_pos()
|
|
|
|
|
.and_then(|cursor_pos| {
|
|
|
|
|
let mut dpi_factor = None;
|
|
|
|
|
for monitor in &monitors {
|
|
|
|
|
if monitor.contains_point(&cursor_pos) {
|
2019-05-29 21:29:54 -04:00
|
|
|
dpi_factor = Some(monitor.hidpi_factor());
|
2018-07-01 11:01:46 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dpi_factor
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(1.0)
|
|
|
|
|
})
|
|
|
|
|
};
|
|
|
|
|
info!("Guessed window DPI factor: {}", guessed_dpi_factor);
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
let dimensions = attributes.inner_size.unwrap_or_else(|| (1024, 768).into());
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-02-04 11:52:00 -05:00
|
|
|
let mut window_flags = WindowFlags::empty();
|
|
|
|
|
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
|
|
|
|
|
window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top);
|
2019-06-21 11:33:15 -04:00
|
|
|
window_flags.set(
|
|
|
|
|
WindowFlags::NO_BACK_BUFFER,
|
|
|
|
|
pl_attribs.no_redirection_bitmap,
|
|
|
|
|
);
|
2019-02-04 11:52:00 -05:00
|
|
|
window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent);
|
|
|
|
|
// WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured.
|
|
|
|
|
window_flags.set(WindowFlags::RESIZABLE, attributes.resizable);
|
|
|
|
|
window_flags.set(WindowFlags::CHILD, pl_attribs.parent.is_some());
|
|
|
|
|
window_flags.set(WindowFlags::ON_TASKBAR, true);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
|
|
|
|
// creating the real window this time, by using the functions in `extra_functions`
|
|
|
|
|
let real_window = {
|
2019-02-04 11:52:00 -05:00
|
|
|
let (style, ex_style) = window_flags.to_window_styles();
|
|
|
|
|
let handle = winuser::CreateWindowExW(
|
|
|
|
|
ex_style,
|
2017-06-26 21:21:13 +02:00
|
|
|
class_name.as_ptr(),
|
2017-12-24 15:46:47 +02:00
|
|
|
title.as_ptr() as LPCWSTR,
|
2019-02-04 11:52:00 -05:00
|
|
|
style,
|
2019-06-21 11:33:15 -04:00
|
|
|
winuser::CW_USEDEFAULT,
|
|
|
|
|
winuser::CW_USEDEFAULT,
|
|
|
|
|
winuser::CW_USEDEFAULT,
|
|
|
|
|
winuser::CW_USEDEFAULT,
|
2017-06-26 21:21:13 +02:00
|
|
|
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
2018-06-14 19:42:18 -04:00
|
|
|
ptr::null_mut(),
|
|
|
|
|
libloaderapi::GetModuleHandleW(ptr::null()),
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
|
|
|
|
if handle.is_null() {
|
2019-05-29 21:29:54 -04:00
|
|
|
return Err(os_error!(io::Error::last_os_error()));
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-02 19:03:15 +02:00
|
|
|
WindowWrapper(handle)
|
2017-06-26 21:21:13 +02:00
|
|
|
};
|
|
|
|
|
|
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-28 12:42:33 -04:00
|
|
|
// Set up raw input
|
|
|
|
|
register_all_mice_and_keyboards_for_raw_input(real_window.0);
|
2017-07-04 19:32:59 -07:00
|
|
|
|
2018-04-05 21:25:37 +02:00
|
|
|
// Register for touch events if applicable
|
|
|
|
|
{
|
2019-06-21 11:33:15 -04:00
|
|
|
let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;
|
2018-04-05 21:25:37 +02:00
|
|
|
if digitizer & winuser::NID_READY != 0 {
|
2019-06-21 11:33:15 -04:00
|
|
|
winuser::RegisterTouchWindow(real_window.0, winuser::TWF_WANTPALM);
|
2018-04-05 21:25:37 +02: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-26 20:09:33 -04:00
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
let dpi = hwnd_dpi(real_window.0);
|
2018-06-14 19:42:18 -04:00
|
|
|
let dpi_factor = dpi_to_scale_factor(dpi);
|
2018-07-01 11:01:46 -04:00
|
|
|
if dpi_factor != guessed_dpi_factor {
|
|
|
|
|
let (width, height): (u32, u32) = dimensions.into();
|
2018-06-14 19:42:18 -04:00
|
|
|
let mut packed_dimensions = 0;
|
|
|
|
|
// MAKELPARAM isn't provided by winapi yet.
|
|
|
|
|
let ptr = &mut packed_dimensions as *mut LPARAM as *mut WORD;
|
|
|
|
|
*ptr.offset(0) = width as WORD;
|
|
|
|
|
*ptr.offset(1) = height as WORD;
|
|
|
|
|
winuser::PostMessageW(
|
|
|
|
|
real_window.0,
|
|
|
|
|
*INITIAL_DPI_MSG_ID,
|
|
|
|
|
dpi as WPARAM,
|
|
|
|
|
packed_dimensions,
|
|
|
|
|
);
|
|
|
|
|
}
|
2018-05-07 17:36:21 -04:00
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
// making the window transparent
|
2018-06-22 10:33:29 +09:00
|
|
|
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
|
2018-10-17 17:23:59 -07:00
|
|
|
let region = CreateRectRgn(0, 0, -1, -1); // makes the window transparent
|
|
|
|
|
|
2017-12-24 15:46:47 +02:00
|
|
|
let bb = dwmapi::DWM_BLURBEHIND {
|
2018-10-17 17:23:59 -07:00
|
|
|
dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION,
|
2017-06-26 21:21:13 +02:00
|
|
|
fEnable: 1,
|
2018-10-17 17:23:59 -07:00
|
|
|
hRgnBlur: region,
|
2017-06-26 21:21:13 +02:00
|
|
|
fTransitionOnMaximized: 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
2018-10-17 17:23:59 -07:00
|
|
|
DeleteObject(region as _);
|
|
|
|
|
|
|
|
|
|
if attributes.decorations {
|
|
|
|
|
// HACK: When opaque (opacity 255), there is a trail whenever
|
|
|
|
|
// the transparent window is moved. By reducing it to 254,
|
|
|
|
|
// the window is rendered properly.
|
|
|
|
|
let opacity = 254;
|
|
|
|
|
|
|
|
|
|
// The color key can be any value except for black (0x0).
|
|
|
|
|
let color_key = 0x0030c100;
|
|
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
winuser::SetLayeredWindowAttributes(
|
|
|
|
|
real_window.0,
|
|
|
|
|
color_key,
|
|
|
|
|
opacity,
|
|
|
|
|
winuser::LWA_ALPHA,
|
|
|
|
|
);
|
2018-10-17 17:23:59 -07:00
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2018-04-24 16:20:40 -04:00
|
|
|
|
2019-02-04 11:52:00 -05:00
|
|
|
window_flags.set(WindowFlags::VISIBLE, attributes.visible);
|
|
|
|
|
window_flags.set(WindowFlags::MAXIMIZED, attributes.maximized);
|
|
|
|
|
|
|
|
|
|
let window_state = {
|
2019-06-21 11:33:15 -04:00
|
|
|
let window_state = WindowState::new(&attributes, window_icon, taskbar_icon, dpi_factor);
|
2019-02-04 11:52:00 -05:00
|
|
|
let window_state = Arc::new(Mutex::new(window_state));
|
2019-07-29 21:16:14 +03:00
|
|
|
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
|
2019-02-04 11:52:00 -05:00
|
|
|
window_state
|
|
|
|
|
};
|
|
|
|
|
|
2018-04-13 01:12:15 +08:00
|
|
|
let win = Window {
|
|
|
|
|
window: real_window,
|
2018-07-27 23:34:08 +01:00
|
|
|
window_state,
|
2019-02-05 10:30:33 -05:00
|
|
|
thread_executor: event_loop.create_thread_executor(),
|
2018-04-13 01:12:15 +08:00
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
if let Some(_) = attributes.fullscreen {
|
|
|
|
|
win.set_fullscreen(attributes.fullscreen);
|
2018-04-13 01:12:15 +08:00
|
|
|
force_window_active(win.window.0);
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-29 21:29:54 -04:00
|
|
|
if let Some(dimensions) = attributes.inner_size {
|
2019-02-04 11:52:00 -05:00
|
|
|
win.set_inner_size(dimensions);
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 01:12:15 +08:00
|
|
|
Ok(win)
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-07 17:36:21 -04:00
|
|
|
unsafe fn register_window_class(
|
|
|
|
|
window_icon: &Option<WinIcon>,
|
|
|
|
|
taskbar_icon: &Option<WinIcon>,
|
|
|
|
|
) -> Vec<u16> {
|
|
|
|
|
let class_name: Vec<_> = OsStr::new("Window Class")
|
|
|
|
|
.encode_wide()
|
|
|
|
|
.chain(Some(0).into_iter())
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let h_icon = taskbar_icon
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|icon| icon.handle)
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
|
|
|
|
let h_icon_small = window_icon
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|icon| icon.handle)
|
|
|
|
|
.unwrap_or(ptr::null_mut());
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2017-12-24 15:46:47 +02:00
|
|
|
let class = winuser::WNDCLASSEXW {
|
|
|
|
|
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
|
|
|
|
|
style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC,
|
2019-02-05 10:30:33 -05:00
|
|
|
lpfnWndProc: Some(winuser::DefWindowProcW),
|
2017-06-26 21:21:13 +02:00
|
|
|
cbClsExtra: 0,
|
|
|
|
|
cbWndExtra: 0,
|
2017-12-24 15:46:47 +02:00
|
|
|
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
|
2018-05-07 17:36:21 -04:00
|
|
|
hIcon: h_icon,
|
|
|
|
|
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
2017-06-26 21:21:13 +02:00
|
|
|
hbrBackground: ptr::null_mut(),
|
|
|
|
|
lpszMenuName: ptr::null(),
|
|
|
|
|
lpszClassName: class_name.as_ptr(),
|
2018-05-07 17:36:21 -04:00
|
|
|
hIconSm: h_icon_small,
|
2017-06-26 21:21:13 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 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-24 15:46:47 +02:00
|
|
|
winuser::RegisterClassExW(&class);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
|
|
|
|
class_name
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-13 01:12:15 +08:00
|
|
|
struct ComInitialized(*mut ());
|
|
|
|
|
impl Drop for ComInitialized {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe { combaseapi::CoUninitialize() };
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2018-04-13 01:12:15 +08:00
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
thread_local! {
|
2018-04-13 01:12:15 +08:00
|
|
|
static COM_INITIALIZED: ComInitialized = {
|
|
|
|
|
unsafe {
|
2019-02-05 10:30:33 -05:00
|
|
|
combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED);
|
2018-04-13 01:12:15 +08:00
|
|
|
ComInitialized(ptr::null_mut())
|
|
|
|
|
}
|
|
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2018-06-12 11:58:18 -04:00
|
|
|
static TASKBAR_LIST: Cell<*mut ITaskbarList2> = Cell::new(ptr::null_mut());
|
2018-04-13 01:12:15 +08:00
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2018-04-13 01:12:15 +08:00
|
|
|
pub fn com_initialized() {
|
|
|
|
|
COM_INITIALIZED.with(|_| {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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. :(
|
2019-07-29 21:16:14 +03:00
|
|
|
unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) {
|
2018-04-13 01:12:15 +08:00
|
|
|
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() {
|
2019-06-21 11:33:15 -04:00
|
|
|
use winapi::{shared::winerror::S_OK, Interface};
|
2018-04-13 01:12:15 +08:00
|
|
|
|
|
|
|
|
let hr = combaseapi::CoCreateInstance(
|
2018-06-12 11:58:18 -04:00
|
|
|
&CLSID_TaskbarList,
|
2018-04-13 01:12:15 +08:00
|
|
|
ptr::null_mut(),
|
|
|
|
|
combaseapi::CLSCTX_ALL,
|
2018-06-12 11:58:18 -04:00
|
|
|
&ITaskbarList2::uuidof(),
|
2018-04-13 01:12:15 +08:00
|
|
|
&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-26 21:21:13 +02:00
|
|
|
|
2018-04-13 01:12:15 +08: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-26 21:21:13 +02:00
|
|
|
}
|