2022-12-25 09:57:27 +02:00
|
|
|
#![cfg(windows_platform)]
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use std::cell::Cell;
|
2022-03-07 22:58:12 +01:00
|
|
|
use std::ffi::c_void;
|
2023-05-28 20:02:59 +02:00
|
|
|
use std::mem::{self, MaybeUninit};
|
2022-08-31 18:32:19 +02:00
|
|
|
use std::sync::mpsc::channel;
|
|
|
|
|
use std::sync::{Arc, Mutex, MutexGuard};
|
2023-05-28 20:02:59 +02:00
|
|
|
use std::{io, panic, ptr};
|
2024-04-26 19:11:44 +04:00
|
|
|
|
2024-02-25 19:20:39 -08:00
|
|
|
use tracing::warn;
|
2022-03-07 22:58:12 +01:00
|
|
|
use windows_sys::Win32::Foundation::{
|
2022-08-31 18:32:19 +02:00
|
|
|
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
|
|
|
use windows_sys::Win32::Graphics::Dwm::{
|
2024-01-19 12:43:39 +01:00
|
|
|
DwmEnableBlurBehindWindow, DwmSetWindowAttribute, DWMWA_BORDER_COLOR, DWMWA_CAPTION_COLOR,
|
2024-01-25 20:59:10 +03:00
|
|
|
DWMWA_SYSTEMBACKDROP_TYPE, DWMWA_TEXT_COLOR, DWMWA_WINDOW_CORNER_PREFERENCE, DWM_BB_BLURREGION,
|
|
|
|
|
DWM_BB_ENABLE, DWM_BLURBEHIND, DWM_SYSTEMBACKDROP_TYPE, DWM_WINDOW_CORNER_PREFERENCE,
|
2024-04-26 19:11:44 +04:00
|
|
|
};
|
2024-01-25 20:59:10 +03:00
|
|
|
use windows_sys::Win32::Graphics::Gdi::{
|
2022-03-07 22:58:12 +01:00
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
use windows_sys::Win32::System::Com::{
|
|
|
|
|
CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_APARTMENTTHREADED,
|
|
|
|
|
};
|
|
|
|
|
use windows_sys::Win32::System::Ole::{OleInitialize, RegisterDragDrop};
|
|
|
|
|
use windows_sys::Win32::UI::Input::KeyboardAndMouse::{
|
2023-05-28 20:02:59 +02:00
|
|
|
EnableWindow, GetActiveWindow, MapVirtualKeyW, ReleaseCapture, SendInput, ToUnicode, INPUT,
|
|
|
|
|
INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP, MAPVK_VK_TO_VSC,
|
|
|
|
|
VIRTUAL_KEY, VK_LMENU, VK_MENU, VK_SPACE,
|
2022-03-07 22:58:12 +01:00
|
|
|
};
|
|
|
|
|
use windows_sys::Win32::UI::Input::Touch::{RegisterTouchWindow, TWF_WANTPALM};
|
|
|
|
|
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
2023-10-11 01:16:16 +03:30
|
|
|
CreateWindowExW, EnableMenuItem, FlashWindowEx, GetClientRect, GetCursorPos,
|
|
|
|
|
GetForegroundWindow, GetSystemMenu, GetSystemMetrics, GetWindowPlacement, GetWindowTextLengthW,
|
|
|
|
|
GetWindowTextW, IsWindowVisible, LoadCursorW, PeekMessageW, PostMessageW, RegisterClassExW,
|
|
|
|
|
SetCursor, SetCursorPos, SetForegroundWindow, SetMenuDefaultItem, SetWindowDisplayAffinity,
|
|
|
|
|
SetWindowPlacement, SetWindowPos, SetWindowTextW, TrackPopupMenu, CS_HREDRAW, CS_VREDRAW,
|
2023-07-21 20:01:56 +02:00
|
|
|
CW_USEDEFAULT, FLASHWINFO, FLASHW_ALL, FLASHW_STOP, FLASHW_TIMERNOFG, FLASHW_TRAY,
|
|
|
|
|
GWLP_HINSTANCE, HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, HTRIGHT, HTTOP,
|
2023-10-11 01:16:16 +03:30
|
|
|
HTTOPLEFT, HTTOPRIGHT, MENU_ITEM_STATE, MFS_DISABLED, MFS_ENABLED, MF_BYCOMMAND, NID_READY,
|
|
|
|
|
PM_NOREMOVE, SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, SC_RESTORE, SC_SIZE, SM_DIGITIZER,
|
|
|
|
|
SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOSIZE, SWP_NOZORDER, TPM_LEFTALIGN, TPM_RETURNCMD,
|
|
|
|
|
WDA_EXCLUDEFROMCAPTURE, WDA_NONE, WM_NCLBUTTONDOWN, WM_SYSCOMMAND, WNDCLASSEXW,
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2023-12-25 07:20:52 +01:00
|
|
|
use crate::cursor::Cursor;
|
2024-11-21 17:37:03 +01:00
|
|
|
use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
2024-09-06 17:20:11 +03:00
|
|
|
use crate::error::{NotSupportedError, RequestError};
|
2020-03-07 19:42:21 +00:00
|
|
|
use crate::icon::Icon;
|
2024-08-23 23:40:27 +03:00
|
|
|
use crate::monitor::MonitorHandle as CoreMonitorHandle;
|
2024-01-25 20:59:10 +03:00
|
|
|
use crate::platform::windows::{BackdropType, Color, CornerPreference};
|
2024-01-17 23:37:28 +01:00
|
|
|
use crate::platform_impl::platform::dark_mode::try_theme;
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::platform_impl::platform::definitions::{
|
2022-04-01 20:21:09 +02:00
|
|
|
CLSID_TaskbarList, IID_ITaskbarList, IID_ITaskbarList2, ITaskbarList, ITaskbarList2,
|
|
|
|
|
};
|
2021-11-17 18:33:44 +01:00
|
|
|
use crate::platform_impl::platform::dpi::{
|
|
|
|
|
dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_dpi,
|
2024-04-26 19:11:44 +04:00
|
|
|
};
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::platform_impl::platform::drop_handler::FileDropHandler;
|
2024-01-31 17:29:59 +04:00
|
|
|
use crate::platform_impl::platform::event_loop::{self, ActiveEventLoop, DESTROY_MSG_ID};
|
2024-08-06 18:57:03 +02:00
|
|
|
use crate::platform_impl::platform::icon::{self, IconType};
|
2022-05-07 05:29:25 +03:00
|
|
|
use crate::platform_impl::platform::ime::ImeContext;
|
2023-05-28 20:02:59 +02:00
|
|
|
use crate::platform_impl::platform::keyboard::KeyEventBuilder;
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::platform_impl::platform::window_state::{
|
|
|
|
|
CursorFlags, SavedWindow, WindowFlags, WindowState,
|
2024-04-26 19:11:44 +04:00
|
|
|
};
|
2024-10-08 15:29:40 +02:00
|
|
|
use crate::platform_impl::platform::{monitor, util, Fullscreen, SelectedCursor};
|
2022-11-29 12:03:51 +02:00
|
|
|
use crate::window::{
|
2024-08-23 23:40:27 +03:00
|
|
|
CursorGrabMode, Fullscreen as CoreFullscreen, ImePurpose, ResizeDirection, Theme,
|
2024-10-08 15:29:40 +02:00
|
|
|
UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId,
|
|
|
|
|
WindowLevel,
|
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.
|
2022-09-21 10:04:28 +02:00
|
|
|
pub(crate) struct Window {
|
2017-06-26 21:21:13 +02:00
|
|
|
/// Main handle for the window.
|
2023-12-23 17:06:43 +01:00
|
|
|
window: HWND,
|
2017-06-26 21:21:13 +02:00
|
|
|
|
|
|
|
|
/// 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 {
|
2024-01-13 21:36:53 +01:00
|
|
|
pub(crate) fn new(
|
2024-01-31 17:29:59 +04:00
|
|
|
event_loop: &ActiveEventLoop,
|
2018-05-07 17:36:21 -04:00
|
|
|
w_attr: WindowAttributes,
|
2024-09-06 17:20:11 +03:00
|
|
|
) -> Result<Window, RequestError> {
|
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
|
2024-01-17 23:37:28 +01:00
|
|
|
unsafe { init(w_attr, event_loop) }
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn window_state_lock(&self) -> MutexGuard<'_, WindowState> {
|
|
|
|
|
self.window_state.lock().unwrap()
|
2023-08-14 21:19:57 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
/// Returns the `hwnd` of this window.
|
|
|
|
|
pub fn hwnd(&self) -> HWND {
|
|
|
|
|
self.window
|
2023-08-14 21:19:57 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
pub unsafe fn rwh_06_no_thread_check(
|
|
|
|
|
&self,
|
|
|
|
|
) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
|
|
|
|
let mut window_handle = rwh_06::Win32WindowHandle::new(unsafe {
|
|
|
|
|
// SAFETY: Handle will never be zero.
|
|
|
|
|
std::num::NonZeroIsize::new_unchecked(self.window)
|
|
|
|
|
});
|
|
|
|
|
let hinstance = unsafe { super::get_window_long(self.hwnd(), GWLP_HINSTANCE) };
|
|
|
|
|
window_handle.hinstance = std::num::NonZeroIsize::new(hinstance);
|
|
|
|
|
Ok(rwh_06::RawWindowHandle::Win32(window_handle))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
|
|
|
|
// TODO: Write a test once integration framework is ready to ensure that it holds.
|
|
|
|
|
// If we aren't in the GUI thread, we can't return the window.
|
|
|
|
|
if !self.thread_executor.in_event_loop_thread() {
|
|
|
|
|
tracing::error!("tried to access window handle outside of the main thread");
|
|
|
|
|
return Err(rwh_06::HandleError::Unavailable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SAFETY: We are on the correct thread.
|
|
|
|
|
unsafe { self.rwh_06_no_thread_check() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn raw_display_handle_rwh_06(
|
|
|
|
|
&self,
|
|
|
|
|
) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
|
|
|
|
Ok(rwh_06::RawDisplayHandle::Windows(rwh_06::WindowsDisplayHandle::new()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_enable(&self, enabled: bool) {
|
|
|
|
|
unsafe { EnableWindow(self.hwnd(), enabled.into()) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_skip_taskbar(&self, skip: bool) {
|
|
|
|
|
self.window_state_lock().skip_taskbar = skip;
|
|
|
|
|
unsafe { set_skip_taskbar(self.hwnd(), skip) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_undecorated_shadow(&self, shadow: bool) {
|
|
|
|
|
let window = self.window;
|
|
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
let _ = &window;
|
|
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
|
|
|
|
f.set(WindowFlags::MARKER_UNDECORATED_SHADOW, shadow)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_system_backdrop(&self, backdrop_type: BackdropType) {
|
|
|
|
|
unsafe {
|
|
|
|
|
DwmSetWindowAttribute(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
DWMWA_SYSTEMBACKDROP_TYPE as u32,
|
|
|
|
|
&(backdrop_type as i32) as *const _ as _,
|
|
|
|
|
mem::size_of::<DWM_SYSTEMBACKDROP_TYPE>() as _,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
|
|
|
|
if let Some(ref taskbar_icon) = taskbar_icon {
|
|
|
|
|
taskbar_icon.inner.set_for_window(self.hwnd(), IconType::Big);
|
|
|
|
|
} else {
|
|
|
|
|
icon::unset_for_window(self.hwnd(), IconType::Big);
|
|
|
|
|
}
|
|
|
|
|
self.window_state_lock().taskbar_icon = taskbar_icon;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn handle_os_dragging(&self, wparam: WPARAM) {
|
|
|
|
|
let window = self.window;
|
|
|
|
|
let window_state = self.window_state.clone();
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
{
|
|
|
|
|
let mut guard = window_state.lock().unwrap();
|
|
|
|
|
if !guard.dragging {
|
|
|
|
|
guard.dragging = true;
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let points = {
|
|
|
|
|
let mut pos = unsafe { mem::zeroed() };
|
|
|
|
|
unsafe { GetCursorPos(&mut pos) };
|
|
|
|
|
pos
|
|
|
|
|
};
|
|
|
|
|
let points = POINTS { x: points.x as i16, y: points.y as i16 };
|
|
|
|
|
|
|
|
|
|
// ReleaseCapture needs to execute on the main thread
|
|
|
|
|
unsafe { ReleaseCapture() };
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
PostMessageW(window, WM_NCLBUTTONDOWN, wparam, &points as *const _ as LPARAM)
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe fn handle_showing_window_menu(&self, position: Position) {
|
|
|
|
|
unsafe {
|
|
|
|
|
let point = {
|
|
|
|
|
let mut point = POINT { x: 0, y: 0 };
|
|
|
|
|
let scale_factor = self.scale_factor();
|
|
|
|
|
let (x, y) = position.to_physical::<i32>(scale_factor).into();
|
|
|
|
|
point.x = x;
|
|
|
|
|
point.y = y;
|
|
|
|
|
if ClientToScreen(self.hwnd(), &mut point) == false.into() {
|
|
|
|
|
warn!(
|
|
|
|
|
"Can't convert client-area coordinates to screen coordinates when showing \
|
|
|
|
|
window menu."
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
point
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// get the current system menu
|
|
|
|
|
let h_menu = GetSystemMenu(self.hwnd(), 0);
|
|
|
|
|
if h_menu == 0 {
|
|
|
|
|
warn!("The corresponding window doesn't have a system menu");
|
|
|
|
|
// This situation should not be treated as an error so just return without showing
|
|
|
|
|
// menu.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn enable(b: bool) -> MENU_ITEM_STATE {
|
|
|
|
|
if b {
|
|
|
|
|
MFS_ENABLED
|
|
|
|
|
} else {
|
|
|
|
|
MFS_DISABLED
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Change the menu items according to the current window status.
|
|
|
|
|
|
|
|
|
|
let restore_btn = enable(self.is_maximized() && self.is_resizable());
|
|
|
|
|
let size_btn = enable(!self.is_maximized() && self.is_resizable());
|
|
|
|
|
let maximize_btn = enable(!self.is_maximized() && self.is_resizable());
|
|
|
|
|
|
|
|
|
|
EnableMenuItem(h_menu, SC_RESTORE, MF_BYCOMMAND | restore_btn);
|
|
|
|
|
EnableMenuItem(h_menu, SC_MOVE, MF_BYCOMMAND | enable(!self.is_maximized()));
|
|
|
|
|
EnableMenuItem(h_menu, SC_SIZE, MF_BYCOMMAND | size_btn);
|
|
|
|
|
EnableMenuItem(h_menu, SC_MINIMIZE, MF_BYCOMMAND | MFS_ENABLED);
|
|
|
|
|
EnableMenuItem(h_menu, SC_MAXIMIZE, MF_BYCOMMAND | maximize_btn);
|
|
|
|
|
EnableMenuItem(h_menu, SC_CLOSE, MF_BYCOMMAND | MFS_ENABLED);
|
|
|
|
|
|
|
|
|
|
// Set the default menu item.
|
|
|
|
|
SetMenuDefaultItem(h_menu, SC_CLOSE, 0);
|
|
|
|
|
|
|
|
|
|
// Popup the system menu at the position.
|
|
|
|
|
let result = TrackPopupMenu(
|
|
|
|
|
h_menu,
|
|
|
|
|
TPM_RETURNCMD | TPM_LEFTALIGN, /* for now im using LTR, but we have to use user
|
|
|
|
|
* layout direction */
|
|
|
|
|
point.x,
|
|
|
|
|
point.y,
|
|
|
|
|
0,
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
std::ptr::null_mut(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if result == 0 {
|
|
|
|
|
// User canceled the menu, no need to continue.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send the command that the user select to the corresponding window.
|
|
|
|
|
if PostMessageW(self.hwnd(), WM_SYSCOMMAND, result as _, 0) == 0 {
|
|
|
|
|
warn!("Can't post the system menu message to the window.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_border_color(&self, color: Color) {
|
|
|
|
|
unsafe {
|
|
|
|
|
DwmSetWindowAttribute(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
DWMWA_BORDER_COLOR as u32,
|
|
|
|
|
&color as *const _ as _,
|
|
|
|
|
mem::size_of::<Color>() as _,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_title_background_color(&self, color: Color) {
|
|
|
|
|
unsafe {
|
|
|
|
|
DwmSetWindowAttribute(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
DWMWA_CAPTION_COLOR as u32,
|
|
|
|
|
&color as *const _ as _,
|
|
|
|
|
mem::size_of::<Color>() as _,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_title_text_color(&self, color: Color) {
|
|
|
|
|
unsafe {
|
|
|
|
|
DwmSetWindowAttribute(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
DWMWA_TEXT_COLOR as u32,
|
|
|
|
|
&color as *const _ as _,
|
|
|
|
|
mem::size_of::<Color>() as _,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_corner_preference(&self, preference: CornerPreference) {
|
|
|
|
|
unsafe {
|
|
|
|
|
DwmSetWindowAttribute(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
DWMWA_WINDOW_CORNER_PREFERENCE as u32,
|
|
|
|
|
&(preference as DWM_WINDOW_CORNER_PREFERENCE) as *const _ as _,
|
|
|
|
|
mem::size_of::<DWM_WINDOW_CORNER_PREFERENCE>() as _,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for Window {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
// Restore fullscreen video mode on exit.
|
|
|
|
|
if matches!(self.fullscreen(), Some(CoreFullscreen::Exclusive(_))) {
|
|
|
|
|
self.set_fullscreen(None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
// 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.
|
|
|
|
|
PostMessageW(self.hwnd(), DESTROY_MSG_ID.get(), 0, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl rwh_06::HasDisplayHandle for Window {
|
|
|
|
|
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
|
|
|
|
let raw = self.raw_display_handle_rwh_06()?;
|
|
|
|
|
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
2022-08-31 18:32:19 +02:00
|
|
|
}
|
2024-08-23 23:40:27 +03:00
|
|
|
}
|
2022-08-31 18:32:19 +02:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
impl rwh_06::HasWindowHandle for Window {
|
|
|
|
|
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
|
|
|
|
let raw = self.raw_window_handle_rwh_06()?;
|
|
|
|
|
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CoreWindow for Window {
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_transparent(&self, transparent: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2023-10-28 02:22:45 +04:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
let _ = &window;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
2023-10-28 02:22:45 +04:00
|
|
|
f.set(WindowFlags::TRANSPARENT, transparent)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-01-15 23:39:36 +03:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_blur(&self, _blur: bool) {}
|
2023-10-08 22:53:15 +03:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_visible(&self, visible: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
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;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
2019-12-29 10:39:15 -05:00
|
|
|
f.set(WindowFlags::VISIBLE, visible)
|
|
|
|
|
});
|
|
|
|
|
});
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn is_visible(&self) -> Option<bool> {
|
2023-12-23 17:06:43 +01:00
|
|
|
Some(unsafe { IsWindowVisible(self.window) == 1 })
|
2022-02-17 20:44:14 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn request_redraw(&self) {
|
2023-10-20 14:52:01 +04:00
|
|
|
// NOTE: mark that we requested a redraw to handle requests during `WM_PAINT` handling.
|
|
|
|
|
self.window_state.lock().unwrap().redraw_requested = true;
|
2019-02-05 10:30:33 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn pre_present_notify(&self) {}
|
2023-06-22 08:08:53 +04:00
|
|
|
|
2024-09-06 17:20:11 +03:00
|
|
|
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
2022-08-15 02:36:37 +02:00
|
|
|
util::WindowArea::Outer
|
|
|
|
|
.get_rect(self.hwnd())
|
2022-12-22 21:35:33 +02:00
|
|
|
.map(|rect| Ok(PhysicalPosition::new(rect.left, rect.top)))
|
2024-03-28 11:45:34 -07:00
|
|
|
.expect(
|
|
|
|
|
"Unexpected GetWindowRect failure; please report this error to \
|
|
|
|
|
rust-windowing/winit",
|
|
|
|
|
)
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-21 17:37:03 +01:00
|
|
|
fn surface_position(&self) -> PhysicalPosition<i32> {
|
|
|
|
|
let mut rect: RECT = unsafe { mem::zeroed() };
|
|
|
|
|
if unsafe { GetClientRect(self.hwnd(), &mut rect) } == false.into() {
|
2024-03-28 11:45:34 -07:00
|
|
|
panic!(
|
2024-11-21 17:37:03 +01:00
|
|
|
"Unexpected GetClientRect failure: please report this error to \
|
2024-03-28 11:45:34 -07:00
|
|
|
rust-windowing/winit"
|
|
|
|
|
)
|
2018-04-16 21:40:30 -04:00
|
|
|
}
|
2024-11-21 17:37:03 +01:00
|
|
|
PhysicalPosition::new(rect.left, rect.top)
|
2018-04-16 21:40:30 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
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);
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2019-06-19 16:49:43 -04:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
fn surface_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() {
|
2024-03-28 11:45:34 -07:00
|
|
|
panic!(
|
|
|
|
|
"Unexpected GetClientRect failure: please report this error to \
|
|
|
|
|
rust-windowing/winit"
|
|
|
|
|
)
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2018-06-14 19:42:18 -04:00
|
|
|
PhysicalSize::new((rect.right - rect.left) as u32, (rect.bottom - rect.top) as u32)
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
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| {
|
|
|
|
|
PhysicalSize::new((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
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
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
|
|
|
|
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);
|
2023-07-29 16:22:28 +02:00
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
if physical_size != self.surface_size() {
|
2023-07-29 16:22:28 +02:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2023-07-29 16:22:28 +02:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
let _ = &window;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
2023-07-29 16:22:28 +02:00
|
|
|
f.set(WindowFlags::MAXIMIZED, false)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-10 04:02:26 +00:00
|
|
|
None
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
2018-03-23 05:35:35 -04:00
|
|
|
|
2024-11-21 17:37:03 +01:00
|
|
|
fn safe_area(&self) -> PhysicalInsets<u32> {
|
|
|
|
|
PhysicalInsets::new(0, 0, 0, 0)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
fn set_min_surface_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.
|
2024-09-04 15:04:48 +02:00
|
|
|
let size = self.surface_size();
|
|
|
|
|
let _ = self.request_surface_size(size.into());
|
2018-06-14 19:42:18 -04:00
|
|
|
}
|
|
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
fn set_max_surface_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.
|
2024-09-04 15:04:48 +02:00
|
|
|
let size = self.surface_size();
|
|
|
|
|
let _ = self.request_surface_size(size.into());
|
2018-03-23 05:35:35 -04:00
|
|
|
}
|
2018-06-12 11:58:18 -04:00
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
2024-04-21 13:05:41 +00:00
|
|
|
let w = self.window_state_lock();
|
|
|
|
|
let scale_factor = w.scale_factor;
|
2024-09-04 15:04:48 +02:00
|
|
|
w.surface_resize_increments.map(|size| size.to_physical(scale_factor))
|
2022-09-03 21:50:22 +03:00
|
|
|
}
|
|
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
fn set_surface_resize_increments(&self, increments: Option<Size>) {
|
|
|
|
|
self.window_state_lock().surface_resize_increments = increments;
|
2024-04-21 13:05:41 +00:00
|
|
|
}
|
2022-09-03 21:50:22 +03:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_resizable(&self, resizable: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2019-02-04 11:52:00 -05:00
|
|
|
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;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |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
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_enabled_buttons(&self, buttons: WindowButtons) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2022-11-29 12:03:51 +02:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
|
|
|
|
let _ = &window;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
2022-11-29 12:03:51 +02:00
|
|
|
f.set(WindowFlags::MINIMIZABLE, buttons.contains(WindowButtons::MINIMIZE));
|
|
|
|
|
f.set(WindowFlags::MAXIMIZABLE, buttons.contains(WindowButtons::MAXIMIZE));
|
|
|
|
|
f.set(WindowFlags::CLOSABLE, buttons.contains(WindowButtons::CLOSE))
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn enabled_buttons(&self) -> WindowButtons {
|
2022-11-29 12:03:51 +02:00
|
|
|
let mut buttons = WindowButtons::empty();
|
|
|
|
|
let window_state = self.window_state_lock();
|
|
|
|
|
if window_state.window_flags.contains(WindowFlags::MINIMIZABLE) {
|
|
|
|
|
buttons |= WindowButtons::MINIMIZE;
|
|
|
|
|
}
|
|
|
|
|
if window_state.window_flags.contains(WindowFlags::MAXIMIZABLE) {
|
2024-08-23 23:40:27 +03:00
|
|
|
buttons |= WindowButtons::MAXIMIZE;
|
2024-04-26 09:28:10 -07:00
|
|
|
}
|
2024-08-23 23:40:27 +03:00
|
|
|
if window_state.window_flags.contains(WindowFlags::CLOSABLE) {
|
|
|
|
|
buttons |= WindowButtons::CLOSE;
|
|
|
|
|
}
|
|
|
|
|
buttons
|
2019-08-14 07:57:16 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_cursor(&self, cursor: Cursor) {
|
2023-12-25 07:20:52 +01:00
|
|
|
match cursor {
|
|
|
|
|
Cursor::Icon(icon) => {
|
|
|
|
|
self.window_state_lock().mouse.selected_cursor = SelectedCursor::Named(icon);
|
|
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
|
|
|
|
let cursor = LoadCursorW(0, util::to_windows_cursor(icon));
|
|
|
|
|
SetCursor(cursor);
|
|
|
|
|
});
|
2023-12-16 22:02:17 +02:00
|
|
|
},
|
2023-12-25 07:20:52 +01:00
|
|
|
Cursor::Custom(cursor) => {
|
|
|
|
|
self.window_state_lock().mouse.selected_cursor =
|
2024-08-06 18:57:03 +02:00
|
|
|
SelectedCursor::Custom(cursor.inner.0.clone());
|
2023-12-25 07:20:52 +01:00
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
2024-08-06 18:57:03 +02:00
|
|
|
SetCursor(cursor.inner.0.as_raw_handle());
|
2023-12-25 07:20:52 +01:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
}
|
2023-12-16 22:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:20:11 +03:00
|
|
|
fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), RequestError> {
|
2022-06-13 09:43:14 +03:00
|
|
|
let confine = match mode {
|
|
|
|
|
CursorGrabMode::None => false,
|
|
|
|
|
CursorGrabMode::Confined => true,
|
|
|
|
|
CursorGrabMode::Locked => {
|
2024-09-06 17:20:11 +03:00
|
|
|
return Err(NotSupportedError::new("locked cursor is not supported").into())
|
2022-06-13 09:43:14 +03:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
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
|
2023-12-23 17:06:43 +01:00
|
|
|
.set_cursor_flags(window, |f| f.set(CursorFlags::GRABBED, confine))
|
2024-09-06 17:20:11 +03:00
|
|
|
.map_err(|err| os_error!(err).into());
|
2018-06-18 12:32:18 -04:00
|
|
|
let _ = tx.send(result);
|
|
|
|
|
});
|
2024-09-06 17:20:11 +03:00
|
|
|
|
2018-06-18 12:32:18 -04:00
|
|
|
rx.recv().unwrap()
|
|
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_cursor_visible(&self, visible: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
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
|
2023-12-23 17:06:43 +01:00
|
|
|
.set_cursor_flags(window, |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
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:20:11 +03:00
|
|
|
fn set_cursor_position(&self, position: Position) -> Result<(), RequestError> {
|
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() {
|
2024-09-06 17:20:11 +03:00
|
|
|
return Err(os_error!(io::Error::last_os_error()).into());
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2022-03-07 22:58:12 +01:00
|
|
|
if SetCursorPos(point.x, point.y) == false.into() {
|
2024-09-06 17:20:11 +03:00
|
|
|
return Err(os_error!(io::Error::last_os_error()).into());
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:20:11 +03:00
|
|
|
fn drag_window(&self) -> Result<(), RequestError> {
|
2021-03-07 10:43:23 +01:00
|
|
|
unsafe {
|
2023-07-21 20:01:56 +02:00
|
|
|
self.handle_os_dragging(HTCAPTION as WPARAM);
|
2021-03-07 10:43:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:20:11 +03:00
|
|
|
fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), RequestError> {
|
2023-07-21 20:01:56 +02:00
|
|
|
unsafe {
|
|
|
|
|
self.handle_os_dragging(match direction {
|
|
|
|
|
ResizeDirection::East => HTRIGHT,
|
|
|
|
|
ResizeDirection::North => HTTOP,
|
|
|
|
|
ResizeDirection::NorthEast => HTTOPRIGHT,
|
|
|
|
|
ResizeDirection::NorthWest => HTTOPLEFT,
|
|
|
|
|
ResizeDirection::South => HTBOTTOM,
|
|
|
|
|
ResizeDirection::SouthEast => HTBOTTOMRIGHT,
|
|
|
|
|
ResizeDirection::SouthWest => HTBOTTOMLEFT,
|
|
|
|
|
ResizeDirection::West => HTLEFT,
|
|
|
|
|
} as WPARAM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
2023-01-11 18:07:09 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn show_window_menu(&self, position: Position) {
|
2023-10-11 01:16:16 +03:30
|
|
|
unsafe {
|
|
|
|
|
self.handle_showing_window_menu(position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:20:11 +03:00
|
|
|
fn set_cursor_hittest(&self, hittest: bool) -> Result<(), RequestError> {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2022-04-12 19:10:46 +02:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
2022-04-12 19:10:46 +02:00
|
|
|
f.set(WindowFlags::IGNORE_CURSOR_EVENT, !hittest)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-08 15:29:40 +02:00
|
|
|
fn id(&self) -> WindowId {
|
|
|
|
|
WindowId::from_raw(self.hwnd() as usize)
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
2017-08-28 01:43:34 +01:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_minimized(&self, minimized: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2019-12-22 01:04:11 -05:00
|
|
|
let window_state = Arc::clone(&self.window_state);
|
|
|
|
|
|
2023-01-19 23:39:04 +02:00
|
|
|
let is_minimized = util::is_minimized(self.hwnd());
|
2023-01-17 02:22:52 +02:00
|
|
|
|
2019-12-22 01:04:11 -05:00
|
|
|
self.thread_executor.execute_in_thread(move || {
|
2022-01-13 16:59:57 +11:00
|
|
|
let _ = &window;
|
2023-01-17 02:22:52 +02:00
|
|
|
WindowState::set_window_flags_in_place(&mut window_state.lock().unwrap(), |f| {
|
|
|
|
|
f.set(WindowFlags::MINIMIZED, is_minimized)
|
|
|
|
|
});
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
2019-12-22 01:04:11 -05:00
|
|
|
f.set(WindowFlags::MINIMIZED, minimized)
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn is_minimized(&self) -> Option<bool> {
|
2023-01-19 23:39:04 +02:00
|
|
|
Some(util::is_minimized(self.hwnd()))
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_maximized(&self, maximized: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
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;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |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
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn fullscreen(&self) -> Option<CoreFullscreen> {
|
2022-08-31 18:32:19 +02:00
|
|
|
let window_state = self.window_state_lock();
|
2024-08-23 23:40:27 +03:00
|
|
|
window_state.fullscreen.clone().map(Into::into)
|
2019-04-26 03:09:32 +10:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_fullscreen(&self, fullscreen: Option<CoreFullscreen>) {
|
|
|
|
|
let fullscreen = fullscreen.map(Into::into);
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2019-07-29 21:16:14 +03:00
|
|
|
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();
|
2023-12-16 18:34:18 +02:00
|
|
|
|
|
|
|
|
match (&old_fullscreen, &fullscreen) {
|
|
|
|
|
// Return if we already are in the same fullscreen mode
|
|
|
|
|
_ if old_fullscreen == fullscreen => return,
|
|
|
|
|
// Return if saved Borderless(monitor) is the same as current monitor when requested
|
|
|
|
|
// fullscreen is Borderless(None)
|
|
|
|
|
(Some(Fullscreen::Borderless(Some(monitor))), Some(Fullscreen::Borderless(None)))
|
2023-12-23 17:06:43 +01:00
|
|
|
if *monitor == monitor::current_monitor(window) =>
|
2023-12-16 18:34:18 +02:00
|
|
|
{
|
|
|
|
|
return
|
|
|
|
|
},
|
|
|
|
|
_ => {},
|
2019-07-29 21:16:14 +03:00
|
|
|
}
|
2023-12-16 18:34:18 +02:00
|
|
|
|
2024-03-27 01:20:21 -07:00
|
|
|
window_state_lock.fullscreen.clone_from(&fullscreen);
|
2019-07-29 21:16:14 +03:00
|
|
|
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-09-21 10:04:28 +02:00
|
|
|
let monitor_info = monitor::get_monitor_info(monitor.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-09-21 10:04:28 +02:00
|
|
|
&*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
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |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 {
|
2023-12-23 17:06:43 +01:00
|
|
|
taskbar_mark_fullscreen(window, fullscreen.is_some());
|
2022-03-26 16:43:13 +01:00
|
|
|
}
|
|
|
|
|
|
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();
|
2023-12-23 17:06:43 +01:00
|
|
|
GetWindowPlacement(window, &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(),
|
2023-12-23 17:06:43 +01:00
|
|
|
Fullscreen::Borderless(None) => monitor::current_monitor(window),
|
2019-07-29 21:16:14 +03:00
|
|
|
};
|
|
|
|
|
|
2024-07-23 19:59:37 +02:00
|
|
|
let position: (i32, i32) = monitor.position().unwrap_or_default().into();
|
2019-07-29 21:16:14 +03:00
|
|
|
let size: (u32, u32) = monitor.size().into();
|
|
|
|
|
|
|
|
|
|
unsafe {
|
2022-03-07 22:58:12 +01:00
|
|
|
SetWindowPos(
|
2023-12-23 17:06:43 +01:00
|
|
|
window,
|
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
|
|
|
);
|
2023-12-23 17:06:43 +01:00
|
|
|
InvalidateRgn(window, 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 {
|
2023-12-23 17:06:43 +01:00
|
|
|
SetWindowPlacement(window, &placement);
|
|
|
|
|
InvalidateRgn(window, 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
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_decorations(&self, decorations: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2019-02-04 11:52:00 -05:00
|
|
|
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;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |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
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_window_level(&self, level: WindowLevel) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2019-02-04 11:52:00 -05:00
|
|
|
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;
|
2023-12-23 17:06:43 +01:00
|
|
|
WindowState::set_window_flags(window_state.lock().unwrap(), window, |f| {
|
2022-11-26 03:50:58 +02:00
|
|
|
f.set(WindowFlags::ALWAYS_ON_TOP, level == WindowLevel::AlwaysOnTop);
|
|
|
|
|
f.set(WindowFlags::ALWAYS_ON_BOTTOM, level == WindowLevel::AlwaysOnBottom);
|
2019-06-21 11:33:15 -04:00
|
|
|
});
|
2019-02-04 11:52:00 -05:00
|
|
|
});
|
2018-05-20 10:24:05 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
|
|
|
|
Some(CoreMonitorHandle { inner: monitor::current_monitor(self.hwnd()) })
|
2017-08-28 01:43:34 +01:00
|
|
|
}
|
2018-05-07 17:36:21 -04:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
|
|
|
|
Box::new(monitor::available_monitors().into_iter().map(|inner| CoreMonitorHandle { inner }))
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
|
|
|
|
Some(CoreMonitorHandle { inner: monitor::primary_monitor() })
|
2021-12-01 12:20:56 +01:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_window_icon(&self, window_icon: Option<Icon>) {
|
|
|
|
|
if let Some(ref window_icon) = window_icon {
|
|
|
|
|
window_icon.inner.set_for_window(self.hwnd(), IconType::Small);
|
2018-05-07 17:36:21 -04:00
|
|
|
} else {
|
2024-08-23 23:40:27 +03:00
|
|
|
icon::unset_for_window(self.hwnd(), IconType::Small);
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
2024-08-23 23:40:27 +03:00
|
|
|
self.window_state_lock().window_icon = window_icon;
|
2018-05-07 17:36:21 -04:00
|
|
|
}
|
2018-05-17 21:28:30 -04:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_ime_cursor_area(&self, spot: Position, size: Size) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2023-10-20 15:46:57 +04:00
|
|
|
let state = self.window_state.clone();
|
|
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
|
|
|
|
let scale_factor = state.lock().unwrap().scale_factor;
|
2023-12-23 17:06:43 +01:00
|
|
|
ImeContext::current(window).set_ime_cursor_area(spot, size, scale_factor);
|
2023-10-20 15:46:57 +04:00
|
|
|
});
|
2020-12-10 05:16:59 +09:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_ime_allowed(&self, allowed: bool) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2023-10-20 15:46:57 +04:00
|
|
|
let state = self.window_state.clone();
|
|
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
|
|
|
|
state.lock().unwrap().ime_allowed = allowed;
|
2023-12-23 17:06:43 +01:00
|
|
|
ImeContext::set_ime_allowed(window, allowed);
|
2023-10-20 15:46:57 +04:00
|
|
|
})
|
2018-05-17 21:28:30 -04:00
|
|
|
}
|
2019-12-22 19:04:09 +00:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
2023-01-29 16:46:46 +01:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
2023-12-23 17:06:43 +01:00
|
|
|
let window = self.window;
|
2022-03-07 22:58:12 +01:00
|
|
|
let active_window_handle = unsafe { GetActiveWindow() };
|
2023-12-23 17:06:43 +01:00
|
|
|
if window == active_window_handle {
|
2020-11-27 03:03:08 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.thread_executor.execute_in_thread(move || unsafe {
|
|
|
|
|
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,
|
2023-12-23 17:06:43 +01:00
|
|
|
hwnd: window,
|
2020-11-27 03:03:08 +01:00
|
|
|
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
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_theme(&self, theme: Option<Theme>) {
|
2023-12-23 17:06:43 +01:00
|
|
|
try_theme(self.window, theme);
|
2022-11-29 11:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn theme(&self) -> Option<Theme> {
|
2022-10-19 03:34:36 +09:00
|
|
|
Some(self.window_state_lock().current_theme)
|
2019-12-22 19:04:09 +00:00
|
|
|
}
|
2021-05-19 18:39:53 +02:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn has_focus(&self) -> bool {
|
2023-01-17 03:30:14 +02:00
|
|
|
let window_state = self.window_state.lock().unwrap();
|
|
|
|
|
window_state.has_active_focus()
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn title(&self) -> String {
|
2023-12-23 17:06:43 +01:00
|
|
|
let len = unsafe { GetWindowTextLengthW(self.window) } + 1;
|
2022-11-03 10:11:37 -07:00
|
|
|
let mut buf = vec![0; len as usize];
|
2023-12-23 17:06:43 +01:00
|
|
|
unsafe { GetWindowTextW(self.window, buf.as_mut_ptr(), len) };
|
2022-11-03 10:11:37 -07:00
|
|
|
util::decode_wide(&buf).to_string_lossy().to_string()
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-01 20:21:09 +02:00
|
|
|
#[inline]
|
2024-08-23 23:40:27 +03:00
|
|
|
fn focus_window(&self) {
|
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);
|
2023-01-19 23:39:04 +02:00
|
|
|
let is_minimized = util::is_minimized(self.hwnd());
|
2023-12-23 17:06:43 +01:00
|
|
|
let is_foreground = self.window == unsafe { GetForegroundWindow() };
|
2021-05-19 18:39:53 +02:00
|
|
|
|
|
|
|
|
if is_visible && !is_minimized && !is_foreground {
|
2023-12-23 17:06:43 +01:00
|
|
|
unsafe { force_window_active(self.window) };
|
2021-05-19 18:39:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-23 15:51:34 +02:00
|
|
|
|
|
|
|
|
#[inline]
|
2024-08-23 23:40:27 +03:00
|
|
|
fn set_content_protected(&self, protected: bool) {
|
2022-11-23 15:51:34 +02:00
|
|
|
unsafe {
|
|
|
|
|
SetWindowDisplayAffinity(
|
|
|
|
|
self.hwnd(),
|
|
|
|
|
if protected { WDA_EXCLUDEFROMCAPTURE } else { WDA_NONE },
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
}
|
2023-05-28 20:02:59 +02:00
|
|
|
|
|
|
|
|
#[inline]
|
2024-08-23 23:40:27 +03:00
|
|
|
fn reset_dead_keys(&self) {
|
2023-05-28 20:02:59 +02:00
|
|
|
// `ToUnicode` consumes the dead-key by default, so we are constructing a fake (but valid)
|
|
|
|
|
// key input which we can call `ToUnicode` with.
|
|
|
|
|
unsafe {
|
|
|
|
|
let vk = VK_SPACE as VIRTUAL_KEY;
|
|
|
|
|
let scancode = MapVirtualKeyW(vk as u32, MAPVK_VK_TO_VSC);
|
|
|
|
|
let kbd_state = [0; 256];
|
|
|
|
|
let mut char_buff = [MaybeUninit::uninit(); 8];
|
|
|
|
|
ToUnicode(
|
|
|
|
|
vk as u32,
|
|
|
|
|
scancode,
|
|
|
|
|
kbd_state.as_ptr(),
|
|
|
|
|
char_buff[0].as_mut_ptr(),
|
|
|
|
|
char_buff.len() as i32,
|
|
|
|
|
0,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-19 12:43:39 +01:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
|
|
|
|
self
|
2024-01-19 12:43:39 +01:00
|
|
|
}
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2024-08-23 23:40:27 +03:00
|
|
|
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
|
|
|
|
self
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-13 21:36:53 +01:00
|
|
|
pub(super) struct InitData<'a> {
|
2021-07-16 12:40:48 +02:00
|
|
|
// inputs
|
2024-01-31 17:29:59 +04:00
|
|
|
pub event_loop: &'a ActiveEventLoop,
|
2021-11-17 18:33:44 +01:00
|
|
|
pub attributes: WindowAttributes,
|
|
|
|
|
pub window_flags: WindowFlags,
|
2021-07-16 12:40:48 +02:00
|
|
|
// outputs
|
|
|
|
|
pub window: Option<Window>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-13 21:36:53 +01:00
|
|
|
impl<'a> InitData<'a> {
|
2021-11-17 18:33:44 +01:00
|
|
|
unsafe fn create_window(&self, window: HWND) -> Window {
|
|
|
|
|
// Register for touch events if applicable
|
|
|
|
|
{
|
2023-09-29 16:07:44 +02:00
|
|
|
let digitizer = unsafe { GetSystemMetrics(SM_DIGITIZER) as u32 };
|
2022-03-07 22:58:12 +01:00
|
|
|
if digitizer & NID_READY != 0 {
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { RegisterTouchWindow(window, TWF_WANTPALM) };
|
2021-11-17 18:33:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-29 16:07:44 +02:00
|
|
|
let dpi = unsafe { hwnd_dpi(window) };
|
2021-11-17 18:33:44 +01:00
|
|
|
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).
|
2022-10-19 03:34:36 +09:00
|
|
|
let current_theme = try_theme(window, self.attributes.preferred_theme);
|
2021-11-17 18:33:44 +01:00
|
|
|
|
|
|
|
|
let window_state = {
|
|
|
|
|
let window_state = WindowState::new(
|
|
|
|
|
&self.attributes,
|
|
|
|
|
scale_factor,
|
|
|
|
|
current_theme,
|
2022-10-19 03:34:36 +09:00
|
|
|
self.attributes.preferred_theme,
|
2021-11-17 18:33:44 +01:00
|
|
|
);
|
|
|
|
|
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);
|
|
|
|
|
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { ImeContext::set_ime_allowed(window, false) };
|
2022-05-07 05:29:25 +03:00
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
Window { window, window_state, thread_executor: self.event_loop.create_thread_executor() }
|
|
|
|
|
}
|
|
|
|
|
|
Windows: Make `EventLoopWindowTarget` independent of UserEvent type (#3061)
* make `EventLoopWindowTarget` independent of UserEvent type
the `EventLoopWindowTarget` is needed for window creation. conceptually,
only `EventLoop` and `EventLoopProxy` need to be parameterized, and all
other parts of the backend should be agnostic about the user event type,
parallel to how `Event<T>` is parameterized, but `WindowEvent` is not.
this change removes the dependency on the type of user events from the
`EventLoopWindowTarget` for the Windows backend, but keep a phantom data
to keep the API intact. to achieve this, I moved the `Receiver` end of
the mpsc channel from `ThreadMsgTargetData` into `EventLoop` itself, so
the `UserEvent` is only passed between `EventLoop` and `EventLoopProxy`,
all other part of the backend just use unit type as a placeholder for
user events.
it's similar to the macos backend where an erased `EventHandler` trait
object is used so all component except `EventLoop` and `EventLoopProxy`
need to be parameterized. however `EventLoop` of the Windows backend
already use an `Box<dyn FnMut>` to wrap the user provided event handler
callback, so no need for an dedicated trait object, I just modified the
wrapper to replace the placeholder user event with real value pulled
from the channel. I find this is the approach which need minimum change
to be made to existing code. but it does the job and could serve as a
starting point to future Windows backend re-works.
* fix CI clippy failure.
* make UserEventPlaceholder a new type instead of alias
* invariance is maintained by top-level EventLoopWindowTarget<T>
this field is transitional and her to keep API compatibility only.
the correct variance and such is already ensured by the top-level
`EventLoopWindowTarget`, just use `PhantomData<T>` here.
2024-01-04 23:47:07 +08:00
|
|
|
unsafe fn create_window_data(&self, win: &Window) -> event_loop::WindowData {
|
2024-01-17 23:37:28 +01:00
|
|
|
let file_drop_handler = if self.attributes.platform_specific.drag_and_drop {
|
2023-09-29 16:07:44 +02:00
|
|
|
let ole_init_result = unsafe { 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(
|
2023-12-23 17:06:43 +01:00
|
|
|
win.window,
|
2024-06-24 13:04:55 +03:00
|
|
|
Box::new(move |event| file_drop_runner.send_event(event)),
|
2021-11-17 18:33:44 +01:00
|
|
|
);
|
|
|
|
|
|
2022-03-07 22:58:12 +01:00
|
|
|
let handler_interface_ptr =
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { &mut (*file_drop_handler.data).interface as *mut _ as *mut c_void };
|
2022-03-07 22:58:12 +01:00
|
|
|
|
2023-12-23 17:06:43 +01:00
|
|
|
assert_eq!(unsafe { RegisterDragDrop(win.window, handler_interface_ptr) }, S_OK);
|
2021-11-17 18:33:44 +01:00
|
|
|
Some(file_drop_handler)
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
event_loop::WindowData {
|
|
|
|
|
window_state: win.window_state.clone(),
|
|
|
|
|
event_loop_runner: self.event_loop.runner_shared.clone(),
|
2023-05-28 20:02:59 +02:00
|
|
|
key_event_builder: KeyEventBuilder::default(),
|
2021-11-17 18:33:44 +01:00
|
|
|
_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(|| {
|
2023-09-29 16:07:44 +02:00
|
|
|
let window = unsafe { self.create_window(window) };
|
|
|
|
|
let window_data = unsafe { 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
|
2024-01-17 23:37:28 +01:00
|
|
|
if self.attributes.transparent && !self.attributes.platform_specific.no_redirection_bitmap {
|
2022-01-03 20:54:31 +08:00
|
|
|
// Empty region for the blur effect, so the window is fully transparent
|
2023-09-29 16:07:44 +02:00
|
|
|
let region = unsafe { CreateRectRgn(0, 0, -1, -1) };
|
2022-01-03 20:54:31 +08:00
|
|
|
|
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
|
|
|
};
|
2023-09-29 16:07:44 +02:00
|
|
|
let hr = unsafe { DwmEnableBlurBehindWindow(win.hwnd(), &bb) };
|
2022-03-07 22:58:12 +01:00
|
|
|
if hr < 0 {
|
2022-01-03 20:54:31 +08:00
|
|
|
warn!("Setting transparent window is failed. HRESULT Code: 0x{:X}", hr);
|
|
|
|
|
}
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { DeleteObject(region) };
|
2022-01-03 20:54:31 +08:00
|
|
|
}
|
|
|
|
|
|
2024-01-17 23:37:28 +01:00
|
|
|
win.set_skip_taskbar(self.attributes.platform_specific.skip_taskbar);
|
2022-11-06 22:30:55 +02:00
|
|
|
win.set_window_icon(self.attributes.window_icon.clone());
|
2024-01-17 23:37:28 +01:00
|
|
|
win.set_taskbar_icon(self.attributes.platform_specific.taskbar_icon.clone());
|
2022-04-01 20:21:09 +02:00
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
let attributes = self.attributes.clone();
|
|
|
|
|
|
2022-11-23 15:51:34 +02:00
|
|
|
if attributes.content_protected {
|
|
|
|
|
win.set_content_protected(true);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-26 19:50:58 +01:00
|
|
|
win.set_cursor(attributes.cursor);
|
|
|
|
|
|
2021-11-17 18:33:44 +01:00
|
|
|
// Set visible before setting the size to ensure the
|
|
|
|
|
// attribute is correctly applied.
|
|
|
|
|
win.set_visible(attributes.visible);
|
|
|
|
|
|
2022-11-29 12:03:51 +02:00
|
|
|
win.set_enabled_buttons(attributes.enabled_buttons);
|
|
|
|
|
|
2024-09-04 15:04:48 +02:00
|
|
|
let size = attributes.surface_size.unwrap_or_else(|| PhysicalSize::new(800, 600).into());
|
2024-01-20 02:43:08 +09:00
|
|
|
let max_size = attributes
|
2024-09-04 15:04:48 +02:00
|
|
|
.max_surface_size
|
2024-01-20 02:43:08 +09:00
|
|
|
.unwrap_or_else(|| PhysicalSize::new(f64::MAX, f64::MAX).into());
|
2024-09-04 15:04:48 +02:00
|
|
|
let min_size =
|
|
|
|
|
attributes.min_surface_size.unwrap_or_else(|| PhysicalSize::new(0, 0).into());
|
2024-01-20 02:43:08 +09:00
|
|
|
let clamped_size = Size::clamp(size, min_size, max_size, win.scale_factor());
|
2024-09-04 15:04:48 +02:00
|
|
|
let _ = win.request_surface_size(clamped_size);
|
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);
|
|
|
|
|
}
|
2024-01-19 12:43:39 +01:00
|
|
|
|
2024-01-25 20:59:10 +03:00
|
|
|
win.set_system_backdrop(self.attributes.platform_specific.backdrop_type);
|
|
|
|
|
|
2024-01-19 12:43:39 +01:00
|
|
|
if let Some(color) = self.attributes.platform_specific.border_color {
|
|
|
|
|
win.set_border_color(color);
|
|
|
|
|
}
|
|
|
|
|
if let Some(color) = self.attributes.platform_specific.title_background_color {
|
|
|
|
|
win.set_title_background_color(color);
|
|
|
|
|
}
|
|
|
|
|
if let Some(color) = self.attributes.platform_specific.title_text_color {
|
|
|
|
|
win.set_title_text_color(color);
|
|
|
|
|
}
|
|
|
|
|
if let Some(corner) = self.attributes.platform_specific.corner_preference {
|
|
|
|
|
win.set_corner_preference(corner);
|
|
|
|
|
}
|
2021-11-17 18:33:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-13 21:36:53 +01:00
|
|
|
unsafe fn init(
|
2020-03-07 19:42:21 +00:00
|
|
|
attributes: WindowAttributes,
|
2024-01-31 17:29:59 +04:00
|
|
|
event_loop: &ActiveEventLoop,
|
2024-09-06 17:20:11 +03:00
|
|
|
) -> Result<Window, RequestError> {
|
2022-03-07 22:58:12 +01:00
|
|
|
let title = util::encode_wide(&attributes.title);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2024-01-17 23:37:28 +01:00
|
|
|
let class_name = util::encode_wide(&attributes.platform_specific.class_name);
|
2024-01-13 21:36:53 +01:00
|
|
|
unsafe { register_window_class(&class_name) };
|
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,
|
2024-01-17 23:37:28 +01:00
|
|
|
attributes.platform_specific.decoration_shadow,
|
2022-08-15 02:36:37 +02:00
|
|
|
);
|
2022-11-26 03:50:58 +02:00
|
|
|
window_flags
|
|
|
|
|
.set(WindowFlags::ALWAYS_ON_TOP, attributes.window_level == WindowLevel::AlwaysOnTop);
|
|
|
|
|
window_flags
|
|
|
|
|
.set(WindowFlags::ALWAYS_ON_BOTTOM, attributes.window_level == WindowLevel::AlwaysOnBottom);
|
2019-06-21 11:33:15 -04:00
|
|
|
window_flags
|
2024-01-17 23:37:28 +01:00
|
|
|
.set(WindowFlags::NO_BACK_BUFFER, attributes.platform_specific.no_redirection_bitmap);
|
2023-01-27 07:08:29 +02:00
|
|
|
window_flags.set(WindowFlags::MARKER_ACTIVATE, attributes.active);
|
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);
|
2022-11-29 12:03:51 +02:00
|
|
|
// Will be changed later using `window.set_enabled_buttons` but we need to set a default here
|
|
|
|
|
// so the diffing later can work.
|
|
|
|
|
window_flags.set(WindowFlags::CLOSABLE, true);
|
2024-01-22 19:55:37 +02:00
|
|
|
window_flags.set(WindowFlags::CLIP_CHILDREN, attributes.platform_specific.clip_children);
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2024-01-17 23:37:28 +01:00
|
|
|
let mut fallback_parent = || match attributes.platform_specific.owner {
|
2023-10-14 19:07:39 -07:00
|
|
|
Some(parent) => {
|
|
|
|
|
window_flags.set(WindowFlags::POPUP, true);
|
|
|
|
|
Some(parent)
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
window_flags.set(WindowFlags::ON_TASKBAR, true);
|
|
|
|
|
None
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-26 20:13:02 +01:00
|
|
|
let parent = match attributes.parent_window.as_ref().map(|handle| handle.0) {
|
2023-10-14 19:07:39 -07:00
|
|
|
Some(rwh_06::RawWindowHandle::Win32(handle)) => {
|
2021-04-10 10:47:19 -03:00
|
|
|
window_flags.set(WindowFlags::CHILD, true);
|
2024-01-17 23:37:28 +01:00
|
|
|
if attributes.platform_specific.menu.is_some() {
|
2021-04-10 10:47:19 -03:00
|
|
|
warn!("Setting a menu on a child window is unsupported");
|
|
|
|
|
}
|
2023-10-14 19:07:39 -07:00
|
|
|
Some(handle.hwnd.get() as HWND)
|
2021-04-10 10:47:19 -03:00
|
|
|
},
|
2022-12-22 08:07:13 +08:00
|
|
|
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on Windows"),
|
2023-10-14 19:07:39 -07:00
|
|
|
None => fallback_parent(),
|
2021-04-10 10:47:19 -03:00
|
|
|
};
|
2021-02-04 22:26:33 +01:00
|
|
|
|
2024-01-17 23:37:28 +01:00
|
|
|
let menu = attributes.platform_specific.menu;
|
2024-01-20 02:43:08 +09:00
|
|
|
let fullscreen = attributes.fullscreen.clone();
|
|
|
|
|
let maximized = attributes.maximized;
|
2021-07-16 12:40:48 +02:00
|
|
|
let mut initdata = InitData { event_loop, attributes, window_flags, 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();
|
2023-09-29 16:07:44 +02:00
|
|
|
let handle = unsafe {
|
|
|
|
|
CreateWindowExW(
|
|
|
|
|
ex_style,
|
|
|
|
|
class_name.as_ptr(),
|
|
|
|
|
title.as_ptr(),
|
|
|
|
|
style,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
CW_USEDEFAULT,
|
|
|
|
|
parent.unwrap_or(0),
|
2024-01-17 23:37:28 +01:00
|
|
|
menu.unwrap_or(0),
|
2023-09-29 16:07:44 +02:00
|
|
|
util::get_instance_handle(),
|
|
|
|
|
&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 {
|
2024-09-06 17:20:11 +03:00
|
|
|
return Err(os_error!(io::Error::last_os_error()).into());
|
2021-07-16 12:40:48 +02:00
|
|
|
}
|
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.
|
2024-01-20 02:43:08 +09:00
|
|
|
let win = initdata.window.unwrap();
|
|
|
|
|
|
|
|
|
|
// Need to set FULLSCREEN or MAXIMIZED after CreateWindowEx
|
|
|
|
|
// This is because if the size is changed in WM_CREATE, the restored size will be stored in that
|
|
|
|
|
// size.
|
|
|
|
|
if fullscreen.is_some() {
|
|
|
|
|
win.set_fullscreen(fullscreen.map(Into::into));
|
|
|
|
|
unsafe { force_window_active(win.window) };
|
|
|
|
|
} else if maximized {
|
|
|
|
|
win.set_maximized(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(win)
|
2021-07-16 12:40:48 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-13 21:36:53 +01:00
|
|
|
unsafe fn register_window_class(class_name: &[u16]) {
|
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,
|
2024-01-13 21:36:53 +01:00
|
|
|
lpfnWndProc: Some(super::event_loop::public_window_callback),
|
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(),
|
2022-11-06 22:30:55 +02:00
|
|
|
hIcon: 0,
|
2022-03-07 22:58:12 +01:00
|
|
|
hCursor: 0, // must be null in order for cursor state to work properly
|
2022-11-28 08:28:14 +11:00
|
|
|
hbrBackground: 0,
|
2017-06-26 21:21:13 +02:00
|
|
|
lpszMenuName: ptr::null(),
|
|
|
|
|
lpszClassName: class_name.as_ptr(),
|
2022-11-06 22:30:55 +02:00
|
|
|
hIconSm: 0,
|
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.
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { RegisterClassExW(&class) };
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-06 16:54:29 +01:00
|
|
|
struct ComInitialized(#[allow(dead_code)] *mut ());
|
2018-04-13 01:12:15 +08:00
|
|
|
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 {
|
2024-04-22 17:21:53 +04:00
|
|
|
CoInitializeEx(ptr::null(), COINIT_APARTMENTTHREADED as u32);
|
2018-04-13 01:12:15 +08:00
|
|
|
ComInitialized(ptr::null_mut())
|
|
|
|
|
}
|
|
|
|
|
};
|
2017-06-26 21:21:13 +02:00
|
|
|
|
2024-03-21 18:55:25 +01:00
|
|
|
static TASKBAR_LIST: Cell<*mut ITaskbarList> = const { Cell::new(ptr::null_mut()) };
|
|
|
|
|
static TASKBAR_LIST2: Cell<*mut ITaskbarList2> = const { 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() {
|
2023-09-29 16:07:44 +02:00
|
|
|
let hr = unsafe {
|
|
|
|
|
CoCreateInstance(
|
|
|
|
|
&CLSID_TaskbarList,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
CLSCTX_ALL,
|
|
|
|
|
&IID_ITaskbarList2,
|
|
|
|
|
&mut task_bar_list2 as *mut _ as *mut _,
|
|
|
|
|
)
|
|
|
|
|
};
|
2023-03-01 23:24:04 +01:00
|
|
|
if hr != S_OK {
|
|
|
|
|
// In visual studio retrieving the taskbar list fails
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-04-13 01:12:15 +08:00
|
|
|
|
2023-09-29 16:07:44 +02:00
|
|
|
let hr_init = unsafe { (*(*task_bar_list2).lpVtbl).parent.HrInit };
|
|
|
|
|
if unsafe { 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();
|
2023-09-29 16:07:44 +02:00
|
|
|
let mark_fullscreen_window = unsafe { (*(*task_bar_list2).lpVtbl).MarkFullscreenWindow };
|
|
|
|
|
unsafe { mark_fullscreen_window(task_bar_list2, handle, fullscreen.into()) };
|
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() {
|
2023-09-29 16:07:44 +02:00
|
|
|
let hr = unsafe {
|
|
|
|
|
CoCreateInstance(
|
|
|
|
|
&CLSID_TaskbarList,
|
|
|
|
|
ptr::null_mut(),
|
|
|
|
|
CLSCTX_ALL,
|
|
|
|
|
&IID_ITaskbarList,
|
|
|
|
|
&mut task_bar_list as *mut _ as *mut _,
|
|
|
|
|
)
|
|
|
|
|
};
|
2023-03-01 23:24:04 +01:00
|
|
|
if hr != S_OK {
|
|
|
|
|
// In visual studio retrieving the taskbar list fails
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-07-22 19:33:22 +02:00
|
|
|
|
2023-09-29 16:07:44 +02:00
|
|
|
let hr_init = unsafe { (*(*task_bar_list).lpVtbl).HrInit };
|
|
|
|
|
if unsafe { hr_init(task_bar_list.cast()) } != S_OK {
|
2022-07-22 19:33:22 +02:00
|
|
|
// 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 {
|
2023-09-29 16:07:44 +02:00
|
|
|
let delete_tab = unsafe { (*(*task_bar_list).lpVtbl).DeleteTab };
|
|
|
|
|
unsafe { delete_tab(task_bar_list, hwnd) };
|
2022-07-22 19:33:22 +02:00
|
|
|
} else {
|
2023-09-29 16:07:44 +02:00
|
|
|
let add_tab = unsafe { (*(*task_bar_list).lpVtbl).AddTab };
|
|
|
|
|
unsafe { add_tab(task_bar_list, hwnd) };
|
2022-07-22 19:33:22 +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
|
2023-09-29 16:07:44 +02:00
|
|
|
let alt_sc = unsafe { MapVirtualKeyW(VK_MENU as u32, MAPVK_VK_TO_VSC) };
|
2022-03-07 22:58:12 +01:00
|
|
|
|
|
|
|
|
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
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { SendInput(inputs.len() as u32, inputs.as_ptr(), mem::size_of::<INPUT>() as i32) };
|
2018-04-13 01:12:15 +08:00
|
|
|
|
2023-09-29 16:07:44 +02:00
|
|
|
unsafe { SetForegroundWindow(handle) };
|
2017-06-26 21:21:13 +02:00
|
|
|
}
|