mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
Implement set_maximized, get_current_monitor, set_fullscreen and set_decorations for windows (#457)
* Implement set_fullscreen for windows * Implement get_current_monitor for windows * Implement set_maximized * Implement set_decorations for windows * Update CHANGELOG.md * Fixed minor syntax bug for stable rust version * Added support for WindowBuilder::with_maximized * Move all window sized related functions to main thread * Refactor and formatting force_window_active * Remove unused code * Update CHANGELOG.md * Refactor and change keyboard handling code * Reformatting and refactoring * Added back missing link for comment * Fixed set_maximized and set_fullscreen wrong order bug * Call ShowWindow(SW_RESTORE) when restore_saved_window * Sync system maximized status when set_fullscreen * Fixed wrong function name
This commit is contained in:
parent
457c0b7208
commit
bdc01fee1a
|
@ -1,5 +1,7 @@
|
|||
# Unreleased
|
||||
|
||||
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for Windows.
|
||||
- On Windows, `WindowBuilder::with_dimensions` no longer changing monitor display resolution.
|
||||
- Overhauled X11 window geometry calculations. `get_position` and `set_position` are more universally accurate across different window managers, and `get_outer_size` actually works now.
|
||||
- Fixed SIGSEGV/SIGILL crashes on macOS caused by stabilization of the `!` (never) type.
|
||||
- Implement `WindowEvent::HiDPIFactorChanged` for macOS
|
||||
|
|
|
@ -38,6 +38,9 @@ features = [
|
|||
"libloaderapi",
|
||||
"windowsx",
|
||||
"hidusage",
|
||||
"combaseapi",
|
||||
"objbase",
|
||||
"unknwnbase",
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||
|
|
|
@ -25,24 +25,51 @@ fn main() {
|
|||
monitor
|
||||
};
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Hello world!")
|
||||
.with_fullscreen(Some(monitor))
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut is_fullscreen = true;
|
||||
let mut is_maximized = false;
|
||||
let mut decorations = true;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::Closed => return ControlFlow::Break,
|
||||
WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, ..
|
||||
} => return ControlFlow::Break,
|
||||
_ => ()
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::Closed => return ControlFlow::Break,
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match (virtual_code, state) {
|
||||
(winit::VirtualKeyCode::Escape, _) => return ControlFlow::Break,
|
||||
(winit::VirtualKeyCode::F11, winit::ElementState::Pressed) => {
|
||||
is_fullscreen = !is_fullscreen;
|
||||
if !is_fullscreen {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(window.get_current_monitor()));
|
||||
}
|
||||
}
|
||||
(winit::VirtualKeyCode::M, winit::ElementState::Pressed) => {
|
||||
is_maximized = !is_maximized;
|
||||
window.set_maximized(is_maximized);
|
||||
}
|
||||
(winit::VirtualKeyCode::D, winit::ElementState::Pressed) => {
|
||||
decorations = !decorations;
|
||||
window.set_decorations(decorations);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ extern crate lazy_static;
|
|||
extern crate libc;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[macro_use]
|
||||
extern crate winapi;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
|
|
|
@ -27,9 +27,10 @@ use std::sync::Condvar;
|
|||
use std::thread;
|
||||
|
||||
use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT, LRESULT, MAX_PATH};
|
||||
use winapi::shared::windef::{HWND, POINT};
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::shared::windowsx;
|
||||
use winapi::um::{winuser, shellapi, processthreadsapi};
|
||||
use winapi::um::winnt::LONG;
|
||||
|
||||
use platform::platform::event;
|
||||
use platform::platform::Cursor;
|
||||
|
@ -46,6 +47,16 @@ use WindowEvent;
|
|||
use WindowId as SuperWindowId;
|
||||
use events::{Touch, TouchPhase};
|
||||
|
||||
/// 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 rect: RECT,
|
||||
}
|
||||
|
||||
/// Contains information about states and the window that the callback is going to use.
|
||||
#[derive(Clone)]
|
||||
|
@ -58,6 +69,8 @@ pub struct WindowState {
|
|||
pub attributes: WindowAttributes,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
/// Dummy object that allows inserting a window's state.
|
||||
|
@ -227,18 +240,7 @@ impl EventsLoop {
|
|||
pub(super) fn execute_in_thread<F>(&self, function: F)
|
||||
where F: FnMut(Inserter) + Send + 'static
|
||||
{
|
||||
unsafe {
|
||||
let boxed = Box::new(function) as Box<FnMut(_)>;
|
||||
let boxed2 = Box::new(boxed);
|
||||
|
||||
let raw = Box::into_raw(boxed2);
|
||||
|
||||
let res = winuser::PostThreadMessageA(self.thread_id, *EXEC_MSG_ID,
|
||||
raw as *mut () as usize as WPARAM, 0);
|
||||
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
|
||||
// as the events loop is still alive) or if the queue is full.
|
||||
assert!(res != 0, "PostThreadMessage failed ; is the messages queue full?");
|
||||
}
|
||||
self.create_proxy().execute_in_thread(function)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,6 +275,38 @@ impl EventsLoopProxy {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes a function in the background thread.
|
||||
///
|
||||
/// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
|
||||
/// to the unstable FnBox.
|
||||
///
|
||||
/// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is
|
||||
/// removed automatically if the callback receives a `WM_CLOSE` message for the window.
|
||||
pub fn execute_in_thread<F>(&self, function: F)
|
||||
where
|
||||
F: FnMut(Inserter) + Send + 'static,
|
||||
{
|
||||
unsafe {
|
||||
// We are using double-boxing here because it make casting back much easier
|
||||
let boxed = Box::new(function) as Box<FnMut(_)>;
|
||||
let boxed2 = Box::new(boxed);
|
||||
let raw = Box::into_raw(boxed2);
|
||||
|
||||
let res = winuser::PostThreadMessageA(
|
||||
self.thread_id,
|
||||
*EXEC_MSG_ID,
|
||||
raw as *mut () as usize as WPARAM,
|
||||
0,
|
||||
);
|
||||
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
|
||||
// as the events loop is still alive) or if the queue is full.
|
||||
assert!(
|
||||
res != 0,
|
||||
"PostThreadMessage failed ; is the messages queue full?"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
|
@ -361,11 +395,7 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
|||
context_stash.as_mut().unwrap().windows.remove(&window);
|
||||
});
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
},
|
||||
|
||||
winuser::WM_ERASEBKGND => {
|
||||
1
|
||||
},
|
||||
},
|
||||
|
||||
winuser::WM_PAINT => {
|
||||
use events::WindowEvent::Refresh;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::{DWORD, LPARAM, BOOL, TRUE};
|
||||
use winapi::shared::windef::{HMONITOR, HDC, LPRECT};
|
||||
use winapi::shared::windef::{HMONITOR, HDC, LPRECT, HWND};
|
||||
use winapi::um::winuser;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
@ -87,6 +87,37 @@ impl EventsLoop {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_current_monitor(handle: HWND) -> MonitorId {
|
||||
unsafe {
|
||||
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
|
||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||
|
||||
let hmonitor = winuser::MonitorFromWindow(handle, winuser::MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
winuser::GetMonitorInfoW(
|
||||
hmonitor,
|
||||
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
|
||||
);
|
||||
|
||||
let place = monitor_info.rcMonitor;
|
||||
let position = (place.left as i32, place.top as i32);
|
||||
let dimensions = (
|
||||
(place.right - place.left) as u32,
|
||||
(place.bottom - place.top) as u32,
|
||||
);
|
||||
|
||||
MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: super::monitor::HMonitor(hmonitor),
|
||||
monitor_name: wchar_as_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
hidpi_factor: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
// we simply get all available monitors and return the one with the `MONITORINFOF_PRIMARY` flag
|
||||
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
|
||||
|
|
|
@ -9,11 +9,11 @@ use std::ptr;
|
|||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::cell::Cell;
|
||||
|
||||
use platform::platform::events_loop;
|
||||
use platform::platform::EventsLoop;
|
||||
use platform::platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use platform::platform::MonitorId;
|
||||
use platform::platform::WindowId;
|
||||
|
||||
use CreationError;
|
||||
|
@ -22,11 +22,14 @@ use MouseCursor;
|
|||
use WindowAttributes;
|
||||
use MonitorId as RootMonitorId;
|
||||
|
||||
use winapi::shared::minwindef::{UINT, WORD, DWORD, BOOL};
|
||||
use winapi::shared::minwindef::{UINT, DWORD, BOOL};
|
||||
use winapi::shared::windef::{HWND, HDC, RECT, POINT};
|
||||
use winapi::shared::hidusage;
|
||||
use winapi::um::{winuser, dwmapi, wingdi, libloaderapi, processthreadsapi};
|
||||
use winapi::um::winnt::{LPCWSTR, LONG};
|
||||
use winapi::um::{winuser, dwmapi, libloaderapi, processthreadsapi};
|
||||
use winapi::um::winnt::{LPCWSTR, LONG, HRESULT};
|
||||
use winapi::um::combaseapi;
|
||||
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
pub struct Window {
|
||||
|
@ -35,11 +38,39 @@ pub struct Window {
|
|||
|
||||
/// The current window state.
|
||||
window_state: Arc<Mutex<events_loop::WindowState>>,
|
||||
|
||||
// The events loop proxy.
|
||||
events_loop_proxy: events_loop::EventsLoopProxy,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
|
||||
// The idea here is that we use the AdjustWindowRectEx function to calculate how much additional
|
||||
// non-client area gets added due to the styles we passed. To make the math simple,
|
||||
// we ask for a zero client rectangle, so that the resulting window is all non-client.
|
||||
// And then we pass in the empty rectangle represented by the dot in the middle,
|
||||
// and the AdjustWindowRectEx expands the rectangle in all dimensions.
|
||||
// We see that it added ten pixels to the left, right, and bottom,
|
||||
// and it added fifty pixels to the top.
|
||||
// From this we can perform the reverse calculation: Instead of expanding the rectangle, we shrink it.
|
||||
unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> BOOL {
|
||||
let mut rc: RECT = mem::zeroed();
|
||||
|
||||
winuser::SetRectEmpty(&mut rc);
|
||||
|
||||
let frc = winuser::AdjustWindowRectEx(&mut rc, style, 0, ex_style);
|
||||
if frc != 0 {
|
||||
prc.left -= rc.left;
|
||||
prc.top -= rc.top;
|
||||
prc.right -= rc.right;
|
||||
prc.bottom -= rc.bottom;
|
||||
}
|
||||
|
||||
frc
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(events_loop: &EventsLoop, w_attr: &WindowAttributes,
|
||||
pl_attr: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
|
||||
|
@ -49,9 +80,11 @@ impl Window {
|
|||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let proxy = events_loop.create_proxy();
|
||||
|
||||
events_loop.execute_in_thread(move |inserter| {
|
||||
// We dispatch an `init` function because of code style.
|
||||
let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter) };
|
||||
let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter, proxy.clone()) };
|
||||
let _ = tx.send(win);
|
||||
});
|
||||
|
||||
|
@ -326,23 +359,244 @@ impl Window {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
unimplemented!()
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
window_state.attributes.maximized = maximized;
|
||||
// we only maximized if we are not in fullscreen
|
||||
if window_state.attributes.fullscreen.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let window = self.window.clone();
|
||||
unsafe {
|
||||
// And because ShowWindow will resize the window
|
||||
// We call it in 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) -> (LONG, LONG) {
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
|
||||
if window_state.attributes.fullscreen.is_none() || window_state.saved_window_info.is_none() {
|
||||
let mut rect: RECT = mem::zeroed();
|
||||
|
||||
winuser::GetWindowRect(self.window.0, &mut rect);
|
||||
|
||||
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),
|
||||
rect,
|
||||
});
|
||||
}
|
||||
|
||||
// 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.attributes.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) {
|
||||
let window_state = self.window_state.lock().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.
|
||||
let saved_window_info = window_state.saved_window_info.as_ref().unwrap();
|
||||
|
||||
let rect = saved_window_info.rect.clone();
|
||||
let window = self.window.clone();
|
||||
let (style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||
|
||||
let maximized = window_state.attributes.maximized;
|
||||
|
||||
// On restore, resize to the previous saved rect size.
|
||||
// And because SetWindowPos will resize the window
|
||||
// We call it in the main thread
|
||||
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::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,
|
||||
);
|
||||
|
||||
// if it was set to maximized when it were fullscreened, we restore it as well
|
||||
winuser::ShowWindow(
|
||||
window.0,
|
||||
if maximized {
|
||||
winuser::SW_MAXIMIZE
|
||||
} else {
|
||||
winuser::SW_RESTORE
|
||||
},
|
||||
);
|
||||
|
||||
mark_fullscreen(window.0, false);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||
unimplemented!()
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
unsafe {
|
||||
match &monitor {
|
||||
&Some(RootMonitorId { ref inner }) => {
|
||||
let pos = inner.get_position();
|
||||
let dim = inner.get_dimensions();
|
||||
let window = self.window.clone();
|
||||
|
||||
let (style, ex_style) = self.set_fullscreen_style();
|
||||
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
winuser::SetWindowLongW(
|
||||
window.0,
|
||||
winuser::GWL_STYLE,
|
||||
((style as DWORD) & !(winuser::WS_CAPTION | winuser::WS_THICKFRAME))
|
||||
as LONG,
|
||||
);
|
||||
|
||||
winuser::SetWindowLongW(
|
||||
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(),
|
||||
pos.0,
|
||||
pos.1,
|
||||
dim.0 as i32,
|
||||
dim.1 as i32,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE
|
||||
| winuser::SWP_FRAMECHANGED,
|
||||
);
|
||||
|
||||
mark_fullscreen(window.0, true);
|
||||
});
|
||||
}
|
||||
&None => {
|
||||
self.restore_saved_window();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut window_state = self.window_state.lock().unwrap();
|
||||
window_state.attributes.fullscreen = monitor;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, _decorations: bool) {
|
||||
unimplemented!()
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
if let Ok(mut window_state) = self.window_state.lock() {
|
||||
if window_state.attributes.decorations == decorations {
|
||||
return;
|
||||
}
|
||||
|
||||
let style_flags = (winuser::WS_CAPTION | winuser::WS_THICKFRAME) as LONG;
|
||||
let ex_style_flags = (winuser::WS_EX_WINDOWEDGE) as LONG;
|
||||
|
||||
// if we are in fullscreen mode, we only change the saved window info
|
||||
if window_state.attributes.fullscreen.is_some() {
|
||||
{
|
||||
let mut saved = window_state.saved_window_info.as_mut().unwrap();
|
||||
|
||||
unsafe {
|
||||
unjust_window_rect(&mut saved.rect, saved.style as _, saved.ex_style as _);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
winuser::AdjustWindowRectEx(
|
||||
&mut saved.rect,
|
||||
saved.style as _,
|
||||
0,
|
||||
saved.ex_style as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
window_state.attributes.decorations = decorations;
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut rect: RECT = mem::zeroed();
|
||||
winuser::GetWindowRect(self.window.0, &mut rect);
|
||||
|
||||
let mut style = winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE);
|
||||
let mut ex_style = winuser::GetWindowLongW(self.window.0, winuser::GWL_EXSTYLE);
|
||||
unjust_window_rect(&mut rect, style as _, ex_style as _);
|
||||
|
||||
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,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
window_state.attributes.decorations = decorations;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
unimplemented!()
|
||||
RootMonitorId {
|
||||
inner: EventsLoop::get_current_monitor(self.window.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,10 +613,17 @@ impl Drop for Window {
|
|||
|
||||
/// A simple non-owning wrapper around a window.
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone)]
|
||||
pub struct WindowWrapper(HWND, HDC);
|
||||
|
||||
// Send is not implemented for HWND and HDC, we have to wrap it and implement it manually.
|
||||
// For more info see:
|
||||
// https://github.com/retep998/winapi-rs/issues/360
|
||||
// https://github.com/retep998/winapi-rs/issues/396
|
||||
unsafe impl Send for WindowWrapper {}
|
||||
|
||||
unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
inserter: events_loop::Inserter) -> Result<Window, CreationError> {
|
||||
inserter: events_loop::Inserter, events_loop_proxy: events_loop::EventsLoopProxy) -> Result<Window, CreationError> {
|
||||
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -375,18 +636,8 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
|||
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as LONG,
|
||||
};
|
||||
|
||||
// switching to fullscreen if necessary
|
||||
// this means adjusting the window's position so that it overlaps the right monitor,
|
||||
// and change the monitor's resolution if necessary
|
||||
let fullscreen = if let Some(RootMonitorId { ref inner }) = window.fullscreen {
|
||||
try!(switch_to_fullscreen(&mut rect, inner));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// computing the style and extended style of the window
|
||||
let (ex_style, style) = if fullscreen || !window.decorations {
|
||||
let (ex_style, style) = if !window.decorations {
|
||||
(winuser::WS_EX_APPWINDOW,
|
||||
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
||||
if pl_attribs.parent.is_some() {
|
||||
|
@ -406,7 +657,7 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
|||
|
||||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (width, height) = if fullscreen || window.dimensions.is_some() {
|
||||
let (width, height) = if window.dimensions.is_some() {
|
||||
let min_dimensions = window.min_dimensions
|
||||
.map(|d| (d.0 as raw::c_int, d.1 as raw::c_int))
|
||||
.unwrap_or((0, 0));
|
||||
|
@ -422,12 +673,6 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
|||
(None, None)
|
||||
};
|
||||
|
||||
let (x, y) = if fullscreen {
|
||||
(Some(rect.left), Some(rect.top))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let mut style = if !window.visible {
|
||||
style
|
||||
} else {
|
||||
|
@ -442,7 +687,7 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
|||
class_name.as_ptr(),
|
||||
title.as_ptr() as LPCWSTR,
|
||||
style | winuser::WS_CLIPSIBLINGS | winuser::WS_CLIPCHILDREN,
|
||||
x.unwrap_or(winuser::CW_USEDEFAULT), y.unwrap_or(winuser::CW_USEDEFAULT),
|
||||
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
|
||||
width.unwrap_or(winuser::CW_USEDEFAULT), height.unwrap_or(winuser::CW_USEDEFAULT),
|
||||
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
||||
ptr::null_mut(), libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
|
@ -481,17 +726,15 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Creating a mutex to track the current window state
|
||||
let window_state = Arc::new(Mutex::new(events_loop::WindowState {
|
||||
cursor: winuser::IDC_ARROW, // use arrow by default
|
||||
cursor_state: CursorState::Normal,
|
||||
attributes: window.clone(),
|
||||
mouse_in_window: false,
|
||||
saved_window_info: None,
|
||||
}));
|
||||
|
||||
inserter.insert(real_window.0, window_state.clone());
|
||||
|
||||
// making the window transparent
|
||||
if window.transparent {
|
||||
let bb = dwmapi::DWM_BLURBEHIND {
|
||||
|
@ -503,17 +746,22 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
|||
|
||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||
}
|
||||
|
||||
// calling SetForegroundWindow if fullscreen
|
||||
if fullscreen {
|
||||
winuser::SetForegroundWindow(real_window.0);
|
||||
}
|
||||
|
||||
// Building the struct.
|
||||
Ok(Window {
|
||||
|
||||
let win = Window {
|
||||
window: real_window,
|
||||
window_state: window_state,
|
||||
})
|
||||
events_loop_proxy
|
||||
};
|
||||
|
||||
win.set_maximized(window.maximized);
|
||||
if let Some(_) = window.fullscreen {
|
||||
win.set_fullscreen(window.fullscreen);
|
||||
force_window_active(win.window.0);
|
||||
}
|
||||
|
||||
inserter.insert(win.window.0, win.window_state.clone());
|
||||
|
||||
Ok(win)
|
||||
}
|
||||
|
||||
unsafe fn register_window_class() -> Vec<u16> {
|
||||
|
@ -544,33 +792,127 @@ unsafe fn register_window_class() -> Vec<u16> {
|
|||
class_name
|
||||
}
|
||||
|
||||
unsafe fn switch_to_fullscreen(rect: &mut RECT, monitor: &MonitorId)
|
||||
-> Result<(), CreationError>
|
||||
{
|
||||
// adjusting the rect
|
||||
{
|
||||
let pos = monitor.get_position();
|
||||
rect.left += pos.0 as LONG;
|
||||
rect.right += pos.0 as LONG;
|
||||
rect.top += pos.1 as LONG;
|
||||
rect.bottom += pos.1 as LONG;
|
||||
|
||||
struct ComInitialized(*mut ());
|
||||
impl Drop for ComInitialized {
|
||||
fn drop(&mut self) {
|
||||
unsafe { combaseapi::CoUninitialize() };
|
||||
}
|
||||
|
||||
// changing device settings
|
||||
let mut screen_settings: wingdi::DEVMODEW = mem::zeroed();
|
||||
screen_settings.dmSize = mem::size_of::<wingdi::DEVMODEW>() as WORD;
|
||||
screen_settings.dmPelsWidth = (rect.right - rect.left) as DWORD;
|
||||
screen_settings.dmPelsHeight = (rect.bottom - rect.top) as DWORD;
|
||||
screen_settings.dmBitsPerPel = 32; // TODO: ?
|
||||
screen_settings.dmFields = wingdi::DM_BITSPERPEL | wingdi::DM_PELSWIDTH | wingdi::DM_PELSHEIGHT;
|
||||
|
||||
let result = winuser::ChangeDisplaySettingsExW(monitor.get_adapter_name().as_ptr(),
|
||||
&mut screen_settings, ptr::null_mut(),
|
||||
winuser::CDS_FULLSCREEN, ptr::null_mut());
|
||||
|
||||
if result != winuser::DISP_CHANGE_SUCCESSFUL {
|
||||
return Err(CreationError::OsError(format!("ChangeDisplaySettings failed: {}", result)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
thread_local!{
|
||||
static COM_INITIALIZED: ComInitialized = {
|
||||
unsafe {
|
||||
combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED);
|
||||
ComInitialized(ptr::null_mut())
|
||||
}
|
||||
};
|
||||
|
||||
static TASKBAR_LIST: Cell<*mut taskbar::ITaskbarList2> = Cell::new(ptr::null_mut());
|
||||
}
|
||||
|
||||
pub fn com_initialized() {
|
||||
COM_INITIALIZED.with(|_| {});
|
||||
}
|
||||
|
||||
// TODO: remove these when they get added to winapi
|
||||
// https://github.com/retep998/winapi-rs/pull/592
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(dead_code)]
|
||||
mod taskbar {
|
||||
use super::{IUnknown,IUnknownVtbl,HRESULT, HWND,BOOL};
|
||||
DEFINE_GUID!{CLSID_TaskbarList,
|
||||
0x56fdf344, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90}
|
||||
|
||||
RIDL!(#[uuid(0x56fdf342, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90)]
|
||||
interface ITaskbarList(ITaskbarListVtbl): IUnknown(IUnknownVtbl) {
|
||||
fn HrInit() -> HRESULT,
|
||||
fn AddTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn DeleteTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn ActivateTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn SetActiveAlt(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
});
|
||||
|
||||
RIDL!(#[uuid(0x602d4995, 0xb13a, 0x429b, 0xa6, 0x6e, 0x19, 0x35, 0xe4, 0x4f, 0x43, 0x17)]
|
||||
interface ITaskbarList2(ITaskbarList2Vtbl): ITaskbarList(ITaskbarListVtbl) {
|
||||
fn MarkFullscreenWindow(
|
||||
hwnd: HWND,
|
||||
fFullscreen: BOOL,
|
||||
) -> HRESULT,
|
||||
});
|
||||
}
|
||||
|
||||
// Reference Implementation:
|
||||
// https://github.com/chromium/chromium/blob/f18e79d901f56154f80eea1e2218544285e62623/ui/views/win/fullscreen_handler.cc
|
||||
//
|
||||
// As per MSDN marking the window as fullscreen should ensure that the
|
||||
// taskbar is moved to the bottom of the Z-order when the fullscreen window
|
||||
// is activated. If the window is not fullscreen, the Shell falls back to
|
||||
// heuristics to determine how the window should be treated, which means
|
||||
// that it could still consider the window as fullscreen. :(
|
||||
unsafe fn 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;
|
||||
use winapi::Interface;
|
||||
|
||||
let hr = combaseapi::CoCreateInstance(
|
||||
&taskbar::CLSID_TaskbarList,
|
||||
ptr::null_mut(),
|
||||
combaseapi::CLSCTX_ALL,
|
||||
&taskbar::ITaskbarList2::uuidof(),
|
||||
&mut task_bar_list as *mut _ as *mut _,
|
||||
);
|
||||
|
||||
if hr != S_OK || (*task_bar_list).HrInit() != S_OK {
|
||||
// In some old windows, the taskbar object could not be created, we just ignore it
|
||||
return;
|
||||
}
|
||||
task_bar_list_ptr.set(task_bar_list)
|
||||
}
|
||||
|
||||
task_bar_list = task_bar_list_ptr.get();
|
||||
(*task_bar_list).MarkFullscreenWindow(handle, if fullscreen { 1 } else { 0 });
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn force_window_active(handle: HWND) {
|
||||
// In some situation, calling SetForegroundWindow could not bring up the window,
|
||||
// This is a little hack which can "steal" the foreground window permission
|
||||
// We only call this function in the window creation, so it should be fine.
|
||||
// See : https://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open
|
||||
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;
|
||||
|
||||
// Simulate a key press and release
|
||||
winuser::SendInput(
|
||||
inputs.len() as _,
|
||||
inputs.as_mut_ptr(),
|
||||
mem::size_of::<winuser::INPUT>() as _,
|
||||
);
|
||||
|
||||
winuser::SetForegroundWindow(handle);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue