1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Extension trait for `Window` providing monitor-aware methods.
use bevy::prelude::*;
use bevy::window::MonitorSelection;
use bevy::window::WindowMode;
use bevy::window::WindowPosition;
use crate::MonitorInfo;
use crate::Monitors;
/// Extension trait for `Window` providing monitor-aware methods.
///
/// Import this trait to access methods that require monitor information.
pub trait WindowExt {
/// Get the monitor this window is currently on.
///
/// If the window position is unknown or outside all monitors, returns the
/// closest monitor (or monitor 0 as a last resort).
///
/// # Example
///
/// ```ignore
/// use bevy_window_manager::WindowExt;
///
/// fn show_monitor(window: &Window, monitors: Res<Monitors>) {
/// let monitor = window.monitor(&monitors);
/// println!("Window is on monitor {}", monitor.index);
/// }
/// ```
fn monitor<'a>(&self, monitors: &'a Monitors) -> &'a MonitorInfo;
/// Detect the effective window mode, including macOS green button detection.
///
/// On macOS, clicking the green "maximize" button makes the window fill the
/// screen, but `window.mode` remains `Windowed`. This method detects that case
/// and returns `BorderlessFullscreen` with the correct monitor selection.
///
/// # Returns
///
/// A properly populated [`WindowMode`]:
/// - If `window.mode` is exclusive fullscreen, returns it unchanged (OS-managed)
/// - If window fills a monitor, returns `BorderlessFullscreen` with [`MonitorSelection::Index`]
/// set to the correct monitor
/// - Otherwise returns `Windowed`
///
/// # Example
///
/// ```ignore
/// use bevy_window_manager::WindowExt;
///
/// fn check_mode(window: &Window, monitors: Res<Monitors>) {
/// let effective = window.effective_mode(&monitors);
/// // effective reflects what the user actually sees,
/// // even if window.mode says Windowed
/// }
/// ```
fn effective_mode(&self, monitors: &Monitors) -> WindowMode;
/// Set window position and size in one call.
///
/// This is a convenience method that sets both `window.position` to
/// [`WindowPosition::At`] and calls `resolution.set_physical_resolution`.
fn set_position_and_size(&mut self, position: IVec2, size: UVec2);
}
impl WindowExt for Window {
fn monitor<'a>(&self, monitors: &'a Monitors) -> &'a MonitorInfo {
let WindowPosition::At(pos) = self.position else {
return monitors.first();
};
// Use window center for monitor detection because:
// - It correctly handles windows spanning monitor boundaries
// - It avoids Windows invisible border offset (winit #4107) where maximized/snapped windows
// report top-left outside monitor bounds
let center_x = pos.x + (self.physical_width() / 2) as i32;
let center_y = pos.y + (self.physical_height() / 2) as i32;
monitors.closest_to(center_x, center_y)
}
fn effective_mode(&self, monitors: &Monitors) -> WindowMode {
// Trust exclusive fullscreen - OS manages this mode
if matches!(self.mode, WindowMode::Fullscreen(_, _)) {
return self.mode;
}
// Can't determine effective mode without monitors (e.g., laptop lid closed).
// Returns `self.mode` which may be stale (won't reflect macOS green button exit).
// This is acceptable because `save_window_state` skips saving when monitors are
// empty, so stale values won't be persisted.
if monitors.is_empty() {
return self.mode;
}
// For Windowed and BorderlessFullscreen, check actual screen coverage
// to detect macOS green button fullscreen (Bevy doesn't update window.mode).
// On Wayland, position is unavailable so we can only trust self.mode.
let WindowPosition::At(pos) = self.position else {
return self.mode;
};
let monitor = self.monitor(monitors);
// Check if window spans full width and reaches bottom of monitor.
// We check edges rather than origin+size because on some displays
// (e.g., primary monitor with menu bar), the reported position may
// be offset even when the window visually fills the entire screen.
let full_width = self.physical_width() == monitor.size.x;
let left_aligned = pos.x == monitor.position.x;
let reaches_bottom =
pos.y + self.physical_height() as i32 == monitor.position.y + monitor.size.y as i32;
if full_width && left_aligned && reaches_bottom {
WindowMode::BorderlessFullscreen(MonitorSelection::Index(monitor.index))
} else {
WindowMode::Windowed
}
}
fn set_position_and_size(&mut self, position: IVec2, size: UVec2) {
self.position = WindowPosition::At(position);
self.resolution.set_physical_resolution(size.x, size.y);
}
}