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

Xevion / Pac-Man / 16919127499

12 Aug 2025 07:40PM UTC coverage: 45.274% (-0.7%) from 45.95%
16919127499

push

github

Xevion
refactor: restructure game logic and state management into separate modules

- Moved game logic from `game.rs` to `game/mod.rs` and `game/state.rs` for better organization.
- Updated `App` to utilize the new `Game` struct and its state management.
- Refactored error handling
- Removed unused audio subsystem references

11 of 319 new or added lines in 10 files covered. (3.45%)

4 existing lines in 3 files now uncovered.

891 of 1968 relevant lines covered (45.27%)

413.62 hits per line

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

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

3
use glam::Vec2;
4
use sdl2::event::{Event, WindowEvent};
5
use sdl2::keyboard::Keycode;
6
use sdl2::render::{Canvas, ScaleMode, Texture, TextureCreator};
7
use sdl2::ttf::Sdl2TtfContext;
8
use sdl2::video::{Window, WindowContext};
9
use sdl2::{AudioSubsystem, EventPump, Sdl, VideoSubsystem};
10
use tracing::{error, event};
11

12
use crate::error::{GameError, GameResult};
13

14
use crate::constants::{CANVAS_SIZE, LOOP_TIME, SCALE};
15
use crate::game::Game;
16
use crate::platform::get_platform;
17

18
pub struct App {
19
    game: Game,
20
    canvas: Canvas<Window>,
21
    event_pump: &'static mut EventPump,
22
    backbuffer: Texture<'static>,
23
    paused: bool,
24
    last_tick: Instant,
25
    cursor_pos: Vec2,
26
}
27

28
impl App {
29
    pub fn new() -> GameResult<Self> {
×
30
        let sdl_context: &'static Sdl = Box::leak(Box::new(sdl2::init().map_err(|e| GameError::Sdl(e.to_string()))?));
×
31
        let video_subsystem: &'static VideoSubsystem =
×
32
            Box::leak(Box::new(sdl_context.video().map_err(|e| GameError::Sdl(e.to_string()))?));
×
NEW
33
        let _audio_subsystem: &'static AudioSubsystem =
×
34
            Box::leak(Box::new(sdl_context.audio().map_err(|e| GameError::Sdl(e.to_string()))?));
×
NEW
35
        let _ttf_context: &'static Sdl2TtfContext =
×
36
            Box::leak(Box::new(sdl2::ttf::init().map_err(|e| GameError::Sdl(e.to_string()))?));
×
37
        let event_pump: &'static mut EventPump =
×
38
            Box::leak(Box::new(sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?));
×
39

40
        // Initialize platform-specific console
41
        get_platform().init_console()?;
×
42

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

54
        let mut canvas = window.into_canvas().build().map_err(|e| GameError::Sdl(e.to_string()))?;
×
55
        canvas
×
56
            .set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
×
57
            .map_err(|e| GameError::Sdl(e.to_string()))?;
×
58

59
        let texture_creator: &'static TextureCreator<WindowContext> = Box::leak(Box::new(canvas.texture_creator()));
×
60

NEW
61
        let mut game = Game::new(texture_creator)?;
×
62
        // game.audio.set_mute(cfg!(debug_assertions));
63

64
        let mut backbuffer = texture_creator
×
65
            .create_texture_target(None, CANVAS_SIZE.x, CANVAS_SIZE.y)
×
66
            .map_err(|e| GameError::Sdl(e.to_string()))?;
×
67
        backbuffer.set_scale_mode(ScaleMode::Nearest);
×
68

×
69
        // Initial draw
×
70
        game.draw(&mut canvas, &mut backbuffer)
×
71
            .map_err(|e| GameError::Sdl(e.to_string()))?;
×
72
        game.present_backbuffer(&mut canvas, &backbuffer, glam::Vec2::ZERO)
×
73
            .map_err(|e| GameError::Sdl(e.to_string()))?;
×
74

75
        Ok(Self {
×
76
            game,
×
77
            canvas,
×
78
            event_pump,
×
79
            backbuffer,
×
80
            paused: false,
×
81
            last_tick: Instant::now(),
×
82
            cursor_pos: Vec2::ZERO,
×
83
        })
×
84
    }
×
85

86
    pub fn run(&mut self) -> bool {
×
87
        {
×
88
            let start = Instant::now();
×
89

90
            for event in self.event_pump.poll_iter() {
×
91
                match event {
×
92
                    Event::Window { win_event, .. } => match win_event {
×
93
                        WindowEvent::Hidden => {
94
                            event!(tracing::Level::DEBUG, "Window hidden");
×
95
                        }
96
                        WindowEvent::Shown => {
97
                            event!(tracing::Level::DEBUG, "Window shown");
×
98
                        }
99
                        _ => {}
×
100
                    },
101
                    // It doesn't really make sense to have this available in the browser
102
                    #[cfg(not(target_os = "emscripten"))]
103
                    Event::Quit { .. }
104
                    | Event::KeyDown {
105
                        keycode: Some(Keycode::Escape) | Some(Keycode::Q),
106
                        ..
107
                    } => {
108
                        event!(tracing::Level::INFO, "Exit requested. Exiting...");
×
109
                        return false;
×
110
                    }
111
                    Event::KeyDown {
112
                        keycode: Some(Keycode::P),
113
                        ..
114
                    } => {
115
                        self.paused = !self.paused;
×
116
                        event!(tracing::Level::INFO, "{}", if self.paused { "Paused" } else { "Unpaused" });
×
117
                    }
118
                    Event::KeyDown {
119
                        keycode: Some(Keycode::Space),
120
                        ..
121
                    } => {
×
NEW
122
                        self.game.toggle_debug_mode();
×
123
                    }
×
124
                    Event::KeyDown { keycode: Some(key), .. } => {
×
125
                        self.game.keyboard_event(key);
×
126
                    }
×
127
                    Event::MouseMotion { x, y, .. } => {
×
128
                        // Convert window coordinates to logical coordinates
×
129
                        self.cursor_pos = Vec2::new(x as f32, y as f32);
×
130
                    }
×
131
                    _ => {}
×
132
                }
133
            }
134

135
            let dt = self.last_tick.elapsed().as_secs_f32();
×
136
            self.last_tick = Instant::now();
×
137

×
138
            if !self.paused {
×
139
                self.game.tick(dt);
×
140
                if let Err(e) = self.game.draw(&mut self.canvas, &mut self.backbuffer) {
×
141
                    error!("Failed to draw game: {}", e);
×
142
                }
×
143
                if let Err(e) = self
×
144
                    .game
×
145
                    .present_backbuffer(&mut self.canvas, &self.backbuffer, self.cursor_pos)
×
146
                {
147
                    error!("Failed to present backbuffer: {}", e);
×
148
                }
×
149
            }
×
150

151
            if start.elapsed() < LOOP_TIME {
×
152
                let time = LOOP_TIME.saturating_sub(start.elapsed());
×
153
                if time != Duration::ZERO {
×
154
                    get_platform().sleep(time);
×
155
                }
×
156
            } else {
157
                event!(
×
158
                    tracing::Level::WARN,
×
159
                    "Game loop behind schedule by: {:?}",
×
160
                    start.elapsed() - LOOP_TIME
×
161
                );
162
            }
163

164
            true
×
165
        }
166
    }
×
167
}
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

© 2026 Coveralls, Inc