Add exclusive fullscreen mode (#925)
* Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
This commit is contained in:
parent
131e67ddc1
commit
5bc3cf18d9
31 changed files with 1452 additions and 605 deletions
|
|
@ -4,7 +4,7 @@ use winapi::{self, shared::windef::HWND};
|
|||
|
||||
pub use self::{
|
||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,54 +3,64 @@ use winapi::{
|
|||
minwindef::{BOOL, DWORD, LPARAM, TRUE, WORD},
|
||||
windef::{HDC, HMONITOR, HWND, LPRECT, POINT},
|
||||
},
|
||||
um::{wingdi, winnt::LONG, winuser},
|
||||
um::{wingdi, winuser},
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::{HashSet, VecDeque},
|
||||
collections::{BTreeSet, VecDeque},
|
||||
io, mem, ptr,
|
||||
};
|
||||
|
||||
use super::{util, EventLoop};
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
monitor::VideoMode,
|
||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||
platform_impl::platform::{
|
||||
dpi::{dpi_to_scale_factor, get_monitor_dpi},
|
||||
window::Window,
|
||||
},
|
||||
};
|
||||
|
||||
/// Win32 implementation of the main `MonitorHandle` object.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug, Clone)]
|
||||
pub struct MonitorHandle {
|
||||
/// Monitor handle.
|
||||
hmonitor: HMonitor,
|
||||
#[derivative(Debug = "ignore")]
|
||||
monitor_info: winuser::MONITORINFOEXW,
|
||||
/// The system name of the monitor.
|
||||
monitor_name: String,
|
||||
/// True if this is the primary monitor.
|
||||
primary: bool,
|
||||
/// The position of the monitor in pixels on the desktop.
|
||||
///
|
||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
||||
position: (i32, i32),
|
||||
/// The current resolution in pixels on the monitor.
|
||||
dimensions: (u32, u32),
|
||||
/// DPI scale factor.
|
||||
hidpi_factor: f64,
|
||||
#[derivative(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct VideoMode {
|
||||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate: u16,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
#[derivative(Debug = "ignore", PartialEq = "ignore", Hash = "ignore")]
|
||||
pub(crate) native_video_mode: wingdi::DEVMODEW,
|
||||
}
|
||||
|
||||
impl VideoMode {
|
||||
pub fn size(&self) -> PhysicalSize {
|
||||
self.size.into()
|
||||
}
|
||||
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.bit_depth
|
||||
}
|
||||
|
||||
pub fn refresh_rate(&self) -> u16 {
|
||||
self.refresh_rate
|
||||
}
|
||||
|
||||
pub fn monitor(&self) -> RootMonitorHandle {
|
||||
RootMonitorHandle {
|
||||
inner: self.monitor.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
pub struct MonitorHandle(HMONITOR);
|
||||
|
||||
// Send is not implemented for HMONITOR, 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
|
||||
#[derive(Debug, Clone)]
|
||||
struct HMonitor(HMONITOR);
|
||||
|
||||
unsafe impl Send for HMonitor {}
|
||||
unsafe impl Send for MonitorHandle {}
|
||||
|
||||
unsafe extern "system" fn monitor_enum_proc(
|
||||
hmonitor: HMONITOR,
|
||||
|
|
@ -59,7 +69,7 @@ unsafe extern "system" fn monitor_enum_proc(
|
|||
data: LPARAM,
|
||||
) -> BOOL {
|
||||
let monitors = data as *mut VecDeque<MonitorHandle>;
|
||||
(*monitors).push_back(MonitorHandle::from_hmonitor(hmonitor));
|
||||
(*monitors).push_back(MonitorHandle::new(hmonitor));
|
||||
TRUE // continue enumeration
|
||||
}
|
||||
|
||||
|
|
@ -79,12 +89,12 @@ pub fn available_monitors() -> VecDeque<MonitorHandle> {
|
|||
pub fn primary_monitor() -> MonitorHandle {
|
||||
const ORIGIN: POINT = POINT { x: 0, y: 0 };
|
||||
let hmonitor = unsafe { winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY) };
|
||||
MonitorHandle::from_hmonitor(hmonitor)
|
||||
MonitorHandle::new(hmonitor)
|
||||
}
|
||||
|
||||
pub fn current_monitor(hwnd: HWND) -> MonitorHandle {
|
||||
let hmonitor = unsafe { winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST) };
|
||||
MonitorHandle::from_hmonitor(hmonitor)
|
||||
MonitorHandle::new(hmonitor)
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
|
|
@ -125,73 +135,69 @@ pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINF
|
|||
}
|
||||
|
||||
impl MonitorHandle {
|
||||
pub(crate) fn from_hmonitor(hmonitor: HMONITOR) -> Self {
|
||||
let monitor_info = get_monitor_info(hmonitor).expect("`GetMonitorInfoW` failed");
|
||||
let place = monitor_info.rcMonitor;
|
||||
let dimensions = (
|
||||
(place.right - place.left) as u32,
|
||||
(place.bottom - place.top) as u32,
|
||||
);
|
||||
MonitorHandle {
|
||||
hmonitor: HMonitor(hmonitor),
|
||||
monitor_name: util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()),
|
||||
primary: util::has_flag(monitor_info.dwFlags, winuser::MONITORINFOF_PRIMARY),
|
||||
position: (place.left as i32, place.top as i32),
|
||||
dimensions,
|
||||
hidpi_factor: dpi_to_scale_factor(get_monitor_dpi(hmonitor).unwrap_or(96)),
|
||||
monitor_info,
|
||||
}
|
||||
pub(crate) fn new(hmonitor: HMONITOR) -> Self {
|
||||
MonitorHandle(hmonitor)
|
||||
}
|
||||
|
||||
pub(crate) fn contains_point(&self, point: &POINT) -> bool {
|
||||
let left = self.position.0 as LONG;
|
||||
let right = left + self.dimensions.0 as LONG;
|
||||
let top = self.position.1 as LONG;
|
||||
let bottom = top + self.dimensions.1 as LONG;
|
||||
point.x >= left && point.x <= right && point.y >= top && point.y <= bottom
|
||||
let monitor_info = get_monitor_info(self.0).unwrap();
|
||||
point.x >= monitor_info.rcMonitor.left
|
||||
&& point.x <= monitor_info.rcMonitor.right
|
||||
&& point.y >= monitor_info.rcMonitor.top
|
||||
&& point.y <= monitor_info.rcMonitor.bottom
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> Option<String> {
|
||||
Some(self.monitor_name.clone())
|
||||
let monitor_info = get_monitor_info(self.0).unwrap();
|
||||
Some(util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn native_identifier(&self) -> String {
|
||||
self.monitor_name.clone()
|
||||
self.name().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hmonitor(&self) -> HMONITOR {
|
||||
self.hmonitor.0
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize {
|
||||
self.dimensions.into()
|
||||
let monitor_info = get_monitor_info(self.0).unwrap();
|
||||
PhysicalSize {
|
||||
width: (monitor_info.rcMonitor.right - monitor_info.rcMonitor.left) as f64,
|
||||
height: (monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top) as f64,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition {
|
||||
self.position.into()
|
||||
let monitor_info = get_monitor_info(self.0).unwrap();
|
||||
PhysicalPosition {
|
||||
x: monitor_info.rcMonitor.left as f64,
|
||||
y: monitor_info.rcMonitor.top as f64,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f64 {
|
||||
self.hidpi_factor
|
||||
dpi_to_scale_factor(get_monitor_dpi(self.0).unwrap_or(96))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
||||
// EnumDisplaySettingsExW can return duplicate values (or some of the
|
||||
// fields are probably changing, but we aren't looking at those fields
|
||||
// anyway), so we're using a HashSet deduplicate
|
||||
let mut modes = HashSet::new();
|
||||
// anyway), so we're using a BTreeSet deduplicate
|
||||
let mut modes = BTreeSet::new();
|
||||
let mut i = 0;
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
let device_name = self.monitor_info.szDevice.as_ptr();
|
||||
let monitor_info = get_monitor_info(self.0).unwrap();
|
||||
let device_name = monitor_info.szDevice.as_ptr();
|
||||
let mut mode: wingdi::DEVMODEW = mem::zeroed();
|
||||
mode.dmSize = mem::size_of_val(&mode) as WORD;
|
||||
if winuser::EnumDisplaySettingsExW(device_name, i, &mut mode, 0) == 0 {
|
||||
|
|
@ -205,10 +211,14 @@ impl MonitorHandle {
|
|||
| wingdi::DM_DISPLAYFREQUENCY;
|
||||
assert!(mode.dmFields & REQUIRED_FIELDS == REQUIRED_FIELDS);
|
||||
|
||||
modes.insert(VideoMode {
|
||||
size: (mode.dmPelsWidth, mode.dmPelsHeight),
|
||||
bit_depth: mode.dmBitsPerPel as u16,
|
||||
refresh_rate: mode.dmDisplayFrequency as u16,
|
||||
modes.insert(RootVideoMode {
|
||||
video_mode: VideoMode {
|
||||
size: (mode.dmPelsWidth, mode.dmPelsHeight),
|
||||
bit_depth: mode.dmBitsPerPel as u16,
|
||||
refresh_rate: mode.dmDisplayFrequency as u16,
|
||||
monitor: self.clone(),
|
||||
native_video_mode: mode,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ use crate::{
|
|||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||
PlatformSpecificWindowBuilderAttributes, WindowId,
|
||||
},
|
||||
window::{CursorIcon, Icon, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, Icon, WindowAttributes},
|
||||
};
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
|
|
@ -327,7 +327,7 @@ impl Window {
|
|||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
self.thread_executor.execute_in_thread(move || {
|
||||
WindowState::set_window_flags(window_state.lock(), window.0, None, |f| {
|
||||
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
||||
f.set(WindowFlags::RESIZABLE, resizable)
|
||||
});
|
||||
});
|
||||
|
|
@ -421,80 +421,177 @@ impl Window {
|
|||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
self.thread_executor.execute_in_thread(move || {
|
||||
WindowState::set_window_flags(window_state.lock(), window.0, None, |f| {
|
||||
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
||||
f.set(WindowFlags::MAXIMIZED, maximized)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn fullscreen(&self) -> Option<RootMonitorHandle> {
|
||||
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
let window_state = self.window_state.lock();
|
||||
window_state.fullscreen.clone()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorHandle>) {
|
||||
unsafe {
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
let window = self.window.clone();
|
||||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
match &monitor {
|
||||
&Some(RootMonitorHandle { ref inner }) => {
|
||||
let (x, y): (i32, i32) = inner.position().into();
|
||||
let (width, height): (u32, u32) = inner.size().into();
|
||||
let mut window_state_lock = window_state.lock();
|
||||
let old_fullscreen = window_state_lock.fullscreen.clone();
|
||||
if window_state_lock.fullscreen == fullscreen {
|
||||
return;
|
||||
}
|
||||
window_state_lock.fullscreen = fullscreen.clone();
|
||||
drop(window_state_lock);
|
||||
|
||||
let mut monitor = monitor.clone();
|
||||
self.thread_executor.execute_in_thread(move || {
|
||||
let mut window_state_lock = window_state.lock();
|
||||
self.thread_executor.execute_in_thread(move || {
|
||||
let mut window_state_lock = window_state.lock();
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
window_state_lock.fullscreen = monitor.take();
|
||||
WindowState::refresh_window_state(
|
||||
window_state_lock,
|
||||
window.0,
|
||||
Some(RECT {
|
||||
left: x,
|
||||
top: y,
|
||||
right: x + width as c_int,
|
||||
bottom: y + height as c_int,
|
||||
}),
|
||||
);
|
||||
|
||||
mark_fullscreen(window.0, true);
|
||||
// Save window bounds before entering fullscreen
|
||||
match (&old_fullscreen, &fullscreen) {
|
||||
(&None, &Some(_)) => {
|
||||
let client_rect = util::get_client_rect(window.0).unwrap();
|
||||
window_state_lock.saved_window = Some(SavedWindow {
|
||||
client_rect,
|
||||
dpi_factor: window_state_lock.dpi_factor,
|
||||
});
|
||||
}
|
||||
&None => {
|
||||
self.thread_executor.execute_in_thread(move || {
|
||||
let mut window_state_lock = window_state.lock();
|
||||
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;
|
||||
// Change video mode if we're transitioning to or from exclusive
|
||||
// fullscreen
|
||||
match (&old_fullscreen, &fullscreen) {
|
||||
(&None, &Some(Fullscreen::Exclusive(ref video_mode)))
|
||||
| (
|
||||
&Some(Fullscreen::Borderless(_)),
|
||||
&Some(Fullscreen::Exclusive(ref video_mode)),
|
||||
)
|
||||
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Exclusive(ref video_mode))) =>
|
||||
{
|
||||
let monitor = video_mode.monitor();
|
||||
|
||||
WindowState::refresh_window_state(
|
||||
window_state_lock,
|
||||
let mut display_name = OsStr::new(&monitor.inner.native_identifier())
|
||||
.encode_wide()
|
||||
.collect::<Vec<_>>();
|
||||
// `encode_wide` does not add a null-terminator but
|
||||
// `ChangeDisplaySettingsExW` requires a null-terminated
|
||||
// string, so add it
|
||||
display_name.push(0);
|
||||
|
||||
let mut native_video_mode = video_mode.video_mode.native_video_mode.clone();
|
||||
|
||||
let res = unsafe {
|
||||
winuser::ChangeDisplaySettingsExW(
|
||||
display_name.as_ptr(),
|
||||
&mut native_video_mode,
|
||||
std::ptr::null_mut(),
|
||||
winuser::CDS_FULLSCREEN,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS);
|
||||
debug_assert!(res != winuser::DISP_CHANGE_BADMODE);
|
||||
debug_assert!(res != winuser::DISP_CHANGE_BADPARAM);
|
||||
debug_assert!(res != winuser::DISP_CHANGE_FAILED);
|
||||
assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL);
|
||||
}
|
||||
(&Some(Fullscreen::Exclusive(_)), &None)
|
||||
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => {
|
||||
let res = unsafe {
|
||||
winuser::ChangeDisplaySettingsExW(
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
winuser::CDS_FULLSCREEN,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
debug_assert!(res != winuser::DISP_CHANGE_BADFLAGS);
|
||||
debug_assert!(res != winuser::DISP_CHANGE_BADMODE);
|
||||
debug_assert!(res != winuser::DISP_CHANGE_BADPARAM);
|
||||
debug_assert!(res != winuser::DISP_CHANGE_FAILED);
|
||||
assert_eq!(res, winuser::DISP_CHANGE_SUCCESSFUL);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// There are some scenarios where calling `ChangeDisplaySettingsExW` takes long
|
||||
// enough to execute that the DWM thinks our program has frozen and takes over
|
||||
// our program's window. When that happens, the `SetWindowPos` call below gets
|
||||
// eaten and the window doesn't get set to the proper fullscreen position.
|
||||
//
|
||||
// Calling `PeekMessageW` here notifies Windows that our process is still running
|
||||
// fine, taking control back from the DWM and ensuring that the `SetWindowPos` call
|
||||
// below goes through.
|
||||
let mut msg = mem::zeroed();
|
||||
winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0);
|
||||
}
|
||||
|
||||
// Update window style
|
||||
WindowState::set_window_flags(window_state_lock, window.0, |f| {
|
||||
f.set(WindowFlags::MARKER_FULLSCREEN, fullscreen.is_some())
|
||||
});
|
||||
|
||||
// Update window bounds
|
||||
match &fullscreen {
|
||||
Some(fullscreen) => {
|
||||
let monitor = match fullscreen {
|
||||
Fullscreen::Exclusive(ref video_mode) => video_mode.monitor(),
|
||||
Fullscreen::Borderless(ref monitor) => monitor.clone(),
|
||||
};
|
||||
|
||||
let position: (i32, i32) = monitor.position().into();
|
||||
let size: (u32, u32) = monitor.size().into();
|
||||
|
||||
unsafe {
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
ptr::null_mut(),
|
||||
position.0,
|
||||
position.1,
|
||||
size.0 as i32,
|
||||
size.1 as i32,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let mut window_state_lock = window_state.lock();
|
||||
if let Some(SavedWindow {
|
||||
client_rect,
|
||||
dpi_factor,
|
||||
}) = window_state_lock.saved_window.take()
|
||||
{
|
||||
window_state_lock.dpi_factor = dpi_factor;
|
||||
drop(window_state_lock);
|
||||
|
||||
unsafe {
|
||||
winuser::SetWindowPos(
|
||||
window.0,
|
||||
Some(client_rect),
|
||||
ptr::null_mut(),
|
||||
client_rect.left,
|
||||
client_rect.top,
|
||||
client_rect.right - client_rect.left,
|
||||
client_rect.bottom - client_rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
}
|
||||
|
||||
mark_fullscreen(window.0, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
taskbar_mark_fullscreen(window.0, fullscreen.is_some());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -503,8 +600,7 @@ impl Window {
|
|||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
self.thread_executor.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(), window.0, Some(client_rect), |f| {
|
||||
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
||||
f.set(WindowFlags::DECORATIONS, decorations)
|
||||
});
|
||||
});
|
||||
|
|
@ -516,7 +612,7 @@ impl Window {
|
|||
let window_state = Arc::clone(&self.window_state);
|
||||
|
||||
self.thread_executor.execute_in_thread(move || {
|
||||
WindowState::set_window_flags(window_state.lock(), window.0, None, |f| {
|
||||
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
||||
f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top)
|
||||
});
|
||||
});
|
||||
|
|
@ -769,9 +865,7 @@ unsafe fn init<T: 'static>(
|
|||
let window_state = {
|
||||
let 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(), real_window.0, None, |f| {
|
||||
*f = window_flags
|
||||
});
|
||||
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
|
||||
window_state
|
||||
};
|
||||
|
||||
|
|
@ -865,7 +959,7 @@ pub fn com_initialized() {
|
|||
// 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) {
|
||||
unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) {
|
||||
com_initialized();
|
||||
|
||||
TASKBAR_LIST.with(|task_bar_list_ptr| {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
monitor::MonitorHandle,
|
||||
platform_impl::platform::{event_loop, icon::WinIcon, util},
|
||||
window::{CursorIcon, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
};
|
||||
use parking_lot::MutexGuard;
|
||||
use std::{io, ptr};
|
||||
|
|
@ -29,7 +28,7 @@ pub struct WindowState {
|
|||
pub saved_window: Option<SavedWindow>,
|
||||
pub dpi_factor: f64,
|
||||
|
||||
pub fullscreen: Option<MonitorHandle>,
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
/// Used to supress duplicate redraw attempts when calling `request_redraw` multiple
|
||||
/// times in `EventsCleared`.
|
||||
pub queued_out_of_band_redraw: bool,
|
||||
|
|
@ -84,6 +83,7 @@ bitflags! {
|
|||
WindowFlags::RESIZABLE.bits |
|
||||
WindowFlags::MAXIMIZED.bits
|
||||
);
|
||||
const FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits;
|
||||
const NO_DECORATIONS_AND_MASK = !WindowFlags::RESIZABLE.bits;
|
||||
const INVISIBLE_AND_MASK = !WindowFlags::MAXIMIZED.bits;
|
||||
}
|
||||
|
|
@ -122,32 +122,16 @@ impl WindowState {
|
|||
self.window_flags
|
||||
}
|
||||
|
||||
pub fn set_window_flags<F>(
|
||||
mut this: MutexGuard<'_, Self>,
|
||||
window: HWND,
|
||||
set_client_rect: Option<RECT>,
|
||||
f: F,
|
||||
) where
|
||||
pub fn set_window_flags<F>(mut this: MutexGuard<'_, Self>, window: HWND, 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, |_| ());
|
||||
old_flags.apply_diff(window, new_flags);
|
||||
}
|
||||
|
||||
pub fn set_window_flags_in_place<F>(&mut self, f: F)
|
||||
|
|
@ -185,6 +169,7 @@ impl WindowFlags {
|
|||
fn mask(mut self) -> WindowFlags {
|
||||
if self.contains(WindowFlags::MARKER_FULLSCREEN) {
|
||||
self &= WindowFlags::FULLSCREEN_AND_MASK;
|
||||
self |= WindowFlags::FULLSCREEN_OR_MASK;
|
||||
}
|
||||
if !self.contains(WindowFlags::VISIBLE) {
|
||||
self &= WindowFlags::INVISIBLE_AND_MASK;
|
||||
|
|
@ -236,7 +221,7 @@ impl WindowFlags {
|
|||
}
|
||||
|
||||
/// 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>) {
|
||||
fn apply_diff(mut self, window: HWND, mut new: WindowFlags) {
|
||||
self = self.mask();
|
||||
new = new.mask();
|
||||
|
||||
|
|
@ -295,45 +280,20 @@ impl WindowFlags {
|
|||
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
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
}
|
||||
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::SWP_NOACTIVATE,
|
||||
);
|
||||
}
|
||||
let mut flags = winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOMOVE
|
||||
| winuser::SWP_NOSIZE
|
||||
| winuser::SWP_FRAMECHANGED;
|
||||
|
||||
// We generally don't want style changes here to affect window
|
||||
// focus, but for fullscreen windows they must be activated
|
||||
// (i.e. focused) so that they appear on top of the taskbar
|
||||
if !new.contains(WindowFlags::MARKER_FULLSCREEN) {
|
||||
flags |= winuser::SWP_NOACTIVATE;
|
||||
}
|
||||
|
||||
// Refresh the window frame
|
||||
winuser::SetWindowPos(window, ptr::null_mut(), 0, 0, 0, 0, flags);
|
||||
winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 0, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue