Browse Source

Finalize examples

pull/134/head
Andrea Ciliberti 1 year ago
parent
commit
9c955441dc
  1. 35
      ctru-rs/examples/audio-filters.rs
  2. 24
      ctru-rs/examples/buttons.rs
  3. 22
      ctru-rs/examples/camera-image.rs
  4. 20
      ctru-rs/examples/file-explorer.rs
  5. 26
      ctru-rs/examples/gfx-3d-mode.rs
  6. 29
      ctru-rs/examples/gfx-bitmap.rs
  7. 12
      ctru-rs/examples/gfx-wide-mode.rs
  8. 57
      ctru-rs/examples/graphics-bitmap.rs
  9. 11
      ctru-rs/examples/hashmaps.rs
  10. 11
      ctru-rs/examples/hello-both-screens.rs
  11. 20
      ctru-rs/examples/hello-world.rs
  12. 15
      ctru-rs/examples/linear-memory.rs
  13. 13
      ctru-rs/examples/mii-selector.rs
  14. 21
      ctru-rs/examples/network-sockets.rs
  15. 9
      ctru-rs/examples/output-3dslink.rs
  16. 20
      ctru-rs/examples/romfs.rs
  17. 20
      ctru-rs/examples/software-keyboard.rs
  18. 14
      ctru-rs/examples/system-configuration.rs
  19. 2
      ctru-rs/examples/thread-basic.rs
  20. 2
      ctru-rs/examples/thread-info.rs
  21. 2
      ctru-rs/examples/thread-locals.rs
  22. 25
      ctru-rs/examples/time-rtc.rs
  23. 30
      ctru-rs/examples/title-info.rs
  24. 14
      ctru-rs/examples/touch-screen.rs
  25. 4
      ctru-rs/src/services/gfx.rs

35
ctru-rs/examples/audio-filters.rs

@ -1,3 +1,7 @@
//! Audio Filters example.
//!
//! This example showcases basic audio functionality using [`Ndsp`].
#![feature(allocator_api)] #![feature(allocator_api)]
use std::f32::consts::PI; use std::f32::consts::PI;
@ -9,17 +13,18 @@ use ctru::services::ndsp::{
AudioFormat, AudioMix, InterpolationType, Ndsp, OutputMode, AudioFormat, AudioMix, InterpolationType, Ndsp, OutputMode,
}; };
// Configuration for the NDSP process and channels.
const SAMPLE_RATE: usize = 22050; const SAMPLE_RATE: usize = 22050;
const SAMPLES_PER_BUF: usize = SAMPLE_RATE / 10; // 2205 const SAMPLES_PER_BUF: usize = SAMPLE_RATE / 10; // 2205
const BYTES_PER_SAMPLE: usize = AudioFormat::PCM16Stereo.size(); const BYTES_PER_SAMPLE: usize = AudioFormat::PCM16Stereo.size();
const AUDIO_WAVE_LENGTH: usize = SAMPLES_PER_BUF * BYTES_PER_SAMPLE; const AUDIO_WAVE_LENGTH: usize = SAMPLES_PER_BUF * BYTES_PER_SAMPLE;
// Note Frequencies // Note frequencies.
const NOTEFREQ: [f32; 7] = [220., 440., 880., 1760., 3520., 7040., 14080.]; const NOTEFREQ: [f32; 7] = [220., 440., 880., 1760., 3520., 7040., 14080.];
// The audio format is Stereo PCM16
// As such, a sample is made up of 2 "Mono" samples (2 * i16 = u32), one for each channel (left and right)
fn fill_buffer(audio_data: &mut [u8], frequency: f32) { fn fill_buffer(audio_data: &mut [u8], frequency: f32) {
// The audio format is Stereo PCM16.
// As such, a sample is made up of 2 "Mono" samples (2 * i16), one for each channel (left and right).
let formatted_data = bytemuck::cast_slice_mut::<_, [i16; 2]>(audio_data); let formatted_data = bytemuck::cast_slice_mut::<_, [i16; 2]>(audio_data);
for (i, chunk) in formatted_data.iter_mut().enumerate() { for (i, chunk) in formatted_data.iter_mut().enumerate() {
@ -44,8 +49,7 @@ fn main() {
let mut note: usize = 4; let mut note: usize = 4;
// Filters // Filter names to display.
let filter_names = [ let filter_names = [
"None", "None",
"Low-Pass", "Low-Pass",
@ -60,19 +64,26 @@ fn main() {
// We set up two wave buffers and alternate between the two, // We set up two wave buffers and alternate between the two,
// effectively streaming an infinitely long sine wave. // effectively streaming an infinitely long sine wave.
// We create a buffer on the LINEAR memory that will hold our audio data.
// It's necessary for the buffer to live on the LINEAR memory sector since it needs to be accessed by the DSP processor.
let mut audio_data1 = Box::new_in([0u8; AUDIO_WAVE_LENGTH], LinearAllocator); let mut audio_data1 = Box::new_in([0u8; AUDIO_WAVE_LENGTH], LinearAllocator);
// Fill the buffer with the first set of data. This simply writes a sine wave into the buffer.
fill_buffer(audio_data1.as_mut_slice(), NOTEFREQ[4]); fill_buffer(audio_data1.as_mut_slice(), NOTEFREQ[4]);
// Clone the original buffer to obtain an equal buffer on the LINEAR memory used for double buffering.
let audio_data2 = audio_data1.clone(); let audio_data2 = audio_data1.clone();
// Setup two wave info objects with the correct configuration and ownership of the audio data.
let mut wave_info1 = Wave::new(audio_data1, AudioFormat::PCM16Stereo, false); let mut wave_info1 = Wave::new(audio_data1, AudioFormat::PCM16Stereo, false);
let mut wave_info2 = Wave::new(audio_data2, AudioFormat::PCM16Stereo, false); let mut wave_info2 = Wave::new(audio_data2, AudioFormat::PCM16Stereo, false);
let mut ndsp = Ndsp::new().expect("Couldn't obtain NDSP controller"); // Setup the NDSP service and its configuration.
// This line isn't needed since the default NDSP configuration already sets the output mode to `Stereo` let mut ndsp = Ndsp::new().expect("Couldn't obtain NDSP controller");
ndsp.set_output_mode(OutputMode::Stereo); ndsp.set_output_mode(OutputMode::Stereo);
// Channel configuration. We use channel zero but any channel would do just fine.
let mut channel_zero = ndsp.channel(0).unwrap(); let mut channel_zero = ndsp.channel(0).unwrap();
channel_zero.set_interpolation(InterpolationType::Linear); channel_zero.set_interpolation(InterpolationType::Linear);
channel_zero.set_sample_rate(SAMPLE_RATE as f32); channel_zero.set_sample_rate(SAMPLE_RATE as f32);
@ -82,6 +93,7 @@ fn main() {
let mix = AudioMix::default(); let mix = AudioMix::default();
channel_zero.set_mix(&mix); channel_zero.set_mix(&mix);
// First set of queueing for the two buffers. The second one will only play after the first one has ended.
channel_zero.queue_wave(&mut wave_info1).unwrap(); channel_zero.queue_wave(&mut wave_info1).unwrap();
channel_zero.queue_wave(&mut wave_info2).unwrap(); channel_zero.queue_wave(&mut wave_info2).unwrap();
@ -93,6 +105,8 @@ fn main() {
filter_names[filter as usize] filter_names[filter as usize]
); );
println!("\x1b[29;16HPress Start to exit");
let mut altern = true; // true is wave_info1, false is wave_info2 let mut altern = true; // true is wave_info1, false is wave_info2
while apt.main_loop() { while apt.main_loop() {
@ -101,14 +115,16 @@ fn main() {
if keys_down.contains(KeyPad::START) { if keys_down.contains(KeyPad::START) {
break; break;
} // break in order to return to hbmenu }
// Note frequency controller using the buttons.
if keys_down.intersects(KeyPad::DOWN) { if keys_down.intersects(KeyPad::DOWN) {
note = note.saturating_sub(1); note = note.saturating_sub(1);
} else if keys_down.intersects(KeyPad::UP) { } else if keys_down.intersects(KeyPad::UP) {
note = std::cmp::min(note + 1, NOTEFREQ.len() - 1); note = std::cmp::min(note + 1, NOTEFREQ.len() - 1);
} }
// Filter controller using the buttons.
let mut update_params = false; let mut update_params = false;
if keys_down.intersects(KeyPad::LEFT) { if keys_down.intersects(KeyPad::LEFT) {
filter -= 1; filter -= 1;
@ -139,12 +155,14 @@ fn main() {
} }
} }
// Double buffer alternation depending on the one used.
let current: &mut Wave = if altern { let current: &mut Wave = if altern {
&mut wave_info1 &mut wave_info1
} else { } else {
&mut wave_info2 &mut wave_info2
}; };
// If the current buffer has finished playing, we can refill it with new data and re-queue it.
let status = current.status(); let status = current.status();
if let Status::Done = status { if let Status::Done = status {
fill_buffer(current.get_buffer_mut().unwrap(), NOTEFREQ[note]); fill_buffer(current.get_buffer_mut().unwrap(), NOTEFREQ[note]);
@ -154,7 +172,6 @@ fn main() {
altern = !altern; altern = !altern;
} }
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

24
ctru-rs/examples/buttons.rs

@ -1,3 +1,7 @@
//! Buttons example.
//!
//! This example showcases how to retrieve button inputs from the console's HID.
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -18,28 +22,28 @@ fn main() {
// Scan for user input on the current frame. // Scan for user input on the current frame.
hid.scan_input(); hid.scan_input();
// Get information about which keys were held down on this frame // Get information about which keys were held down on this frame.
let keys = hid.keys_held(); let keys = hid.keys_held();
// We only want to print when the keys we're holding now are different // We only want to print when the keys we're holding now are different
// from what they were on the previous frame // from what they were on the previous frame.
if keys != old_keys { if keys != old_keys {
// Clear the screen // Clear the screen.
console.clear(); console.clear();
// We print these again because we just cleared the screen above // We print these again because we just cleared the screen above.
println!("Hi there! Try pressing a button"); println!("Hi there! Try pressing a button");
println!("\x1b[29;16HPress Start to exit"); println!("\x1b[29;16HPress Start to exit");
// Move the cursor back to the top of the screen // Move the cursor back to the top of the screen.
println!("\x1b[3;0H"); println!("\x1b[3;0H");
// Print to the screen depending on which keys were held. // Print to the screen depending on which keys were held.
// //
// The .contains() method checks for all of the provided keys, // The `.contains()` method checks for all of the provided keys,
// and the .intersects() method checks for any of the provided keys. // and the `.intersects()` method checks for any of the provided keys.
// //
// You can also use the .bits() method to do direct comparisons on // You can also use the `.bits()` method to do direct comparisons on
// the underlying bits // the underlying bits
if keys.contains(KeyPad::A) { if keys.contains(KeyPad::A) {
@ -54,13 +58,13 @@ fn main() {
if keys.intersects(KeyPad::L | KeyPad::R | KeyPad::ZL | KeyPad::ZR) { if keys.intersects(KeyPad::L | KeyPad::R | KeyPad::ZL | KeyPad::ZR) {
println!("You held a shoulder button!"); println!("You held a shoulder button!");
} }
if keys.intersects(KeyPad::START) { if keys.contains(KeyPad::START) {
println!("See ya!"); println!("See ya!");
break; break;
} }
} }
// Save our current key presses for the next frame // Save our current key presses for the next frame.
old_keys = keys; old_keys = keys;
gfx.wait_for_vblank(); gfx.wait_for_vblank();

22
ctru-rs/examples/camera-image.rs

@ -1,3 +1,7 @@
//! Camera image example.
//!
//! This example demonstrates how to use the built-in cameras to take a picture and display it to the screen.
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::cam::{Cam, Camera, OutputFormat, ShutterSound, ViewSize}; use ctru::services::cam::{Cam, Camera, OutputFormat, ShutterSound, ViewSize};
use ctru::services::gfx::{Flush, Screen, Swap}; use ctru::services::gfx::{Flush, Screen, Swap};
@ -8,7 +12,7 @@ use std::time::Duration;
const WIDTH: usize = 400; const WIDTH: usize = 400;
const HEIGHT: usize = 240; const HEIGHT: usize = 240;
// The screen size is the width and height multiplied by 2 (RGB565 store pixels in 2 bytes) // The screen size is the width and height multiplied by 2 (RGB565 store pixels in 2 bytes).
const BUF_SIZE: usize = WIDTH * HEIGHT * 2; const BUF_SIZE: usize = WIDTH * HEIGHT * 2;
const WAIT_TIMEOUT: Duration = Duration::from_millis(300); const WAIT_TIMEOUT: Duration = Duration::from_millis(300);
@ -26,12 +30,11 @@ fn main() {
let _console = Console::new(gfx.bottom_screen.borrow_mut()); let _console = Console::new(gfx.bottom_screen.borrow_mut());
let mut keys_down;
println!("Initializing camera"); println!("Initializing camera");
let mut cam = Cam::new().expect("Failed to initialize CAM service."); let mut cam = Cam::new().expect("Failed to initialize CAM service.");
// Camera setup.
{ {
let camera = &mut cam.outer_right_cam; let camera = &mut cam.outer_right_cam;
@ -58,21 +61,23 @@ fn main() {
let mut buf = vec![0u8; BUF_SIZE]; let mut buf = vec![0u8; BUF_SIZE];
println!("\nPress R to take a new picture"); println!("\nPress R to take a new picture");
println!("Press Start to exit to Homebrew Launcher"); println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();
keys_down = hid.keys_down(); let keys_down = hid.keys_down();
if keys_down.contains(KeyPad::START) { if keys_down.contains(KeyPad::START) {
break; break;
} }
// If the user presses the R button.
if keys_down.contains(KeyPad::R) { if keys_down.contains(KeyPad::R) {
println!("Capturing new image"); println!("Capturing new image");
let camera = &mut cam.outer_right_cam; let camera = &mut cam.outer_right_cam;
// Take a picture and write it to the buffer.
camera camera
.take_picture( .take_picture(
&mut buf, &mut buf,
@ -82,12 +87,14 @@ fn main() {
) )
.expect("Failed to take picture"); .expect("Failed to take picture");
// Play the normal shutter sound.
cam.play_shutter_sound(ShutterSound::Normal) cam.play_shutter_sound(ShutterSound::Normal)
.expect("Failed to play shutter sound"); .expect("Failed to play shutter sound");
// Rotate the image and correctly display it on the screen.
rotate_image_to_screen(&buf, top_screen.raw_framebuffer().ptr, WIDTH, HEIGHT); rotate_image_to_screen(&buf, top_screen.raw_framebuffer().ptr, WIDTH, HEIGHT);
// We will only flush the "camera" screen, since the other screen is handled by `Console` // We will only flush and swap the "camera" screen, since the other screen is handled by the `Console`.
top_screen.flush_buffers(); top_screen.flush_buffers();
top_screen.swap_buffers(); top_screen.swap_buffers();
@ -99,6 +106,7 @@ fn main() {
// The 3DS' screens are 2 vertical LCD panels rotated by 90 degrees. // The 3DS' screens are 2 vertical LCD panels rotated by 90 degrees.
// As such, we'll need to write a "vertical" image to the framebuffer to have it displayed properly. // As such, we'll need to write a "vertical" image to the framebuffer to have it displayed properly.
// This functions rotates an horizontal image by 90 degrees to the right. // This functions rotates an horizontal image by 90 degrees to the right.
// This function is only supposed to be used in this example. In a real world application, the program should use the GPU to draw to the screen.
fn rotate_image_to_screen(src: &[u8], framebuf: *mut u8, width: usize, height: usize) { fn rotate_image_to_screen(src: &[u8], framebuf: *mut u8, width: usize, height: usize) {
for j in 0..height { for j in 0..height {
for i in 0..width { for i in 0..width {
@ -115,7 +123,7 @@ fn rotate_image_to_screen(src: &[u8], framebuf: *mut u8, width: usize, height: u
let draw_index = (draw_x * height + draw_y) * 2; // This 2 stands for the number of bytes per pixel (16 bits) let draw_index = (draw_x * height + draw_y) * 2; // This 2 stands for the number of bytes per pixel (16 bits)
unsafe { unsafe {
// We'll work with pointers since the frambuffer is a raw pointer regardless. // We'll work with pointers since the framebuffer is a raw pointer regardless.
// The offsets are completely safe as long as the width and height are correct. // The offsets are completely safe as long as the width and height are correct.
let pixel_pointer = framebuf.offset(draw_index as isize); let pixel_pointer = framebuf.offset(draw_index as isize);
pixel_pointer.copy_from(src.as_ptr().offset(read_index as isize), 2); pixel_pointer.copy_from(src.as_ptr().offset(read_index as isize), 2);

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

@ -1,5 +1,7 @@
//! A file explorer which shows off using standard library file system APIs to //! File Explorer example.
//! read the SD card. //!
//! This (rather complex) example creates a working text-based file explorer which shows off using standard library file system APIs to
//! read the SD card and RomFS (if properly read via the `romfs:/` prefix).
use ctru::applets::swkbd::{Button, SoftwareKeyboard}; use ctru::applets::swkbd::{Button, SoftwareKeyboard};
use ctru::prelude::*; use ctru::prelude::*;
@ -15,6 +17,7 @@ fn main() {
let mut hid = Hid::new().unwrap(); let mut hid = Hid::new().unwrap();
let gfx = Gfx::new().unwrap(); let gfx = Gfx::new().unwrap();
// Mount the RomFS if available.
#[cfg(all(feature = "romfs", romfs_exists))] #[cfg(all(feature = "romfs", romfs_exists))]
let _romfs = ctru::services::romfs::RomFS::new().unwrap(); let _romfs = ctru::services::romfs::RomFS::new().unwrap();
@ -50,6 +53,7 @@ impl<'a> FileExplorer<'a> {
fn run(&mut self) { fn run(&mut self) {
self.running = true; self.running = true;
// Print the file explorer commands.
self.print_menu(); self.print_menu();
while self.running && self.apt.main_loop() { while self.running && self.apt.main_loop() {
@ -62,8 +66,10 @@ impl<'a> FileExplorer<'a> {
self.path.pop(); self.path.pop();
self.console.clear(); self.console.clear();
self.print_menu(); self.print_menu();
// Open a directory/file to read.
} else if input.contains(KeyPad::A) { } else if input.contains(KeyPad::A) {
self.get_input_and_run(Self::set_next_path); self.get_input_and_run(Self::set_next_path);
// Open a specific path using the `SoftwareKeyboard` applet.
} else if input.contains(KeyPad::X) { } else if input.contains(KeyPad::X) {
self.get_input_and_run(Self::set_exact_path); self.get_input_and_run(Self::set_exact_path);
} }
@ -100,7 +106,7 @@ impl<'a> FileExplorer<'a> {
} }
}; };
println!("Start to exit, A to select an entry by number, B to go up a directory, X to set the path."); println!("Press Start to exit, A to select an entry by number, B to go up a directory, X to set the path.");
} }
fn print_dir_entries(&mut self) { fn print_dir_entries(&mut self) {
@ -137,7 +143,7 @@ impl<'a> FileExplorer<'a> {
} }
} }
/// Paginate output /// Paginate output.'
fn wait_for_page_down(&mut self) { fn wait_for_page_down(&mut self) {
println!("Press A to go to next page, or Start to exit"); println!("Press A to go to next page, or Start to exit");
@ -163,14 +169,14 @@ impl<'a> FileExplorer<'a> {
match keyboard.get_string(2048) { match keyboard.get_string(2048) {
Ok((path, Button::Right)) => { Ok((path, Button::Right)) => {
// Clicked "OK" // Clicked "OK".
action(self, path); action(self, path);
} }
Ok((_, Button::Left)) => { Ok((_, Button::Left)) => {
// Clicked "Cancel" // Clicked "Cancel".
} }
Ok((_, Button::Middle)) => { Ok((_, Button::Middle)) => {
// This button wasn't shown // This button wasn't shown.
unreachable!() unreachable!()
} }
Err(e) => { Err(e) => {

26
ctru-rs/examples/gfx-3d-mode.rs

@ -1,10 +1,15 @@
//! 3D Graphics example.
//!
//! This example showcases 3D mode rendering (using the CPU).
//! In a normal application, all rendering should be hanlded via the GPU.
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::gfx::{Flush, Screen, Side, Swap, TopScreen3D}; use ctru::services::gfx::{Flush, Screen, Side, Swap, TopScreen3D};
/// See `graphics-bitmap.rs` for details on how the image is generated. // See `graphics-bitmap.rs` for details on how the image is generated.
/// //
/// WARNING: this example uses 3D mode in a rather unnatural way, and should // WARNING: this example uses 3D mode in a rather unnatural way, and should
/// probably not be viewed for too long or at all if you are photosensitive. // probably not be viewed for too long or at all if you are photosensitive.
const IMAGE: &[u8] = include_bytes!("assets/ferris.rgb"); const IMAGE: &[u8] = include_bytes!("assets/ferris.rgb");
static ZERO: &[u8] = &[0; IMAGE.len()]; static ZERO: &[u8] = &[0; IMAGE.len()];
@ -17,7 +22,8 @@ fn main() {
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.bottom_screen.borrow_mut()); let _console = Console::new(gfx.bottom_screen.borrow_mut());
println!("Press Start to exit.\nPress A to switch sides (be sure to have 3D mode enabled)."); println!("Press A to switch sides (be sure to have set the 3D slider correctly).");
println!("\x1b[29;16HPress Start to exit");
gfx.top_screen.borrow_mut().set_double_buffering(true); gfx.top_screen.borrow_mut().set_double_buffering(true);
@ -25,39 +31,40 @@ fn main() {
let mut current_side = Side::Left; let mut current_side = Side::Left;
// Main loop
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
// Split the TopScreen3D to get references to the two render surfaces.
let (mut left, mut right) = top_screen.split_mut(); let (mut left, mut right) = top_screen.split_mut();
let left_buf = left.raw_framebuffer(); let left_buf = left.raw_framebuffer();
let right_buf = right.raw_framebuffer(); let right_buf = right.raw_framebuffer();
// Clear both buffers every time, in case the user switches sides this loop // Clear both buffers every time, in case the user switches sides this loop.
unsafe { unsafe {
left_buf.ptr.copy_from(ZERO.as_ptr(), ZERO.len()); left_buf.ptr.copy_from(ZERO.as_ptr(), ZERO.len());
right_buf.ptr.copy_from(ZERO.as_ptr(), ZERO.len()); right_buf.ptr.copy_from(ZERO.as_ptr(), ZERO.len());
} }
if hid.keys_down().contains(KeyPad::A) { if hid.keys_down().contains(KeyPad::A) {
// flip which buffer we're writing to // Switch which buffer we're writing to.
current_side = match current_side { current_side = match current_side {
Side::Left => Side::Right, Side::Left => Side::Right,
Side::Right => Side::Left, Side::Right => Side::Left,
}; };
} }
// Obtain the framebuffer of the currently rendered side.
let buf = match current_side { let buf = match current_side {
Side::Left => left_buf.ptr, Side::Left => left_buf.ptr,
Side::Right => right_buf.ptr, Side::Right => right_buf.ptr,
}; };
// Render the image to the surface's buffer.
unsafe { unsafe {
buf.copy_from(IMAGE.as_ptr(), IMAGE.len()); buf.copy_from(IMAGE.as_ptr(), IMAGE.len());
} }
@ -67,7 +74,6 @@ fn main() {
top_screen.flush_buffers(); top_screen.flush_buffers();
top_screen.swap_buffers(); top_screen.swap_buffers();
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

29
ctru-rs/examples/gfx-bitmap.rs

@ -1,3 +1,6 @@
/// Bitmap Graphics example.
///
/// This example uses the CPU to render a simple bitmap image to the screen.
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::gfx::{Flush, Screen, Swap}; use ctru::services::gfx::{Flush, Screen, Swap};
@ -22,7 +25,8 @@ fn main() {
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
println!("\x1b[21;4HPress Start to exit, or A to flip the image."); println!("\x1b[21;4HPress A to flip the image.");
println!("\x1b[29;16HPress Start to exit");
let mut bottom_screen = gfx.bottom_screen.borrow_mut(); let mut bottom_screen = gfx.bottom_screen.borrow_mut();
@ -37,18 +41,23 @@ fn main() {
let mut image_bytes = IMAGE; let mut image_bytes = IMAGE;
// Main loop // We assume the image is the correct size already, so we drop width + height.
let frame_buffer = bottom_screen.raw_framebuffer();
// We copy the image to the framebuffer.
unsafe {
frame_buffer
.ptr
.copy_from(image_bytes.as_ptr(), image_bytes.len());
}
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
// We assume the image is the correct size already, so we drop width + height.
let frame_buffer = bottom_screen.raw_framebuffer();
if hid.keys_down().contains(KeyPad::A) { if hid.keys_down().contains(KeyPad::A) {
image_bytes = if std::ptr::eq(image_bytes, IMAGE) { image_bytes = if std::ptr::eq(image_bytes, IMAGE) {
&flipped_image[..] &flipped_image[..]
@ -57,18 +66,10 @@ fn main() {
}; };
} }
// this copies more than necessary (once per frame) but it's fine...
unsafe {
frame_buffer
.ptr
.copy_from(image_bytes.as_ptr(), image_bytes.len());
}
// Flush framebuffers. Since we're not using double buffering, // Flush framebuffers. Since we're not using double buffering,
// this will render the pixels immediately // this will render the pixels immediately
bottom_screen.flush_buffers(); bottom_screen.flush_buffers();
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

12
ctru-rs/examples/gfx-wide-mode.rs

@ -1,3 +1,10 @@
//! Wide-Mode Graphics example.
//!
//! This example demonstrates the wide-mode capability of the top screen
//! which doubles the horizontal resolution of the screen by merging the 2 stereoscopic 3D sides.
//!
//! Beware, wide-mode doesn't work on Old 2DS consoles.
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -9,6 +16,7 @@ fn main() {
let mut console = Console::new(gfx.top_screen.borrow_mut()); let mut console = Console::new(gfx.top_screen.borrow_mut());
println!("Press A to enable/disable wide screen mode."); println!("Press A to enable/disable wide screen mode.");
println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();
@ -17,14 +25,18 @@ fn main() {
break; break;
} }
// Since we can't set wide-mode while running the console (since that would break the currently displayed text),
// we need to rebuild the console each time we want to make the switch.
if hid.keys_down().contains(KeyPad::A) { if hid.keys_down().contains(KeyPad::A) {
drop(console); drop(console);
// Switch the state of the wide-mode.
let wide_mode = gfx.top_screen.borrow().is_wide(); let wide_mode = gfx.top_screen.borrow().is_wide();
gfx.top_screen.borrow_mut().set_wide_mode(!wide_mode); gfx.top_screen.borrow_mut().set_wide_mode(!wide_mode);
console = Console::new(gfx.top_screen.borrow_mut()); console = Console::new(gfx.top_screen.borrow_mut());
println!("Press A to enable/disable wide screen mode."); println!("Press A to enable/disable wide screen mode.");
println!("\x1b[29;16HPress Start to exit");
} }
gfx.wait_for_vblank(); gfx.wait_for_vblank();

57
ctru-rs/examples/graphics-bitmap.rs

@ -1,57 +0,0 @@
use ctru::prelude::*;
use ctru::services::gfx::{Flush, Screen, Swap};
/// Ferris image taken from <https://rustacean.net> and scaled down to 320x240px.
/// To regenerate the data, you will need to install `imagemagick` and run this
/// command from the `examples` directory:
///
/// ```sh
/// magick assets/ferris.png -channel-fx "red<=>blue" -rotate 90 assets/ferris.rgb
/// ```
///
/// This creates an image appropriate for the default frame buffer format of
/// [`Bgr8`](ctru::services::gspgpu::FramebufferFormat::Bgr8)
/// and rotates the image 90° to account for the portrait mode screen.
static IMAGE: &[u8] = include_bytes!("assets/ferris.rgb");
fn main() {
ctru::use_panic_handler();
let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.top_screen.borrow_mut());
println!("\x1b[21;16HPress Start to exit.");
let mut bottom_screen = gfx.bottom_screen.borrow_mut();
// We don't need double buffering in this example.
// In this way we can draw our image only once on screen.
bottom_screen.set_double_buffering(false);
// We assume the image is the correct size already, so we drop width + height.
let frame_buffer = bottom_screen.raw_framebuffer();
// Copy the image into the frame buffer
unsafe {
frame_buffer.ptr.copy_from(IMAGE.as_ptr(), IMAGE.len());
}
// Main loop
while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
}
// Flush and swap framebuffers
bottom_screen.flush_buffers();
bottom_screen.swap_buffers();
//Wait for VBlank
gfx.wait_for_vblank();
}
}

11
ctru-rs/examples/hashmaps.rs

@ -1,10 +1,16 @@
//! Hashmap example.
//!
//! This example showcases using Hashmaps on the 3DS console using the functionality implemented by the standard library.
//! While it may seem inappropriate for such a simple (and somewhat out-of-scope) example to be included here, it's important to note how
//! normally Hashmaps wouldn't work on the console, and are only capable to because of the internal implementations made by `ctru-rs`.
//!
//! As such, this example functions more closely to a test than a demonstration.
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
ctru::use_panic_handler(); ctru::use_panic_handler();
// Initialize services
//
// HashMaps generate hashes thanks to the 3DS' cryptografically secure generator. // HashMaps generate hashes thanks to the 3DS' cryptografically secure generator.
// This generator is only active when activating the `PS` service. // This generator is only active when activating the `PS` service.
// This service is automatically initialized. // This service is automatically initialized.
@ -19,6 +25,7 @@ fn main() {
map.remove("A Key!"); map.remove("A Key!");
println!("{map:#?}"); println!("{map:#?}");
println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
gfx.wait_for_vblank(); gfx.wait_for_vblank();

11
ctru-rs/examples/hello-both-screens.rs

@ -1,3 +1,7 @@
//! Hello World example using both screens.
//!
//! This is similar to the `hello-world` example, with the main difference of using 2 virtual `Console`s that can be alternated to print on both screens.
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -11,14 +15,15 @@ fn main() {
let top_screen = Console::new(gfx.top_screen.borrow_mut()); let top_screen = Console::new(gfx.top_screen.borrow_mut());
// Start a console on the bottom screen. // Start a console on the bottom screen.
// The most recently initialized console will be active by default // The most recently initialized console will be active by default.
let bottom_screen = Console::new(gfx.bottom_screen.borrow_mut()); let bottom_screen = Console::new(gfx.bottom_screen.borrow_mut());
// Let's print on the top screen first // Let's print on the top screen first.
// Since the bottom screen is currently selected (being created afterwards), it is required to select the top screen console first.
top_screen.select(); top_screen.select();
println!("This is the top screen! We have a lot of space up here!"); println!("This is the top screen! We have a lot of space up here!");
// Now let's print something on the bottom screen // Now let's print something on the bottom screen.
bottom_screen.select(); bottom_screen.select();
println!("\x1b[14;00HThis is the bottom screen."); println!("\x1b[14;00HThis is the bottom screen.");
println!("There's not as much space down here, but that's okay."); println!("There's not as much space down here, but that's okay.");

20
ctru-rs/examples/hello-world.rs

@ -1,15 +1,25 @@
use ctru::prelude::*; //! Hello World example.
//!
//! Simple "Hello World" application to showcase the basic setup needed for any user-oriented app to work.
use ctru::prelude::*;
use std::io::BufWriter; use std::io::BufWriter;
fn main() { fn main() {
// Setup the custom panic handler in case any errors arise.
// Thanks to it the user will get promptly notified of any panics.
ctru::use_panic_handler(); ctru::use_panic_handler();
// Setup Graphics, Controller Inputs, Application runtime.
// These is standard setup any app would need.
let gfx = Gfx::new().expect("Couldn't obtain GFX controller"); let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
let mut hid = Hid::new().expect("Couldn't obtain HID controller"); let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
// Create a Console to print our "Hello, World!" to.
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
// Snazzy message created via `ferris_says`.
let out = b"Hello fellow Rustaceans, I'm on the Nintendo 3DS!"; let out = b"Hello fellow Rustaceans, I'm on the Nintendo 3DS!";
let width = 24; let width = 24;
@ -21,16 +31,18 @@ fn main() {
String::from_utf8_lossy(&writer.into_inner().unwrap()) String::from_utf8_lossy(&writer.into_inner().unwrap())
); );
// Main loop println!("\x1b[29;16HPress Start to exit");
// Main application loop. This checks whether the app is normally running in the foreground.
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame // Scan all the controller inputs.
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
//Wait for VBlank // Use VSync to cap the framerate at the same speed as the LCD screen's refresh rate (60 fps).
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

15
ctru-rs/examples/linear-memory.rs

@ -1,3 +1,9 @@
//! LINEAR memory example.
//!
//! This example showcases simple allocation on the LINEAR memory sector.
//! Using LINEAR memory is required when sending data to the GPU or DSP processor.
// You will need to activate this unstable feature to use custom allocators.
#![feature(allocator_api)] #![feature(allocator_api)]
use ctru::linear::LinearAllocator; use ctru::linear::LinearAllocator;
@ -11,11 +17,13 @@ fn main() {
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
// The `LinearAllocator` is always available for use.
// Luckily, we can always read how much memory is available to be allocated on it.
let linear_space_before = LinearAllocator::free_space(); let linear_space_before = LinearAllocator::free_space();
// Normal `Box` on the heap // Normal `Box` on the heap.
let heap_box = Box::new(1492); let heap_box = Box::new(1492);
// `Box` living on the linear memory sector // `Box` living on the LINEAR memory.
let linear_box: Box<i32, LinearAllocator> = Box::new_in(2022, LinearAllocator); let linear_box: Box<i32, LinearAllocator> = Box::new_in(2022, LinearAllocator);
println!("This value is from the heap: {heap_box}"); println!("This value is from the heap: {heap_box}");
@ -29,16 +37,13 @@ fn main() {
println!("\x1b[29;16HPress Start to exit"); println!("\x1b[29;16HPress Start to exit");
// Main loop
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

13
ctru-rs/examples/mii-selector.rs

@ -1,3 +1,7 @@
//! Mii Selector example.
//!
//! This example showcases the use of the MiiSelector applet to obtain Mii data from the user's input.
use ctru::applets::mii_selector::{Error, MiiSelector, Options}; use ctru::applets::mii_selector::{Error, MiiSelector, Options};
use ctru::prelude::*; use ctru::prelude::*;
@ -9,12 +13,16 @@ fn main() {
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
// Setup the Mii Selector configuration.
let mut mii_selector = MiiSelector::new(); let mut mii_selector = MiiSelector::new();
// The Mii Selector window can be closed without selecting a Mii.
mii_selector.set_options(Options::ENABLE_CANCEL); mii_selector.set_options(Options::ENABLE_CANCEL);
mii_selector.set_initial_index(3); mii_selector.set_initial_index(3);
// The first user-made Mii cannot be used.
mii_selector.blacklist_user_mii(0.into()); mii_selector.blacklist_user_mii(0.into());
mii_selector.set_title("Great Mii Selector!"); mii_selector.set_title("Great Mii Selector!");
// Launch the Mii Selector and use its result to print the selected Mii's information.
match mii_selector.launch() { match mii_selector.launch() {
Ok(result) => { Ok(result) => {
println!("Mii type: {:?}", result.mii_type); println!("Mii type: {:?}", result.mii_type);
@ -29,16 +37,15 @@ fn main() {
Err(Error::NoMiiSelected) => println!("No Mii selected"), Err(Error::NoMiiSelected) => println!("No Mii selected"),
} }
// Main loop println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

21
ctru-rs/examples/network-sockets.rs

@ -1,3 +1,7 @@
//! Network Sockets example.
//!
//! This example showcases the use of network sockets via the `Soc` service and the standard library's implementations.
use ctru::prelude::*; use ctru::prelude::*;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
@ -14,20 +18,27 @@ fn main() {
println!("\nlibctru sockets demo\n"); println!("\nlibctru sockets demo\n");
// Owning a living handle to the `Soc` service is required to use network functionalities.
let soc = Soc::new().unwrap(); let soc = Soc::new().unwrap();
// Listen on the standard HTTP port (80).
let server = TcpListener::bind("0.0.0.0:80").unwrap(); let server = TcpListener::bind("0.0.0.0:80").unwrap();
server.set_nonblocking(true).unwrap(); server.set_nonblocking(true).unwrap();
println!("Point your browser to http://{}/\n", soc.host_address()); println!("Point your browser to http://{}/\n", soc.host_address());
println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
gfx.wait_for_vblank(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
};
match server.accept() { match server.accept() {
Ok((mut stream, socket_addr)) => { Ok((mut stream, socket_addr)) => {
println!("Got connection from {socket_addr}"); println!("Got connection from {socket_addr}");
// Print the HTTP request sent by the client (most likely, a web browser).
let mut buf = [0u8; 4096]; let mut buf = [0u8; 4096];
match stream.read(&mut buf) { match stream.read(&mut buf) {
Ok(_) => { Ok(_) => {
@ -43,15 +54,18 @@ fn main() {
} }
} }
// Return a HTML page with a simple "Hello World!".
let response = b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<html><body>Hello world</body></html>\r\n"; let response = b"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<html><body>Hello world</body></html>\r\n";
if let Err(e) = stream.write(response) { if let Err(e) = stream.write(response) {
println!("Error writing http response: {e}"); println!("Error writing http response: {e}");
} }
// Shutdown the stream (depending on the web browser used to view the page, this might cause some issues).
stream.shutdown(Shutdown::Both).unwrap(); stream.shutdown(Shutdown::Both).unwrap();
} }
Err(e) => match e.kind() { Err(e) => match e.kind() {
// If the TCP socket would block execution, just try again.
std::io::ErrorKind::WouldBlock => {} std::io::ErrorKind::WouldBlock => {}
_ => { _ => {
println!("Error accepting connection: {e}"); println!("Error accepting connection: {e}");
@ -60,9 +74,6 @@ fn main() {
}, },
} }
hid.scan_input(); gfx.wait_for_vblank();
if hid.keys_down().contains(KeyPad::START) {
break;
};
} }
} }

9
ctru-rs/examples/output-3dslink.rs

@ -1,4 +1,6 @@
//! Use the `3dslink --server` option for redirecting output from the 3DS back //! Output redirection example.
//!
//! This example uses the `3dslink --server` option for redirecting output from the 3DS back
//! to the device that sent the executable. //! to the device that sent the executable.
//! //!
//! For now, `cargo 3ds run` does not support this flag, so to run this example //! For now, `cargo 3ds run` does not support this flag, so to run this example
@ -17,24 +19,23 @@ fn main() {
let mut hid = Hid::new().expect("Couldn't obtain HID controller"); let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
// We need to use network sockets to send the data stream back.
let mut soc = Soc::new().expect("Couldn't obtain SOC controller"); let mut soc = Soc::new().expect("Couldn't obtain SOC controller");
// Set the output to be redirected to the `3dslink` server.
soc.redirect_to_3dslink(true, true) soc.redirect_to_3dslink(true, true)
.expect("unable to redirect stdout/err to 3dslink server"); .expect("unable to redirect stdout/err to 3dslink server");
println!("Hello 3dslink!"); println!("Hello 3dslink!");
eprintln!("Press Start on the device to disconnect and exit."); eprintln!("Press Start on the device to disconnect and exit.");
// Main loop
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

20
ctru-rs/examples/romfs.rs

@ -1,3 +1,7 @@
//! RomFS example.
//!
//! This example showcases the RomFS service and how to mount it to include a read-only filesystem within the application bundle.
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -9,35 +13,35 @@ fn main() {
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
cfg_if::cfg_if! { cfg_if::cfg_if! {
// Run this code if RomFS are wanted and available // Run this code if RomFS are wanted and available.
// This never fails as `ctru-rs` examples inherit all of the `ctru` features, // This never fails as `ctru-rs` examples inherit all of the `ctru-rs` features,
// but it might if a normal user application wasn't setup correctly // but it might if a normal user application wasn't setup correctly.
if #[cfg(all(feature = "romfs", romfs_exists))] { if #[cfg(all(feature = "romfs", romfs_exists))] {
// Mount the romfs volume.
let _romfs = ctru::services::romfs::RomFS::new().unwrap(); let _romfs = ctru::services::romfs::RomFS::new().unwrap();
// Open a simple text file present in the RomFS volume.
// Remember to use the `romfs:/` prefix when working with `RomFS`.
let f = std::fs::read_to_string("romfs:/test-file.txt").unwrap(); let f = std::fs::read_to_string("romfs:/test-file.txt").unwrap();
println!("Contents of test-file.txt: \n{f}\n"); println!("Contents of test-file.txt: \n{f}\n");
let f = std::fs::read_to_string("romfs:/ファイル.txt").unwrap(); let f = std::fs::read_to_string("romfs:/ファイル.txt").unwrap();
// While RomFS supports UTF-16 file paths, `Console` doesn't... // While `RomFS` supports UTF-16 file paths, `Console` does not, so we'll use a placeholder for the text.
println!("Contents of [UTF-16 File]: \n{f}\n"); println!("Contents of [UTF-16 File]: \n{f}\n");
} else { } else {
println!("No RomFS was found, are you sure you included it?") println!("No RomFS was found, are you sure you included it?")
} }
} }
println!("\nPress START to exit"); println!("\x1b[29;16HPress Start to exit");
// Main loop
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

20
ctru-rs/examples/software-keyboard.rs

@ -1,3 +1,7 @@
//! Software Keyboard example.
//!
//! This example showcases the use of the Software Keyboard applet to receive text input from the user.
use ctru::applets::swkbd::{Button, SoftwareKeyboard}; use ctru::applets::swkbd::{Button, SoftwareKeyboard};
use ctru::prelude::*; use ctru::prelude::*;
@ -9,13 +13,17 @@ fn main() {
let gfx = Gfx::new().unwrap(); let gfx = Gfx::new().unwrap();
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
println!("Press A to enter some text or press Start to quit"); println!("Press A to enter some text.");
println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
gfx.wait_for_vblank();
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
}
// If the user request to write some input.
if hid.keys_down().contains(KeyPad::A) { if hid.keys_down().contains(KeyPad::A) {
// Prepares a software keyboard with two buttons: One to cancel input and one // Prepares a software keyboard with two buttons: One to cancel input and one
// to accept it. You can also use `SoftwareKeyboard::new()` to launch the keyboard in different // to accept it. You can also use `SoftwareKeyboard::new()` to launch the keyboard in different
@ -23,7 +31,7 @@ fn main() {
let mut keyboard = SoftwareKeyboard::default(); let mut keyboard = SoftwareKeyboard::default();
// Raise the software keyboard. You can perform different actions depending on which // Raise the software keyboard. You can perform different actions depending on which
// software button the user pressed // software button the user pressed.
match keyboard.get_string(2048) { match keyboard.get_string(2048) {
Ok((text, Button::Right)) => println!("You entered: {text}"), Ok((text, Button::Right)) => println!("You entered: {text}"),
Ok((_, Button::Left)) => println!("Cancelled"), Ok((_, Button::Left)) => println!("Cancelled"),
@ -32,8 +40,6 @@ fn main() {
} }
} }
if hid.keys_down().contains(KeyPad::START) { gfx.wait_for_vblank();
break;
}
} }
} }

14
ctru-rs/examples/system-configuration.rs

@ -1,3 +1,8 @@
//! System Configuration example.
//!
//! This example showcases the CFGU service to retrieve information about the console that the application is running on,
//! such as the model, region and used language.
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::cfgu::Cfgu; use ctru::services::cfgu::Cfgu;
@ -7,23 +12,24 @@ fn main() {
let gfx = Gfx::new().expect("Couldn't obtain GFX controller"); let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
let mut hid = Hid::new().expect("Couldn't obtain HID controller"); let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
let cfgu = Cfgu::new().expect("Couldn't obtain CFGU controller");
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
// Initialize the CFGU service to retrieve all wanted information.
let cfgu = Cfgu::new().expect("Couldn't obtain CFGU controller");
println!("\x1b[0;0HRegion: {:?}", cfgu.region().unwrap()); println!("\x1b[0;0HRegion: {:?}", cfgu.region().unwrap());
println!("\x1b[10;0HLanguage: {:?}", cfgu.language().unwrap()); println!("\x1b[10;0HLanguage: {:?}", cfgu.language().unwrap());
println!("\x1b[20;0HModel: {:?}", cfgu.model().unwrap()); println!("\x1b[20;0HModel: {:?}", cfgu.model().unwrap());
// Main loop println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
break; break;
} }
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

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

@ -33,6 +33,8 @@ fn main() {
println!("Created thread {ix}"); println!("Created thread {ix}");
} }
println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
gfx.wait_for_vblank(); gfx.wait_for_vblank();

2
ctru-rs/examples/thread-info.rs

@ -36,7 +36,7 @@ fn main() {
.unwrap(); .unwrap();
println!("sys thread exited"); println!("sys thread exited");
println!("\nPress Start to exit"); println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();

2
ctru-rs/examples/thread-locals.rs

@ -52,7 +52,7 @@ fn main() {
); );
}); });
println!("Press Start to exit"); println!("\x1b[29;16HPress Start to exit");
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();

25
ctru-rs/examples/time-rtc.rs

@ -1,3 +1,8 @@
//! Time Clock example.
//!
//! This example showcases how to retrieve the local time set in the console's configuration
//! using the implementations of the standard library.
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -9,11 +14,9 @@ fn main() {
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
print!("\x1b[30;16HPress Start to exit."); println!("\x1b[29;16HPress Start to exit");
// Main loop
while apt.main_loop() { while apt.main_loop() {
// Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
@ -21,22 +24,12 @@ fn main() {
} }
// Technically, this actually just gets local time and assumes it's UTC, // Technically, this actually just gets local time and assumes it's UTC,
// since the 3DS doesn't seem to support timezones... // since the 3DS doesn't seem to support timezones.
let cur_time = time::OffsetDateTime::now_utc(); let cur_time = time::OffsetDateTime::now_utc();
let hours = cur_time.hour(); // Display the retrieved information.
let minutes = cur_time.minute(); println!("\x1b[1;1H{cur_time}");
let seconds = cur_time.second();
let weekday = cur_time.weekday().to_string();
let month = cur_time.month().to_string();
let day = cur_time.day();
let year = cur_time.year();
println!("\x1b[1;1H{hours:0>2}:{minutes:0>2}:{seconds:0>2}");
println!("{weekday} {month} {day} {year}");
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

30
ctru-rs/examples/title-info.rs

@ -1,3 +1,8 @@
//! Title Info example.
//!
//! This example showcases how to retrieve information about the titles installed on the console running the application
//! via the Application Manager (Am) service.
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::am::Am; use ctru::services::am::Am;
use ctru::services::fs::FsMediaType; use ctru::services::fs::FsMediaType;
@ -8,20 +13,27 @@ fn main() {
let gfx = Gfx::new().expect("Couldn't obtain GFX controller"); let gfx = Gfx::new().expect("Couldn't obtain GFX controller");
let mut hid = Hid::new().expect("Couldn't obtain HID controller"); let mut hid = Hid::new().expect("Couldn't obtain HID controller");
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
let am = Am::new().expect("Couldn't obtain AM controller");
let top_screen = Console::new(gfx.top_screen.borrow_mut()); let top_screen = Console::new(gfx.top_screen.borrow_mut());
let bottom_screen = Console::new(gfx.bottom_screen.borrow_mut()); let bottom_screen = Console::new(gfx.bottom_screen.borrow_mut());
// Setup the AM service to retrieve the wanted information.
let am = Am::new().expect("Couldn't obtain AM controller");
// Amount of titles installed on the SD card.
let sd_count = am let sd_count = am
.title_count(FsMediaType::Sd) .title_count(FsMediaType::Sd)
.expect("Failed to get sd title count"); .expect("Failed to get sd title count");
// List of titles installed on the SD card.
let sd_list = am let sd_list = am
.title_list(FsMediaType::Sd) .title_list(FsMediaType::Sd)
.expect("Failed to get sd title list"); .expect("Failed to get sd title list");
// Amount of titles installed on the NAND storage.
let nand_count = am let nand_count = am
.title_count(FsMediaType::Nand) .title_count(FsMediaType::Nand)
.expect("Failed to get nand title count"); .expect("Failed to get nand title count");
// List of titles installed on the NAND storage.
let nand_list = am let nand_list = am
.title_list(FsMediaType::Nand) .title_list(FsMediaType::Nand)
.expect("Failed to get nand title list"); .expect("Failed to get nand title list");
@ -30,9 +42,7 @@ fn main() {
let mut refresh = true; let mut refresh = true;
let mut use_nand = false; let mut use_nand = false;
// Main loop
while apt.main_loop() { while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input(); hid.scan_input();
if hid.keys_down().contains(KeyPad::START) { if hid.keys_down().contains(KeyPad::START) {
@ -58,13 +68,15 @@ fn main() {
} }
} }
// Render the title list via a scrollable text UI.
if refresh { if refresh {
let mut selected_title = cur_list.iter().skip(offset).next().unwrap(); let mut selected_title = cur_list.iter().skip(offset).next().unwrap();
// Clear top screen and write title ids to it
// Clear the top screen and write title IDs to it.
top_screen.select(); top_screen.select();
print!("\x1b[2J"); print!("\x1b[2J");
// Top screen seems to have only 30 rows // Top screen has 30 rows.
for (i, title) in cur_list.iter().skip(offset).take(29).enumerate() { for (i, title) in cur_list.iter().skip(offset).take(29).enumerate() {
if i == 0 { if i == 0 {
selected_title = title; selected_title = title;
@ -74,17 +86,18 @@ fn main() {
} }
} }
// Clear bottom screen and write properties of selected title to it // Clear the bottom screen and write the properties of selected title to it.
bottom_screen.select(); bottom_screen.select();
println!("\x1b[2J"); println!("\x1b[2J");
// Move cursor to top left
// Move cursor to top left.
println!("\x1b[1;1"); println!("\x1b[1;1");
println!("Size: {} kB", selected_title.size() / 1024); println!("Size: {} kB", selected_title.size() / 1024);
println!("Version: 0x{:x}", selected_title.version()); println!("Version: 0x{:x}", selected_title.version());
println!("Product code: \"{}\"", selected_title.product_code()); println!("Product code: \"{}\"", selected_title.product_code());
println!("\x1b[26;0HPress START to exit"); println!("\x1b[29;16HPress Start to exit");
if use_nand { if use_nand {
println!("Press SELECT to choose SD Card"); println!("Press SELECT to choose SD Card");
println!("Current medium: NAND"); println!("Current medium: NAND");
@ -98,7 +111,6 @@ fn main() {
refresh = false; refresh = false;
} }
//Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();
} }
} }

14
ctru-rs/examples/touch-screen.rs

@ -1,3 +1,7 @@
//! Touch Screen example.
//!
//! This example showcases how to retrieve the touch screen's touch information via the HID service.
use ctru::prelude::*; use ctru::prelude::*;
fn main() { fn main() {
@ -9,7 +13,7 @@ fn main() {
let console = Console::new(gfx.top_screen.borrow_mut()); let console = Console::new(gfx.top_screen.borrow_mut());
// We'll hold the previous touch position for comparison. // We'll save the previous touch position for comparison.
let mut old_touch: (u16, u16) = (0, 0); let mut old_touch: (u16, u16) = (0, 0);
println!("\x1b[29;16HPress Start to exit"); println!("\x1b[29;16HPress Start to exit");
@ -26,22 +30,22 @@ fn main() {
let touch: (u16, u16) = hid.touch_position(); let touch: (u16, u16) = hid.touch_position();
// We only want to print the position when it's different // We only want to print the position when it's different
// from what it was on the previous frame // from what it was on the previous frame.
if touch != old_touch { if touch != old_touch {
// Special case for when the user lifts the stylus/finger from the screen. // Special case for when the user lifts the stylus/finger from the screen.
// This is done to avoid some screen tearing. // This is done to avoid some screen tearing.
if touch == (0, 0) { if touch == (0, 0) {
console.clear(); console.clear();
// Print again because we just cleared the screen // Print again because we just cleared the screen.
println!("\x1b[29;16HPress Start to exit"); println!("\x1b[29;16HPress Start to exit");
} }
// Move the cursor back to the top of the screen and print the coordinates // Move the cursor back to the top of the screen and print the coordinates.
print!("\x1b[1;1HTouch Screen position: {:#?}", touch); print!("\x1b[1;1HTouch Screen position: {:#?}", touch);
} }
// Save our current touch position for the next frame // Save our current touch position for the next frame.
old_touch = touch; old_touch = touch;
gfx.wait_for_vblank(); gfx.wait_for_vblank();

4
ctru-rs/src/services/gfx.rs

@ -414,8 +414,12 @@ impl TopScreen {
/// Enable or disable wide mode on the top screen. /// Enable or disable wide mode on the top screen.
/// ///
/// # Notes
///
/// [`Swap::swap_buffers`] must be called after this method for the configuration /// [`Swap::swap_buffers`] must be called after this method for the configuration
/// to take effect. /// to take effect.
///
/// Wide mode does NOT work on Old 2DS models (but still does on New 2DS XL models).
#[doc(alias = "gfxSetWide")] #[doc(alias = "gfxSetWide")]
pub fn set_wide_mode(&mut self, enable: bool) { pub fn set_wide_mode(&mut self, enable: bool) {
unsafe { unsafe {

Loading…
Cancel
Save