use std::path::PathBuf;
use bevy::prelude::*;
use bevy::window::MonitorSelection;
use bevy::window::VideoMode;
use bevy::window::VideoModeSelection;
use bevy::window::WindowMode;
use bevy_kana::ToI32;
use bevy_kana::ToU32;
use serde::Deserialize;
use serde::Serialize;
use super::WindowKey;
use super::constants::DEFAULT_SCALE_FACTOR;
use super::constants::SETTLE_STABILITY_SECS;
use super::constants::SETTLE_TIMEOUT_SECS;
#[derive(EntityEvent, Debug, Clone, Reflect)]
pub struct WindowRestored {
pub entity: Entity,
pub window_id: WindowKey,
pub position: Option<IVec2>,
pub size: UVec2,
pub logical_size: UVec2,
pub mode: WindowMode,
pub monitor_index: usize,
}
#[derive(EntityEvent, Debug, Clone, Reflect)]
pub struct WindowRestoreMismatch {
pub entity: Entity,
pub window_id: WindowKey,
pub expected_position: Option<IVec2>,
pub actual_position: Option<IVec2>,
pub expected_size: UVec2,
pub actual_size: UVec2,
pub expected_logical_size: UVec2,
pub actual_logical_size: UVec2,
pub expected_mode: WindowMode,
pub actual_mode: WindowMode,
pub expected_monitor: usize,
pub actual_monitor: usize,
pub expected_scale: f64,
pub actual_scale: f64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Reflect)]
pub(crate) struct SavedVideoMode {
pub physical_size: UVec2,
pub bit_depth: u16,
pub refresh_rate_millihertz: u32,
}
impl SavedVideoMode {
#[must_use]
pub const fn to_video_mode(&self) -> VideoMode {
VideoMode {
physical_size: self.physical_size,
bit_depth: self.bit_depth,
refresh_rate_millihertz: self.refresh_rate_millihertz,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Reflect)]
pub(crate) enum SavedWindowMode {
Windowed,
BorderlessFullscreen,
Fullscreen {
video_mode: Option<SavedVideoMode>,
},
}
impl SavedWindowMode {
#[must_use]
pub const fn to_window_mode(&self, monitor_index: usize) -> WindowMode {
let selection = MonitorSelection::Index(monitor_index);
match self {
Self::Windowed => WindowMode::Windowed,
Self::BorderlessFullscreen => WindowMode::BorderlessFullscreen(selection),
Self::Fullscreen { video_mode: None } => {
WindowMode::Fullscreen(selection, VideoModeSelection::Current)
},
Self::Fullscreen {
video_mode: Some(saved),
} => WindowMode::Fullscreen(
selection,
VideoModeSelection::Specific(saved.to_video_mode()),
),
}
}
#[must_use]
pub const fn is_fullscreen(&self) -> bool { !matches!(self, Self::Windowed) }
}
impl From<&WindowMode> for SavedWindowMode {
fn from(mode: &WindowMode) -> Self {
match mode {
WindowMode::Windowed => Self::Windowed,
WindowMode::BorderlessFullscreen(_) => Self::BorderlessFullscreen,
WindowMode::Fullscreen(_, video_mode_selection) => Self::Fullscreen {
video_mode: match video_mode_selection {
VideoModeSelection::Current => None,
VideoModeSelection::Specific(mode) => Some(SavedVideoMode {
physical_size: mode.physical_size,
bit_depth: mode.bit_depth,
refresh_rate_millihertz: mode.refresh_rate_millihertz,
}),
},
},
}
}
}
pub(crate) struct WindowDecoration {
pub width: u32,
pub height: u32,
}
#[derive(Resource)]
pub(crate) struct WinitInfo {
pub starting_monitor_index: usize,
pub decoration: WindowDecoration,
}
impl WinitInfo {
#[must_use]
pub const fn decoration(&self) -> UVec2 {
UVec2::new(self.decoration.width, self.decoration.height)
}
}
#[derive(Component)]
pub(crate) struct X11FrameCompensated;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
pub(crate) enum WindowRestoreState {
NeedInitialMove,
WaitingForScaleChange,
ApplySize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
pub(crate) enum FullscreenRestoreState {
MoveToMonitor,
WaitForMove,
WaitForSurface,
ApplyMode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
pub(crate) enum MonitorScaleStrategy {
ApplyUnchanged,
CompensateSizeOnly(WindowRestoreState),
LowerToHigher,
HigherToLower(WindowRestoreState),
}
#[derive(Component, Reflect)]
#[reflect(Component)]
pub(crate) struct TargetPosition {
pub position: Option<IVec2>,
pub width: u32,
pub height: u32,
pub logical_width: u32,
pub logical_height: u32,
pub target_scale: f64,
pub starting_scale: f64,
pub scale_strategy: MonitorScaleStrategy,
pub mode: SavedWindowMode,
pub target_monitor_index: usize,
pub fullscreen_state: Option<FullscreenRestoreState>,
pub settle_state: Option<SettleState>,
}
#[derive(Debug, Clone, Reflect)]
pub(crate) struct SettleState {
pub total_timeout: Timer,
pub stability_timer: Timer,
pub last_snapshot: Option<SettleSnapshot>,
}
impl SettleState {
#[must_use]
pub fn new() -> Self {
Self {
total_timeout: Timer::from_seconds(SETTLE_TIMEOUT_SECS, TimerMode::Once),
stability_timer: Timer::from_seconds(SETTLE_STABILITY_SECS, TimerMode::Once),
last_snapshot: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
pub(crate) struct SettleSnapshot {
pub position: Option<IVec2>,
pub size: UVec2,
pub mode: WindowMode,
pub monitor: usize,
}
impl TargetPosition {
#[must_use]
pub const fn position(&self) -> Option<IVec2> { self.position }
#[must_use]
pub const fn size(&self) -> UVec2 { UVec2::new(self.width, self.height) }
#[must_use]
pub const fn logical_size(&self) -> UVec2 {
UVec2::new(self.logical_width, self.logical_height)
}
#[must_use]
pub fn ratio(&self) -> f64 { self.starting_scale / self.target_scale }
#[must_use]
pub fn compensated_position(&self) -> Option<IVec2> {
let ratio = self.ratio();
self.position.map(|pos| {
IVec2::new(
(f64::from(pos.x) * ratio).to_i32(),
(f64::from(pos.y) * ratio).to_i32(),
)
})
}
#[must_use]
pub fn compensated_size(&self) -> UVec2 {
let ratio = self.ratio();
UVec2::new(
(f64::from(self.width) * ratio).to_u32(),
(f64::from(self.height) * ratio).to_u32(),
)
}
}
#[derive(Resource, Clone)]
pub(crate) struct RestoreWindowConfig {
pub path: PathBuf,
pub loaded_states: std::collections::HashMap<WindowKey, WindowState>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct WindowState {
#[serde(alias = "position")]
pub logical_position: Option<(i32, i32)>,
pub logical_width: u32,
pub logical_height: u32,
#[serde(default = "default_monitor_scale")]
pub monitor_scale: f64,
pub monitor_index: usize,
pub mode: SavedWindowMode,
#[serde(default)]
pub app_name: String,
}
const fn default_monitor_scale() -> f64 { DEFAULT_SCALE_FACTOR }
#[derive(Component, Clone, Reflect)]
#[reflect(Component)]
pub struct ManagedWindow {
pub name: String,
}
#[derive(Resource, Default, Clone, Debug, PartialEq, Eq, Reflect)]
#[reflect(Resource)]
pub enum ManagedWindowPersistence {
#[default]
RememberAll,
ActiveOnly,
}
#[derive(Resource, Default)]
pub(crate) struct ManagedWindowRegistry {
pub(crate) names: std::collections::HashSet<String>,
pub(crate) entities: std::collections::HashMap<Entity, String>,
}