diff --git a/ctru-rs/examples/ir-user.rs b/ctru-rs/examples/ir-user.rs index 2b04260..252dbf7 100644 --- a/ctru-rs/examples/ir-user.rs +++ b/ctru-rs/examples/ir-user.rs @@ -2,6 +2,7 @@ use ctru::prelude::*; use ctru::services::ir_user::{CirclePadProInputResponse, IrDeviceId, IrUser}; +use ctru_sys::Handle; use std::io::Write; use std::time::Duration; @@ -20,36 +21,13 @@ fn main() { let top_console = Console::init(gfx.top_screen.borrow_mut()); let bottom_console = Console::init(gfx.bottom_screen.borrow_mut()); - println!("Welcome to the ir:USER / Circle Pad Pro Demo"); - - println!("Starting up ir:USER service"); - let ir_user = IrUser::init( - PACKET_BUFFER_SIZE, - PACKET_COUNT, - PACKET_BUFFER_SIZE, - PACKET_COUNT, - ) - .expect("Couldn't initialize ir:USER service"); - println!("ir:USER service initialized\nPress A to connect to the CPP"); - - let print_status_info = || { - top_console.select(); - top_console.clear(); - println!("{:#x?}", ir_user.get_status_info()); - bottom_console.select(); - }; + let demo = CirclePadProDemo::new(top_console, bottom_console); + demo.print_status_info(); - // Get event handles - let conn_status_event = ir_user - .get_connection_status_event() - .expect("Couldn't get ir:USER connection status event"); - let recv_event = ir_user - .get_recv_event() - .expect("Couldn't get ir:USER recv event"); - print_status_info(); + println!("Press A to connect to the CPP"); let mut is_connected = false; - 'main_loop: while apt.main_loop() { + while apt.main_loop() { hid.scan_input(); // Check if we need to exit @@ -58,132 +36,197 @@ fn main() { } // Check if we've received a packet from the circle pad pro - let packet_received = IrUser::wait_for_event(recv_event, Duration::ZERO).is_ok(); + let packet_received = + IrUser::wait_for_event(demo.receive_packet_event, Duration::ZERO).is_ok(); if packet_received { - handle_packet(&ir_user, &top_console, &bottom_console); + demo.handle_packets(); } // Check if we should start the connection if hid.keys_down().contains(KeyPad::KEY_A) && !is_connected { println!("Attempting to connect to the CPP"); - // Connection loop - loop { - hid.scan_input(); - if hid.keys_held().contains(KeyPad::KEY_START) { - break 'main_loop; - } + match demo.connect_to_cpp(&hid) { + ConnectionResult::Connected => is_connected = true, + ConnectionResult::Canceled => break, + } + } - // Start the connection process - ir_user - .require_connection(IrDeviceId::CirclePadPro) - .expect("Couldn't initialize circle pad pro connection"); - - // Wait for the connection to establish - if let Err(e) = - IrUser::wait_for_event(conn_status_event, Duration::from_millis(100)) - { - if !e.is_timeout() { - panic!("Couldn't initialize circle pad pro connection: {e}"); - } - } + gfx.flush_buffers(); + gfx.swap_buffers(); + gfx.wait_for_vblank(); + } +} - print_status_info(); - if ir_user.get_status_info().connection_status == 2 { - println!("Connected!"); - break; - } +struct CirclePadProDemo<'screen> { + top_console: Console<'screen>, + bottom_console: Console<'screen>, + ir_user: IrUser, + connection_status_event: Handle, + receive_packet_event: Handle, +} - // If not connected (ex. timeout), disconnect so we can retry - ir_user - .disconnect() - .expect("Failed to disconnect circle pad pro connection"); - - // Wait for the disconnect to go through - if let Err(e) = - IrUser::wait_for_event(conn_status_event, Duration::from_millis(100)) - { - if !e.is_timeout() { - panic!("Couldn't initialize circle pad pro connection: {e}"); - } - } +enum ConnectionResult { + Connected, + Canceled, +} + +impl<'screen> CirclePadProDemo<'screen> { + fn new(top_console: Console<'screen>, bottom_console: Console<'screen>) -> Self { + bottom_console.select(); + println!("Welcome to the ir:USER / Circle Pad Pro Demo"); + + println!("Starting up ir:USER service"); + let ir_user = IrUser::init( + PACKET_BUFFER_SIZE, + PACKET_COUNT, + PACKET_BUFFER_SIZE, + PACKET_COUNT, + ) + .expect("Couldn't initialize ir:USER service"); + println!("ir:USER service initialized"); + + // Get event handles + let connection_status_event = ir_user + .get_connection_status_event() + .expect("Couldn't get ir:USER connection status event"); + let receive_packet_event = ir_user + .get_recv_event() + .expect("Couldn't get ir:USER recv event"); + + Self { + top_console, + bottom_console, + ir_user, + connection_status_event, + receive_packet_event, + } + } + + fn print_status_info(&self) { + self.top_console.select(); + self.top_console.clear(); + println!("{:#x?}", self.ir_user.get_status_info()); + self.bottom_console.select(); + } + + fn connect_to_cpp(&self, hid: &Hid) -> ConnectionResult { + // Connection loop + loop { + hid.scan_input(); + if hid.keys_held().contains(KeyPad::KEY_START) { + return ConnectionResult::Canceled; } - // Sending first packet retry loop - loop { - hid.scan_input(); - if hid.keys_held().contains(KeyPad::KEY_START) { - break 'main_loop; + // Start the connection process + self.ir_user + .require_connection(IrDeviceId::CirclePadPro) + .expect("Couldn't initialize circle pad pro connection"); + + // Wait for the connection to establish + if let Err(e) = + IrUser::wait_for_event(self.connection_status_event, Duration::from_millis(100)) + { + if !e.is_timeout() { + panic!("Couldn't initialize circle pad pro connection: {e}"); } + } - // Send a request for input to the CPP - if let Err(e) = ir_user.request_input_polling(CPP_CONNECTION_POLLING_PERIOD_MS) { - println!("Error: {e:?}"); + self.print_status_info(); + if self.ir_user.get_status_info().connection_status == 2 { + println!("Connected!"); + break; + } + + // If not connected (ex. timeout), disconnect so we can retry + self.ir_user + .disconnect() + .expect("Failed to disconnect circle pad pro connection"); + + // Wait for the disconnect to go through + if let Err(e) = + IrUser::wait_for_event(self.connection_status_event, Duration::from_millis(100)) + { + if !e.is_timeout() { + panic!("Couldn't initialize circle pad pro connection: {e}"); } - print_status_info(); + } + } - // Wait for the response - let recv_event_result = - IrUser::wait_for_event(recv_event, Duration::from_millis(100)); - print_status_info(); + // Sending first packet retry loop + loop { + hid.scan_input(); + if hid.keys_held().contains(KeyPad::KEY_START) { + return ConnectionResult::Canceled; + } - if recv_event_result.is_ok() { - println!("Got first packet from CPP"); - handle_packet(&ir_user, &top_console, &bottom_console); - break; - } + // Send a request for input to the CPP + if let Err(e) = self + .ir_user + .request_input_polling(CPP_CONNECTION_POLLING_PERIOD_MS) + { + println!("Error: {e:?}"); + } + self.print_status_info(); + + // Wait for the response + let recv_event_result = + IrUser::wait_for_event(self.receive_packet_event, Duration::from_millis(100)); + self.print_status_info(); - // We didn't get a response in time, so loop and retry + if recv_event_result.is_ok() { + println!("Got first packet from CPP"); + self.handle_packets(); + break; } - is_connected = true; + // We didn't get a response in time, so loop and retry } - gfx.flush_buffers(); - gfx.swap_buffers(); - gfx.wait_for_vblank(); + ConnectionResult::Connected } -} -fn handle_packet(ir_user: &IrUser, top_console: &Console, bottom_console: &Console) { - // 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?}", ir_user.get_status_info()).unwrap(); - - ir_user.process_shared_memory(|ir_mem| { - writeln!(&mut output_buffer, "\nReceiveBufferInfo:").unwrap(); - write_buffer_as_hex(&ir_mem[0x10..0x20], &mut output_buffer); - - 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(); - }); - - let packets = ir_user.get_packets(); - let packet_count = packets.len(); - writeln!(&mut output_buffer, "\nPacket count: {packet_count}").unwrap(); - let last_packet = packets.last().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(); - - // Write output to top screen - top_console.select(); - top_console.clear(); - std::io::stdout().write_all(&output_buffer).unwrap(); - bottom_console.select(); - - // Done handling the packet, release it - ir_user - .release_received_data(packet_count as u32) - .expect("Failed to release ir:USER packet"); - - // Remind the CPP that we're still listening - if let Err(e) = ir_user.request_input_polling(CPP_POLLING_PERIOD_MS) { - println!("Error: {e:?}"); + fn handle_packets(&self) { + let packets = self.ir_user.get_packets(); + let packet_count = packets.len(); + let Some(last_packet) = packets.last() else { return }; + + // 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(); + + 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); + + 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(); + }); + + 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(); + + // Write output to top screen + self.top_console.select(); + self.top_console.clear(); + std::io::stdout().write_all(&output_buffer).unwrap(); + self.bottom_console.select(); + + // Done handling the packets, release them + self.ir_user + .release_received_data(packet_count as u32) + .expect("Failed to release ir:USER packet"); + + // Remind the CPP that we're still listening + if let Err(e) = self.ir_user.request_input_polling(CPP_POLLING_PERIOD_MS) { + println!("Error: {e:?}"); + } } }