gc_adapter/lib.rs
1//! A library for working with the Nintendo Gamecube controller adapter.
2//!
3//! **Supports:**
4//!
5//! * Official Nintendo Gamecube Controller Adapter for Wii U and Switch
6//! * Mayflash Gamecube Controller Adapter (in "Wii U/Switch" mode)
7//! * Other 3rd party adapters (untested)
8//!
9//! ## Example
10//!
11//! ```rust
12//! use gc_adapter::GcAdapter;
13//!
14//! // get adapter from global context
15//! let mut adapter = GcAdapter::from_usb().unwrap();
16//!
17//! // refresh inputs to ensure they are up to date
18//! adapter.refresh_inputs();
19//!
20//! // read and display all controller ports
21//! dbg!(adapter.read_controllers());
22//!
23//! // enable rumble for only ports 4
24//! adapter.set_rumble([false, false, false, true]);
25//!
26//! std::thread::sleep(std::time::Duration::from_millis(100));
27//!
28//! // on drop all rumble will be disabled and the USB connection
29//! // will be cleaned up
30//! let _ = adapter;
31//! ```
32
33#[cfg(feature = "libusb")]
34pub use rusb;
35
36/// Vendor and Product IDs for adapter
37pub mod constants {
38 pub const ADAPTER_VID: u16 = 0x057e;
39 pub const ADAPTER_PID: u16 = 0x0337;
40}
41
42/// Packet parsing code
43mod parsing;
44pub use parsing::*;
45
46/// Types for represent various axis types
47mod axis;
48pub use axis::{SignedAxis, UnsignedAxis};
49
50/// Types/Traits for handling USB connections
51mod usb;
52pub use usb::*;
53
54/// A connection to a gamecube adapter
55pub struct GcAdapter<T: AdapterHardware> {
56 usb: T
57}
58
59impl<T: AdapterHardware> GcAdapter<T> {
60 /// Set rumble for all 4 ports at once
61 pub fn set_rumble(&mut self, ports: [bool; 4]) {
62 let payload = [
63 0x11,
64 ports[0] as u8,
65 ports[1] as u8,
66 ports[2] as u8,
67 ports[3] as u8
68 ];
69
70 self.usb.write_interrupt(&payload[..]);
71 let mut buf = [0u8; 37];
72 self.usb.read_interrupt(&mut buf);
73 }
74
75 /// Refresh the set of inputs by polling the adapter 10 times, this ensures the results of
76 /// [`read_controllers`](GcAdapter::read_controllers) is current.
77 pub fn refresh_inputs(&mut self) {
78 for _ in 0..10 {
79 let mut buf = [0u8; 37];
80 self.usb.read_interrupt(&mut buf);
81 }
82 }
83
84 /// Read the current state of all the controllers plugged into the adapter
85 pub fn read_controllers(&mut self) -> [Controller; 4] {
86 let mut buf = [0u8; 37];
87 self.usb.read_interrupt(&mut buf);
88
89 if let Packet::ControllerInfo { ports } = Packet::parse(buf) {
90 ports
91 } else {
92 Default::default()
93 }
94 }
95
96 /// Creates a new `GcAdapter` from a USB connection that implements
97 /// [`AdapterHardware`](AdapterHardware).
98 pub fn new(usb: T) -> Self {
99 Self { usb }
100 }
101}
102
103#[cfg(feature = "libusb")]
104impl GcAdapter<LibUsbAdapter<rusb::GlobalContext>> {
105 /// Get an adapter from the libusb global context.
106 /// Returns false if no adapter is plugged in.
107 pub fn from_usb() -> Option<Self> {
108 LibUsbAdapter::from_usb().map(Self::new)
109 }
110}
111
112impl<T: AdapterHardware> Drop for GcAdapter<T> {
113 fn drop(&mut self) {
114 // clear rumble on drop
115 self.set_rumble([false; 4]);
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 #[cfg(feature = "libusb")]
125 fn test_display_controllers() {
126 // get adapter from global context
127 let mut adapter = GcAdapter::from_usb().unwrap();
128
129 // refresh inputs to ensure they are up to date
130 adapter.refresh_inputs();
131
132 // read and display all controller ports
133 dbg!(adapter.read_controllers());
134
135 dbg!(adapter.read_controllers()[3].left_stick.coords());
136 }
137}