2017-06-26 21:21:13 +02:00
|
|
|
#![cfg(target_os = "windows")]
|
|
|
|
|
|
2022-07-21 22:22:36 +03:00
|
|
|
use raw_window_handle::{
|
|
|
|
|
RawDisplayHandle, RawWindowHandle, Win32WindowHandle, WindowsDisplayHandle,
|
|
|
|
|
};
|
2019-06-21 11:33:15 -04:00
|
|
|
use std::{
|
|
|
|
|
cell::Cell,
|
2022-03-07 22:58:12 +01:00
|
|
|
ffi::c_void,
|
|
|
|
|
io, mem, panic, ptr,
|
2022-08-31 18:32:19 +02:00
|
|
|
sync::{mpsc::channel, Arc, Mutex, MutexGuard},
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
use windows_sys::Win32::{
|
|
|
|
|
Foundation::{
|
|
|
|
|
HINSTANCE, HWND, LPARAM, OLE_E_WRONGCOMPOBJ, POINT, POINTS, RECT, RPC_E_CHANGED_MODE, S_OK,
|
|
|
|
|
WPARAM,
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
2022-03-07 22:58:12 +01:00
|
|
|
Graphics::{
|
|
|
|
|
Dwm::{DwmEnableBlurBehindWindow, DWM_BB_BLURREGION, DWM_BB_ENABLE, DWM_BLURBEHIND},
|
|
|
|
|
Gdi::{
|
|
|
|
|
ChangeDisplaySettingsExW, ClientToScreen, CreateRectRgn, DeleteObject, InvalidateRgn,
|
|
|
|
|
RedrawWindow, CDS_FULLSCREEN, DISP_CHANGE_BADFLAGS, DISP_CHANGE_BADMODE,
|
|
|
|
|
DISP_CHANGE_BADPARAM, DISP_CHANGE_FAILED, DISP_CHANGE_SUCCESSFUL, RDW_INTERNALPAINT,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
System::{
|
|
|
|
|
Com::{
|
|
|
|
|
CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_APARTMENTTHREADED,
|
|
|
|
|
},
|
|
|
|
|
Ole::{OleInitialize, RegisterDragDrop},
|
|
|
|
|
},
|
|
|
|
|
UI::{
|
|
|
|
|
Input::{
|
|
|
|
|
KeyboardAndMouse::{
|
|
|
|
|
EnableWindow, GetActiveWindow, MapVirtualKeyW, ReleaseCapture, SendInput, INPUT,
|
|
|
|
|
INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP,
|
|
|
|
|
VK_LMENU, VK_MENU,
|
|
|
|
|
},
|
|
|
|
|
Touch::{RegisterTouchWindow, TWF_WANTPALM},
|
|
|
|
|
},
|
|
|
|
|
WindowsAndMessaging::{
|
|
|
|
|
CreateWindowExW, FlashWindowEx, GetClientRect, GetCursorPos, GetForegroundWindow,
|
|
|
|
|
GetSystemMetrics, GetWindowPlacement, IsWindowVisible, LoadCursorW, PeekMessageW,
|
|
|
|
|
PostMessageW, RegisterClassExW, SetCursor, SetCursorPos, SetForegroundWindow,
|
2022-03-26 22:59:13 +01:00
|
|
|
SetWindowPlacement, SetWindowPos, SetWindowTextW, CS_HREDRAW, CS_VREDRAW,
|
2022-03-07 22:58:12 +01:00
|
|
|
CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY,
|
|
|
|
|
GWLP_HINSTANCE, HTCAPTION, MAPVK_VK_TO_VSC, NID_READY, PM_NOREMOVE, SM_DIGITIZER,
|
2022-05-07 05:29:25 +03:00
|
|
|
SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, WM_NCLBUTTONDOWN,
|
|
|
|
|
WNDCLASSEXW,
|
2022-03-07 22:58:12 +01:00
|
|
|
},
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
|
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::{
|
2019-06-19 16:49:43 -04:00
|
|
|
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
2019-06-21 11:33:15 -04:00
|
|
|
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
2020-03-07 19:42:21 +00:00
|
|
|
icon::Icon,
|
2019-06-21 11:33:15 -04:00
|
|
|
monitor::MonitorHandle as RootMonitorHandle,
|
|
|
|
|
platform_impl::platform::{
|
2020-11-30 19:04:26 +01:00
|
|
|
dark_mode::try_theme,
|
2022-04-01 20:21:09 +02:00
|
|
|
definitions::{
|
|
|
|
|
CLSID_TaskbarList, IID_ITaskbarList, IID_ITaskbarList2, ITaskbarList, ITaskbarList2,
|
|
|
|
|
},
|
2021-11-17 18:33:44 +01:00
|
|
|
dpi::{dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_dpi},
|
2019-06-21 11:33:15 -04:00
|
|
|
drop_handler::FileDropHandler,
|
2022-03-07 22:58:12 +01:00
|
|
|
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
2020-03-07 19:42:21 +00:00
|
|
|
icon::{self, IconType},
|
2022-05-07 05:29:25 +03:00
|
|
|
ime::ImeContext,
|
2019-12-30 14:11:11 -05:00
|
|
|
monitor, util,
|
2019-06-21 11:33:15 -04:00
|
|
|
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
2021-04-10 10:47:19 -03:00
|
|
|
Parent, PlatformSpecificWindowBuilderAttributes, WindowId,
|
2019-06-21 11:33:15 -04:00
|
|
|
},
|
2022-06-13 09:43:14 +03:00
|
|
|
window::{CursorGrabMode, CursorIcon, Fullscreen, Theme, UserAttentionType, 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 {
|
2022-06-10 19:05:28 +02:00
|
|
|
pub(crate) fn new<T: 'static>(
|
2019-02-05 10:30:33 -05:00
|
|
|
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
|
2021-11-17 18:33:44 +01:00
|
|
|
unsafe { init(w_attr, pl_attr, event_loop) }
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-31 18:32:19 +02:00
|
|
|
fn window_state_lock(&self) -> MutexGuard<'_, WindowState> {
|
|
|
|
|
self.window_state.lock().unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
pub fn set_title(&self, text: &str) {
|
2022-03-07 22:58:12 +01:00
|
|
|
let wide_text = util::encode_wide(text);
|
2017-06-26 21:21:13 +02:00
|
|
|
unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
SetWindowTextW(self.hwnd(), wide_text.as_ptr());
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-05-29 21:29:54 -04:00
|
|
|
pub fn set_visible(&self, visible: bool) {
|
2019-12-29 10:39:15 -05:00
|
|
|
let window = self.window.clone();
|
2019-06-19 16:49:43 -04:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2019-12-29 10:39:15 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2019-12-29 10:39:15 -05:00
|
|
|
f.set(WindowFlags::VISIBLE, visible)
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-17 20:44:14 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_visible(&self) -> Option<bool> {
|
2022-03-07 22:58:12 +01:00
|
|
|
Some(unsafe { IsWindowVisible(self.window.0) == 1 })
|
2022-02-17 20:44:14 +02:00
|
|
|
}
|
|
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
#[inline]
|
|
|
|
|
pub fn request_redraw(&self) {
|
|
|
|
|
unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
RedrawWindow(self.hwnd(), ptr::null(), 0, RDW_INTERNALPAINT);
|
2019-02-05 10:30:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 16:49:43 -04:00
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
2022-08-15 02:36:37 +02:00
|
|
|
util::WindowArea::Outer.get_rect(self.hwnd())
|
2020-01-04 01:33:07 -05:00
|
|
|
.map(|rect| Ok(PhysicalPosition::new(rect.left as i32, rect.top as i32)))
|
2019-06-19 16:49:43 -04:00
|
|
|
.expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit")
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
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() };
|
2022-03-07 22:58:12 +01:00
|
|
|
if unsafe { ClientToScreen(self.hwnd(), &mut position) } == false.into() {
|
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
|
|
|
}
|
2020-01-04 01:33:07 -05:00
|
|
|
Ok(PhysicalPosition::new(position.x as i32, position.y as i32))
|
2018-04-16 21:40:30 -04:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
#[inline]
|
2019-06-19 16:49:43 -04:00
|
|
|
pub fn set_outer_position(&self, position: Position) {
|
2020-01-03 14:52:27 -05:00
|
|
|
let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
|
2019-06-19 16:49:43 -04:00
|
|
|
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2019-06-19 16:49:43 -04:00
|
|
|
f.set(WindowFlags::MAXIMIZED, false)
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-06-14 19:42:18 -04:00
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
SetWindowPos(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
0,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
2018-06-14 19:42:18 -04:00
|
|
|
0,
|
|
|
|
|
0,
|
2022-03-07 22:58:12 +01:00
|
|
|
SWP_ASYNCWINDOWPOS | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE,
|
2018-06-14 19:42:18 -04:00
|
|
|
);
|
2022-03-07 22:58:12 +01:00
|
|
|
InvalidateRgn(self.hwnd(), 0, false.into());
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn inner_size(&self) -> PhysicalSize<u32> {
|
2019-06-29 00:07:36 +02:00
|
|
|
let mut rect: RECT = unsafe { mem::zeroed() };
|
2022-03-07 22:58:12 +01:00
|
|
|
if unsafe { GetClientRect(self.hwnd(), &mut rect) } == false.into() {
|
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-06-19 16:49:43 -04:00
|
|
|
PhysicalSize::new(
|
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]
|
2020-01-04 01:33:07 -05:00
|
|
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
2022-08-15 02:36:37 +02:00
|
|
|
util::WindowArea::Outer
|
|
|
|
|
.get_rect(self.hwnd())
|
2019-06-21 11:33:15 -04:00
|
|
|
.map(|rect| {
|
2019-06-19 16:49:43 -04:00
|
|
|
PhysicalSize::new(
|
2019-06-21 11:33:15 -04:00
|
|
|
(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-03-23 05:35:35 -04:00
|
|
|
#[inline]
|
2019-06-19 16:49:43 -04:00
|
|
|
pub fn set_inner_size(&self, size: Size) {
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
2022-08-15 02:36:37 +02:00
|
|
|
let physical_size = size.to_physical::<u32>(scale_factor);
|
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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2019-08-26 22:07:15 -04:00
|
|
|
f.set(WindowFlags::MAXIMIZED, false)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2022-08-31 18:32:19 +02:00
|
|
|
let window_flags = self.window_state_lock().window_flags;
|
2022-08-15 02:36:37 +02:00
|
|
|
window_flags.set_size(self.hwnd(), physical_size);
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2018-03-23 05:35:35 -04:00
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-19 16:49:43 -04:00
|
|
|
pub fn set_min_inner_size(&self, size: Option<Size>) {
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().min_size = size;
|
2018-03-23 05:35:35 -04:00
|
|
|
// Make windows re-check the window size bounds.
|
2019-06-19 16:49:43 -04:00
|
|
|
let size = self.inner_size();
|
|
|
|
|
self.set_inner_size(size.into());
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
2019-06-19 16:49:43 -04:00
|
|
|
pub fn set_max_inner_size(&self, size: Option<Size>) {
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().max_size = size;
|
2019-06-19 16:49:43 -04:00
|
|
|
// Make windows re-check the window size bounds.
|
|
|
|
|
let size = self.inner_size();
|
|
|
|
|
self.set_inner_size(size.into());
|
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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), 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
|
|
|
}
|
|
|
|
|
|
2022-02-17 17:03:17 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_resizable(&self) -> bool {
|
2022-08-31 18:32:19 +02:00
|
|
|
let window_state = self.window_state_lock();
|
2022-02-17 17:03:17 +02:00
|
|
|
window_state.window_flags.contains(WindowFlags::RESIZABLE)
|
|
|
|
|
}
|
|
|
|
|
|
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-10-05 20:52:40 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn hinstance(&self) -> HINSTANCE {
|
2022-03-07 22:58:12 +01:00
|
|
|
unsafe { super::get_window_long(self.hwnd(), GWLP_HINSTANCE) }
|
2019-10-05 20:52:40 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-14 07:57:16 -04:00
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_window_handle(&self) -> RawWindowHandle {
|
2022-07-21 22:22:36 +03:00
|
|
|
let mut window_handle = Win32WindowHandle::empty();
|
|
|
|
|
window_handle.hwnd = self.window.0 as *mut _;
|
|
|
|
|
window_handle.hinstance = self.hinstance() as *mut _;
|
|
|
|
|
RawWindowHandle::Win32(window_handle)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
|
|
|
|
RawDisplayHandle::Windows(WindowsDisplayHandle::empty())
|
2019-08-14 07:57:16 -04:00
|
|
|
}
|
|
|
|
|
|
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) {
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().mouse.cursor = cursor;
|
2019-02-05 10:30:33 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
let cursor = LoadCursorW(0, cursor.to_windows_cursor());
|
|
|
|
|
SetCursor(cursor);
|
2018-09-22 21:03:38 -04:00
|
|
|
});
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-06-18 12:32:18 -04:00
|
|
|
#[inline]
|
2022-06-13 09:43:14 +03:00
|
|
|
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
|
|
|
|
|
let confine = match mode {
|
|
|
|
|
CursorGrabMode::None => false,
|
|
|
|
|
CursorGrabMode::Confined => true,
|
|
|
|
|
CursorGrabMode::Locked => {
|
|
|
|
|
return Err(ExternalError::NotSupported(NotSupportedError::new()))
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2019-06-21 11:33:15 -04:00
|
|
|
let result = window_state
|
|
|
|
|
.lock()
|
2022-08-31 18:32:19 +02:00
|
|
|
.unwrap()
|
2019-06-21 11:33:15 -04:00
|
|
|
.mouse
|
2022-06-13 09:43:14 +03:00
|
|
|
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, confine))
|
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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2019-06-21 11:33:15 -04:00
|
|
|
let result = window_state
|
|
|
|
|
.lock()
|
2022-08-31 18:32:19 +02:00
|
|
|
.unwrap()
|
2019-06-21 11:33:15 -04:00
|
|
|
.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]
|
2020-01-03 14:52:27 -05:00
|
|
|
pub fn scale_factor(&self) -> f64 {
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().scale_factor
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2019-06-19 16:49:43 -04:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
|
2020-02-13 20:41:41 +01:00
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
let (x, y) = position.to_physical::<i32>(scale_factor).into();
|
2019-06-19 16:49:43 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let mut point = POINT { x, y };
|
2017-06-26 21:21:13 +02:00
|
|
|
unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
if ClientToScreen(self.hwnd(), &mut point) == false.into() {
|
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
|
|
|
}
|
2022-03-07 22:58:12 +01:00
|
|
|
if SetCursorPos(point.x, point.y) == false.into() {
|
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(())
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-07 10:43:23 +01:00
|
|
|
#[inline]
|
|
|
|
|
pub fn drag_window(&self) -> Result<(), ExternalError> {
|
|
|
|
|
unsafe {
|
|
|
|
|
let points = {
|
|
|
|
|
let mut pos = mem::zeroed();
|
2022-03-07 22:58:12 +01:00
|
|
|
GetCursorPos(&mut pos);
|
2021-03-07 10:43:23 +01:00
|
|
|
pos
|
|
|
|
|
};
|
|
|
|
|
let points = POINTS {
|
2022-03-07 22:58:12 +01:00
|
|
|
x: points.x as i16,
|
|
|
|
|
y: points.y as i16,
|
2021-03-07 10:43:23 +01:00
|
|
|
};
|
2022-03-07 22:58:12 +01:00
|
|
|
ReleaseCapture();
|
|
|
|
|
PostMessageW(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
WM_NCLBUTTONDOWN,
|
|
|
|
|
HTCAPTION as WPARAM,
|
2021-03-07 10:43:23 +01:00
|
|
|
&points as *const _ as LPARAM,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-12 19:10:46 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2022-04-12 19:10:46 +02:00
|
|
|
f.set(WindowFlags::IGNORE_CURSOR_EVENT, !hittest)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-26 21:21:13 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn id(&self) -> WindowId {
|
2022-03-07 22:58:12 +01:00
|
|
|
WindowId(self.hwnd())
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2017-08-28 01:43:34 +01:00
|
|
|
|
2019-12-22 01:04:11 -05:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_minimized(&self, minimized: bool) {
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2019-12-22 01:04:11 -05:00
|
|
|
f.set(WindowFlags::MINIMIZED, minimized)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), 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
|
|
|
}
|
|
|
|
|
|
2021-01-27 20:01:17 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_maximized(&self) -> bool {
|
2022-08-31 18:32:19 +02:00
|
|
|
let window_state = self.window_state_lock();
|
2021-01-27 20:01:17 +02:00
|
|
|
window_state.window_flags.contains(WindowFlags::MAXIMIZED)
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-26 03:09:32 +10:00
|
|
|
#[inline]
|
2019-07-29 21:16:14 +03:00
|
|
|
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
2022-08-31 18:32:19 +02:00
|
|
|
let window_state = self.window_state_lock();
|
2019-04-26 03:09:32 +10:00
|
|
|
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
|
|
|
|
2022-08-31 18:32:19 +02:00
|
|
|
let mut window_state_lock = window_state.lock().unwrap();
|
2019-07-29 21:16:14 +03:00
|
|
|
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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2019-07-29 21:16:14 +03:00
|
|
|
// Change video mode if we're transitioning to or from exclusive
|
|
|
|
|
// fullscreen
|
|
|
|
|
match (&old_fullscreen, &fullscreen) {
|
2022-03-24 05:08:04 +11:00
|
|
|
(_, Some(Fullscreen::Exclusive(video_mode))) => {
|
2019-07-29 21:16:14 +03:00
|
|
|
let monitor = video_mode.monitor();
|
2022-03-07 22:58:12 +01:00
|
|
|
let monitor_info = monitor::get_monitor_info(monitor.inner.hmonitor()).unwrap();
|
2019-07-29 21:16:14 +03:00
|
|
|
|
|
|
|
|
let res = unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
ChangeDisplaySettingsExW(
|
|
|
|
|
monitor_info.szDevice.as_ptr(),
|
2022-03-24 05:08:04 +11:00
|
|
|
&*video_mode.video_mode.native_video_mode,
|
2022-03-07 22:58:12 +01:00
|
|
|
0,
|
|
|
|
|
CDS_FULLSCREEN,
|
|
|
|
|
ptr::null(),
|
2019-07-29 21:16:14 +03:00
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
debug_assert!(res != DISP_CHANGE_BADFLAGS);
|
|
|
|
|
debug_assert!(res != DISP_CHANGE_BADMODE);
|
|
|
|
|
debug_assert!(res != DISP_CHANGE_BADPARAM);
|
|
|
|
|
debug_assert!(res != DISP_CHANGE_FAILED);
|
|
|
|
|
assert_eq!(res, DISP_CHANGE_SUCCESSFUL);
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
2022-03-24 05:08:04 +11:00
|
|
|
(Some(Fullscreen::Exclusive(_)), _) => {
|
2019-07-29 21:16:14 +03:00
|
|
|
let res = unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
ChangeDisplaySettingsExW(
|
|
|
|
|
ptr::null(),
|
|
|
|
|
ptr::null(),
|
|
|
|
|
0,
|
|
|
|
|
CDS_FULLSCREEN,
|
|
|
|
|
ptr::null(),
|
2019-07-29 21:16:14 +03:00
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
debug_assert!(res != DISP_CHANGE_BADFLAGS);
|
|
|
|
|
debug_assert!(res != DISP_CHANGE_BADMODE);
|
|
|
|
|
debug_assert!(res != DISP_CHANGE_BADPARAM);
|
|
|
|
|
debug_assert!(res != DISP_CHANGE_FAILED);
|
|
|
|
|
assert_eq!(res, DISP_CHANGE_SUCCESSFUL);
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2022-03-07 22:58:12 +01:00
|
|
|
PeekMessageW(&mut msg, 0, 0, 0, PM_NOREMOVE);
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update window style
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2020-10-19 15:15:23 +01:00
|
|
|
f.set(
|
|
|
|
|
WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN,
|
|
|
|
|
matches!(fullscreen, Some(Fullscreen::Exclusive(_))),
|
|
|
|
|
);
|
|
|
|
|
f.set(
|
|
|
|
|
WindowFlags::MARKER_BORDERLESS_FULLSCREEN,
|
|
|
|
|
matches!(fullscreen, Some(Fullscreen::Borderless(_))),
|
|
|
|
|
);
|
2019-07-29 21:16:14 +03:00
|
|
|
});
|
|
|
|
|
|
2022-03-26 16:43:13 +01:00
|
|
|
// Mark as fullscreen window wrt to z-order
|
|
|
|
|
//
|
|
|
|
|
// this needs to be called before the below fullscreen SetWindowPos as this itself
|
|
|
|
|
// will generate WM_SIZE messages of the old window size that can race with what we set below
|
|
|
|
|
unsafe {
|
|
|
|
|
taskbar_mark_fullscreen(window.0, fullscreen.is_some());
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:16:14 +03:00
|
|
|
// Update window bounds
|
|
|
|
|
match &fullscreen {
|
|
|
|
|
Some(fullscreen) => {
|
2020-12-13 20:06:53 +02:00
|
|
|
// Save window bounds before entering fullscreen
|
|
|
|
|
let placement = unsafe {
|
|
|
|
|
let mut placement = mem::zeroed();
|
2022-03-07 22:58:12 +01:00
|
|
|
GetWindowPlacement(window.0, &mut placement);
|
2020-12-13 20:06:53 +02:00
|
|
|
placement
|
|
|
|
|
};
|
|
|
|
|
|
2022-08-31 18:32:19 +02:00
|
|
|
window_state.lock().unwrap().saved_window = Some(SavedWindow { placement });
|
2020-12-13 20:06:53 +02:00
|
|
|
|
2020-09-22 04:54:47 +03:00
|
|
|
let monitor = match &fullscreen {
|
|
|
|
|
Fullscreen::Exclusive(video_mode) => video_mode.monitor(),
|
|
|
|
|
Fullscreen::Borderless(Some(monitor)) => monitor.clone(),
|
|
|
|
|
Fullscreen::Borderless(None) => RootMonitorHandle {
|
|
|
|
|
inner: monitor::current_monitor(window.0),
|
|
|
|
|
},
|
2019-07-29 21:16:14 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let position: (i32, i32) = monitor.position().into();
|
|
|
|
|
let size: (u32, u32) = monitor.size().into();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
SetWindowPos(
|
2019-07-29 21:16:14 +03:00
|
|
|
window.0,
|
2022-03-07 22:58:12 +01:00
|
|
|
0,
|
2019-07-29 21:16:14 +03:00
|
|
|
position.0,
|
|
|
|
|
position.1,
|
|
|
|
|
size.0 as i32,
|
|
|
|
|
size.1 as i32,
|
2022-03-07 22:58:12 +01:00
|
|
|
SWP_ASYNCWINDOWPOS | SWP_NOZORDER,
|
2019-07-29 21:16:14 +03:00
|
|
|
);
|
2022-03-07 22:58:12 +01:00
|
|
|
InvalidateRgn(window.0, 0, false.into());
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => {
|
2022-08-31 18:32:19 +02:00
|
|
|
let mut window_state_lock = window_state.lock().unwrap();
|
2020-12-13 20:06:53 +02:00
|
|
|
if let Some(SavedWindow { placement }) = window_state_lock.saved_window.take() {
|
2019-07-29 21:16:14 +03:00
|
|
|
drop(window_state_lock);
|
|
|
|
|
unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
SetWindowPlacement(window.0, &placement);
|
|
|
|
|
InvalidateRgn(window.0, 0, false.into());
|
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
|
|
|
});
|
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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2022-08-15 02:36:37 +02:00
|
|
|
f.set(WindowFlags::MARKER_DECORATIONS, decorations)
|
2019-06-21 11:33:15 -04:00
|
|
|
});
|
2019-02-04 11:52:00 -05:00
|
|
|
});
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
|
2022-02-17 15:31:13 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn is_decorated(&self) -> bool {
|
2022-08-31 18:32:19 +02:00
|
|
|
let window_state = self.window_state_lock();
|
2022-08-15 02:36:37 +02:00
|
|
|
window_state
|
|
|
|
|
.window_flags
|
|
|
|
|
.contains(WindowFlags::MARKER_DECORATIONS)
|
2022-02-17 15:31:13 +02: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 || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), 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]
|
2020-09-07 20:09:24 +03:00
|
|
|
pub fn current_monitor(&self) -> Option<RootMonitorHandle> {
|
|
|
|
|
Some(RootMonitorHandle {
|
2022-03-07 22:58:12 +01:00
|
|
|
inner: monitor::current_monitor(self.hwnd()),
|
2020-09-07 20:09:24 +03:00
|
|
|
})
|
2017-08-28 01:43:34 +01:00
|
|
|
}
|
2018-05-07 17:36:21 -04:00
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-07 19:42:21 +00:00
|
|
|
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
2018-05-07 17:36:21 -04:00
|
|
|
if let Some(ref window_icon) = window_icon {
|
2020-03-07 19:42:21 +00:00
|
|
|
window_icon
|
|
|
|
|
.inner
|
2022-03-07 22:58:12 +01:00
|
|
|
.set_for_window(self.hwnd(), IconType::Small);
|
2018-05-07 17:36:21 -04:00
|
|
|
} else {
|
2022-03-07 22:58:12 +01:00
|
|
|
icon::unset_for_window(self.hwnd(), IconType::Small);
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().window_icon = window_icon;
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
|
|
|
|
|
2021-12-01 12:20:56 +01:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_enable(&self, enabled: bool) {
|
2022-03-07 22:58:12 +01:00
|
|
|
unsafe { EnableWindow(self.hwnd(), enabled.into()) };
|
2021-12-01 12:20:56 +01:00
|
|
|
}
|
|
|
|
|
|
2018-05-07 17:36:21 -04:00
|
|
|
#[inline]
|
2020-03-07 19:42:21 +00:00
|
|
|
pub fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
2018-05-07 17:36:21 -04:00
|
|
|
if let Some(ref taskbar_icon) = taskbar_icon {
|
2020-03-07 19:42:21 +00:00
|
|
|
taskbar_icon
|
|
|
|
|
.inner
|
2022-03-07 22:58:12 +01:00
|
|
|
.set_for_window(self.hwnd(), IconType::Big);
|
2018-05-07 17:36:21 -04:00
|
|
|
} else {
|
2022-03-07 22:58:12 +01:00
|
|
|
icon::unset_for_window(self.hwnd(), IconType::Big);
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
2022-08-31 18:32:19 +02: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
|
|
|
|
2022-05-07 05:29:25 +03:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_ime_position(&self, spot: Position) {
|
|
|
|
|
unsafe {
|
|
|
|
|
ImeContext::current(self.hwnd()).set_ime_position(spot, self.scale_factor());
|
2020-12-10 05:16:59 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 21:28:30 -04:00
|
|
|
#[inline]
|
2022-05-07 05:29:25 +03:00
|
|
|
pub fn set_ime_allowed(&self, allowed: bool) {
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().ime_allowed = allowed;
|
2022-05-07 05:29:25 +03:00
|
|
|
unsafe {
|
|
|
|
|
ImeContext::set_ime_allowed(self.hwnd(), allowed);
|
|
|
|
|
}
|
2018-05-17 21:28:30 -04:00
|
|
|
}
|
2019-12-22 19:04:09 +00:00
|
|
|
|
2020-11-27 03:03:08 +01:00
|
|
|
#[inline]
|
|
|
|
|
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
|
|
|
|
let window = self.window.clone();
|
2022-03-07 22:58:12 +01:00
|
|
|
let active_window_handle = unsafe { GetActiveWindow() };
|
2020-11-27 03:03:08 +01:00
|
|
|
if window.0 == active_window_handle {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2020-11-27 03:03:08 +01:00
|
|
|
let (flags, count) = request_type
|
|
|
|
|
.map(|ty| match ty {
|
2022-03-07 22:58:12 +01:00
|
|
|
UserAttentionType::Critical => (FLASHW_ALL | FLASHW_TIMERNOFG, u32::MAX),
|
|
|
|
|
UserAttentionType::Informational => (FLASHW_TRAY | FLASHW_TIMERNOFG, 0),
|
2020-11-27 03:03:08 +01:00
|
|
|
})
|
2022-03-07 22:58:12 +01:00
|
|
|
.unwrap_or((FLASHW_STOP, 0));
|
2020-11-27 03:03:08 +01:00
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
let flash_info = FLASHWINFO {
|
|
|
|
|
cbSize: mem::size_of::<FLASHWINFO>() as u32,
|
2020-11-27 03:03:08 +01:00
|
|
|
hwnd: window.0,
|
|
|
|
|
dwFlags: flags,
|
|
|
|
|
uCount: count,
|
|
|
|
|
dwTimeout: 0,
|
|
|
|
|
};
|
2022-03-07 22:58:12 +01:00
|
|
|
FlashWindowEx(&flash_info);
|
2020-11-27 03:03:08 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-22 19:04:09 +00:00
|
|
|
#[inline]
|
2020-11-30 19:04:26 +01:00
|
|
|
pub fn theme(&self) -> Theme {
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().current_theme
|
2019-12-22 19:04:09 +00:00
|
|
|
}
|
2021-05-19 18:39:53 +02:00
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_skip_taskbar(&self, skip: bool) {
|
2022-08-31 18:32:19 +02:00
|
|
|
self.window_state_lock().skip_taskbar = skip;
|
2022-07-22 19:33:22 +02:00
|
|
|
unsafe { set_skip_taskbar(self.hwnd(), skip) };
|
2022-04-01 20:21:09 +02:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 02:36:37 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn set_undecorated_shadow(&self, shadow: bool) {
|
|
|
|
|
let window = self.window.clone();
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
let _ = &window;
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window.0, |f| {
|
2022-08-15 02:36:37 +02:00
|
|
|
f.set(WindowFlags::MARKER_UNDECORATED_SHADOW, shadow)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-19 18:39:53 +02:00
|
|
|
#[inline]
|
|
|
|
|
pub fn focus_window(&self) {
|
|
|
|
|
let window = self.window.clone();
|
2022-08-31 18:32:19 +02:00
|
|
|
let window_flags = self.window_state_lock().window_flags();
|
2021-05-19 18:39:53 +02:00
|
|
|
|
|
|
|
|
let is_visible = window_flags.contains(WindowFlags::VISIBLE);
|
|
|
|
|
let is_minimized = window_flags.contains(WindowFlags::MINIMIZED);
|
2022-03-07 22:58:12 +01:00
|
|
|
let is_foreground = window.0 == unsafe { GetForegroundWindow() };
|
2021-05-19 18:39:53 +02:00
|
|
|
|
|
|
|
|
if is_visible && !is_minimized && !is_foreground {
|
|
|
|
|
unsafe { force_window_active(window.0) };
|
|
|
|
|
}
|
|
|
|
|
}
|
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.
|
2022-03-07 22:58:12 +01:00
|
|
|
PostMessageW(self.hwnd(), *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 {}
|
|
|
|
|
|
2021-07-16 12:40:48 +02:00
|
|
|
pub(super) struct InitData<'a, T: 'static> {
|
|
|
|
|
// inputs
|
|
|
|
|
pub event_loop: &'a EventLoopWindowTarget<T>,
|
2021-11-17 18:33:44 +01:00
|
|
|
pub attributes: WindowAttributes,
|
|
|
|
|
pub pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
|
|
|
|
pub window_flags: WindowFlags,
|
2021-07-16 12:40:48 +02:00
|
|
|
// outputs
|
|
|
|
|
pub window: Option<Window>,
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
impl<'a, T: 'static> InitData<'a, T> {
|
|
|
|
|
unsafe fn create_window(&self, window: HWND) -> Window {
|
|
|
|
|
// Register for touch events if applicable
|
|
|
|
|
{
|
2022-03-07 22:58:12 +01:00
|
|
|
let digitizer = GetSystemMetrics(SM_DIGITIZER) as u32;
|
|
|
|
|
if digitizer & NID_READY != 0 {
|
|
|
|
|
RegisterTouchWindow(window, TWF_WANTPALM);
|
2021-11-17 18:33:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let dpi = hwnd_dpi(window);
|
|
|
|
|
let scale_factor = dpi_to_scale_factor(dpi);
|
|
|
|
|
|
|
|
|
|
// If the system theme is dark, we need to set the window theme now
|
|
|
|
|
// before we update the window flags (and possibly show the
|
|
|
|
|
// window for the first time).
|
|
|
|
|
let current_theme = try_theme(window, self.pl_attribs.preferred_theme);
|
|
|
|
|
|
|
|
|
|
let window_state = {
|
|
|
|
|
let window_state = WindowState::new(
|
|
|
|
|
&self.attributes,
|
|
|
|
|
self.pl_attribs.taskbar_icon.clone(),
|
|
|
|
|
scale_factor,
|
|
|
|
|
current_theme,
|
|
|
|
|
self.pl_attribs.preferred_theme,
|
|
|
|
|
);
|
|
|
|
|
let window_state = Arc::new(Mutex::new(window_state));
|
2022-08-31 18:32:19 +02:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
|
|
|
|
*f = self.window_flags
|
|
|
|
|
});
|
2021-11-17 18:33:44 +01:00
|
|
|
window_state
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enable_non_client_dpi_scaling(window);
|
|
|
|
|
|
2022-05-07 05:29:25 +03:00
|
|
|
ImeContext::set_ime_allowed(window, false);
|
|
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
Window {
|
|
|
|
|
window: WindowWrapper(window),
|
|
|
|
|
window_state,
|
|
|
|
|
thread_executor: self.event_loop.create_thread_executor(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn create_window_data(&self, win: &Window) -> event_loop::WindowData<T> {
|
|
|
|
|
let file_drop_handler = if self.pl_attribs.drag_and_drop {
|
2022-03-07 22:58:12 +01:00
|
|
|
let ole_init_result = OleInitialize(ptr::null_mut());
|
2021-11-17 18:33:44 +01:00
|
|
|
// 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`. \
|
|
|
|
|
Make sure other crates are not using multithreaded COM library \
|
|
|
|
|
on the same thread or disable drag and drop support."
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let file_drop_runner = self.event_loop.runner_shared.clone();
|
|
|
|
|
let file_drop_handler = FileDropHandler::new(
|
|
|
|
|
win.window.0,
|
|
|
|
|
Box::new(move |event| {
|
|
|
|
|
if let Ok(e) = event.map_nonuser_event() {
|
|
|
|
|
file_drop_runner.send_event(e)
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
let handler_interface_ptr =
|
|
|
|
|
&mut (*file_drop_handler.data).interface as *mut _ as *mut c_void;
|
|
|
|
|
|
|
|
|
|
assert_eq!(RegisterDragDrop(win.window.0, handler_interface_ptr), S_OK);
|
2021-11-17 18:33:44 +01:00
|
|
|
Some(file_drop_handler)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
self.event_loop.runner_shared.register_window(win.window.0);
|
|
|
|
|
|
|
|
|
|
event_loop::WindowData {
|
|
|
|
|
window_state: win.window_state.clone(),
|
|
|
|
|
event_loop_runner: self.event_loop.runner_shared.clone(),
|
|
|
|
|
_file_drop_handler: file_drop_handler,
|
|
|
|
|
userdata_removed: Cell::new(false),
|
|
|
|
|
recurse_depth: Cell::new(0),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns a pointer to window user data on success.
|
|
|
|
|
// The user data will be registered for the window and can be accessed within the window event callback.
|
2022-03-07 22:58:12 +01:00
|
|
|
pub unsafe fn on_nccreate(&mut self, window: HWND) -> Option<isize> {
|
2021-11-17 18:33:44 +01:00
|
|
|
let runner = self.event_loop.runner_shared.clone();
|
2022-02-25 12:27:52 +01:00
|
|
|
let result = runner.catch_unwind(|| {
|
2022-03-24 05:08:04 +11:00
|
|
|
let window = self.create_window(window);
|
|
|
|
|
let window_data = self.create_window_data(&window);
|
2021-11-17 18:33:44 +01:00
|
|
|
(window, window_data)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
result.map(|(win, userdata)| {
|
|
|
|
|
self.window = Some(win);
|
|
|
|
|
let userdata = Box::into_raw(Box::new(userdata));
|
|
|
|
|
userdata as _
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub unsafe fn on_create(&mut self) {
|
|
|
|
|
let win = self.window.as_mut().expect("failed window creation");
|
2022-01-03 20:54:31 +08:00
|
|
|
|
|
|
|
|
// making the window transparent
|
|
|
|
|
if self.attributes.transparent && !self.pl_attribs.no_redirection_bitmap {
|
|
|
|
|
// Empty region for the blur effect, so the window is fully transparent
|
|
|
|
|
let region = CreateRectRgn(0, 0, -1, -1);
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
let bb = DWM_BLURBEHIND {
|
|
|
|
|
dwFlags: DWM_BB_ENABLE | DWM_BB_BLURREGION,
|
|
|
|
|
fEnable: true.into(),
|
2022-01-03 20:54:31 +08:00
|
|
|
hRgnBlur: region,
|
2022-03-07 22:58:12 +01:00
|
|
|
fTransitionOnMaximized: false.into(),
|
2022-01-03 20:54:31 +08:00
|
|
|
};
|
2022-03-07 22:58:12 +01:00
|
|
|
let hr = DwmEnableBlurBehindWindow(win.hwnd(), &bb);
|
|
|
|
|
if hr < 0 {
|
2022-01-03 20:54:31 +08:00
|
|
|
warn!(
|
|
|
|
|
"Setting transparent window is failed. HRESULT Code: 0x{:X}",
|
|
|
|
|
hr
|
|
|
|
|
);
|
|
|
|
|
}
|
2022-03-07 22:58:12 +01:00
|
|
|
DeleteObject(region);
|
2022-01-03 20:54:31 +08:00
|
|
|
}
|
|
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
win.set_skip_taskbar(self.pl_attribs.skip_taskbar);
|
|
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
let attributes = self.attributes.clone();
|
|
|
|
|
|
|
|
|
|
// Set visible before setting the size to ensure the
|
|
|
|
|
// attribute is correctly applied.
|
|
|
|
|
win.set_visible(attributes.visible);
|
|
|
|
|
|
|
|
|
|
if attributes.fullscreen.is_some() {
|
|
|
|
|
win.set_fullscreen(attributes.fullscreen);
|
|
|
|
|
force_window_active(win.window.0);
|
2022-03-26 16:43:13 +01:00
|
|
|
} else {
|
2022-08-04 23:03:55 +02:00
|
|
|
let size = attributes
|
2022-03-26 16:43:13 +01:00
|
|
|
.inner_size
|
|
|
|
|
.unwrap_or_else(|| PhysicalSize::new(800, 600).into());
|
2022-08-04 23:03:55 +02:00
|
|
|
let max_size = attributes
|
|
|
|
|
.max_inner_size
|
|
|
|
|
.unwrap_or_else(|| PhysicalSize::new(f64::MAX, f64::MAX).into());
|
|
|
|
|
let min_size = attributes
|
|
|
|
|
.min_inner_size
|
|
|
|
|
.unwrap_or_else(|| PhysicalSize::new(0, 0).into());
|
|
|
|
|
let clamped_size = Size::clamp(size, min_size, max_size, win.scale_factor());
|
|
|
|
|
win.set_inner_size(clamped_size);
|
2022-03-26 16:43:13 +01:00
|
|
|
|
|
|
|
|
if attributes.maximized {
|
|
|
|
|
// Need to set MAXIMIZED after setting `inner_size` as
|
|
|
|
|
// `Window::set_inner_size` changes MAXIMIZED to false.
|
|
|
|
|
win.set_maximized(true);
|
|
|
|
|
}
|
2021-11-17 18:33:44 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-15 02:36:37 +02:00
|
|
|
// let margins = MARGINS {
|
|
|
|
|
// cxLeftWidth: 1,
|
|
|
|
|
// cxRightWidth: 1,
|
|
|
|
|
// cyTopHeight: 1,
|
|
|
|
|
// cyBottomHeight: 1,
|
|
|
|
|
// };
|
|
|
|
|
// dbg!(DwmExtendFrameIntoClientArea(win.hwnd(), &margins as *const _));
|
|
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
if let Some(position) = attributes.position {
|
|
|
|
|
win.set_outer_position(position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
unsafe fn init<T>(
|
2020-03-07 19:42:21 +00:00
|
|
|
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>,
|
2021-07-16 12:40:48 +02:00
|
|
|
) -> Result<Window, RootOsError>
|
|
|
|
|
where
|
|
|
|
|
T: 'static,
|
|
|
|
|
{
|
2022-03-07 22:58:12 +01:00
|
|
|
let title = util::encode_wide(&attributes.title);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2021-07-16 12:40:48 +02:00
|
|
|
let class_name = register_window_class::<T>(&attributes.window_icon, &pl_attribs.taskbar_icon);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-02-04 11:52:00 -05:00
|
|
|
let mut window_flags = WindowFlags::empty();
|
2022-08-15 02:36:37 +02:00
|
|
|
window_flags.set(WindowFlags::MARKER_DECORATIONS, attributes.decorations);
|
|
|
|
|
window_flags.set(
|
|
|
|
|
WindowFlags::MARKER_UNDECORATED_SHADOW,
|
|
|
|
|
pl_attribs.decoration_shadow,
|
|
|
|
|
);
|
2019-02-04 11:52:00 -05:00
|
|
|
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);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2021-04-10 10:47:19 -03:00
|
|
|
let parent = match pl_attribs.parent {
|
|
|
|
|
Parent::ChildOf(parent) => {
|
|
|
|
|
window_flags.set(WindowFlags::CHILD, true);
|
|
|
|
|
if pl_attribs.menu.is_some() {
|
|
|
|
|
warn!("Setting a menu on a child window is unsupported");
|
|
|
|
|
}
|
|
|
|
|
Some(parent)
|
|
|
|
|
}
|
|
|
|
|
Parent::OwnedBy(parent) => {
|
|
|
|
|
window_flags.set(WindowFlags::POPUP, true);
|
|
|
|
|
Some(parent)
|
|
|
|
|
}
|
|
|
|
|
Parent::None => {
|
|
|
|
|
window_flags.set(WindowFlags::ON_TASKBAR, true);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
};
|
2021-02-04 22:26:33 +01:00
|
|
|
|
2021-07-16 12:40:48 +02:00
|
|
|
let mut initdata = InitData {
|
|
|
|
|
event_loop,
|
2021-11-17 18:33:44 +01:00
|
|
|
attributes,
|
|
|
|
|
pl_attribs: pl_attribs.clone(),
|
|
|
|
|
window_flags,
|
2021-07-16 12:40:48 +02:00
|
|
|
window: None,
|
|
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2021-07-16 12:40:48 +02:00
|
|
|
let (style, ex_style) = window_flags.to_window_styles();
|
2022-03-07 22:58:12 +01:00
|
|
|
let handle = CreateWindowExW(
|
2021-07-16 12:40:48 +02:00
|
|
|
ex_style,
|
|
|
|
|
class_name.as_ptr(),
|
2022-03-07 22:58:12 +01:00
|
|
|
title.as_ptr(),
|
2021-07-16 12:40:48 +02:00
|
|
|
style,
|
2022-03-07 22:58:12 +01:00
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
parent.unwrap_or(0),
|
|
|
|
|
pl_attribs.menu.unwrap_or(0),
|
2022-05-29 17:12:46 +02:00
|
|
|
util::get_instance_handle(),
|
2021-07-16 12:40:48 +02:00
|
|
|
&mut initdata as *mut _ as *mut _,
|
|
|
|
|
);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
// If the window creation in `InitData` panicked, then should resume panicking here
|
2021-07-16 12:40:48 +02:00
|
|
|
if let Err(panic_error) = event_loop.runner_shared.take_panic_error() {
|
|
|
|
|
panic::resume_unwind(panic_error)
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
if handle == 0 {
|
2021-07-16 12:40:48 +02:00
|
|
|
return Err(os_error!(io::Error::last_os_error()));
|
|
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2021-07-16 12:40:48 +02:00
|
|
|
// If the handle is non-null, then window creation must have succeeded, which means
|
|
|
|
|
// that we *must* have populated the `InitData.window` field.
|
|
|
|
|
Ok(initdata.window.unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn register_window_class<T: 'static>(
|
2020-03-07 19:42:21 +00:00
|
|
|
window_icon: &Option<Icon>,
|
|
|
|
|
taskbar_icon: &Option<Icon>,
|
2018-05-07 17:36:21 -04:00
|
|
|
) -> Vec<u16> {
|
2022-03-07 22:58:12 +01:00
|
|
|
let class_name = util::encode_wide("Window Class");
|
2018-05-07 17:36:21 -04:00
|
|
|
|
|
|
|
|
let h_icon = taskbar_icon
|
|
|
|
|
.as_ref()
|
2020-03-07 19:42:21 +00:00
|
|
|
.map(|icon| icon.inner.as_raw_handle())
|
2022-03-07 22:58:12 +01:00
|
|
|
.unwrap_or(0);
|
2018-05-07 17:36:21 -04:00
|
|
|
let h_icon_small = window_icon
|
|
|
|
|
.as_ref()
|
2020-03-07 19:42:21 +00:00
|
|
|
.map(|icon| icon.inner.as_raw_handle())
|
2022-03-07 22:58:12 +01:00
|
|
|
.unwrap_or(0);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2022-08-15 02:36:37 +02:00
|
|
|
use windows_sys::Win32::UI::WindowsAndMessaging::COLOR_WINDOWFRAME;
|
2022-03-07 22:58:12 +01:00
|
|
|
let class = WNDCLASSEXW {
|
|
|
|
|
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
|
2022-03-26 22:59:13 +01:00
|
|
|
style: CS_HREDRAW | CS_VREDRAW,
|
2021-07-16 12:40:48 +02:00
|
|
|
lpfnWndProc: Some(super::event_loop::public_window_callback::<T>),
|
2017-06-26 21:21:13 +02:00
|
|
|
cbClsExtra: 0,
|
|
|
|
|
cbWndExtra: 0,
|
2022-05-29 17:12:46 +02:00
|
|
|
hInstance: util::get_instance_handle(),
|
2018-05-07 17:36:21 -04:00
|
|
|
hIcon: h_icon,
|
2022-03-07 22:58:12 +01:00
|
|
|
hCursor: 0, // must be null in order for cursor state to work properly
|
2022-08-15 02:36:37 +02:00
|
|
|
hbrBackground: COLOR_WINDOWFRAME as _,
|
2017-06-26 21:21:13 +02:00
|
|
|
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.
|
2022-03-07 22:58:12 +01:00
|
|
|
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) {
|
2022-03-07 22:58:12 +01:00
|
|
|
unsafe { 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 {
|
2022-03-07 22:58:12 +01:00
|
|
|
CoInitializeEx(ptr::null(), COINIT_APARTMENTTHREADED);
|
2018-04-13 01:12:15 +08:00
|
|
|
ComInitialized(ptr::null_mut())
|
|
|
|
|
}
|
|
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
static TASKBAR_LIST: Cell<*mut ITaskbarList> = Cell::new(ptr::null_mut());
|
|
|
|
|
static TASKBAR_LIST2: 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();
|
|
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
TASKBAR_LIST2.with(|task_bar_list2_ptr| {
|
|
|
|
|
let mut task_bar_list2 = task_bar_list2_ptr.get();
|
2018-04-13 01:12:15 +08:00
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
if task_bar_list2.is_null() {
|
2022-03-07 22:58:12 +01:00
|
|
|
let hr = CoCreateInstance(
|
2018-06-12 11:58:18 -04:00
|
|
|
&CLSID_TaskbarList,
|
2018-04-13 01:12:15 +08:00
|
|
|
ptr::null_mut(),
|
2022-03-07 22:58:12 +01:00
|
|
|
CLSCTX_ALL,
|
|
|
|
|
&IID_ITaskbarList2,
|
2022-04-01 20:21:09 +02:00
|
|
|
&mut task_bar_list2 as *mut _ as *mut _,
|
2018-04-13 01:12:15 +08:00
|
|
|
);
|
|
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
let hr_init = (*(*task_bar_list2).lpVtbl).parent.HrInit;
|
2022-03-07 22:58:12 +01:00
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
if hr != S_OK || hr_init(task_bar_list2.cast()) != S_OK {
|
2018-04-13 01:12:15 +08:00
|
|
|
// In some old windows, the taskbar object could not be created, we just ignore it
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-04-01 20:21:09 +02:00
|
|
|
task_bar_list2_ptr.set(task_bar_list2)
|
2018-04-13 01:12:15 +08:00
|
|
|
}
|
|
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
task_bar_list2 = task_bar_list2_ptr.get();
|
|
|
|
|
let mark_fullscreen_window = (*(*task_bar_list2).lpVtbl).MarkFullscreenWindow;
|
|
|
|
|
mark_fullscreen_window(task_bar_list2, handle, if fullscreen { 1 } else { 0 });
|
2018-04-13 01:12:15 +08:00
|
|
|
})
|
|
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2022-07-22 19:33:22 +02:00
|
|
|
pub(crate) unsafe fn set_skip_taskbar(hwnd: HWND, skip: bool) {
|
|
|
|
|
com_initialized();
|
|
|
|
|
TASKBAR_LIST.with(|task_bar_list_ptr| {
|
|
|
|
|
let mut task_bar_list = task_bar_list_ptr.get();
|
|
|
|
|
|
|
|
|
|
if task_bar_list.is_null() {
|
|
|
|
|
let hr = CoCreateInstance(
|
|
|
|
|
&CLSID_TaskbarList,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
CLSCTX_ALL,
|
|
|
|
|
&IID_ITaskbarList,
|
|
|
|
|
&mut task_bar_list as *mut _ as *mut _,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let hr_init = (*(*task_bar_list).lpVtbl).HrInit;
|
|
|
|
|
|
|
|
|
|
if hr != S_OK || hr_init(task_bar_list.cast()) != 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();
|
|
|
|
|
if skip {
|
|
|
|
|
let delete_tab = (*(*task_bar_list).lpVtbl).DeleteTab;
|
|
|
|
|
delete_tab(task_bar_list, hwnd);
|
|
|
|
|
} else {
|
|
|
|
|
let add_tab = (*(*task_bar_list).lpVtbl).AddTab;
|
|
|
|
|
add_tab(task_bar_list, hwnd);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
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
|
2022-03-07 22:58:12 +01:00
|
|
|
let alt_sc = MapVirtualKeyW(VK_MENU as u32, MAPVK_VK_TO_VSC);
|
|
|
|
|
|
|
|
|
|
let inputs = [
|
|
|
|
|
INPUT {
|
|
|
|
|
r#type: INPUT_KEYBOARD,
|
|
|
|
|
Anonymous: INPUT_0 {
|
|
|
|
|
ki: KEYBDINPUT {
|
|
|
|
|
wVk: VK_LMENU,
|
|
|
|
|
wScan: alt_sc as u16,
|
|
|
|
|
dwFlags: KEYEVENTF_EXTENDEDKEY,
|
|
|
|
|
dwExtraInfo: 0,
|
|
|
|
|
time: 0,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
INPUT {
|
|
|
|
|
r#type: INPUT_KEYBOARD,
|
|
|
|
|
Anonymous: INPUT_0 {
|
|
|
|
|
ki: KEYBDINPUT {
|
|
|
|
|
wVk: VK_LMENU,
|
|
|
|
|
wScan: alt_sc as u16,
|
|
|
|
|
dwFlags: KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP,
|
|
|
|
|
dwExtraInfo: 0,
|
|
|
|
|
time: 0,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
2018-04-13 01:12:15 +08:00
|
|
|
|
|
|
|
|
// Simulate a key press and release
|
2022-03-07 22:58:12 +01:00
|
|
|
SendInput(
|
|
|
|
|
inputs.len() as u32,
|
|
|
|
|
inputs.as_ptr(),
|
|
|
|
|
mem::size_of::<INPUT>() as i32,
|
2018-04-13 01:12:15 +08:00
|
|
|
);
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
SetForegroundWindow(handle);
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|