Browse Source

Merge branch 'master' into example/time-rtc

pull/27/head
Ian Chamberlain 3 years ago
parent
commit
71f1d2fc83
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 8
      ctru-rs/Cargo.toml
  2. 32
      ctru-rs/build.rs
  3. 178
      ctru-rs/examples/file-explorer.rs
  4. 47
      ctru-rs/examples/thread-basic.rs
  5. 0
      ctru-rs/examples/time-rtc.rs
  6. 1
      ctru-rs/romfs/test-file.txt
  7. 21
      ctru-rs/src/lib.rs
  8. 36
      ctru-rs/src/romfs.rs
  9. 20
      ctru-rs/src/sdmc.rs
  10. 3
      ctru-rs/src/thread.rs

8
ctru-rs/Cargo.toml

@ -11,6 +11,7 @@ crate-type = ["rlib"] @@ -11,6 +11,7 @@ crate-type = ["rlib"]
name = "ctru"
[dependencies]
cfg-if = "1.0"
ctru-sys = { path = "../ctru-sys", version = "0.4" }
const-zero = "0.1.0"
linker-fix-3ds = { git = "https://github.com/Meziu/rust-linker-fix-3ds.git" }
@ -19,6 +20,13 @@ libc = "0.2" @@ -19,6 +20,13 @@ libc = "0.2"
bitflags = "1.0.0"
widestring = "0.2.2"
[build-dependencies]
toml = "0.5"
[dev-dependencies]
ferris-says = "0.2.1"
time = "0.3.7"
[features]
default = ["romfs"]
romfs = []

32
ctru-rs/build.rs

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
use std::path::PathBuf;
fn main() {
// Open Cargo.toml
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let manifest_path = format!("{manifest_dir}/Cargo.toml");
let manifest_str = std::fs::read_to_string(&manifest_path)
.unwrap_or_else(|e| panic!("Could not open {manifest_path}: {e}"));
let manifest_data: toml::Value =
toml::de::from_str(&manifest_str).expect("Could not parse Cargo manifest as TOML");
// Find the romfs setting and compute the path
let romfs_dir_setting = manifest_data
.as_table()
.and_then(|table| table.get("package"))
.and_then(toml::Value::as_table)
.and_then(|table| table.get("metadata"))
.and_then(toml::Value::as_table)
.and_then(|table| table.get("cargo-3ds"))
.and_then(toml::Value::as_table)
.and_then(|table| table.get("romfs_dir"))
.and_then(toml::Value::as_str)
.unwrap_or("romfs");
let romfs_path = PathBuf::from(format!("{manifest_dir}/{romfs_dir_setting}"));
// Check if the romfs path exists so we can compile the module
if romfs_path.exists() {
println!("cargo:rustc-cfg=romfs_exists");
}
println!("cargo:rerun-if-changed={}", manifest_dir);
}

178
ctru-rs/examples/file-explorer.rs

@ -0,0 +1,178 @@ @@ -0,0 +1,178 @@
//! A file explorer which shows off using standard library file system APIs to
//! read the SD card.
use ctru::applets::swkbd::{Button, Swkbd};
use ctru::console::Console;
use ctru::services::hid::KeyPad;
use ctru::services::{Apt, Hid};
use ctru::Gfx;
use std::fs::DirEntry;
use std::path::{Path, PathBuf};
fn main() {
ctru::init();
let apt = Apt::init().unwrap();
let hid = Hid::init().unwrap();
let gfx = Gfx::default();
#[cfg(all(feature = "romfs", romfs_exists))]
let _romfs = ctru::romfs::RomFS::new().unwrap();
FileExplorer::init(&apt, &hid, &gfx).run();
}
struct FileExplorer<'a> {
apt: &'a Apt,
hid: &'a Hid,
gfx: &'a Gfx,
console: Console<'a>,
path: PathBuf,
entries: Vec<DirEntry>,
running: bool,
}
impl<'a> FileExplorer<'a> {
fn init(apt: &'a Apt, hid: &'a Hid, gfx: &'a Gfx) -> Self {
gfx.top_screen.borrow_mut().set_wide_mode(true);
let console = Console::init(gfx.top_screen.borrow_mut());
FileExplorer {
apt,
hid,
gfx,
console,
path: PathBuf::from("/"),
entries: Vec::new(),
running: false,
}
}
fn run(&mut self) {
self.running = true;
self.print_menu();
while self.running && self.apt.main_loop() {
self.hid.scan_input();
let input = self.hid.keys_down();
if input.contains(KeyPad::KEY_START) {
break;
} else if input.contains(KeyPad::KEY_B) {
self.path.pop();
self.console.clear();
self.print_menu();
} else if input.contains(KeyPad::KEY_A) {
self.get_input_and_run(Self::set_next_path);
} else if input.contains(KeyPad::KEY_X) {
self.get_input_and_run(Self::set_exact_path);
}
self.gfx.flush_buffers();
self.gfx.swap_buffers();
self.gfx.wait_for_vblank();
}
}
fn print_menu(&mut self) {
println!("Viewing {}", self.path.display());
let dir_listing = std::fs::read_dir(&self.path).expect("Failed to open path");
self.entries = Vec::new();
for (i, entry) in dir_listing.enumerate() {
match entry {
Ok(entry) => {
println!("{:2} - {}", i, entry.file_name().to_string_lossy());
self.entries.push(entry);
// Paginate the output
if (i + 1) % 20 == 0 {
println!("Press A to go to next page, or Start to exit");
while self.apt.main_loop() {
self.hid.scan_input();
let input = self.hid.keys_down();
if input.contains(KeyPad::KEY_A) {
break;
}
if input.contains(KeyPad::KEY_START) {
self.running = false;
return;
}
self.gfx.wait_for_vblank();
}
}
}
Err(e) => {
println!("{} - Error: {}", i, e);
}
}
}
println!("Start to exit, A to select an entry by number, B to go up a directory, X to set the path.");
}
fn get_input_and_run(&mut self, action: impl FnOnce(&mut Self, String)) {
let mut keyboard = Swkbd::default();
let mut new_path_str = String::new();
match keyboard.get_utf8(&mut new_path_str) {
Ok(Button::Right) => {
// Clicked "OK"
action(self, new_path_str);
}
Ok(Button::Left) => {
// Clicked "Cancel"
}
Ok(Button::Middle) => {
// This button wasn't shown
unreachable!()
}
Err(e) => {
panic!("Error: {:?}", e)
}
}
}
fn set_next_path(&mut self, next_path_index: String) {
let next_path_index: usize = match next_path_index.parse() {
Ok(index) => index,
Err(e) => {
println!("Number parsing error: {}", e);
return;
}
};
let next_entry = match self.entries.get(next_path_index) {
Some(entry) => entry,
None => {
println!("Input number of bounds");
return;
}
};
if !next_entry.file_type().unwrap().is_dir() {
println!("Not a directory: {}", next_path_index);
return;
}
self.console.clear();
self.path = next_entry.path();
self.print_menu();
}
fn set_exact_path(&mut self, new_path_str: String) {
let new_path = Path::new(&new_path_str);
if !new_path.is_dir() {
println!("Not a directory: {}", new_path_str);
return;
}
self.console.clear();
self.path = new_path.to_path_buf();
self.print_menu();
}
}

47
ctru-rs/examples/thread-basic.rs

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
use ctru::console::Console;
use ctru::gfx::Gfx;
use ctru::services::apt::Apt;
use ctru::services::hid::{Hid, KeyPad};
use ctru::thread;
use std::time::Duration;
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());
let prio = thread::current().priority();
println!("Main thread prio: {}\n", prio);
for ix in 0..3 {
thread::Builder::new()
.priority(prio - 1)
.spawn(move || {
let sleep_duration: u64 = 1000 + ix * 250;
let mut i = 0;
loop {
println!("Thread{ix} says {i}");
i += 1;
thread::sleep(Duration::from_millis(sleep_duration));
}
})
.unwrap();
println!("Created thread {ix}");
}
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;
}
}
}

0
ctru-rs/examples/time_rtc.rs → ctru-rs/examples/time-rtc.rs

1
ctru-rs/romfs/test-file.txt

@ -0,0 +1 @@ @@ -0,0 +1 @@
test

21
ctru-rs/src/lib.rs

@ -33,13 +33,30 @@ pub mod applets; @@ -33,13 +33,30 @@ pub mod applets;
pub mod console;
pub mod error;
pub mod gfx;
pub mod sdmc;
pub mod services;
pub mod srv;
pub mod thread;
cfg_if::cfg_if! {
if #[cfg(all(feature = "romfs", romfs_exists))] {
pub mod romfs;
} else {
pub mod romfs {
//! The RomFS folder has not been detected and/or the `romfs` feature has not been enabled.
//!
//! Configure the path in Cargo.toml (the default path is "romfs"). Paths are relative to the
//! `CARGO_MANIFEST_DIR` environment variable, which is the directory containing the manifest of
//! your package.
//!
//! ```toml
//! [package.metadata.cargo-3ds]
//! romfs_dir = "romfs"
//! ```
}
}
}
pub use crate::error::{Error, Result};
pub use crate::gfx::Gfx;
pub use crate::sdmc::Sdmc;
pub use crate::srv::Srv;

36
ctru-rs/src/romfs.rs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
//! This module only gets compiled if the configured RomFS directory is found and the `romfs`
//! feature is enabled.
//!
//! Configure the path in Cargo.toml (the default path is "romfs"). Paths are relative to the
//! `CARGO_MANIFEST_DIR` environment variable, which is the directory containing the manifest of
//! your package.
//!
//! ```toml
//! [package.metadata.cargo-3ds]
//! romfs_dir = "romfs"
//! ```
use std::ffi::CStr;
#[non_exhaustive]
pub struct RomFS;
impl RomFS {
pub fn new() -> crate::Result<Self> {
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap();
let result = unsafe { ctru_sys::romfsMountSelf(mount_name.as_ptr()) };
if result < 0 {
Err(result.into())
} else {
Ok(Self)
}
}
}
impl Drop for RomFS {
fn drop(&mut self) {
let mount_name = CStr::from_bytes_with_nul(b"romfs\0").unwrap();
unsafe { ctru_sys::romfsUnmount(mount_name.as_ptr()) };
}
}

20
ctru-rs/src/sdmc.rs

@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
pub struct Sdmc(());
impl Sdmc {
pub fn init() -> crate::Result<Sdmc> {
unsafe {
let r = ctru_sys::archiveMountSdmc();
if r < 0 {
Err(r.into())
} else {
Ok(Sdmc(()))
}
}
}
}
impl Drop for Sdmc {
fn drop(&mut self) {
unsafe { ctru_sys::archiveUnmountAll() };
}
}

3
ctru-rs/src/thread.rs

@ -10,8 +10,7 @@ @@ -10,8 +10,7 @@
//! 3DS-specific threading API
//!
//! While it is possible to create threads on the 3DS using functions found in
//! `std::thread`, the standard API does not expose the ability to set a thread's
//! The standard API does not expose the ability to set a thread's
//! priority level and to pin a thread to a specific CPU core. This module exists
//! to address those and other shortcomings.
//!

Loading…
Cancel
Save