#[cfg(all(target_os = "macos", feature = "workaround-winit-4441"))]
mod macos_drag_back_fix;
mod monitors;
mod state;
mod systems;
mod types;
mod window_ext;
#[cfg(all(target_os = "windows", feature = "workaround-winit-4341"))]
mod windows_dpi_fix;
#[cfg(all(target_os = "linux", feature = "workaround-winit-4445"))]
mod x11_frame_extents;
use std::path::PathBuf;
use bevy::prelude::*;
use bevy::window::PrimaryWindow;
#[cfg(all(target_os = "macos", feature = "workaround-winit-4441"))]
pub use macos_drag_back_fix::DragBackSizeProtection;
pub use monitors::CurrentMonitor;
pub use monitors::MonitorInfo;
use monitors::MonitorPlugin;
pub use monitors::Monitors;
use monitors::init_monitors;
use types::RestoreWindowConfig;
use types::TargetPosition;
pub use types::WindowTargetLoaded;
use types::X11FrameCompensated;
pub use window_ext::WindowExt;
pub struct WindowManagerPlugin;
impl WindowManagerPlugin {
#[must_use]
#[expect(clippy::expect_used, reason = "fail fast if path cannot be determined")]
pub fn with_app_name(app_name: impl Into<String>) -> impl Plugin {
WindowManagerPluginCustomPath {
path: state::get_state_path_for_app(&app_name.into())
.expect("Could not determine state file path"),
}
}
#[must_use]
pub fn with_path(path: impl Into<PathBuf>) -> impl Plugin {
WindowManagerPluginCustomPath { path: path.into() }
}
}
impl Plugin for WindowManagerPlugin {
#[expect(clippy::expect_used, reason = "fail fast if path cannot be determined")]
fn build(&self, app: &mut App) {
let path = state::get_default_state_path().expect("Could not determine state file path");
build_plugin(app, path);
}
}
struct WindowManagerPluginCustomPath {
path: PathBuf,
}
impl Plugin for WindowManagerPluginCustomPath {
fn build(&self, app: &mut App) { build_plugin(app, self.path.clone()); }
}
fn hide_window_on_creation(add: On<Add, PrimaryWindow>, mut windows: Query<&mut Window>) {
debug!(
"[hide_window_on_creation] Observer fired for entity {:?}",
add.entity
);
if let Ok(mut window) = windows.get_mut(add.entity) {
debug!("[hide_window_on_creation] Setting window.visible = false");
window.visible = false;
}
}
fn build_plugin(app: &mut App, path: PathBuf) {
#[cfg(all(target_os = "linux", feature = "workaround-winit-4445"))]
let should_hide = systems::is_wayland();
#[cfg(not(all(target_os = "linux", feature = "workaround-winit-4445")))]
let should_hide = true;
if should_hide {
let mut query = app
.world_mut()
.query_filtered::<&mut Window, With<PrimaryWindow>>();
if let Some(mut window) = query.iter_mut(app.world_mut()).next() {
debug!("[build_plugin] Window already exists, hiding immediately");
window.visible = false;
} else {
debug!("[build_plugin] Window doesn't exist yet, registering observer");
app.add_observer(hide_window_on_creation);
}
} else {
debug!("[build_plugin] Linux X11: skipping window hide for frame extent compensation");
}
#[cfg(all(target_os = "macos", feature = "workaround-winit-4441"))]
macos_drag_back_fix::init(app);
#[cfg(all(target_os = "windows", feature = "workaround-winit-4341"))]
windows_dpi_fix::init(app);
app.add_plugins(MonitorPlugin)
.insert_resource(RestoreWindowConfig { path })
.add_systems(
PreStartup,
(
systems::init_winit_info,
systems::load_target_position,
systems::move_to_target_monitor.run_if(resource_exists::<TargetPosition>),
)
.chain()
.after(init_monitors),
);
#[cfg(all(target_os = "linux", feature = "workaround-winit-4445"))]
app.add_systems(
Update,
x11_frame_extents::compensate_target_position
.run_if(resource_exists::<TargetPosition>)
.run_if(not(resource_exists::<X11FrameCompensated>))
.run_if(not(systems::is_wayland)),
);
app.add_systems(
Update,
systems::restore_primary_window
.run_if(resource_exists::<TargetPosition>)
.run_if(resource_exists::<X11FrameCompensated>),
);
#[cfg(target_os = "linux")]
app.add_systems(
Update,
(
systems::update_wayland_monitor.run_if(systems::is_wayland),
systems::save_window_state
.run_if(not(resource_exists::<TargetPosition>))
.after(systems::update_wayland_monitor),
),
);
#[cfg(not(target_os = "linux"))]
app.add_systems(
Update,
systems::save_window_state.run_if(not(resource_exists::<TargetPosition>)),
);
}