1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! Panic handler implementation for [`pros-rs`](https://crates.io/crates/pros-rs).
//! Supports printing a backtrace when running in the simulator.
//! If the `display_panics` feature is enabled, it will also display the panic message on the V5 Brain display.

#![no_std]

extern crate alloc;

use alloc::{format, string::String};

use pros_core::eprintln;
#[cfg(feature = "display_panics")]
use pros_devices::Screen;

#[cfg(target_arch = "wasm32")]
extern "C" {
    /// Prints a backtrace to the debug console
    fn sim_log_backtrace();
}

/// Draw an error box to the screen.
///
/// This function is internally used by the pros-rs panic handler for displaying
/// panic messages graphically before exiting.
#[cfg(feature = "display_panics")]
fn draw_error(
    screen: &mut pros_devices::screen::Screen,
    msg: &str,
) -> Result<(), pros_devices::screen::ScreenError> {
    const ERROR_BOX_MARGIN: i16 = 16;
    const ERROR_BOX_PADDING: i16 = 16;
    const LINE_MAX_WIDTH: usize = 52;

    let error_box_rect = pros_devices::screen::Rect::new(
        ERROR_BOX_MARGIN,
        ERROR_BOX_MARGIN,
        Screen::HORIZONTAL_RESOLUTION - ERROR_BOX_MARGIN,
        Screen::VERTICAL_RESOLUTION - ERROR_BOX_MARGIN,
    );

    screen.fill(&error_box_rect, pros_devices::color::Rgb::RED)?;
    screen.stroke(&error_box_rect, pros_devices::color::Rgb::WHITE)?;

    let mut buffer = String::new();
    let mut line: i16 = 0;

    for (i, character) in msg.char_indices() {
        if !character.is_ascii_control() {
            buffer.push(character);
        }

        if character == '\n' || ((buffer.len() % LINE_MAX_WIDTH == 0) && (i > 0)) {
            screen.fill(
                &pros_devices::screen::Text::new(
                    buffer.as_str(),
                    pros_devices::screen::TextPosition::Point(
                        ERROR_BOX_MARGIN + ERROR_BOX_PADDING,
                        ERROR_BOX_MARGIN + ERROR_BOX_PADDING + (line * Screen::LINE_HEIGHT),
                    ),
                    pros_devices::screen::TextFormat::Small,
                ),
                pros_devices::color::Rgb::WHITE,
            )?;

            line += 1;
            buffer.clear();
        }
    }

    screen.fill(
        &pros_devices::screen::Text::new(
            buffer.as_str(),
            pros_devices::screen::TextPosition::Point(
                ERROR_BOX_MARGIN + ERROR_BOX_PADDING,
                ERROR_BOX_MARGIN + ERROR_BOX_PADDING + (line * Screen::LINE_HEIGHT),
            ),
            pros_devices::screen::TextFormat::Small,
        ),
        pros_devices::color::Rgb::WHITE,
    )?;

    Ok(())
}

#[panic_handler]
/// The panic handler for pros-rs.
pub fn panic(info: &core::panic::PanicInfo<'_>) -> ! {
    let current_task = pros_core::task::current();

    let task_name = current_task.name().unwrap_or_else(|_| "<unknown>".into());

    // task 'User Initialization (PROS)' panicked at src/lib.rs:22:1:
    // panic message here
    let msg = format!("task '{task_name}' {info}");

    eprintln!("{msg}");

    unsafe {
        #[cfg(feature = "display_panics")]
        draw_error(&mut Screen::new(), &msg).unwrap_or_else(|err| {
            eprintln!("Failed to draw error message to screen: {err}");
        });

        #[cfg(target_arch = "wasm32")]
        sim_log_backtrace();

        pros_sys::exit(1);
    }
}