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}