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 basic PTM service implementation #198

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

- uses: rust3ds/test-runner/setup@v1
with:
toolchain: nightly-2024-02-18
toolchain: nightly-2024-03-10

- name: Build workspace docs
run: cargo 3ds --verbose doc --verbose --no-deps --workspace
Expand Down
46 changes: 46 additions & 0 deletions ctru-rs/examples/ptm-user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Power-Time Services example.
//!
//! This example shows off common functionality found in the PTM family of system services, like pedometer steps count, battery state reading
//! and some light shows with the notification LED.

use ctru::prelude::*;
use ctru::services::ptm::user::{BatteryLevel, PTMUser};

fn main() {
let apt = Apt::new().unwrap();
let mut hid = Hid::new().unwrap();
let gfx = Gfx::new().unwrap();
let _top_screen = Console::new(gfx.top_screen.borrow_mut());

let ptm_user = PTMUser::new().unwrap();

// Let's gather some simple data with PTM:User
let battery_level = ptm_user.battery_level().unwrap();
let charging = ptm_user.is_charging().unwrap();
let steps = ptm_user.step_count().unwrap();

if battery_level >= BatteryLevel::Low {
println!("The battery level is sufficient to play a while.")
} else {
println!("The battery level is low.")
}

if charging {
println!("The battery is currently charging.")
} else {
println!("The battery is discharging.")
}

println!("You accumulated a total of {steps} steps.");

println!("\x1b[29;16HPress Start to exit");

while apt.main_loop() {
gfx.wait_for_vblank();

hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
}
}
}
1 change: 1 addition & 0 deletions ctru-rs/src/services/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod hid;
pub mod ir_user;
pub mod ndsp;
pub mod ps;
pub mod ptm;
mod reference;
pub mod soc;
pub mod sslc;
Expand Down
11 changes: 11 additions & 0 deletions ctru-rs/src/services/ptm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Power-Time service.
//!
//! This service manages user information such as registered playtime, step count (using the pedometer) and control to various
//! hardware and features to notify the user during play (such as the Notification/Info LED).
#![doc(alias = "led")]
#![doc(alias = "playtime")]
#![doc(alias = "step")]
#![doc(alias = "power")]

pub mod sysm;
pub mod user;
150 changes: 150 additions & 0 deletions ctru-rs/src/services/ptm/sysm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//! PTM SystemMenu service.
//!
//! This sub-service of the Power-Time service family is able to control shutdown/sleep functionality and how those states are
//! communicated to the user (such as via the notification/battery LED).
#[doc(alias = "sleep")]
#[doc(alias = "shutdown")]
#[doc(alias = "led")]
use std::sync::Mutex;
use std::time::Duration;

use crate::error::{Result, ResultCode};
use crate::services::ServiceReference;

static PTMSYSM_ACTIVE: Mutex<()> = Mutex::new(());

/// Handle to the PTM:SysM service.
pub struct PTMSysM {
_service_handler: ServiceReference,
}

impl PTMSysM {
/// Initialize a new service handle.
///
/// # Errors
///
/// This function will return an error if the service was unable to be initialized.
/// Since this service requires no special or elevated permissions, errors are rare in practice.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::ptm::sysm::PTMSysM;
///
/// let ptm_sysm = PTMSysM::new()?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "ptmSysmInit")]
pub fn new() -> Result<Self> {
let handler = ServiceReference::new(
&PTMSYSM_ACTIVE,
|| {
ResultCode(unsafe { ctru_sys::ptmSysmInit() })?;

Ok(())
},
|| unsafe {
ctru_sys::ptmSysmExit();
},
)?;

Ok(Self {
_service_handler: handler,
})
}

/// Try putting the console in sleep mode.
///
/// # Notes
///
/// This request can be denied for various reasons. This does not "force" the console to sleep.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::ptm::sysm::PTMSysM;
/// use std::time::Duration;
///
/// let ptm_sysm = PTMSysM::new()?;
///
/// // Request the activation of sleep mode.
/// ptm_sysm.request_sleep().unwrap();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "PTMSYSM_RequestSleep")]
pub fn request_sleep(&self) -> Result<()> {
ResultCode(unsafe { ctru_sys::PTMSYSM_RequestSleep() })?;

Ok(())
}

/// Request a system shutdown within the given timeout.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::ptm::sysm::PTMSysM;
/// use std::time::Duration;
///
/// let ptm_sysm = PTMSysM::new()?;
///
/// // Shutdown the system (usually the request succeeds immediately).
/// ptm_sysm.request_shutdown(Duration::from_nanos(0)).unwrap();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "PTMSYSM_ShutdownAsync")]
pub fn request_shutdown(&self, timeout: Duration) -> Result<()> {
let timeout = timeout.as_nanos() as u64;

ResultCode(unsafe { ctru_sys::PTMSYSM_ShutdownAsync(timeout) })?;

Ok(())
}

/// Request a system reboot within the given timeout.
///
/// # Example
///
/// ```
/// # let _runner = test_runner::GdbRunner::default();
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::ptm::sysm::PTMSysM;
/// use std::time::Duration;
///
/// let ptm_sysm = PTMSysM::new()?;
///
/// // Reboot the system.
/// ptm_sysm.request_reboot(Duration::from_nanos(0)).unwrap();
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "PTMSYSM_RebootAsync")]
pub fn request_reboot(&self, timeout: Duration) -> Result<()> {
let timeout = timeout.as_nanos() as u64;

ResultCode(unsafe { ctru_sys::PTMSYSM_RebootAsync(timeout) })?;

Ok(())
}
}
Loading
Loading