• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Xevion / Pac-Man / 17503409222

05 Sep 2025 08:10PM UTC coverage: 31.217% (-0.01%) from 31.228%
17503409222

push

github

Xevion
refactor: reorganize game.rs new() into separate functions

0 of 120 new or added lines in 1 file covered. (0.0%)

49 existing lines in 3 files now uncovered.

1067 of 3418 relevant lines covered (31.22%)

795.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/app.rs
1
use std::collections::HashMap;
2
use std::time::{Duration, Instant};
3

4
use crate::error::{GameError, GameResult};
5

6
use crate::constants::{CANVAS_SIZE, LOOP_TIME, SCALE};
7
use crate::formatter;
8
use crate::game::Game;
9
use crate::platform;
10
use sdl2::pixels::PixelFormatEnum;
11
use sdl2::render::RendererInfo;
12
use sdl2::{AudioSubsystem, Sdl};
13
use tracing::debug;
14

15
/// Main application wrapper that manages SDL initialization, window lifecycle, and the game loop.
16
///
17
/// Handles platform-specific setup, maintains consistent frame timing, and delegates
18
/// game logic to the contained `Game` instance. The app manages focus state to
19
/// optimize CPU usage when the window loses focus.
20
pub struct App {
21
    pub game: Game,
22
    last_tick: Instant,
23
    focused: bool,
24
    // Keep SDL alive for the app lifetime so subsystems (audio) are not shut down
25
    _sdl_context: Sdl,
26
    _audio_subsystem: AudioSubsystem,
27
}
28

29
impl App {
30
    /// Initializes SDL subsystems, creates the game window, and sets up the game state.
31
    ///
32
    /// Performs comprehensive initialization including video/audio subsystems,
33
    /// window creation with proper scaling, and canvas configuration. All SDL
34
    /// resources are leaked to maintain 'static lifetimes required by the game architecture.
35
    ///
36
    /// # Errors
37
    ///
38
    /// Returns `GameError::Sdl` if any SDL initialization step fails, or propagates
39
    /// errors from `Game::new()` during game state setup.
40
    pub fn new() -> GameResult<Self> {
×
41
        let sdl_context = sdl2::init().map_err(|e| GameError::Sdl(e.to_string()))?;
×
42
        let video_subsystem = sdl_context.video().map_err(|e| GameError::Sdl(e.to_string()))?;
×
43
        let audio_subsystem = sdl_context.audio().map_err(|e| GameError::Sdl(e.to_string()))?;
×
UNCOV
44
        let event_pump = sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?;
×
45

46
        let window = video_subsystem
×
47
            .window(
×
48
                "Pac-Man",
×
49
                (CANVAS_SIZE.x as f32 * SCALE).round() as u32,
×
50
                (CANVAS_SIZE.y as f32 * SCALE).round() as u32,
×
51
            )
×
52
            .resizable()
×
53
            .position_centered()
×
54
            .build()
×
55
            .map_err(|e| GameError::Sdl(e.to_string()))?;
×
56

57
        #[derive(Debug)]
58
        struct DriverDetail {
59
            info: RendererInfo,
60
            index: usize,
61
        }
62

63
        let drivers: HashMap<&'static str, DriverDetail> = sdl2::render::drivers()
×
64
            .enumerate()
×
65
            .map(|(index, d)| (d.name, DriverDetail { info: d, index }))
×
66
            .collect::<HashMap<_, _>>();
×
67

×
68
        let get_driver =
×
69
            |name: &'static str| -> Option<u32> { drivers.get(name.to_lowercase().as_str()).map(|d| d.index as u32) };
×
70

71
        {
72
            let mut names = drivers.keys().collect::<Vec<_>>();
×
73
            names.sort_by_key(|k| get_driver(k));
×
74
            debug!("Drivers: {names:?}")
×
75
        }
76

77
        // Count the number of times each pixel format is supported by each driver
78
        let pixel_format_counts: HashMap<PixelFormatEnum, usize> = drivers
×
79
            .values()
×
80
            .flat_map(|d| d.info.texture_formats.iter())
×
81
            .fold(HashMap::new(), |mut counts, format| {
×
82
                *counts.entry(*format).or_insert(0) += 1;
×
83
                counts
×
84
            });
×
85

×
86
        debug!("Pixel format counts: {pixel_format_counts:?}");
×
87

88
        let index = get_driver("direct3d");
×
89
        debug!("Driver index: {index:?}");
×
90

91
        let mut canvas = window
×
92
            .into_canvas()
×
93
            .accelerated()
×
94
            // .index(index)
×
95
            .build()
×
96
            .map_err(|e| GameError::Sdl(e.to_string()))?;
×
97

98
        canvas
×
99
            .set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
×
100
            .map_err(|e| GameError::Sdl(e.to_string()))?;
×
101
        debug!("Renderer: {:?}", canvas.info());
×
102

103
        let texture_creator = canvas.texture_creator();
×
104

105
        let game = Game::new(canvas, texture_creator, event_pump)?;
×
106
        // game.audio.set_mute(cfg!(debug_assertions));
107

108
        Ok(App {
×
109
            game,
×
110
            focused: true,
×
111
            last_tick: Instant::now(),
×
112
            _sdl_context: sdl_context,
×
113
            _audio_subsystem: audio_subsystem,
×
114
        })
×
115
    }
×
116

117
    /// Executes a single frame of the game loop with consistent timing and optional sleep.
118
    ///
119
    /// Calculates delta time since the last frame, runs game logic via `game.tick()`,
120
    /// and implements frame rate limiting by sleeping for remaining time if the frame
121
    /// completed faster than the target `LOOP_TIME`. Sleep behavior varies based on
122
    /// window focus to conserve CPU when the game is not active.
123
    ///
124
    /// # Returns
125
    ///
126
    /// `true` if the game should continue running, `false` if the game requested exit.
127
    pub fn run(&mut self) -> bool {
×
128
        {
×
129
            let start = Instant::now();
×
130

×
131
            let dt = self.last_tick.elapsed().as_secs_f32();
×
132
            self.last_tick = start;
×
133

×
134
            // Increment the global tick counter for tracing
×
135
            formatter::increment_tick();
×
136

×
137
            let exit = self.game.tick(dt);
×
138

×
139
            if exit {
×
140
                return false;
×
141
            }
×
142

×
143
            // Sleep if we still have time left
×
144
            if start.elapsed() < LOOP_TIME {
×
145
                let time = LOOP_TIME.saturating_sub(start.elapsed());
×
146
                if time != Duration::ZERO {
×
147
                    platform::sleep(time, self.focused);
×
148
                }
×
149
            }
×
150

151
            true
×
152
        }
153
    }
×
154
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc