2019-06-22 01:33:15 +10:00
|
|
|
use crate::{
|
|
|
|
dpi::LogicalSize,
|
|
|
|
monitor::MonitorHandle,
|
|
|
|
platform_impl::platform::{event_loop, icon::WinIcon, util},
|
|
|
|
window::{CursorIcon, WindowAttributes},
|
|
|
|
};
|
2019-02-06 02:30:33 +11:00
|
|
|
use parking_lot::MutexGuard;
|
2019-06-22 01:33:15 +10:00
|
|
|
use std::{io, ptr};
|
|
|
|
use winapi::{
|
|
|
|
shared::{
|
|
|
|
minwindef::DWORD,
|
|
|
|
windef::{HWND, RECT},
|
|
|
|
},
|
|
|
|
um::winuser,
|
|
|
|
};
|
2019-02-05 03:52:00 +11:00
|
|
|
|
|
|
|
/// Contains information about states and the window that the callback is going to use.
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct WindowState {
|
|
|
|
pub mouse: MouseProperties,
|
|
|
|
|
|
|
|
/// Used by `WM_GETMINMAXINFO`.
|
|
|
|
pub min_size: Option<LogicalSize>,
|
|
|
|
pub max_size: Option<LogicalSize>,
|
|
|
|
|
|
|
|
pub window_icon: Option<WinIcon>,
|
|
|
|
pub taskbar_icon: Option<WinIcon>,
|
|
|
|
|
|
|
|
pub saved_window: Option<SavedWindow>,
|
|
|
|
pub dpi_factor: f64,
|
|
|
|
|
2019-02-06 02:30:33 +11:00
|
|
|
pub fullscreen: Option<MonitorHandle>,
|
2019-02-05 03:52:00 +11:00
|
|
|
window_flags: WindowFlags,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct SavedWindow {
|
|
|
|
pub client_rect: RECT,
|
|
|
|
pub dpi_factor: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct MouseProperties {
|
2019-05-30 11:29:54 +10:00
|
|
|
pub cursor: CursorIcon,
|
2019-02-06 02:30:33 +11:00
|
|
|
pub buttons_down: u32,
|
2019-02-05 03:52:00 +11:00
|
|
|
cursor_flags: CursorFlags,
|
|
|
|
}
|
|
|
|
|
|
|
|
bitflags! {
|
|
|
|
pub struct CursorFlags: u8 {
|
|
|
|
const GRABBED = 1 << 0;
|
|
|
|
const HIDDEN = 1 << 1;
|
|
|
|
const IN_WINDOW = 1 << 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bitflags! {
|
|
|
|
pub struct WindowFlags: u32 {
|
|
|
|
const RESIZABLE = 1 << 0;
|
|
|
|
const DECORATIONS = 1 << 1;
|
|
|
|
const VISIBLE = 1 << 2;
|
|
|
|
const ON_TASKBAR = 1 << 3;
|
|
|
|
const ALWAYS_ON_TOP = 1 << 4;
|
|
|
|
const NO_BACK_BUFFER = 1 << 5;
|
|
|
|
const TRANSPARENT = 1 << 6;
|
|
|
|
const CHILD = 1 << 7;
|
|
|
|
const MAXIMIZED = 1 << 8;
|
|
|
|
|
|
|
|
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
|
|
|
|
/// included here to make masking easier.
|
|
|
|
const MARKER_FULLSCREEN = 1 << 9;
|
|
|
|
|
|
|
|
/// The `WM_SIZE` event contains some parameters that can effect the state of `WindowFlags`.
|
|
|
|
/// In most cases, it's okay to let those parameters change the state. However, when we're
|
|
|
|
/// running the `WindowFlags::apply_diff` function, we *don't* want those parameters to
|
|
|
|
/// effect our stored state, because the purpose of `apply_diff` is to update the actual
|
|
|
|
/// window's state to match our stored state. This controls whether to accept those changes.
|
|
|
|
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 10;
|
|
|
|
|
|
|
|
const FULLSCREEN_AND_MASK = !(
|
|
|
|
WindowFlags::DECORATIONS.bits |
|
|
|
|
WindowFlags::RESIZABLE.bits |
|
|
|
|
WindowFlags::MAXIMIZED.bits
|
|
|
|
);
|
|
|
|
const NO_DECORATIONS_AND_MASK = !WindowFlags::RESIZABLE.bits;
|
|
|
|
const INVISIBLE_AND_MASK = !WindowFlags::MAXIMIZED.bits;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WindowState {
|
|
|
|
pub fn new(
|
|
|
|
attributes: &WindowAttributes,
|
|
|
|
window_icon: Option<WinIcon>,
|
|
|
|
taskbar_icon: Option<WinIcon>,
|
2019-06-22 01:33:15 +10:00
|
|
|
dpi_factor: f64,
|
2019-02-05 03:52:00 +11:00
|
|
|
) -> WindowState {
|
|
|
|
WindowState {
|
|
|
|
mouse: MouseProperties {
|
2019-05-30 11:29:54 +10:00
|
|
|
cursor: CursorIcon::default(),
|
2019-02-06 02:30:33 +11:00
|
|
|
buttons_down: 0,
|
2019-02-05 03:52:00 +11:00
|
|
|
cursor_flags: CursorFlags::empty(),
|
|
|
|
},
|
|
|
|
|
2019-05-30 11:29:54 +10:00
|
|
|
min_size: attributes.min_inner_size,
|
|
|
|
max_size: attributes.max_inner_size,
|
2019-02-05 03:52:00 +11:00
|
|
|
|
|
|
|
window_icon,
|
|
|
|
taskbar_icon,
|
|
|
|
|
|
|
|
saved_window: None,
|
|
|
|
dpi_factor,
|
|
|
|
|
|
|
|
fullscreen: None,
|
2019-06-22 01:33:15 +10:00
|
|
|
window_flags: WindowFlags::empty(),
|
2019-02-05 03:52:00 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn window_flags(&self) -> WindowFlags {
|
|
|
|
self.window_flags
|
|
|
|
}
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
pub fn set_window_flags<F>(
|
|
|
|
mut this: MutexGuard<'_, Self>,
|
|
|
|
window: HWND,
|
|
|
|
set_client_rect: Option<RECT>,
|
|
|
|
f: F,
|
|
|
|
) where
|
|
|
|
F: FnOnce(&mut WindowFlags),
|
2019-02-05 03:52:00 +11:00
|
|
|
{
|
|
|
|
let old_flags = this.window_flags;
|
|
|
|
f(&mut this.window_flags);
|
|
|
|
|
|
|
|
let is_fullscreen = this.fullscreen.is_some();
|
2019-06-22 01:33:15 +10:00
|
|
|
this.window_flags
|
|
|
|
.set(WindowFlags::MARKER_FULLSCREEN, is_fullscreen);
|
2019-02-05 03:52:00 +11:00
|
|
|
let new_flags = this.window_flags;
|
|
|
|
|
|
|
|
drop(this);
|
|
|
|
old_flags.apply_diff(window, new_flags, set_client_rect);
|
|
|
|
}
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
pub fn refresh_window_state(
|
|
|
|
this: MutexGuard<'_, Self>,
|
|
|
|
window: HWND,
|
|
|
|
set_client_rect: Option<RECT>,
|
|
|
|
) {
|
2019-02-05 03:52:00 +11:00
|
|
|
Self::set_window_flags(this, window, set_client_rect, |_| ());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_window_flags_in_place<F>(&mut self, f: F)
|
2019-06-22 01:33:15 +10:00
|
|
|
where
|
|
|
|
F: FnOnce(&mut WindowFlags),
|
2019-02-05 03:52:00 +11:00
|
|
|
{
|
|
|
|
f(&mut self.window_flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MouseProperties {
|
|
|
|
pub fn cursor_flags(&self) -> CursorFlags {
|
|
|
|
self.cursor_flags
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_cursor_flags<F>(&mut self, window: HWND, f: F) -> Result<(), io::Error>
|
2019-06-22 01:33:15 +10:00
|
|
|
where
|
|
|
|
F: FnOnce(&mut CursorFlags),
|
2019-02-05 03:52:00 +11:00
|
|
|
{
|
|
|
|
let old_flags = self.cursor_flags;
|
|
|
|
f(&mut self.cursor_flags);
|
|
|
|
match self.cursor_flags.refresh_os_cursor(window) {
|
|
|
|
Ok(()) => (),
|
|
|
|
Err(e) => {
|
|
|
|
self.cursor_flags = old_flags;
|
|
|
|
return Err(e);
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-05 03:52:00 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WindowFlags {
|
|
|
|
fn mask(mut self) -> WindowFlags {
|
|
|
|
if self.contains(WindowFlags::MARKER_FULLSCREEN) {
|
|
|
|
self &= WindowFlags::FULLSCREEN_AND_MASK;
|
|
|
|
}
|
|
|
|
if !self.contains(WindowFlags::VISIBLE) {
|
|
|
|
self &= WindowFlags::INVISIBLE_AND_MASK;
|
|
|
|
}
|
|
|
|
if !self.contains(WindowFlags::DECORATIONS) {
|
|
|
|
self &= WindowFlags::NO_DECORATIONS_AND_MASK;
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_window_styles(self) -> (DWORD, DWORD) {
|
|
|
|
use winapi::um::winuser::*;
|
|
|
|
|
|
|
|
let (mut style, mut style_ex) = (0, 0);
|
|
|
|
|
|
|
|
if self.contains(WindowFlags::RESIZABLE) {
|
|
|
|
style |= WS_SIZEBOX | WS_MAXIMIZEBOX;
|
|
|
|
}
|
|
|
|
if self.contains(WindowFlags::DECORATIONS) {
|
|
|
|
style |= WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER;
|
|
|
|
style_ex = WS_EX_WINDOWEDGE;
|
|
|
|
}
|
|
|
|
if self.contains(WindowFlags::VISIBLE) {
|
|
|
|
style |= WS_VISIBLE;
|
|
|
|
}
|
|
|
|
if self.contains(WindowFlags::ON_TASKBAR) {
|
|
|
|
style_ex |= WS_EX_APPWINDOW;
|
|
|
|
}
|
|
|
|
if self.contains(WindowFlags::ALWAYS_ON_TOP) {
|
|
|
|
style_ex |= WS_EX_TOPMOST;
|
|
|
|
}
|
|
|
|
if self.contains(WindowFlags::NO_BACK_BUFFER) {
|
|
|
|
style_ex |= WS_EX_NOREDIRECTIONBITMAP;
|
|
|
|
}
|
2019-06-21 13:59:27 +10:00
|
|
|
// if self.contains(WindowFlags::TRANSPARENT) {
|
|
|
|
// }
|
2019-02-05 03:52:00 +11:00
|
|
|
if self.contains(WindowFlags::CHILD) {
|
|
|
|
style |= WS_CHILD; // This is incompatible with WS_POPUP if that gets added eventually.
|
|
|
|
}
|
|
|
|
if self.contains(WindowFlags::MAXIMIZED) {
|
|
|
|
style |= WS_MAXIMIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU;
|
|
|
|
style_ex |= WS_EX_ACCEPTFILES;
|
|
|
|
|
|
|
|
(style, style_ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adjust the window client rectangle to the return value, if present.
|
|
|
|
fn apply_diff(mut self, window: HWND, mut new: WindowFlags, set_client_rect: Option<RECT>) {
|
|
|
|
self = self.mask();
|
|
|
|
new = new.mask();
|
|
|
|
|
|
|
|
let diff = self ^ new;
|
|
|
|
if diff == WindowFlags::empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff.contains(WindowFlags::VISIBLE) {
|
|
|
|
unsafe {
|
|
|
|
winuser::ShowWindow(
|
|
|
|
window,
|
|
|
|
match new.contains(WindowFlags::VISIBLE) {
|
|
|
|
true => winuser::SW_SHOW,
|
2019-06-22 01:33:15 +10:00
|
|
|
false => winuser::SW_HIDE,
|
|
|
|
},
|
2019-02-05 03:52:00 +11:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if diff.contains(WindowFlags::ALWAYS_ON_TOP) {
|
|
|
|
unsafe {
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
window,
|
|
|
|
match new.contains(WindowFlags::ALWAYS_ON_TOP) {
|
2019-06-22 01:33:15 +10:00
|
|
|
true => winuser::HWND_TOPMOST,
|
2019-02-05 03:52:00 +11:00
|
|
|
false => winuser::HWND_NOTOPMOST,
|
|
|
|
},
|
2019-06-22 01:33:15 +10:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
2019-02-05 03:52:00 +11:00
|
|
|
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOMOVE | winuser::SWP_NOSIZE,
|
|
|
|
);
|
|
|
|
winuser::UpdateWindow(window);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff.contains(WindowFlags::MAXIMIZED) || new.contains(WindowFlags::MAXIMIZED) {
|
|
|
|
unsafe {
|
|
|
|
winuser::ShowWindow(
|
|
|
|
window,
|
|
|
|
match new.contains(WindowFlags::MAXIMIZED) {
|
|
|
|
true => winuser::SW_MAXIMIZE,
|
2019-06-22 01:33:15 +10:00
|
|
|
false => winuser::SW_RESTORE,
|
|
|
|
},
|
2019-02-05 03:52:00 +11:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if diff != WindowFlags::empty() {
|
|
|
|
let (style, style_ex) = new.to_window_styles();
|
|
|
|
|
|
|
|
unsafe {
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 1, 0);
|
2019-02-05 03:52:00 +11:00
|
|
|
|
|
|
|
winuser::SetWindowLongW(window, winuser::GWL_STYLE, style as _);
|
|
|
|
winuser::SetWindowLongW(window, winuser::GWL_EXSTYLE, style_ex as _);
|
|
|
|
|
2019-06-22 01:33:15 +10:00
|
|
|
match set_client_rect
|
|
|
|
.and_then(|r| util::adjust_window_rect_with_styles(window, style, style_ex, r))
|
|
|
|
{
|
2019-02-05 03:52:00 +11:00
|
|
|
Some(client_rect) => {
|
|
|
|
let (x, y, w, h) = (
|
|
|
|
client_rect.left,
|
|
|
|
client_rect.top,
|
|
|
|
client_rect.right - client_rect.left,
|
|
|
|
client_rect.bottom - client_rect.top,
|
|
|
|
);
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
window,
|
|
|
|
ptr::null_mut(),
|
2019-06-22 01:33:15 +10:00
|
|
|
x,
|
|
|
|
y,
|
|
|
|
w,
|
|
|
|
h,
|
2019-06-26 14:04:49 +10:00
|
|
|
winuser::SWP_NOZORDER
|
|
|
|
| winuser::SWP_FRAMECHANGED
|
|
|
|
| winuser::SWP_NOACTIVATE,
|
2019-02-05 03:52:00 +11:00
|
|
|
);
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-05 03:52:00 +11:00
|
|
|
None => {
|
|
|
|
// Refresh the window frame.
|
|
|
|
winuser::SetWindowPos(
|
|
|
|
window,
|
|
|
|
ptr::null_mut(),
|
2019-06-22 01:33:15 +10:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
0,
|
2019-02-05 03:52:00 +11:00
|
|
|
winuser::SWP_NOZORDER
|
2019-06-22 01:33:15 +10:00
|
|
|
| winuser::SWP_NOMOVE
|
|
|
|
| winuser::SWP_NOSIZE
|
2019-06-26 14:04:49 +10:00
|
|
|
| winuser::SWP_FRAMECHANGED
|
|
|
|
| winuser::SWP_NOACTIVATE,
|
2019-02-05 03:52:00 +11:00
|
|
|
);
|
2019-06-25 02:14:55 +10:00
|
|
|
}
|
2019-02-05 03:52:00 +11:00
|
|
|
}
|
2019-02-06 02:30:33 +11:00
|
|
|
winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 0, 0);
|
2019-02-05 03:52:00 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CursorFlags {
|
|
|
|
fn refresh_os_cursor(self, window: HWND) -> Result<(), io::Error> {
|
|
|
|
let client_rect = util::get_client_rect(window)?;
|
|
|
|
|
|
|
|
if util::is_focused(window) {
|
|
|
|
if self.contains(CursorFlags::GRABBED) {
|
|
|
|
util::set_cursor_clip(Some(client_rect))?;
|
|
|
|
} else {
|
|
|
|
util::set_cursor_clip(None)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let cursor_in_client = self.contains(CursorFlags::IN_WINDOW);
|
|
|
|
if cursor_in_client {
|
|
|
|
util::set_cursor_hidden(self.contains(CursorFlags::HIDDEN));
|
|
|
|
} else {
|
|
|
|
util::set_cursor_hidden(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|