Browse Source

Try using double buffering to reduce flickering (actually increased :/)

Required refactoring Console to take screen type as a generic. Not all
Screen types support swapping or flushing.
pull/86/head
AzureMarker 1 year ago
parent
commit
09361e50da
  1. 75
      ctru-rs/examples/ir-user.rs
  2. 15
      ctru-rs/src/console.rs

75
ctru-rs/examples/ir-user.rs

@ -1,10 +1,10 @@
//! A demo of using the ir:USER service to connect to the Circle Pad Pro. //! A demo of using the ir:USER service to connect to the Circle Pad Pro.
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::gfx::{BottomScreen, Flush, Swap, TopScreen};
use ctru::services::ir_user::{CirclePadProInputResponse, ConnectionStatus, IrDeviceId, IrUser}; use ctru::services::ir_user::{CirclePadProInputResponse, ConnectionStatus, IrDeviceId, IrUser};
use ctru::services::srv::HandleExt; use ctru::services::srv::HandleExt;
use ctru_sys::Handle; use ctru_sys::Handle;
use std::io::Write;
use std::time::Duration; use std::time::Duration;
const PACKET_INFO_SIZE: usize = 8; const PACKET_INFO_SIZE: usize = 8;
@ -19,7 +19,7 @@ fn main() {
let gfx = Gfx::new().unwrap(); let gfx = Gfx::new().unwrap();
let top_console = Console::new(gfx.top_screen.borrow_mut()); let top_console = Console::new(gfx.top_screen.borrow_mut());
let bottom_console = Console::new(gfx.bottom_screen.borrow_mut()); let bottom_console = Console::new(gfx.bottom_screen.borrow_mut());
let demo = CirclePadProDemo::new(top_console, bottom_console); let mut demo = CirclePadProDemo::new(top_console, bottom_console);
demo.print_status_info(); demo.print_status_info();
// Initialize HID after ir:USER because libctru also initializes ir:rst, // Initialize HID after ir:USER because libctru also initializes ir:rst,
@ -62,8 +62,8 @@ fn main() {
} }
struct CirclePadProDemo<'screen> { struct CirclePadProDemo<'screen> {
top_console: Console<'screen>, top_console: Console<'screen, TopScreen>,
bottom_console: Console<'screen>, bottom_console: Console<'screen, BottomScreen>,
ir_user: IrUser, ir_user: IrUser,
connection_status_event: Handle, connection_status_event: Handle,
receive_packet_event: Handle, receive_packet_event: Handle,
@ -75,7 +75,17 @@ enum ConnectionResult {
} }
impl<'screen> CirclePadProDemo<'screen> { impl<'screen> CirclePadProDemo<'screen> {
fn new(top_console: Console<'screen>, bottom_console: Console<'screen>) -> Self { fn new(
mut top_console: Console<'screen, TopScreen>,
bottom_console: Console<'screen, BottomScreen>,
) -> Self {
// Set up double buffering on top screen
top_console.with_screen(|screen| {
screen.set_double_buffering(true);
screen.swap_buffers();
});
// Write messages to bottom screen (not double buffered)
bottom_console.select(); bottom_console.select();
println!("Welcome to the ir:USER / Circle Pad Pro Demo"); println!("Welcome to the ir:USER / Circle Pad Pro Demo");
@ -106,14 +116,18 @@ impl<'screen> CirclePadProDemo<'screen> {
} }
} }
fn print_status_info(&self) { fn print_status_info(&mut self) {
self.top_console.select(); self.top_console.select();
self.top_console.clear(); self.top_console.clear();
println!("{:#x?}", self.ir_user.get_status_info()); println!("{:#x?}", self.ir_user.get_status_info());
self.top_console.with_screen(|screen| {
screen.flush_buffers();
screen.swap_buffers();
});
self.bottom_console.select(); self.bottom_console.select();
} }
fn connect_to_cpp(&self, hid: &mut Hid) -> ConnectionResult { fn connect_to_cpp(&mut self, hid: &mut Hid) -> ConnectionResult {
// Connection loop // Connection loop
loop { loop {
hid.scan_input(); hid.scan_input();
@ -192,7 +206,7 @@ impl<'screen> CirclePadProDemo<'screen> {
ConnectionResult::Connected ConnectionResult::Connected
} }
fn handle_packets(&self) { fn handle_packets(&mut self) {
let packets = self let packets = self
.ir_user .ir_user
.get_packets() .get_packets()
@ -201,32 +215,33 @@ impl<'screen> CirclePadProDemo<'screen> {
let Some(last_packet) = packets.last() else { let Some(last_packet) = packets.last() else {
return; return;
}; };
let status_info = self.ir_user.get_status_info();
let cpp_response = CirclePadProInputResponse::try_from(last_packet)
.expect("Failed to parse CPP response from IR packet");
// Use a buffer to avoid flickering the screen (write all output at once) // Write data to top screen
let mut output_buffer = Vec::with_capacity(0x1000); self.top_console.select();
self.top_console.clear();
writeln!(&mut output_buffer, "{:x?}", self.ir_user.get_status_info()).unwrap(); println!("{:x?}", status_info);
self.ir_user.process_shared_memory(|ir_mem| { self.ir_user.process_shared_memory(|ir_mem| {
writeln!(&mut output_buffer, "\nReceiveBufferInfo:").unwrap(); println!("\nReceiveBufferInfo:");
write_buffer_as_hex(&ir_mem[0x10..0x20], &mut output_buffer); print_buffer_as_hex(&ir_mem[0x10..0x20]);
writeln!(&mut output_buffer, "\nReceiveBuffer:").unwrap(); println!("\nReceiveBuffer:");
write_buffer_as_hex(&ir_mem[0x20..0x20 + PACKET_BUFFER_SIZE], &mut output_buffer); print_buffer_as_hex(&ir_mem[0x20..0x20 + PACKET_BUFFER_SIZE]);
writeln!(&mut output_buffer).unwrap(); println!();
}); });
writeln!(&mut output_buffer, "\nPacket count: {packet_count}").unwrap(); println!("\nPacket count: {packet_count}");
writeln!(&mut output_buffer, "{last_packet:02x?}").unwrap(); println!("{last_packet:02x?}");
println!("\n{cpp_response:#02x?}");
let cpp_response = CirclePadProInputResponse::try_from(last_packet)
.expect("Failed to parse CPP response from IR packet");
writeln!(&mut output_buffer, "\n{cpp_response:#02x?}").unwrap();
// Write output to top screen // Flush output and switch back to bottom screen
self.top_console.select(); self.top_console.with_screen(|screen| {
self.top_console.clear(); screen.flush_buffers();
std::io::stdout().write_all(&output_buffer).unwrap(); screen.swap_buffers();
});
self.bottom_console.select(); self.bottom_console.select();
// Done handling the packets, release them // Done handling the packets, release them
@ -241,13 +256,13 @@ impl<'screen> CirclePadProDemo<'screen> {
} }
} }
fn write_buffer_as_hex(buffer: &[u8], output: &mut Vec<u8>) { fn print_buffer_as_hex(buffer: &[u8]) {
let mut counter = 0; let mut counter = 0;
for byte in buffer { for byte in buffer {
write!(output, "{byte:02x} ").unwrap(); print!("{byte:02x} ");
counter += 1; counter += 1;
if counter % 16 == 0 { if counter % 16 == 0 {
writeln!(output).unwrap(); println!();
} }
} }
} }

15
ctru-rs/src/console.rs

@ -58,12 +58,12 @@ pub enum Dimension {
/// you can try using [`Soc::redirect_to_3dslink`](crate::services::soc::Soc::redirect_to_3dslink) while activating the `--server` flag for `3dslink` (also supported by `cargo-3ds`). /// you can try using [`Soc::redirect_to_3dslink`](crate::services::soc::Soc::redirect_to_3dslink) while activating the `--server` flag for `3dslink` (also supported by `cargo-3ds`).
/// More info in the [`cargo-3ds` docs](https://github.com/rust3ds/cargo-3ds#running-executables). /// More info in the [`cargo-3ds` docs](https://github.com/rust3ds/cargo-3ds#running-executables).
#[doc(alias = "PrintConsole")] #[doc(alias = "PrintConsole")]
pub struct Console<'screen> { pub struct Console<'screen, S: Screen> {
context: Box<PrintConsole>, context: Box<PrintConsole>,
screen: RefMut<'screen, dyn Screen>, screen: RefMut<'screen, S>,
} }
impl<'screen> Console<'screen> { impl<'screen, S: Screen> Console<'screen, S> {
/// Initialize a console on the chosen screen. /// Initialize a console on the chosen screen.
/// ///
/// # Notes /// # Notes
@ -102,7 +102,7 @@ impl<'screen> Console<'screen> {
/// # } /// # }
/// ``` /// ```
#[doc(alias = "consoleInit")] #[doc(alias = "consoleInit")]
pub fn new(screen: RefMut<'screen, dyn Screen>) -> Self { pub fn new(screen: RefMut<'screen, S>) -> Self {
let mut context = Box::<PrintConsole>::default(); let mut context = Box::<PrintConsole>::default();
unsafe { consoleInit(screen.as_raw(), context.as_mut()) }; unsafe { consoleInit(screen.as_raw(), context.as_mut()) };
@ -322,9 +322,14 @@ impl<'screen> Console<'screen> {
_ => unreachable!(), _ => unreachable!(),
} }
} }
/// Run a function with access to the underlying screen.
pub fn with_screen(&mut self, action: impl FnOnce(&mut RefMut<'_, S>)) {
action(&mut self.screen);
}
} }
impl Drop for Console<'_> { impl<S: Screen> Drop for Console<'_, S> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// Safety: We are about to deallocate the PrintConsole data pointed // Safety: We are about to deallocate the PrintConsole data pointed

Loading…
Cancel
Save