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

15
ctru-rs/src/console.rs

@ -58,12 +58,12 @@ pub enum Dimension { @@ -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`).
/// More info in the [`cargo-3ds` docs](https://github.com/rust3ds/cargo-3ds#running-executables).
#[doc(alias = "PrintConsole")]
pub struct Console<'screen> {
pub struct Console<'screen, S: Screen> {
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.
///
/// # Notes
@ -102,7 +102,7 @@ impl<'screen> Console<'screen> { @@ -102,7 +102,7 @@ impl<'screen> Console<'screen> {
/// # }
/// ```
#[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();
unsafe { consoleInit(screen.as_raw(), context.as_mut()) };
@ -322,9 +322,14 @@ impl<'screen> Console<'screen> { @@ -322,9 +322,14 @@ impl<'screen> Console<'screen> {
_ => 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) {
unsafe {
// Safety: We are about to deallocate the PrintConsole data pointed

Loading…
Cancel
Save