Browse Source

Merge branch 'master' into example/futures-tokio

pull/42/head
AzureMarker 3 years ago
parent
commit
5b75bf5e83
No known key found for this signature in database
GPG Key ID: 47A133F3BF9D03D3
  1. 3
      .gitignore
  2. 1
      ctru-rs/Cargo.toml
  3. 73
      ctru-rs/examples/futures-basic.rs
  4. 39
      ctru-rs/examples/hashmaps.rs
  5. 1
      ctru-rs/src/services/mod.rs
  6. 85
      ctru-rs/src/services/ps.rs

3
.gitignore vendored

@ -1,3 +1,6 @@
target target
Cargo.lock Cargo.lock
.cargo .cargo
# IDE files
.idea

1
ctru-rs/Cargo.toml

@ -25,6 +25,7 @@ toml = "0.5"
[dev-dependencies] [dev-dependencies]
ferris-says = "0.2.1" ferris-says = "0.2.1"
futures = "0.3"
time = "0.3.7" time = "0.3.7"
tokio = { version = "1.16", features = ["rt", "time", "sync", "macros"] } tokio = { version = "1.16", features = ["rt", "time", "sync", "macros"] }

73
ctru-rs/examples/futures-basic.rs

@ -0,0 +1,73 @@
//! This example runs a basic future executor from the `futures` library.
//! Every 60 frames (about 1 second) it prints "Tick" to the console.
//! The executor runs on a separate thread. Internally it yields when it has no more work to do,
//! allowing other threads to run.
//! The example also implements clean shutdown by using a oneshot channel to end the future, thus
//! ending the executor and the thread it runs on.
use ctru::console::Console;
use ctru::services::hid::KeyPad;
use ctru::services::{Apt, Hid};
use ctru::Gfx;
use futures::StreamExt;
fn main() {
ctru::init();
let gfx = Gfx::default();
let hid = Hid::init().expect("Couldn't obtain HID controller");
let apt = Apt::init().expect("Couldn't obtain APT controller");
let _console = Console::init(gfx.top_screen.borrow_mut());
// Give ourselves up to 30% of the system core's time
apt.set_app_cpu_time_limit(30)
.expect("Failed to enable system core");
println!("Starting executor...");
let (exit_sender, mut exit_receiver) = futures::channel::oneshot::channel();
let (mut timer_sender, mut timer_receiver) = futures::channel::mpsc::channel(0);
let executor_thread = ctru::thread::Builder::new()
.affinity(1)
.spawn(move || {
let mut executor = futures::executor::LocalPool::new();
executor.run_until(async move {
loop {
futures::select! {
_ = exit_receiver => break,
_ = timer_receiver.next() => {
println!("Tick");
}
}
}
});
})
.expect("Failed to create executor thread");
println!("Executor started!");
let mut frame_count = 0;
while apt.main_loop() {
hid.scan_input();
if hid.keys_down().contains(KeyPad::KEY_START) {
println!("Shutting down...");
let _ = exit_sender.send(());
let _ = executor_thread.join();
break;
}
frame_count += 1;
if frame_count == 60 {
if let Err(e) = timer_sender.try_send(()) {
println!("Error sending timer message: {e}");
}
frame_count = 0;
}
gfx.flush_buffers();
gfx.swap_buffers();
gfx.wait_for_vblank();
}
}

39
ctru-rs/examples/hashmaps.rs

@ -0,0 +1,39 @@
use ctru::console::Console;
use ctru::gfx::Gfx;
use ctru::services::apt::Apt;
use ctru::services::hid::{Hid, KeyPad};
use ctru::services::ps::Ps;
fn main() {
// Initialize services
ctru::init();
let apt = Apt::init().unwrap();
let hid = Hid::init().unwrap();
let gfx = Gfx::default();
let _console = Console::init(gfx.top_screen.borrow_mut());
// HashMaps generate hashes thanks to the 3DS' criptografically secure generator.
// Sadly, this generator is only active when activating the `Ps` service.
// To do this, we have to make sure the `Ps` service handle is alive for the whole
// run time (or at least, when `HashMaps` are used).
// Not having a living `Ps` instance when using `HashMap`s results in a panic
let _ps = Ps::init().unwrap();
let mut map = std::collections::HashMap::new();
map.insert("A Key!", 102);
map.insert("Another key?", 543);
map.remove("A Key!");
println!("{:#?}", map);
while apt.main_loop() {
gfx.flush_buffers();
gfx.swap_buffers();
gfx.wait_for_vblank();
hid.scan_input();
if hid.keys_down().contains(KeyPad::KEY_START) {
break;
}
}
}

1
ctru-rs/src/services/mod.rs

@ -2,6 +2,7 @@ pub mod apt;
pub mod fs; pub mod fs;
pub mod gspgpu; pub mod gspgpu;
pub mod hid; pub mod hid;
pub mod ps;
pub mod soc; pub mod soc;
pub mod sslc; pub mod sslc;

85
ctru-rs/src/services/ps.rs

@ -0,0 +1,85 @@
//! Process Services (PS) module. This is used for miscellaneous utility tasks, but
//! is particularly important because it is used to generate random data, which
//! is required for common things like [`HashMap`](std::collections::HashMap).
//! See also <https://www.3dbrew.org/wiki/Process_Services>
/// PS handle. This must not be dropped in order for random generation
/// to work (in most cases, the lifetime of an application).
#[non_exhaustive]
pub struct Ps;
#[repr(u32)]
pub enum AESAlgorithm {
CbcEnc,
CbcDec,
CtrEnc,
CtrDec,
CcmEnc,
CcmDec,
}
#[repr(u32)]
pub enum AESKeyType {
Keyslot0D,
Keyslot2D,
Keyslot31,
Keyslot38,
Keyslot32,
Keyslot39Dlp,
Keyslot2E,
KeyslotInvalid,
Keyslot36,
Keyslot39Nfc,
}
impl Ps {
/// Initialize the PS module.
pub fn init() -> crate::Result<Self> {
let r = unsafe { ctru_sys::psInit() };
if r < 0 {
Err(r.into())
} else {
Ok(Self)
}
}
pub fn local_friend_code_seed(&self) -> crate::Result<u64> {
let mut seed: u64 = 0;
let r = unsafe { ctru_sys::PS_GetLocalFriendCodeSeed(&mut seed) };
if r < 0 {
Err(r.into())
} else {
Ok(seed)
}
}
pub fn device_id(&self) -> crate::Result<u32> {
let mut id: u32 = 0;
let r = unsafe { ctru_sys::PS_GetDeviceId(&mut id) };
if r < 0 {
Err(r.into())
} else {
Ok(id)
}
}
pub fn generate_random_bytes(&self, out: &mut [u8]) -> crate::Result<()> {
let r =
unsafe { ctru_sys::PS_GenerateRandomBytes(out as *mut _ as *mut _, out.len() as u32) };
if r < 0 {
Err(r.into())
} else {
Ok(())
}
}
}
impl Drop for Ps {
fn drop(&mut self) {
unsafe {
ctru_sys::psExit();
}
}
}
Loading…
Cancel
Save