#![cfg(target_os = "windows")]
use parking_lot::Mutex;
use raw_window_handle::{windows::WindowsHandle, RawWindowHandle};
use std::{
cell::Cell,
ffi::OsStr,
io, mem,
os::windows::ffi::OsStrExt,
ptr,
sync::{mpsc::channel, Arc},
};
use winapi::{
ctypes::c_int,
shared::{
minwindef::{HINSTANCE, UINT},
windef::{HWND, POINT, RECT},
},
um::{
combaseapi, dwmapi,
imm::{CFS_POINT, COMPOSITIONFORM},
libloaderapi,
objbase::COINIT_APARTMENTTHREADED,
ole2,
oleidl::LPDROPTARGET,
shobjidl_core::{CLSID_TaskbarList, ITaskbarList2},
winnt::LPCWSTR,
winuser,
},
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
icon::Icon,
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::platform::{
dark_mode::try_theme,
dpi::{dpi_to_scale_factor, hwnd_dpi},
drop_handler::FileDropHandler,
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
icon::{self, IconType},
monitor, util,
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
PlatformSpecificWindowBuilderAttributes, WindowId,
},
window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes},
};
pub struct Window {
window: WindowWrapper,
window_state: Arc<Mutex<WindowState>>,
thread_executor: event_loop::EventLoopThreadExecutor,
}
impl Window {
pub fn new<T: 'static>(
event_loop: &EventLoopWindowTarget<T>,
w_attr: WindowAttributes,
pl_attr: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, RootOsError> {
unsafe {
let drag_and_drop = pl_attr.drag_and_drop;
init(w_attr, pl_attr, event_loop).map(|win| {
let file_drop_handler = if drag_and_drop {
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK};
let ole_init_result = ole2::OleInitialize(ptr::null_mut());
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 = 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)
}
}),
);
let handler_interface_ptr =
&mut (*file_drop_handler.data).interface as LPDROPTARGET;
assert_eq!(
ole2::RegisterDragDrop(win.window.0, handler_interface_ptr),
S_OK
);
Some(file_drop_handler)
} else {
None
};
let subclass_input = event_loop::SubclassInput {
window_state: win.window_state.clone(),
event_loop_runner: event_loop.runner_shared.clone(),
file_drop_handler,
};
event_loop::subclass_window(win.window.0, subclass_input);
win
})
}
}
pub fn set_title(&self, text: &str) {
let text = OsStr::new(text)
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>();
unsafe {
winuser::SetWindowTextW(self.window.0, text.as_ptr() as LPCWSTR);
}
}
#[inline]
pub fn set_visible(&self, visible: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::VISIBLE, visible)
});
});
}
#[inline]
pub fn request_redraw(&self) {
unsafe {
winuser::RedrawWindow(
self.window.0,
ptr::null(),
ptr::null_mut(),
winuser::RDW_INTERNALPAINT,
);
}
}
#[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
util::get_window_rect(self.window.0)
.map(|rect| Ok(PhysicalPosition::new(rect.left as i32, rect.top as i32)))
.expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit")
}
#[inline]
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let mut position: POINT = unsafe { mem::zeroed() };
if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
panic!("Unexpected ClientToScreen failure: please report this error to https://github.com/rust-windowing/winit")
}
Ok(PhysicalPosition::new(position.x as i32, position.y as i32))
}
#[inline]
pub fn set_outer_position(&self, position: Position) {
let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::MAXIMIZED, false)
});
});
unsafe {
winuser::SetWindowPos(
self.window.0,
ptr::null_mut(),
x as c_int,
y as c_int,
0,
0,
winuser::SWP_ASYNCWINDOWPOS
| winuser::SWP_NOZORDER
| winuser::SWP_NOSIZE
| winuser::SWP_NOACTIVATE,
);
winuser::InvalidateRgn(self.window.0, ptr::null_mut(), 0);
}
}
#[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> {
let mut rect: RECT = unsafe { mem::zeroed() };
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
panic!("Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit")
}
PhysicalSize::new(
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32,
)
}
#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
util::get_window_rect(self.window.0)
.map(|rect| {
PhysicalSize::new(
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32,
)
})
.unwrap()
}
#[inline]
pub fn set_inner_size(&self, size: Size) {
let scale_factor = self.scale_factor();
let (width, height) = size.to_physical::<u32>(scale_factor).into();
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::MAXIMIZED, false)
});
});
util::set_inner_size_physical(self.window.0, width, height);
}
#[inline]
pub fn set_min_inner_size(&self, size: Option<Size>) {
self.window_state.lock().min_size = size;
let size = self.inner_size();
self.set_inner_size(size.into());
}
#[inline]
pub fn set_max_inner_size(&self, size: Option<Size>) {
self.window_state.lock().max_size = size;
let size = self.inner_size();
self.set_inner_size(size.into());
}
#[inline]
pub fn set_resizable(&self, resizable: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::RESIZABLE, resizable)
});
});
}
#[inline]
pub fn hwnd(&self) -> HWND {
self.window.0
}
#[inline]
pub fn hinstance(&self) -> HINSTANCE {
unsafe { winuser::GetWindowLongW(self.hwnd(), winuser::GWL_HINSTANCE) as *mut _ }
}
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let handle = WindowsHandle {
hwnd: self.window.0 as *mut _,
hinstance: self.hinstance() as *mut _,
..WindowsHandle::empty()
};
RawWindowHandle::Windows(handle)
}
#[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
self.window_state.lock().mouse.cursor = cursor;
self.thread_executor.execute_in_thread(move || unsafe {
let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor());
winuser::SetCursor(cursor);
});
}
#[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
let (tx, rx) = channel();
self.thread_executor.execute_in_thread(move || {
let result = window_state
.lock()
.mouse
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab))
.map_err(|e| ExternalError::Os(os_error!(e)));
let _ = tx.send(result);
});
rx.recv().unwrap()
}
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
let (tx, rx) = channel();
self.thread_executor.execute_in_thread(move || {
let result = window_state
.lock()
.mouse
.set_cursor_flags(window.0, |f| f.set(CursorFlags::HIDDEN, !visible))
.map_err(|e| e.to_string());
let _ = tx.send(result);
});
rx.recv().unwrap().ok();
}
#[inline]
pub fn scale_factor(&self) -> f64 {
self.window_state.lock().scale_factor
}
#[inline]
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
let scale_factor = self.scale_factor();
let (x, y) = position.to_physical::<i32>(scale_factor).into();
let mut point = POINT { x, y };
unsafe {
if winuser::ClientToScreen(self.window.0, &mut point) == 0 {
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
}
if winuser::SetCursorPos(point.x, point.y) == 0 {
return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
}
}
Ok(())
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(self.window.0)
}
#[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 || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::MINIMIZED, minimized)
});
});
}
#[inline]
pub fn set_maximized(&self, maximized: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::MAXIMIZED, maximized)
});
});
}
#[inline]
pub fn fullscreen(&self) -> Option<Fullscreen> {
let window_state = self.window_state.lock();
window_state.fullscreen.clone()
}
#[inline]
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
let mut window_state_lock = window_state.lock();
let old_fullscreen = window_state_lock.fullscreen.clone();
if window_state_lock.fullscreen == fullscreen {
return;
}
window_state_lock.fullscreen = fullscreen.clone();
drop(window_state_lock);
self.thread_executor.execute_in_thread(move || {
let mut window_state_lock = window_state.lock();
match (&old_fullscreen, &fullscreen) {
(&None, &Some(_)) => {
let client_rect = util::get_client_rect(window.0).unwrap();
window_state_lock.saved_window = Some(SavedWindow {
client_rect,
scale_factor: window_state_lock.scale_factor,
});
}
_ => (),
}
match (&old_fullscreen, &fullscreen) {
(&None, &Some(Fullscreen::Exclusive(ref video_mode)))
| (
&Some(Fullscreen::Borderless(_)),
&Some(Fullscreen::Exclusive(ref video_mode)),
)
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Exclusive(ref video_mode))) =>
{
let monitor = video_mode.monitor();
let mut display_name = OsStr::new(&monitor.inner.native_identifier())
.encode_wide()
.collect::<Vec<_>>();
display_name.push(0);
let mut native_video_mode = video_mode.video_mode.native_video_mode.clone();
let res = unsafe {
winuser::ChangeDisplaySettingsExW(
display_name.as_ptr(),
&mut native_video_mode,
std::ptr::null_mut(),
winuser::CDS_FULLSCREEN,
std::ptr::null_mut(),
)
};
debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS);
debug_assert!(res != winuser::DISP_CHANGE_BADMODE);
debug_assert!(res != winuser::DISP_CHANGE_BADPARAM);
debug_assert!(res != winuser::DISP_CHANGE_FAILED);
assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL);
}
(&Some(Fullscreen::Exclusive(_)), &None)
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => {
let res = unsafe {
winuser::ChangeDisplaySettingsExW(
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
winuser::CDS_FULLSCREEN,
std::ptr::null_mut(),
)
};
debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS);
debug_assert!(res != winuser::DISP_CHANGE_BADMODE);
debug_assert!(res != winuser::DISP_CHANGE_BADPARAM);
debug_assert!(res != winuser::DISP_CHANGE_FAILED);
assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL);
}
_ => (),
}
unsafe {
let mut msg = mem::zeroed();
winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0);
}
WindowState::set_window_flags(window_state_lock, window.0, |f| {
f.set(
WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN,
matches!(fullscreen, Some(Fullscreen::Exclusive(_))),
);
f.set(
WindowFlags::MARKER_BORDERLESS_FULLSCREEN,
matches!(fullscreen, Some(Fullscreen::Borderless(_))),
);
});
match &fullscreen {
Some(fullscreen) => {
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),
},
};
let position: (i32, i32) = monitor.position().into();
let size: (u32, u32) = monitor.size().into();
unsafe {
winuser::SetWindowPos(
window.0,
ptr::null_mut(),
position.0,
position.1,
size.0 as i32,
size.1 as i32,
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER,
);
winuser::InvalidateRgn(window.0, ptr::null_mut(), 0);
}
}
None => {
let mut window_state_lock = window_state.lock();
if let Some(SavedWindow {
client_rect,
scale_factor,
}) = window_state_lock.saved_window.take()
{
window_state_lock.scale_factor = scale_factor;
drop(window_state_lock);
let client_rect = util::adjust_window_rect(window.0, client_rect).unwrap();
unsafe {
winuser::SetWindowPos(
window.0,
ptr::null_mut(),
client_rect.left,
client_rect.top,
client_rect.right - client_rect.left,
client_rect.bottom - client_rect.top,
winuser::SWP_ASYNCWINDOWPOS
| winuser::SWP_NOZORDER
| winuser::SWP_NOACTIVATE,
);
winuser::InvalidateRgn(window.0, ptr::null_mut(), 0);
}
}
}
}
unsafe {
taskbar_mark_fullscreen(window.0, fullscreen.is_some());
}
});
}
#[inline]
pub fn set_decorations(&self, decorations: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::DECORATIONS, decorations)
});
});
}
#[inline]
pub fn set_always_on_top(&self, always_on_top: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top)
});
});
}
#[inline]
pub fn current_monitor(&self) -> Option<RootMonitorHandle> {
Some(RootMonitorHandle {
inner: monitor::current_monitor(self.window.0),
})
}
#[inline]
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
if let Some(ref window_icon) = window_icon {
window_icon
.inner
.set_for_window(self.window.0, IconType::Small);
} else {
icon::unset_for_window(self.window.0, IconType::Small);
}
self.window_state.lock().window_icon = window_icon;
}
#[inline]
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.window.0, IconType::Big);
} else {
icon::unset_for_window(self.window.0, IconType::Big);
}
self.window_state.lock().taskbar_icon = taskbar_icon;
}
pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) {
if unsafe { winuser::GetSystemMetrics(winuser::SM_IMMENABLED) } != 0 {
let mut composition_form = COMPOSITIONFORM {
dwStyle: CFS_POINT,
ptCurrentPos: POINT { x, y },
rcArea: unsafe { mem::zeroed() },
};
unsafe {
let himc = winapi::um::imm::ImmGetContext(self.window.0);
winapi::um::imm::ImmSetCompositionWindow(himc, &mut composition_form);
winapi::um::imm::ImmReleaseContext(self.window.0, himc);
}
}
}
#[inline]
pub fn set_ime_position(&self, spot: Position) {
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
self.set_ime_position_physical(x, y);
}
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let window = self.window.clone();
let active_window_handle = unsafe { winuser::GetActiveWindow() };
if window.0 == active_window_handle {
return;
}
self.thread_executor.execute_in_thread(move || unsafe {
let (flags, count) = request_type
.map(|ty| match ty {
UserAttentionType::Critical => {
(winuser::FLASHW_ALL | winuser::FLASHW_TIMERNOFG, u32::MAX)
}
UserAttentionType::Informational => {
(winuser::FLASHW_TRAY | winuser::FLASHW_TIMERNOFG, 0)
}
})
.unwrap_or((winuser::FLASHW_STOP, 0));
let mut flash_info = winuser::FLASHWINFO {
cbSize: mem::size_of::<winuser::FLASHWINFO>() as UINT,
hwnd: window.0,
dwFlags: flags,
uCount: count,
dwTimeout: 0,
};
winuser::FlashWindowEx(&mut flash_info);
});
}
#[inline]
pub fn theme(&self) -> Theme {
self.window_state.lock().current_theme
}
}
impl Drop for Window {
#[inline]
fn drop(&mut self) {
unsafe {
winuser::PostMessageW(self.window.0, *DESTROY_MSG_ID, 0, 0);
}
}
}
#[doc(hidden)]
#[derive(Clone)]
pub struct WindowWrapper(HWND);
unsafe impl Sync for WindowWrapper {}
unsafe impl Send for WindowWrapper {}
unsafe fn init<T: 'static>(
attributes: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
event_loop: &EventLoopWindowTarget<T>,
) -> Result<Window, RootOsError> {
let title = OsStr::new(&attributes.title)
.encode_wide()
.chain(Some(0).into_iter())
.collect::<Vec<_>>();
let class_name = register_window_class(&attributes.window_icon, &pl_attribs.taskbar_icon);
let mut window_flags = WindowFlags::empty();
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top);
window_flags.set(
WindowFlags::NO_BACK_BUFFER,
pl_attribs.no_redirection_bitmap,
);
window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent);
window_flags.set(WindowFlags::RESIZABLE, attributes.resizable);
window_flags.set(WindowFlags::CHILD, pl_attribs.parent.is_some());
window_flags.set(WindowFlags::ON_TASKBAR, true);
let real_window = {
let (style, ex_style) = window_flags.to_window_styles();
let handle = winuser::CreateWindowExW(
ex_style,
class_name.as_ptr(),
title.as_ptr() as LPCWSTR,
style,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
pl_attribs.parent.unwrap_or(ptr::null_mut()),
ptr::null_mut(),
libloaderapi::GetModuleHandleW(ptr::null()),
ptr::null_mut(),
);
if handle.is_null() {
return Err(os_error!(io::Error::last_os_error()));
}
WindowWrapper(handle)
};
{
let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;
if digitizer & winuser::NID_READY != 0 {
winuser::RegisterTouchWindow(real_window.0, winuser::TWF_WANTPALM);
}
}
let dpi = hwnd_dpi(real_window.0);
let scale_factor = dpi_to_scale_factor(dpi);
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
let bb = dwmapi::DWM_BLURBEHIND {
dwFlags: dwmapi::DWM_BB_ENABLE,
fEnable: 1,
hRgnBlur: ptr::null_mut(),
fTransitionOnMaximized: 0,
};
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
if attributes.decorations {
let opacity = 255;
winuser::SetLayeredWindowAttributes(real_window.0, 0, opacity, winuser::LWA_ALPHA);
}
}
let current_theme = try_theme(real_window.0, pl_attribs.preferred_theme);
let window_state = {
let window_state = WindowState::new(
&attributes,
pl_attribs.taskbar_icon,
scale_factor,
current_theme,
pl_attribs.preferred_theme,
);
let window_state = Arc::new(Mutex::new(window_state));
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
window_state
};
let win = Window {
window: real_window,
window_state,
thread_executor: event_loop.create_thread_executor(),
};
let dimensions = attributes
.inner_size
.unwrap_or_else(|| PhysicalSize::new(1024, 768).into());
win.set_inner_size(dimensions);
if attributes.maximized {
win.set_maximized(true);
}
win.set_visible(attributes.visible);
if let Some(_) = attributes.fullscreen {
win.set_fullscreen(attributes.fullscreen);
force_window_active(win.window.0);
}
Ok(win)
}
unsafe fn register_window_class(
window_icon: &Option<Icon>,
taskbar_icon: &Option<Icon>,
) -> Vec<u16> {
let class_name: Vec<_> = OsStr::new("Window Class")
.encode_wide()
.chain(Some(0).into_iter())
.collect();
let h_icon = taskbar_icon
.as_ref()
.map(|icon| icon.inner.as_raw_handle())
.unwrap_or(ptr::null_mut());
let h_icon_small = window_icon
.as_ref()
.map(|icon| icon.inner.as_raw_handle())
.unwrap_or(ptr::null_mut());
let class = winuser::WNDCLASSEXW {
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC,
lpfnWndProc: Some(winuser::DefWindowProcW),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
hIcon: h_icon,
hCursor: ptr::null_mut(), hbrBackground: ptr::null_mut(),
lpszMenuName: ptr::null(),
lpszClassName: class_name.as_ptr(),
hIconSm: h_icon_small,
};
winuser::RegisterClassExW(&class);
class_name
}
struct ComInitialized(*mut ());
impl Drop for ComInitialized {
fn drop(&mut self) {
unsafe { combaseapi::CoUninitialize() };
}
}
thread_local! {
static COM_INITIALIZED: ComInitialized = {
unsafe {
combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED);
ComInitialized(ptr::null_mut())
}
};
static TASKBAR_LIST: Cell<*mut ITaskbarList2> = Cell::new(ptr::null_mut());
}
pub fn com_initialized() {
COM_INITIALIZED.with(|_| {});
}
unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) {
com_initialized();
TASKBAR_LIST.with(|task_bar_list_ptr| {
let mut task_bar_list = task_bar_list_ptr.get();
if task_bar_list == ptr::null_mut() {
use winapi::{shared::winerror::S_OK, Interface};
let hr = combaseapi::CoCreateInstance(
&CLSID_TaskbarList,
ptr::null_mut(),
combaseapi::CLSCTX_ALL,
&ITaskbarList2::uuidof(),
&mut task_bar_list as *mut _ as *mut _,
);
if hr != S_OK || (*task_bar_list).HrInit() != S_OK {
return;
}
task_bar_list_ptr.set(task_bar_list)
}
task_bar_list = task_bar_list_ptr.get();
(*task_bar_list).MarkFullscreenWindow(handle, if fullscreen { 1 } else { 0 });
})
}
unsafe fn force_window_active(handle: HWND) {
let alt_sc = winuser::MapVirtualKeyW(winuser::VK_MENU as _, winuser::MAPVK_VK_TO_VSC);
let mut inputs: [winuser::INPUT; 2] = mem::zeroed();
inputs[0].type_ = winuser::INPUT_KEYBOARD;
inputs[0].u.ki_mut().wVk = winuser::VK_LMENU as _;
inputs[0].u.ki_mut().wScan = alt_sc as _;
inputs[0].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY;
inputs[1].type_ = winuser::INPUT_KEYBOARD;
inputs[1].u.ki_mut().wVk = winuser::VK_LMENU as _;
inputs[1].u.ki_mut().wScan = alt_sc as _;
inputs[1].u.ki_mut().dwFlags = winuser::KEYEVENTF_EXTENDEDKEY | winuser::KEYEVENTF_KEYUP;
winuser::SendInput(
inputs.len() as _,
inputs.as_mut_ptr(),
mem::size_of::<winuser::INPUT>() as _,
);
winuser::SetForegroundWindow(handle);
}