mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-23 02:16:33 +11:00
Refactor win32 window state code (#730)
* Overhaul win32 window state * Fix warnings * Add CHANGELOG entry * Rephrase CHANGELOG entries * Fix 1.28.0 build * Remove WS_POPUP styling * Slight style correction * Make set_maximized work * Fix rect restore not working after winit set_maximized call * Add a few comments
This commit is contained in:
parent
c91dfdd6fe
commit
7be1d16263
9 changed files with 661 additions and 608 deletions
|
@ -3,6 +3,10 @@
|
|||
- On Wayland, add `set_wayland_theme()` to control client decoration color theme
|
||||
- Added serde serialization to `os::unix::XWindowType`.
|
||||
- **Breaking:** `image` crate upgraded to 0.21. This is exposed as part of the `icon_loading` API.
|
||||
- On Windows, fix malformed function pointer typecast that could invoke undefined behavior.
|
||||
- Refactored Windows state/flag-setting code.
|
||||
- On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on.
|
||||
- On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area.
|
||||
|
||||
# Version 0.18.1 (2018-12-30)
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ core-graphics = "0.17.3"
|
|||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
backtrace = "0.3"
|
||||
bitflags = "1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3.6"
|
||||
|
|
|
@ -100,6 +100,9 @@ extern crate serde;
|
|||
extern crate winapi;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate backtrace;
|
||||
#[macro_use]
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate bitflags;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
|
|
|
@ -51,7 +51,7 @@ use {
|
|||
WindowId as SuperWindowId,
|
||||
};
|
||||
use events::{DeviceEvent, Touch, TouchPhase};
|
||||
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
|
||||
use platform::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util};
|
||||
use platform::platform::dpi::{
|
||||
become_dpi_aware,
|
||||
dpi_to_scale_factor,
|
||||
|
@ -60,64 +60,9 @@ use platform::platform::dpi::{
|
|||
};
|
||||
use platform::platform::drop_handler::FileDropHandler;
|
||||
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
|
||||
use platform::platform::icon::WinIcon;
|
||||
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
|
||||
use platform::platform::window::adjust_size;
|
||||
|
||||
/// Contains saved window info for switching between fullscreen
|
||||
#[derive(Clone)]
|
||||
pub struct SavedWindowInfo {
|
||||
/// Window style
|
||||
pub style: LONG,
|
||||
/// Window ex-style
|
||||
pub ex_style: LONG,
|
||||
/// Window position and size
|
||||
pub client_rect: RECT,
|
||||
// Since a window can be fullscreened to a different monitor, a DPI change can be triggered. This could result in
|
||||
// the window being automitcally resized to smaller/larger than it was supposed to be restored to, so we thus must
|
||||
// check if the post-fullscreen DPI matches the pre-fullscreen DPI.
|
||||
pub is_fullscreen: bool,
|
||||
pub dpi_factor: Option<f64>,
|
||||
}
|
||||
|
||||
/// Contains information about states and the window that the callback is going to use.
|
||||
#[derive(Clone)]
|
||||
pub struct WindowState {
|
||||
/// Cursor to set at the next `WM_SETCURSOR` event received.
|
||||
pub cursor: Cursor,
|
||||
pub cursor_grabbed: bool,
|
||||
pub cursor_hidden: bool,
|
||||
/// Used by `WM_GETMINMAXINFO`.
|
||||
pub max_size: Option<PhysicalSize>,
|
||||
pub min_size: Option<PhysicalSize>,
|
||||
/// Will contain `true` if the mouse is hovering the window.
|
||||
pub mouse_in_window: bool,
|
||||
/// Saved window info for fullscreen restored
|
||||
pub saved_window_info: Option<SavedWindowInfo>,
|
||||
// This is different from the value in `SavedWindowInfo`! That one represents the DPI saved upon entering
|
||||
// fullscreen. This will always be the most recent DPI for the window.
|
||||
pub dpi_factor: f64,
|
||||
pub fullscreen: Option<::MonitorId>,
|
||||
pub window_icon: Option<WinIcon>,
|
||||
pub taskbar_icon: Option<WinIcon>,
|
||||
pub decorations: bool,
|
||||
pub always_on_top: bool,
|
||||
pub maximized: bool,
|
||||
pub resizable: bool,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
pub fn update_min_max(&mut self, old_dpi_factor: f64, new_dpi_factor: f64) {
|
||||
let scale_factor = new_dpi_factor / old_dpi_factor;
|
||||
let dpi_adjuster = |mut physical_size: PhysicalSize| -> PhysicalSize {
|
||||
physical_size.width *= scale_factor;
|
||||
physical_size.height *= scale_factor;
|
||||
physical_size
|
||||
};
|
||||
self.max_size = self.max_size.map(&dpi_adjuster);
|
||||
self.min_size = self.min_size.map(&dpi_adjuster);
|
||||
}
|
||||
}
|
||||
use platform::platform::window_state::{CursorFlags, WindowFlags, WindowState};
|
||||
|
||||
/// Dummy object that allows inserting a window's state.
|
||||
// We store a pointer in order to !impl Send and Sync.
|
||||
|
@ -278,6 +223,7 @@ impl EventsLoop {
|
|||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy {
|
||||
thread_id: self.thread_id,
|
||||
thread_msg_target: self.thread_msg_target,
|
||||
sender: self.sender.clone(),
|
||||
}
|
||||
|
@ -308,6 +254,7 @@ impl Drop for EventsLoop {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy {
|
||||
thread_id: DWORD,
|
||||
thread_msg_target: HWND,
|
||||
sender: mpsc::Sender<EventsLoopEvent>,
|
||||
}
|
||||
|
@ -333,26 +280,32 @@ impl EventsLoopProxy {
|
|||
/// `WindowState` then you should call this within the lock of `WindowState`. Otherwise the
|
||||
/// events may be sent to the other thread in different order to the one in which you set
|
||||
/// `WindowState`, leaving them out of sync.
|
||||
pub fn execute_in_thread<F>(&self, function: F)
|
||||
pub fn execute_in_thread<F>(&self, mut function: F)
|
||||
where
|
||||
F: FnMut(Inserter) + Send + 'static,
|
||||
{
|
||||
// We are using double-boxing here because it make casting back much easier
|
||||
let double_box = Box::new(Box::new(function) as Box<FnMut(_)>);
|
||||
let raw = Box::into_raw(double_box);
|
||||
if unsafe{ processthreadsapi::GetCurrentThreadId() } == self.thread_id {
|
||||
function(Inserter(ptr::null_mut()));
|
||||
} else {
|
||||
// We are using double-boxing here because it make casting back much easier
|
||||
let double_box: ThreadExecFn = Box::new(Box::new(function) as Box<FnMut(_)>);
|
||||
let raw = Box::into_raw(double_box);
|
||||
|
||||
let res = unsafe {
|
||||
winuser::PostMessageW(
|
||||
self.thread_msg_target,
|
||||
*EXEC_MSG_ID,
|
||||
raw as *mut () as usize as WPARAM,
|
||||
0,
|
||||
)
|
||||
};
|
||||
assert!(res != 0, "PostMessage failed; is the messages queue full?");
|
||||
let res = unsafe {
|
||||
winuser::PostMessageW(
|
||||
self.thread_msg_target,
|
||||
*EXEC_MSG_ID,
|
||||
raw as *mut () as usize as WPARAM,
|
||||
0,
|
||||
)
|
||||
};
|
||||
assert!(res != 0, "PostMessage failed; is the messages queue full?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ThreadExecFn = Box<Box<FnMut(Inserter)>>;
|
||||
|
||||
lazy_static! {
|
||||
// Message sent when we want to execute a closure in the thread.
|
||||
// WPARAM contains a Box<Box<FnMut()>> that must be retrieved with `Box::from_raw`,
|
||||
|
@ -376,6 +329,11 @@ lazy_static! {
|
|||
winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR)
|
||||
}
|
||||
};
|
||||
// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the
|
||||
// documentation in the `window_state` module for more information.
|
||||
pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe {
|
||||
winuser::RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr() as LPCSTR)
|
||||
};
|
||||
static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec<u16> = unsafe {
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
|
@ -622,18 +580,28 @@ unsafe fn callback_inner(
|
|||
let w = LOWORD(lparam as DWORD) as u32;
|
||||
let h = HIWORD(lparam as DWORD) as u32;
|
||||
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
let logical_size = LogicalSize::from_physical((w, h), dpi_factor);
|
||||
let event = Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Resized(logical_size),
|
||||
};
|
||||
|
||||
// Wait for the parent thread to process the resize event before returning from the
|
||||
// callback.
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
let cstash = context_stash.as_mut().unwrap();
|
||||
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
let logical_size = LogicalSize::from_physical((w, h), dpi_factor);
|
||||
let event = Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Resized(logical_size),
|
||||
};
|
||||
if let Some(w) = cstash.windows.get_mut(&window) {
|
||||
let mut w = w.lock().unwrap();
|
||||
|
||||
// See WindowFlags::MARKER_RETAIN_STATE_ON_SIZE docs for info on why this `if` check exists.
|
||||
if !w.window_flags().contains(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE) {
|
||||
let maximized = wparam == winuser::SIZE_MAXIMIZED;
|
||||
w.set_window_flags_in_place(|f| f.set(WindowFlags::MAXIMIZED, maximized));
|
||||
}
|
||||
}
|
||||
|
||||
cstash.sender.send(EventsLoopEvent::WinitEvent(event)).ok();
|
||||
});
|
||||
|
@ -661,22 +629,26 @@ unsafe fn callback_inner(
|
|||
|
||||
winuser::WM_MOUSEMOVE => {
|
||||
use events::WindowEvent::{CursorEntered, CursorMoved};
|
||||
let mouse_outside_window = CONTEXT_STASH.with(|context_stash| {
|
||||
let x = windowsx::GET_X_LPARAM(lparam);
|
||||
let y = windowsx::GET_Y_LPARAM(lparam);
|
||||
|
||||
let mouse_was_outside_window = CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
if let Some(w) = context_stash.windows.get_mut(&window) {
|
||||
let mut w = w.lock().unwrap();
|
||||
if !w.mouse_in_window {
|
||||
w.mouse_in_window = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW);
|
||||
w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true)).ok();
|
||||
return was_outside_window;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
if mouse_outside_window {
|
||||
|
||||
if mouse_was_outside_window {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorEntered { device_id: DEVICE_ID },
|
||||
|
@ -691,10 +663,8 @@ unsafe fn callback_inner(
|
|||
});
|
||||
}
|
||||
|
||||
let x = windowsx::GET_X_LPARAM(lparam) as f64;
|
||||
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
|
||||
let dpi_factor = get_hwnd_scale_factor(window);
|
||||
let position = LogicalPosition::from_physical((x, y), dpi_factor);
|
||||
let position = LogicalPosition::from_physical((x as f64, y as f64), dpi_factor);
|
||||
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
|
@ -706,27 +676,21 @@ unsafe fn callback_inner(
|
|||
|
||||
winuser::WM_MOUSELEAVE => {
|
||||
use events::WindowEvent::CursorLeft;
|
||||
let mouse_in_window = CONTEXT_STASH.with(|context_stash| {
|
||||
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let mut context_stash = context_stash.borrow_mut();
|
||||
if let Some(context_stash) = context_stash.as_mut() {
|
||||
if let Some(w) = context_stash.windows.get_mut(&window) {
|
||||
let mut w = w.lock().unwrap();
|
||||
if w.mouse_in_window {
|
||||
w.mouse_in_window = false;
|
||||
return true;
|
||||
}
|
||||
w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok();
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
|
||||
if mouse_in_window {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorLeft { device_id: DEVICE_ID }
|
||||
});
|
||||
}
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: CursorLeft { device_id: DEVICE_ID }
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
@ -1101,31 +1065,31 @@ unsafe fn callback_inner(
|
|||
},
|
||||
|
||||
winuser::WM_SETCURSOR => {
|
||||
let call_def_window_proc = CONTEXT_STASH.with(|context_stash| {
|
||||
let set_cursor_to = CONTEXT_STASH.with(|context_stash| {
|
||||
context_stash
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|cstash| cstash.windows.get(&window))
|
||||
.map(|window_state_mutex| {
|
||||
.and_then(|window_state_mutex| {
|
||||
let window_state = window_state_mutex.lock().unwrap();
|
||||
if window_state.mouse_in_window {
|
||||
let cursor = winuser::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
window_state.cursor.0,
|
||||
);
|
||||
winuser::SetCursor(cursor);
|
||||
false
|
||||
if window_state.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW) {
|
||||
Some(window_state.mouse.cursor)
|
||||
} else {
|
||||
true
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(true)
|
||||
});
|
||||
|
||||
if call_def_window_proc {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
0
|
||||
match set_cursor_to {
|
||||
Some(cursor) => {
|
||||
let cursor = winuser::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
cursor.to_windows_cursor(),
|
||||
);
|
||||
winuser::SetCursor(cursor);
|
||||
0
|
||||
},
|
||||
None => winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1148,10 +1112,12 @@ unsafe fn callback_inner(
|
|||
let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD;
|
||||
let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD;
|
||||
if let Some(min_size) = window_state.min_size {
|
||||
let min_size = min_size.to_physical(window_state.dpi_factor);
|
||||
let (width, height) = adjust_size(min_size, style, ex_style);
|
||||
(*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 };
|
||||
}
|
||||
if let Some(max_size) = window_state.max_size {
|
||||
let max_size = max_size.to_physical(window_state.dpi_factor);
|
||||
let (width, height) = adjust_size(max_size, style, ex_style);
|
||||
(*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 };
|
||||
}
|
||||
|
@ -1175,38 +1141,21 @@ unsafe fn callback_inner(
|
|||
let new_dpi_x = u32::from(LOWORD(wparam as DWORD));
|
||||
let new_dpi_factor = dpi_to_scale_factor(new_dpi_x);
|
||||
|
||||
let suppress_resize = CONTEXT_STASH.with(|context_stash| {
|
||||
context_stash
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.and_then(|cstash| cstash.windows.get(&window))
|
||||
.map(|window_state_mutex| {
|
||||
let mut window_state = window_state_mutex.lock().unwrap();
|
||||
let suppress_resize = window_state.saved_window_info
|
||||
.as_mut()
|
||||
.map(|saved_window_info| {
|
||||
let dpi_changed = if !saved_window_info.is_fullscreen {
|
||||
saved_window_info.dpi_factor.take() != Some(new_dpi_factor)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
!dpi_changed || saved_window_info.is_fullscreen
|
||||
})
|
||||
.unwrap_or(false);
|
||||
// Now we adjust the min/max dimensions for the new DPI.
|
||||
if !suppress_resize {
|
||||
let old_dpi_factor = window_state.dpi_factor;
|
||||
window_state.update_min_max(old_dpi_factor, new_dpi_factor);
|
||||
}
|
||||
window_state.dpi_factor = new_dpi_factor;
|
||||
suppress_resize
|
||||
})
|
||||
.unwrap_or(false)
|
||||
let allow_resize = CONTEXT_STASH.with(|context_stash| {
|
||||
if let Some(wstash) = context_stash.borrow().as_ref().and_then(|cstash| cstash.windows.get(&window)) {
|
||||
let mut window_state = wstash.lock().unwrap();
|
||||
let old_dpi_factor = window_state.dpi_factor;
|
||||
window_state.dpi_factor = new_dpi_factor;
|
||||
|
||||
new_dpi_factor != old_dpi_factor && window_state.fullscreen.is_none()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
// This prevents us from re-applying DPI adjustment to the restored size after exiting
|
||||
// fullscreen (the restored size is already DPI adjusted).
|
||||
if !suppress_resize {
|
||||
if allow_resize {
|
||||
// Resize window to the size suggested by Windows.
|
||||
let rect = &*(lparam as *const RECT);
|
||||
winuser::SetWindowPos(
|
||||
|
@ -1272,6 +1221,16 @@ unsafe fn callback_inner(
|
|||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
0
|
||||
} else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
if let Some(cstash) = context_stash.borrow().as_ref() {
|
||||
if let Some(wstash) = cstash.windows.get(&window) {
|
||||
let mut window_state = wstash.lock().unwrap();
|
||||
window_state.set_window_flags_in_place(|f| f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0));
|
||||
}
|
||||
}
|
||||
});
|
||||
0
|
||||
} else {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
|
@ -1289,8 +1248,8 @@ pub unsafe extern "system" fn thread_event_target_callback(
|
|||
run_catch_panic(-1, || {
|
||||
match msg {
|
||||
_ if msg == *EXEC_MSG_ID => {
|
||||
let mut function: Box<Box<FnMut()>> = Box::from_raw(wparam as usize as *mut _);
|
||||
function();
|
||||
let mut function: ThreadExecFn = Box::from_raw(wparam as usize as *mut _);
|
||||
function(Inserter(ptr::null_mut()));
|
||||
0
|
||||
},
|
||||
_ => winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
|
|
|
@ -71,3 +71,4 @@ mod monitor;
|
|||
mod raw_input;
|
||||
mod util;
|
||||
mod window;
|
||||
mod window_state;
|
||||
|
|
|
@ -99,7 +99,7 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError> {
|
||||
pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, util::WinError> {
|
||||
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() };
|
||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||
let status = unsafe {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::{self, mem, ptr, slice};
|
||||
use std::{self, mem, ptr, slice, io};
|
||||
use std::ops::BitAnd;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use MouseCursor;
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::{BOOL, DWORD};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
|
@ -47,6 +49,14 @@ pub unsafe fn status_map<T, F: FnMut(&mut T) -> BOOL>(mut fun: F) -> Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn win_to_err<F: FnOnce() -> BOOL>(f: F) -> Result<(), io::Error> {
|
||||
if f() != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_cursor_pos() -> Option<POINT> {
|
||||
unsafe { status_map(|cursor_pos| winuser::GetCursorPos(cursor_pos)) }
|
||||
}
|
||||
|
@ -55,26 +65,56 @@ pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
|
|||
unsafe { status_map(|rect| winuser::GetWindowRect(hwnd, rect)) }
|
||||
}
|
||||
|
||||
pub fn get_client_rect(hwnd: HWND) -> Option<RECT> {
|
||||
unsafe { status_map(|rect| {
|
||||
pub fn get_client_rect(hwnd: HWND) -> Result<RECT, io::Error> {
|
||||
unsafe {
|
||||
let mut rect = mem::uninitialized();
|
||||
let mut top_left = mem::zeroed();
|
||||
if 0 == winuser::ClientToScreen(hwnd, &mut top_left) {return 0;};
|
||||
if 0 == winuser::GetClientRect(hwnd, rect) {return 0};
|
||||
|
||||
win_to_err(|| winuser::ClientToScreen(hwnd, &mut top_left))?;
|
||||
win_to_err(|| winuser::GetClientRect(hwnd, &mut rect))?;
|
||||
rect.left += top_left.x;
|
||||
rect.top += top_left.y;
|
||||
rect.right += top_left.x;
|
||||
rect.bottom += top_left.y;
|
||||
1
|
||||
|
||||
Ok(rect)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adjust_window_rect(hwnd: HWND, rect: RECT) -> Option<RECT> {
|
||||
unsafe {
|
||||
let style = winuser::GetWindowLongW(hwnd, winuser::GWL_STYLE);
|
||||
let style_ex = winuser::GetWindowLongW(hwnd, winuser::GWL_EXSTYLE);
|
||||
adjust_window_rect_with_styles(hwnd, style as _, style_ex as _, rect)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adjust_window_rect_with_styles(hwnd: HWND, style: DWORD, style_ex: DWORD, rect: RECT) -> Option<RECT> {
|
||||
unsafe { status_map(|r| {
|
||||
*r = rect;
|
||||
|
||||
let b_menu = !winuser::GetMenu(hwnd).is_null() as BOOL;
|
||||
winuser::AdjustWindowRectEx(r, style as _ , b_menu, style_ex as _)
|
||||
}) }
|
||||
}
|
||||
|
||||
// This won't be needed anymore if we just add a derive to winapi.
|
||||
pub fn rect_eq(a: &RECT, b: &RECT) -> bool {
|
||||
let left_eq = a.left == b.left;
|
||||
let right_eq = a.right == b.right;
|
||||
let top_eq = a.top == b.top;
|
||||
let bottom_eq = a.bottom == b.bottom;
|
||||
left_eq && right_eq && top_eq && bottom_eq
|
||||
pub fn set_cursor_hidden(hidden: bool) {
|
||||
static HIDDEN: AtomicBool = AtomicBool::new(false);
|
||||
let changed = HIDDEN.swap(hidden, Ordering::SeqCst) ^ hidden;
|
||||
if changed {
|
||||
unsafe{ winuser::ShowCursor(!hidden as BOOL) };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_clip(rect: Option<RECT>) -> Result<(), io::Error> {
|
||||
unsafe {
|
||||
let rect_ptr = rect.as_ref().map(|r| r as *const RECT).unwrap_or(ptr::null());
|
||||
win_to_err(|| winuser::ClipCursor(rect_ptr))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_focused(window: HWND) -> bool {
|
||||
window == unsafe{ winuser::GetActiveWindow() }
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
|
@ -115,3 +155,29 @@ pub unsafe fn get_last_error() -> Option<String> {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl MouseCursor {
|
||||
pub(crate) fn to_windows_cursor(self) -> *const wchar_t {
|
||||
match self {
|
||||
MouseCursor::Arrow | MouseCursor::Default => winuser::IDC_ARROW,
|
||||
MouseCursor::Hand => winuser::IDC_HAND,
|
||||
MouseCursor::Crosshair => winuser::IDC_CROSS,
|
||||
MouseCursor::Text | MouseCursor::VerticalText => winuser::IDC_IBEAM,
|
||||
MouseCursor::NotAllowed | MouseCursor::NoDrop => winuser::IDC_NO,
|
||||
MouseCursor::Grab | MouseCursor::Grabbing |
|
||||
MouseCursor::Move | MouseCursor::AllScroll => winuser::IDC_SIZEALL,
|
||||
MouseCursor::EResize | MouseCursor::WResize |
|
||||
MouseCursor::EwResize | MouseCursor::ColResize => winuser::IDC_SIZEWE,
|
||||
MouseCursor::NResize | MouseCursor::SResize |
|
||||
MouseCursor::NsResize | MouseCursor::RowResize => winuser::IDC_SIZENS,
|
||||
MouseCursor::NeResize | MouseCursor::SwResize |
|
||||
MouseCursor::NeswResize => winuser::IDC_SIZENESW,
|
||||
MouseCursor::NwResize | MouseCursor::SeResize |
|
||||
MouseCursor::NwseResize => winuser::IDC_SIZENWSE,
|
||||
MouseCursor::Wait => winuser::IDC_WAIT,
|
||||
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
||||
MouseCursor::Help => winuser::IDC_HELP,
|
||||
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ use std::sync::{Arc, Mutex};
|
|||
use std::sync::mpsc::channel;
|
||||
|
||||
use winapi::ctypes::c_int;
|
||||
use winapi::shared::minwindef::{BOOL, DWORD, FALSE, LPARAM, TRUE, UINT, WORD, WPARAM};
|
||||
use winapi::shared::windef::{HWND, LPPOINT, POINT, RECT};
|
||||
use winapi::shared::minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser};
|
||||
use winapi::um::objbase::COINIT_MULTITHREADED;
|
||||
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2};
|
||||
|
@ -26,16 +26,14 @@ use {
|
|||
PhysicalSize,
|
||||
WindowAttributes,
|
||||
};
|
||||
use platform::platform::{Cursor, PlatformSpecificWindowBuilderAttributes, WindowId};
|
||||
use platform::platform::{PlatformSpecificWindowBuilderAttributes, WindowId};
|
||||
use platform::platform::dpi::{dpi_to_scale_factor, get_hwnd_dpi};
|
||||
use platform::platform::events_loop::{self, EventsLoop, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID};
|
||||
use platform::platform::events_loop::WindowState;
|
||||
use platform::platform::icon::{self, IconType, WinIcon};
|
||||
use platform::platform::monitor::get_available_monitors;
|
||||
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
||||
use platform::platform::util;
|
||||
|
||||
const WS_RESIZABLE: DWORD = winuser::WS_SIZEBOX | winuser::WS_MAXIMIZEBOX;
|
||||
use platform::platform::window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState};
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
pub struct Window {
|
||||
|
@ -182,16 +180,16 @@ impl Window {
|
|||
|
||||
pub(crate) fn set_inner_size_physical(&self, x: u32, y: u32) {
|
||||
unsafe {
|
||||
let mut rect = RECT {
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: y as LONG,
|
||||
right: x as LONG,
|
||||
};
|
||||
let dw_style = winuser::GetWindowLongA(self.window.0, winuser::GWL_STYLE) as DWORD;
|
||||
let b_menu = !winuser::GetMenu(self.window.0).is_null() as BOOL;
|
||||
let dw_style_ex = winuser::GetWindowLongA(self.window.0, winuser::GWL_EXSTYLE) as DWORD;
|
||||
winuser::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||
let rect = util::adjust_window_rect(
|
||||
self.window.0,
|
||||
RECT {
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: y as LONG,
|
||||
right: x as LONG,
|
||||
}
|
||||
).expect("adjust_window_rect failed");
|
||||
|
||||
let outer_x = (rect.right - rect.left).abs() as c_int;
|
||||
let outer_y = (rect.top - rect.bottom).abs() as c_int;
|
||||
winuser::SetWindowPos(
|
||||
|
@ -251,25 +249,17 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.resizable, resizable) != resizable {
|
||||
// If we're in fullscreen, update stored configuration but don't apply anything.
|
||||
if window_state.fullscreen.is_none() {
|
||||
let mut style = unsafe {
|
||||
winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE)
|
||||
};
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
if resizable {
|
||||
style |= WS_RESIZABLE as LONG;
|
||||
} else {
|
||||
style &= !WS_RESIZABLE as LONG;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
winuser::SetWindowLongW(self.window.0, winuser::GWL_STYLE, style as _);
|
||||
};
|
||||
}
|
||||
}
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
WindowState::set_window_flags(
|
||||
window_state.lock().unwrap(),
|
||||
window.0,
|
||||
None,
|
||||
|f| f.set(WindowFlags::RESIZABLE, resizable),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the `hwnd` of this window.
|
||||
|
@ -280,123 +270,44 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
let cursor_id = Cursor(match cursor {
|
||||
MouseCursor::Arrow | MouseCursor::Default => winuser::IDC_ARROW,
|
||||
MouseCursor::Hand => winuser::IDC_HAND,
|
||||
MouseCursor::Crosshair => winuser::IDC_CROSS,
|
||||
MouseCursor::Text | MouseCursor::VerticalText => winuser::IDC_IBEAM,
|
||||
MouseCursor::NotAllowed | MouseCursor::NoDrop => winuser::IDC_NO,
|
||||
MouseCursor::Grab | MouseCursor::Grabbing |
|
||||
MouseCursor::Move | MouseCursor::AllScroll => winuser::IDC_SIZEALL,
|
||||
MouseCursor::EResize | MouseCursor::WResize |
|
||||
MouseCursor::EwResize | MouseCursor::ColResize => winuser::IDC_SIZEWE,
|
||||
MouseCursor::NResize | MouseCursor::SResize |
|
||||
MouseCursor::NsResize | MouseCursor::RowResize => winuser::IDC_SIZENS,
|
||||
MouseCursor::NeResize | MouseCursor::SwResize |
|
||||
MouseCursor::NeswResize => winuser::IDC_SIZENESW,
|
||||
MouseCursor::NwResize | MouseCursor::SeResize |
|
||||
MouseCursor::NwseResize => winuser::IDC_SIZENWSE,
|
||||
MouseCursor::Wait => winuser::IDC_WAIT,
|
||||
MouseCursor::Progress => winuser::IDC_APPSTARTING,
|
||||
MouseCursor::Help => winuser::IDC_HELP,
|
||||
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
||||
});
|
||||
self.window_state.lock().unwrap().cursor = cursor_id;
|
||||
self.window_state.lock().unwrap().mouse.cursor = cursor;
|
||||
self.events_loop_proxy.execute_in_thread(move |_| unsafe {
|
||||
let cursor = winuser::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
cursor_id.0,
|
||||
cursor.to_windows_cursor(),
|
||||
);
|
||||
winuser::SetCursor(cursor);
|
||||
});
|
||||
}
|
||||
|
||||
unsafe fn cursor_is_grabbed(&self) -> Result<bool, String> {
|
||||
let mut client_rect: RECT = mem::uninitialized();
|
||||
let mut clip_rect: RECT = mem::uninitialized();
|
||||
if winuser::GetClientRect(self.window.0, &mut client_rect) == 0 {
|
||||
return Err("`GetClientRect` failed".to_owned());
|
||||
}
|
||||
// A `POINT` is two `LONG`s (x, y), and the `RECT` field after `left` is `top`.
|
||||
if winuser::ClientToScreen(self.window.0, &mut client_rect.left as *mut _ as LPPOINT) == 0 {
|
||||
return Err("`ClientToScreen` (left, top) failed".to_owned());
|
||||
}
|
||||
if winuser::ClientToScreen(self.window.0, &mut client_rect.right as *mut _ as LPPOINT) == 0 {
|
||||
return Err("`ClientToScreen` (right, bottom) failed".to_owned());
|
||||
}
|
||||
if winuser::GetClipCursor(&mut clip_rect) == 0 {
|
||||
return Err("`GetClipCursor` failed".to_owned());
|
||||
}
|
||||
Ok(util::rect_eq(&client_rect, &clip_rect))
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn grab_cursor_inner(window: &WindowWrapper, grab: bool) -> Result<(), String> {
|
||||
if grab {
|
||||
let mut rect = mem::uninitialized();
|
||||
if winuser::GetClientRect(window.0, &mut rect) == 0 {
|
||||
return Err("`GetClientRect` failed".to_owned());
|
||||
}
|
||||
// A `POINT` is two `LONG`s (x, y), and the `RECT` field after `left` is `top`.
|
||||
if winuser::ClientToScreen(window.0, &mut rect.left as *mut _ as LPPOINT) == 0 {
|
||||
return Err("`ClientToScreen` (left, top) failed".to_owned());
|
||||
}
|
||||
if winuser::ClientToScreen(window.0, &mut rect.right as *mut _ as LPPOINT) == 0 {
|
||||
return Err("`ClientToScreen` (right, bottom) failed".to_owned());
|
||||
}
|
||||
if winuser::ClipCursor(&rect) == 0 {
|
||||
return Err("`ClipCursor` failed".to_owned());
|
||||
}
|
||||
} else {
|
||||
if winuser::ClipCursor(ptr::null()) == 0 {
|
||||
return Err("`ClipCursor` failed".to_owned());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn grab_cursor(&self, grab: bool) -> Result<(), String> {
|
||||
let currently_grabbed = unsafe { self.cursor_is_grabbed() }?;
|
||||
let window_state_lock = self.window_state.lock().unwrap();
|
||||
if currently_grabbed == grab && grab == window_state_lock.cursor_grabbed {
|
||||
return Ok(());
|
||||
}
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
let (tx, rx) = channel();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let result = unsafe { Self::grab_cursor_inner(&window, grab) };
|
||||
if result.is_ok() {
|
||||
window_state.lock().unwrap().cursor_grabbed = grab;
|
||||
}
|
||||
let result = window_state.lock().unwrap().mouse
|
||||
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab))
|
||||
.map_err(|e| e.to_string());
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
drop(window_state_lock);
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn hide_cursor_inner(hide: bool) {
|
||||
if hide {
|
||||
winuser::ShowCursor(FALSE);
|
||||
} else {
|
||||
winuser::ShowCursor(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide_cursor(&self, hide: bool) {
|
||||
let window_state_lock = self.window_state.lock().unwrap();
|
||||
// We don't want to increment/decrement the display count more than once!
|
||||
if hide == window_state_lock.cursor_hidden { return; }
|
||||
let (tx, rx) = channel();
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
let (tx, rx) = channel();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
unsafe { Self::hide_cursor_inner(hide) };
|
||||
window_state.lock().unwrap().cursor_hidden = hide;
|
||||
let _ = tx.send(());
|
||||
let result = window_state.lock().unwrap().mouse
|
||||
.set_cursor_flags(window.0, |f| f.set(CursorFlags::HIDDEN, hide))
|
||||
.map_err(|e| e.to_string());
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
drop(window_state_lock);
|
||||
rx.recv().unwrap()
|
||||
rx.recv().unwrap().ok();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -431,271 +342,107 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.maximized, maximized) != maximized {
|
||||
// We only maximize if we're not in fullscreen.
|
||||
if window_state.fullscreen.is_none() {
|
||||
let window = self.window.clone();
|
||||
unsafe {
|
||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::ShowWindow(
|
||||
window.0,
|
||||
if maximized {
|
||||
winuser::SW_MAXIMIZE
|
||||
} else {
|
||||
winuser::SW_RESTORE
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn set_fullscreen_style(&self, window_state: &mut WindowState) -> (LONG, LONG) {
|
||||
if window_state.fullscreen.is_none() || window_state.saved_window_info.is_none() {
|
||||
let client_rect = util::get_client_rect(self.window.0).expect("client rect retrieval failed");
|
||||
let dpi_factor = Some(window_state.dpi_factor);
|
||||
window_state.saved_window_info = Some(events_loop::SavedWindowInfo {
|
||||
style: winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE),
|
||||
ex_style: winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE),
|
||||
client_rect,
|
||||
is_fullscreen: true,
|
||||
dpi_factor,
|
||||
});
|
||||
}
|
||||
|
||||
// We sync the system maximized state here, it will be used when restoring
|
||||
let mut placement: winuser::WINDOWPLACEMENT = mem::zeroed();
|
||||
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as u32;
|
||||
winuser::GetWindowPlacement(self.window.0, &mut placement);
|
||||
window_state.maximized = placement.showCmd == (winuser::SW_SHOWMAXIMIZED as u32);
|
||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||
|
||||
(saved_window_info.style, saved_window_info.ex_style)
|
||||
}
|
||||
|
||||
unsafe fn restore_saved_window(&self, window_state_lock: &mut WindowState) {
|
||||
let (client_rect, mut style, ex_style) = {
|
||||
// 'saved_window_info' can be None if the window has never been
|
||||
// in fullscreen mode before this method gets called.
|
||||
if window_state_lock.saved_window_info.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let saved_window_info = window_state_lock.saved_window_info.as_mut().unwrap();
|
||||
|
||||
// Reset original window style and size. The multiple window size/moves
|
||||
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
||||
// repainted. Better-looking methods welcome.
|
||||
saved_window_info.is_fullscreen = false;
|
||||
|
||||
let client_rect = saved_window_info.client_rect.clone();
|
||||
let (style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||
(client_rect, style, ex_style)
|
||||
};
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
let resizable = window_state_lock.resizable;
|
||||
let decorations = window_state_lock.decorations;
|
||||
let maximized = window_state_lock.maximized;
|
||||
|
||||
// We're restoring the window to its size and position from before being fullscreened.
|
||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let _ = Self::grab_cursor_inner(&window, false);
|
||||
|
||||
if resizable && decorations {
|
||||
style |= WS_RESIZABLE as LONG;
|
||||
} else {
|
||||
style &= !WS_RESIZABLE as LONG;
|
||||
}
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||
|
||||
let mut rect = client_rect;
|
||||
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
WindowState::set_window_flags(
|
||||
window_state.lock().unwrap(),
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
None,
|
||||
|f| f.set(WindowFlags::MAXIMIZED, maximized),
|
||||
);
|
||||
|
||||
// We apply any requested changes to maximization state that occurred while we were in fullscreen.
|
||||
winuser::ShowWindow(
|
||||
window.0,
|
||||
if maximized {
|
||||
winuser::SW_MAXIMIZE
|
||||
} else {
|
||||
winuser::SW_RESTORE
|
||||
},
|
||||
);
|
||||
|
||||
mark_fullscreen(window.0, false);
|
||||
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
let _ = Self::grab_cursor_inner(&window, window_state_lock.cursor_grabbed);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
let mut window_state_lock = self.window_state.lock().unwrap();
|
||||
unsafe {
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
match &monitor {
|
||||
&Some(RootMonitorId { ref inner }) => {
|
||||
let (x, y): (i32, i32) = inner.get_position().into();
|
||||
let (width, height): (u32, u32) = inner.get_dimensions().into();
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
let (style, ex_style) = self.set_fullscreen_style(&mut window_state_lock);
|
||||
let mut monitor = monitor.clone();
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let _ = Self::grab_cursor_inner(&window, false);
|
||||
let mut window_state_lock = window_state.lock().unwrap();
|
||||
|
||||
winuser::SetWindowLongW(
|
||||
window.0,
|
||||
winuser::GWL_STYLE,
|
||||
((style as DWORD) & !(winuser::WS_CAPTION | winuser::WS_THICKFRAME))
|
||||
as LONG,
|
||||
);
|
||||
let client_rect = util::get_client_rect(window.0).expect("get client rect failed!");
|
||||
window_state_lock.saved_window = Some(SavedWindow {
|
||||
client_rect,
|
||||
dpi_factor: window_state_lock.dpi_factor
|
||||
});
|
||||
|
||||
winuser::SetWindowLongW(
|
||||
window_state_lock.fullscreen = monitor.take();
|
||||
WindowState::refresh_window_state(
|
||||
window_state_lock,
|
||||
window.0,
|
||||
winuser::GWL_EXSTYLE,
|
||||
((ex_style as DWORD)
|
||||
& !(winuser::WS_EX_DLGMODALFRAME | winuser::WS_EX_WINDOWEDGE
|
||||
| winuser::WS_EX_CLIENTEDGE
|
||||
| winuser::WS_EX_STATICEDGE))
|
||||
as LONG,
|
||||
);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
x as c_int,
|
||||
y as c_int,
|
||||
width as c_int,
|
||||
height as c_int,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
Some(RECT {
|
||||
left: x,
|
||||
top: y,
|
||||
right: x + width as c_int,
|
||||
bottom: y + height as c_int,
|
||||
})
|
||||
);
|
||||
|
||||
mark_fullscreen(window.0, true);
|
||||
|
||||
let window_state_lock = window_state.lock().unwrap();
|
||||
let _ = Self::grab_cursor_inner(&window, window_state_lock.cursor_grabbed);
|
||||
});
|
||||
}
|
||||
&None => {
|
||||
self.restore_saved_window(&mut window_state_lock);
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let mut window_state_lock = window_state.lock().unwrap();
|
||||
window_state_lock.fullscreen = None;
|
||||
|
||||
if let Some(SavedWindow{client_rect, dpi_factor}) = window_state_lock.saved_window {
|
||||
window_state_lock.dpi_factor = dpi_factor;
|
||||
window_state_lock.saved_window = None;
|
||||
|
||||
WindowState::refresh_window_state(
|
||||
window_state_lock,
|
||||
window.0,
|
||||
Some(client_rect)
|
||||
);
|
||||
}
|
||||
|
||||
mark_fullscreen(window.0, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window_state_lock.fullscreen = monitor;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.decorations, decorations) != decorations {
|
||||
let style_flags = (winuser::WS_CAPTION | winuser::WS_THICKFRAME) as LONG;
|
||||
let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG;
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
// if we are in fullscreen mode, we only change the saved window info
|
||||
if window_state.fullscreen.is_some() {
|
||||
let resizable = window_state.resizable;
|
||||
let saved = window_state.saved_window_info.as_mut().unwrap();
|
||||
|
||||
if decorations {
|
||||
saved.style = saved.style | style_flags;
|
||||
saved.ex_style = saved.ex_style | ex_style_flags;
|
||||
} else {
|
||||
saved.style = saved.style & !style_flags;
|
||||
saved.ex_style = saved.ex_style & !ex_style_flags;
|
||||
}
|
||||
if resizable {
|
||||
saved.style |= WS_RESIZABLE as LONG;
|
||||
} else {
|
||||
saved.style &= !WS_RESIZABLE as LONG;
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
let mut rect = util::get_client_rect(self.window.0).expect("Get client rect failed!");
|
||||
|
||||
let mut style = winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE);
|
||||
let mut ex_style = winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE);
|
||||
|
||||
if decorations {
|
||||
style = style | style_flags;
|
||||
ex_style = ex_style | ex_style_flags;
|
||||
} else {
|
||||
style = style & !style_flags;
|
||||
ex_style = ex_style & !ex_style_flags;
|
||||
}
|
||||
|
||||
let window = self.window.clone();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
||||
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
rect.left,
|
||||
rect.top,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let client_rect = util::get_client_rect(window.0).expect("get client rect failed!");
|
||||
WindowState::set_window_flags(
|
||||
window_state.lock().unwrap(),
|
||||
window.0,
|
||||
Some(client_rect),
|
||||
|f| f.set(WindowFlags::DECORATIONS, decorations),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_always_on_top(&self, always_on_top: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
if mem::replace(&mut window_state.always_on_top, always_on_top) != always_on_top {
|
||||
let window = self.window.clone();
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
let insert_after = if always_on_top {
|
||||
winuser::HWND_TOPMOST
|
||||
} else {
|
||||
winuser::HWND_NOTOPMOST
|
||||
};
|
||||
unsafe {
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
insert_after,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOMOVE | winuser::SWP_NOSIZE,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
}
|
||||
});
|
||||
}
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
WindowState::set_window_flags(
|
||||
window_state.lock().unwrap(),
|
||||
window.0,
|
||||
None,
|
||||
|f| f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -846,84 +593,27 @@ unsafe fn init(
|
|||
info!("Guessed window DPI factor: {}", guessed_dpi_factor);
|
||||
|
||||
let dimensions = attributes.dimensions.unwrap_or_else(|| (1024, 768).into());
|
||||
let (width, height): (u32, u32) = dimensions.to_physical(guessed_dpi_factor).into();
|
||||
// building a RECT object with coordinates
|
||||
let mut rect = RECT {
|
||||
left: 0,
|
||||
right: width as LONG,
|
||||
top: 0,
|
||||
bottom: height as LONG,
|
||||
};
|
||||
|
||||
// computing the style and extended style of the window
|
||||
let (mut ex_style, style) = if !attributes.decorations {
|
||||
(winuser::WS_EX_APPWINDOW,
|
||||
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
||||
if pl_attribs.parent.is_some() {
|
||||
winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN
|
||||
}
|
||||
else {
|
||||
winuser::WS_POPUP | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN
|
||||
}
|
||||
)
|
||||
} else {
|
||||
(winuser::WS_EX_APPWINDOW | winuser::WS_EX_WINDOWEDGE,
|
||||
winuser::WS_OVERLAPPEDWINDOW | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN)
|
||||
};
|
||||
|
||||
if attributes.always_on_top {
|
||||
ex_style |= winuser::WS_EX_TOPMOST;
|
||||
}
|
||||
if pl_attribs.no_redirection_bitmap {
|
||||
ex_style |= winuser::WS_EX_NOREDIRECTIONBITMAP;
|
||||
}
|
||||
if attributes.transparent && attributes.decorations {
|
||||
ex_style |= winuser::WS_EX_LAYERED;
|
||||
}
|
||||
|
||||
// adjusting the window coordinates using the style
|
||||
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
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);
|
||||
// WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured.
|
||||
window_flags.set(WindowFlags::RESIZABLE, attributes.resizable);
|
||||
window_flags.set(WindowFlags::CHILD, pl_attribs.parent.is_some());
|
||||
window_flags.set(WindowFlags::ON_TASKBAR, true);
|
||||
|
||||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (adjusted_width, adjusted_height) = if attributes.dimensions.is_some() {
|
||||
let min_dimensions = attributes.min_dimensions
|
||||
.map(|logical_size| PhysicalSize::from_logical(logical_size, guessed_dpi_factor))
|
||||
.map(|physical_size| adjust_size(physical_size, style, ex_style))
|
||||
.unwrap_or((0, 0));
|
||||
let max_dimensions = attributes.max_dimensions
|
||||
.map(|logical_size| PhysicalSize::from_logical(logical_size, guessed_dpi_factor))
|
||||
.map(|physical_size| adjust_size(physical_size, style, ex_style))
|
||||
.unwrap_or((c_int::max_value(), c_int::max_value()));
|
||||
(
|
||||
Some((rect.right - rect.left).min(max_dimensions.0).max(min_dimensions.0)),
|
||||
Some((rect.bottom - rect.top).min(max_dimensions.1).max(min_dimensions.1))
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let mut style = if !attributes.visible {
|
||||
style
|
||||
} else {
|
||||
style | winuser::WS_VISIBLE
|
||||
};
|
||||
|
||||
if !attributes.resizable {
|
||||
style &= !WS_RESIZABLE;
|
||||
}
|
||||
|
||||
if pl_attribs.parent.is_some() {
|
||||
style |= winuser::WS_CHILD;
|
||||
}
|
||||
|
||||
let handle = winuser::CreateWindowExW(ex_style | winuser::WS_EX_ACCEPTFILES,
|
||||
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::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN,
|
||||
style,
|
||||
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
|
||||
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
|
||||
adjusted_width.unwrap_or(winuser::CW_USEDEFAULT),
|
||||
adjusted_height.unwrap_or(winuser::CW_USEDEFAULT),
|
||||
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
||||
ptr::null_mut(),
|
||||
libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
|
@ -935,6 +625,9 @@ unsafe fn init(
|
|||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
winuser::SetWindowLongW(handle, winuser::GWL_STYLE, 0);
|
||||
winuser::SetWindowLongW(handle, winuser::GWL_EXSTYLE, 0);
|
||||
|
||||
WindowWrapper(handle)
|
||||
};
|
||||
|
||||
|
@ -966,32 +659,6 @@ unsafe fn init(
|
|||
);
|
||||
}
|
||||
|
||||
let window_state = {
|
||||
let max_size = attributes.max_dimensions
|
||||
.map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor));
|
||||
let min_size = attributes.min_dimensions
|
||||
.map(|logical_size| PhysicalSize::from_logical(logical_size, dpi_factor));
|
||||
let mut window_state = events_loop::WindowState {
|
||||
cursor: Cursor(winuser::IDC_ARROW), // use arrow by default
|
||||
cursor_grabbed: false,
|
||||
cursor_hidden: false,
|
||||
max_size,
|
||||
min_size,
|
||||
mouse_in_window: false,
|
||||
saved_window_info: None,
|
||||
dpi_factor,
|
||||
fullscreen: attributes.fullscreen.clone(),
|
||||
window_icon,
|
||||
taskbar_icon,
|
||||
decorations: attributes.decorations,
|
||||
maximized: attributes.maximized,
|
||||
resizable: attributes.resizable,
|
||||
always_on_top: attributes.always_on_top,
|
||||
};
|
||||
// Creating a mutex to track the current window state
|
||||
Arc::new(Mutex::new(window_state))
|
||||
};
|
||||
|
||||
// making the window transparent
|
||||
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
|
||||
let region = CreateRectRgn(0, 0, -1, -1); // makes the window transparent
|
||||
|
@ -1019,18 +686,41 @@ unsafe fn init(
|
|||
}
|
||||
}
|
||||
|
||||
window_flags.set(WindowFlags::VISIBLE, attributes.visible);
|
||||
window_flags.set(WindowFlags::MAXIMIZED, attributes.maximized);
|
||||
|
||||
let window_state = {
|
||||
let mut window_state = WindowState::new(
|
||||
&attributes,
|
||||
window_icon,
|
||||
taskbar_icon,
|
||||
dpi_factor,
|
||||
);
|
||||
let window_state = Arc::new(Mutex::new(window_state));
|
||||
WindowState::set_window_flags(
|
||||
window_state.lock().unwrap(),
|
||||
real_window.0,
|
||||
None,
|
||||
|f| *f = window_flags,
|
||||
);
|
||||
window_state
|
||||
};
|
||||
|
||||
let win = Window {
|
||||
window: real_window,
|
||||
window_state,
|
||||
events_loop_proxy,
|
||||
};
|
||||
|
||||
win.set_maximized(attributes.maximized);
|
||||
if let Some(_) = attributes.fullscreen {
|
||||
win.set_fullscreen(attributes.fullscreen);
|
||||
force_window_active(win.window.0);
|
||||
}
|
||||
|
||||
if let Some(dimensions) = attributes.dimensions {
|
||||
win.set_inner_size(dimensions);
|
||||
}
|
||||
|
||||
inserter.insert(win.window.0, win.window_state.clone());
|
||||
|
||||
Ok(win)
|
||||
|
|
329
src/platform/windows/window_state.rs
Normal file
329
src/platform/windows/window_state.rs
Normal file
|
@ -0,0 +1,329 @@
|
|||
use {MouseCursor, WindowAttributes};
|
||||
use std::{io, ptr};
|
||||
use std::sync::MutexGuard;
|
||||
use dpi::LogicalSize;
|
||||
use platform::platform::{util, events_loop};
|
||||
use platform::platform::icon::WinIcon;
|
||||
use winapi::shared::windef::{RECT, HWND};
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::winuser;
|
||||
|
||||
/// 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,
|
||||
|
||||
pub fullscreen: Option<::MonitorId>,
|
||||
window_flags: WindowFlags,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SavedWindow {
|
||||
pub client_rect: RECT,
|
||||
pub dpi_factor: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MouseProperties {
|
||||
pub cursor: MouseCursor,
|
||||
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>,
|
||||
dpi_factor: f64
|
||||
) -> WindowState {
|
||||
WindowState {
|
||||
mouse: MouseProperties {
|
||||
cursor: MouseCursor::default(),
|
||||
cursor_flags: CursorFlags::empty(),
|
||||
},
|
||||
|
||||
min_size: attributes.min_dimensions,
|
||||
max_size: attributes.max_dimensions,
|
||||
|
||||
window_icon,
|
||||
taskbar_icon,
|
||||
|
||||
saved_window: None,
|
||||
dpi_factor,
|
||||
|
||||
fullscreen: None,
|
||||
window_flags: WindowFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_flags(&self) -> WindowFlags {
|
||||
self.window_flags
|
||||
}
|
||||
|
||||
pub fn set_window_flags<F>(mut this: MutexGuard<Self>, window: HWND, set_client_rect: Option<RECT>, f: F)
|
||||
where F: FnOnce(&mut WindowFlags)
|
||||
{
|
||||
let old_flags = this.window_flags;
|
||||
f(&mut this.window_flags);
|
||||
|
||||
let is_fullscreen = this.fullscreen.is_some();
|
||||
this.window_flags.set(WindowFlags::MARKER_FULLSCREEN, is_fullscreen);
|
||||
let new_flags = this.window_flags;
|
||||
|
||||
drop(this);
|
||||
old_flags.apply_diff(window, new_flags, set_client_rect);
|
||||
}
|
||||
|
||||
pub fn refresh_window_state(this: MutexGuard<Self>, window: HWND, set_client_rect: Option<RECT>) {
|
||||
Self::set_window_flags(this, window, set_client_rect, |_| ());
|
||||
}
|
||||
|
||||
pub fn set_window_flags_in_place<F>(&mut self, f: F)
|
||||
where F: FnOnce(&mut WindowFlags)
|
||||
{
|
||||
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>
|
||||
where F: FnOnce(&mut CursorFlags)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if self.contains(WindowFlags::TRANSPARENT) {
|
||||
// Is this necessary? The docs say that WS_EX_LAYERED requires a windows class without
|
||||
// CS_OWNDC, and Winit windows have that flag set.
|
||||
style_ex |= WS_EX_LAYERED;
|
||||
}
|
||||
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,
|
||||
false => winuser::SW_HIDE
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
if diff.contains(WindowFlags::ALWAYS_ON_TOP) {
|
||||
unsafe {
|
||||
winuser::SetWindowPos(
|
||||
window,
|
||||
match new.contains(WindowFlags::ALWAYS_ON_TOP) {
|
||||
true => winuser::HWND_TOPMOST,
|
||||
false => winuser::HWND_NOTOPMOST,
|
||||
},
|
||||
0, 0, 0, 0,
|
||||
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,
|
||||
false => winuser::SW_RESTORE
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if diff != WindowFlags::empty() {
|
||||
let (style, style_ex) = new.to_window_styles();
|
||||
|
||||
unsafe {
|
||||
winuser::SendMessageW(window, *events_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 1, 0);
|
||||
|
||||
winuser::SetWindowLongW(window, winuser::GWL_STYLE, style as _);
|
||||
winuser::SetWindowLongW(window, winuser::GWL_EXSTYLE, style_ex as _);
|
||||
|
||||
match set_client_rect.and_then(|r| util::adjust_window_rect_with_styles(window, style, style_ex, r)) {
|
||||
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(),
|
||||
x, y, w, h,
|
||||
winuser::SWP_NOZORDER
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
},
|
||||
None => {
|
||||
// Refresh the window frame.
|
||||
winuser::SetWindowPos(
|
||||
window,
|
||||
ptr::null_mut(),
|
||||
0, 0, 0, 0,
|
||||
winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOMOVE
|
||||
| winuser::SWP_NOSIZE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
}
|
||||
}
|
||||
winuser::SendMessageW(window, *events_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue