monitor_info/
monitor_info.rs

1//! Displays information about available monitors (displays).
2
3use bevy::{
4    camera::RenderTarget,
5    prelude::*,
6    window::{ExitCondition, Monitor, WindowMode, WindowRef},
7};
8
9fn main() {
10    App::new()
11        .add_plugins(DefaultPlugins.set(WindowPlugin {
12            primary_window: None,
13            exit_condition: ExitCondition::DontExit,
14            ..default()
15        }))
16        .add_systems(Update, (update, close_on_esc))
17        .run();
18}
19
20#[derive(Component)]
21struct MonitorRef(Entity);
22
23fn update(
24    mut commands: Commands,
25    monitors_added: Query<(Entity, &Monitor), Added<Monitor>>,
26    mut monitors_removed: RemovedComponents<Monitor>,
27    monitor_refs: Query<(Entity, &MonitorRef)>,
28) {
29    for (entity, monitor) in monitors_added.iter() {
30        // Spawn a new window on each monitor
31        let name = monitor.name.clone().unwrap_or_else(|| "<no name>".into());
32        let size = format!("{}x{}px", monitor.physical_height, monitor.physical_width);
33        let hz = monitor
34            .refresh_rate_millihertz
35            .map(|x| format!("{}Hz", x as f32 / 1000.0))
36            .unwrap_or_else(|| "<unknown>".into());
37        let position = format!(
38            "x={} y={}",
39            monitor.physical_position.x, monitor.physical_position.y
40        );
41        let scale = format!("{:.2}", monitor.scale_factor);
42
43        let window = commands
44            .spawn((
45                Window {
46                    title: name.clone(),
47                    mode: WindowMode::Fullscreen(
48                        MonitorSelection::Entity(entity),
49                        VideoModeSelection::Current,
50                    ),
51                    position: WindowPosition::Centered(MonitorSelection::Entity(entity)),
52                    ..default()
53                },
54                MonitorRef(entity),
55            ))
56            .id();
57
58        let camera = commands
59            .spawn((
60                Camera2d,
61                Camera {
62                    target: RenderTarget::Window(WindowRef::Entity(window)),
63                    ..default()
64                },
65            ))
66            .id();
67
68        let info_text = format!(
69            "Monitor: {name}\nSize: {size}\nRefresh rate: {hz}\nPosition: {position}\nScale: {scale}\n\n",
70        );
71        commands.spawn((
72            Text(info_text),
73            Node {
74                position_type: PositionType::Relative,
75                height: percent(100),
76                width: percent(100),
77                ..default()
78            },
79            UiTargetCamera(camera),
80            MonitorRef(entity),
81        ));
82    }
83
84    // Remove windows for removed monitors
85    for monitor_entity in monitors_removed.read() {
86        for (ref_entity, monitor_ref) in monitor_refs.iter() {
87            if monitor_ref.0 == monitor_entity {
88                commands.entity(ref_entity).despawn();
89            }
90        }
91    }
92}
93
94fn close_on_esc(
95    mut commands: Commands,
96    focused_windows: Query<(Entity, &Window)>,
97    input: Res<ButtonInput<KeyCode>>,
98) {
99    for (window, focus) in focused_windows.iter() {
100        if !focus.focused {
101            continue;
102        }
103
104        if input.just_pressed(KeyCode::Escape) {
105            commands.entity(window).despawn();
106        }
107    }
108}