Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add error applet support + error applet panic hook #162

Merged
merged 17 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions ctru-rs/src/applets/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Error applet
//!
//! This applet displays error text as a pop-up message on the lower screen.
FenrirWolf marked this conversation as resolved.
Show resolved Hide resolved
#![doc(alias = "Error")]
FenrirWolf marked this conversation as resolved.
Show resolved Hide resolved

use crate::services::{apt::Apt, gfx::Gfx};

use ctru_sys::errorConf;

/// Configuration struct to set up the Error applet.
#[doc(alias = "errorConf")]
pub struct ErrorApplet {
FenrirWolf marked this conversation as resolved.
Show resolved Hide resolved
state: Box<errorConf>,
}

/// The kind of error applet to display.
#[doc(alias = "errorType")]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum Kind {
/// Error text is centered in the error applet window.
Center = ctru_sys::ERROR_TEXT,
/// Error text starts at the top of the error applet window.
Top = ctru_sys::ERROR_TEXT_WORD_WRAP,
AzureMarker marked this conversation as resolved.
Show resolved Hide resolved
}

impl ErrorApplet {
/// Initialize the error applet with the provided text kind.
#[doc(alias = "errorInit")]
pub fn new(kind: Kind) -> Self {
let mut state = Box::<errorConf>::default();
AzureMarker marked this conversation as resolved.
Show resolved Hide resolved

unsafe { ctru_sys::errorInit(state.as_mut(), kind as _, 0) };
AzureMarker marked this conversation as resolved.
Show resolved Hide resolved

Self { state }
}

/// Sets the error text to display.
#[doc(alias = "errorText")]
pub fn set_text(&mut self, text: &str) {
for (idx, code_unit) in text
.encode_utf16()
AzureMarker marked this conversation as resolved.
Show resolved Hide resolved
.chain(std::iter::once(0))
FenrirWolf marked this conversation as resolved.
Show resolved Hide resolved
.take(self.state.Text.len() - 1)
.enumerate()
{
self.state.Text[idx] = code_unit;
}
}

/// Launches the error applet.
#[doc(alias = "errorDisp")]
pub fn launch(&mut self, _apt: &Apt, _gfx: &Gfx) {
unsafe { ctru_sys::errorDisp(self.state.as_mut()) }
}
}
1 change: 1 addition & 0 deletions ctru-rs/src/applets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
//!
//! Applets block execution of the thread that launches them as long as the user doesn't close the applet.

pub mod error;
pub mod mii_selector;
pub mod swkbd;
2 changes: 2 additions & 0 deletions ctru-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ fn main() {
.blocklist_type("u(8|16|32|64)")
.blocklist_type("__builtin_va_list")
.blocklist_type("__va_list")
.blocklist_type("errorReturnCode")
.blocklist_type("errorScreenFlag")
.opaque_type("MiiData")
.derive_default(true)
.wrap_static_fns(true)
Expand Down
18 changes: 18 additions & 0 deletions ctru-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@
pub mod result;
pub use result::*;

// Fun fact: C compilers are allowed to represent enums as the smallest integer type that can hold all of its variants,
// meaning that enums are allowed to be the size of a `c_short` or a `c_char` rather than the size of a `c_int`.
// Libctru's `errorConf` struct contains two enums that depend on this narrowing property for size and alignment purposes,
// and since `bindgen` generates all enums with `c_int` sizing, we have to blocklist those types and manually define them
// here with the proper size.
Comment on lines +21 to +23
Copy link
Member

@ian-h-chamberlain ian-h-chamberlain Feb 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really interesting! Similar to what I mentioned in #166 , maybe we can just force these constants to generate using errorReturnCode as the type instead of c_int?

To catch the struct size/alignment issues, I wonder if we could have caught that using bindgen's layout tests. I think they were omitted before because we didn't have any good way to run them, but with test-runner now we might be able to actually do it in CI. I'll file an issue (edit: #167)

Edit: bindgen also has a .fit_macro_constants(true) we could try to use for this purpose, but I'm not sure if it would help with the actual struct size.

pub const ERROR_UNKNOWN: errorReturnCode = -1;
pub const ERROR_NONE: errorReturnCode = 0;
pub const ERROR_SUCCESS: errorReturnCode = 1;
pub const ERROR_NOT_SUPPORTED: errorReturnCode = 2;
pub const ERROR_HOME_BUTTON: errorReturnCode = 10;
pub const ERROR_SOFTWARE_RESET: errorReturnCode = 11;
pub const ERROR_POWER_BUTTON: errorReturnCode = 12;
pub type errorReturnCode = libc::c_schar;
FenrirWolf marked this conversation as resolved.
Show resolved Hide resolved

pub const ERROR_NORMAL: errorScreenFlag = 0;
pub const ERROR_STEREO: errorScreenFlag = 1;
pub type errorScreenFlag = libc::c_char;

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

/// In lieu of a proper errno function exposed by libc
Expand Down
Loading