diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..0554648 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,16 @@ +name: build +on: [push] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Rust setup + uses: ATiltedTree/setup-rust@v1 + with: + rust-version: stable + - name: Install libudev + run: | + sudo apt-get install libudev-dev + - name: Check build errors + run: cargo build diff --git a/.github/workflows/check_format.yml b/.github/workflows/check_format.yml new file mode 100644 index 0000000..c2b536f --- /dev/null +++ b/.github/workflows/check_format.yml @@ -0,0 +1,14 @@ +name: check format +on: [push] +jobs: + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Rust setup + uses: ATiltedTree/setup-rust@v1 + with: + rust-version: stable + components: rustfmt + - name: Check formatting + run: cargo fmt --check diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..83951f6 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,14 @@ +name: run linter +on: [push] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Rust setup + uses: ATiltedTree/setup-rust@v1 + with: + rust-version: stable + components: clippy + - name: Check clippy + run: cargo clippy --no-deps diff --git a/Cargo.lock b/Cargo.lock index a231516..cebb102 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "dualsense-rs" -version = "0.2.0" +version = "0.3.0" dependencies = [ "hidapi", ] diff --git a/Cargo.toml b/Cargo.toml index a96b5a1..b504fa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dualsense-rs" -version = "0.2.0" +version = "0.3.0" edition = "2021" license = "MIT" description = "Rust programmatic wrapper over HID messages sent and received by the PS5 DualSense controller." diff --git a/examples/log.rs b/examples/log.rs index 5ad6d3c..117b0cc 100644 --- a/examples/log.rs +++ b/examples/log.rs @@ -17,16 +17,30 @@ fn main() { // controller.on_r2_changed(&|r2| println!("right pad y: {r2}")); // controller.on_symbols_changed(&|sym| println!("pressed symbol {}", sym as u8)); - - controller.on_gyro_x_changed(&|val| println!("gyro x: {val}")); - controller.on_gyro_y_changed(&|val| println!("gyro y: {val}")); - controller.on_gyro_z_changed(&|val| println!("gyro z: {val}")); + // controller.on_l1_changed(&|pressed| println!("l1 {pressed}")); + // controller.on_r1_changed(&|pressed| println!("r1 {pressed}")); + // controller.on_l3_changed(&|pressed| println!("l3 {pressed}")); + // controller.on_r3_changed(&|pressed| println!("r3 {pressed}")); + // controller.on_options_changed(&|pressed| println!("options {pressed}")); + // controller.on_share_changed(&|pressed| println!("share {pressed}")); + // + // controller.on_touchpad_changed(&|pressed| println!("touchpad {pressed}")); + // controller.on_mute_changed(&|pressed| println!("mute {pressed}")); + // controller.on_playstation_changed(&|pressed| println!("ps {pressed}")); + + // controller.on_gyro_x_changed(&|val| println!("gyro x: {val}")); + // controller.on_gyro_y_changed(&|val| println!("gyro y: {val}")); + // controller.on_gyro_z_changed(&|val| println!("gyro z: {val}")); + + // controller.on_accel_x_changed(&|val| println!("accel x: {val}")); + // controller.on_accel_y_changed(&|val| println!("accel y: {val}")); + // controller.on_accel_z_changed(&|val| println!("accel z: {val}")); controller.on_touchpad1_x_changed(&|val| println!("touchpad 1 x: {val}")); controller.on_touchpad1_y_changed(&|val| println!("touchpad 1 y: {val}")); controller.on_touchpad2_x_changed(&|val| println!("touchpad 2 x: {val}")); controller.on_touchpad2_y_changed(&|val| println!("touchpad 2 y: {val}")); - + controller.on_touchpad1_pressed(&|val| println!("touchpad 1 pressed: {val}")); controller.on_touchpad2_pressed(&|val| println!("touchpad 2 pressed: {val}")); let handle = controller.run(); diff --git a/src/dualsense/mod.rs b/src/dualsense/mod.rs index 775efb5..43ae7b9 100644 --- a/src/dualsense/mod.rs +++ b/src/dualsense/mod.rs @@ -1,4 +1,4 @@ pub mod stream; pub use stream::*; -pub(crate) mod properties; pub(crate) mod callback_helpers; +pub(crate) mod properties; diff --git a/src/dualsense/properties/dpad.rs b/src/dualsense/properties/dpad.rs index 2e8f8e9..5adb4ff 100644 --- a/src/dualsense/properties/dpad.rs +++ b/src/dualsense/properties/dpad.rs @@ -1,3 +1,4 @@ +/// Directional pad values #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum DPad { Up = 0, diff --git a/src/dualsense/properties/mod.rs b/src/dualsense/properties/mod.rs index 039460a..5d70b56 100644 --- a/src/dualsense/properties/mod.rs +++ b/src/dualsense/properties/mod.rs @@ -1,5 +1,5 @@ -pub(crate) mod offset; pub(crate) mod dpad; +pub(crate) mod offset; pub(crate) mod property; pub(crate) mod symbols; pub(crate) mod valuetype; diff --git a/src/dualsense/properties/property.rs b/src/dualsense/properties/property.rs index b7fbfc6..0cd623e 100644 --- a/src/dualsense/properties/property.rs +++ b/src/dualsense/properties/property.rs @@ -116,13 +116,13 @@ impl Property { | Property::AccelerationY | Property::AccelerationZ => ValueType::U16((data[1] as u16) << 8 | data[0] as u16), - Property::TouchPadFinger1Active => ValueType::Bool(data[0] & 0x80 == 1), + Property::TouchPadFinger1Active => ValueType::Bool(data[0] & 0x80 == 0x80), Property::TouchPad1Id => ValueType::U8(data[0] & 0x7F), Property::TouchPad1X => ValueType::U16(((data[1] as u16 & 0x0F) << 8) | data[0] as u16), Property::TouchPad1Y => { ValueType::U16(((data[1] as u16) << 4) | (data[0] as u16 & 0xF0) >> 4) } - Property::TouchPadFinger2Active => ValueType::Bool(data[0] & 0x80 == 1), + Property::TouchPadFinger2Active => ValueType::Bool(data[0] & 0x80 == 0x80), Property::TouchPad2Id => ValueType::U8(data[0] & 0x7F), Property::TouchPad2X => ValueType::U16((data[1] as u16 & 0x0F) << 8 | data[0] as u16), Property::TouchPad2Y => { diff --git a/src/dualsense/properties/symbols.rs b/src/dualsense/properties/symbols.rs index d308967..a662f14 100644 --- a/src/dualsense/properties/symbols.rs +++ b/src/dualsense/properties/symbols.rs @@ -1,3 +1,4 @@ +/// Symbols values #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Symbols { Square = 1, diff --git a/src/dualsense/stream.rs b/src/dualsense/stream.rs index 03d1952..74fe24c 100644 --- a/src/dualsense/stream.rs +++ b/src/dualsense/stream.rs @@ -11,10 +11,11 @@ const VENDOR_ID: u16 = 1356; const PRODUCT_ID: u16 = 3302; const PACKET_SIZE: usize = 64; +type CBFunction = Box; /// Main struct used for interacting with the controller pub struct DualSense { device: HidDevice, - callbacks: HashMap>>, + callbacks: HashMap>, cache: HashMap, } @@ -31,7 +32,7 @@ impl DualSense { /// Start listening to HID packets from the controller pub fn run(mut self) -> JoinHandle<()> { - thread::spawn(move || loop { + thread::spawn(move || loop { let mut buf = [0u8; PACKET_SIZE]; let bytes_read = self.device.read(&mut buf); match bytes_read { @@ -51,6 +52,11 @@ impl DualSense { }) } + #[allow(dead_code)] + fn write(&mut self, _data: u8) { + // self.device.write(data); + } + /// Provide a callback to be called when the left stick's x coordinate changes /// left: 0x00, right: 0xFF pub fn on_left_pad_x_changed(&mut self, cb: &'static F) @@ -134,19 +140,19 @@ impl DualSense { } /// Provide a callback to be called when the options button is pressed - pub fn on_options_changed(&mut self, cb: &'static F) + pub fn on_share_changed(&mut self, cb: &'static F) where F: Fn(bool) + Send + Sync, { - self.register_bool(Property::Options, cb); + self.register_bool(Property::Share, cb); } /// Provide a callback to be called when the options button is pressed - pub fn on_share_changed(&mut self, cb: &'static F) + pub fn on_options_changed(&mut self, cb: &'static F) where F: Fn(bool) + Send + Sync, { - self.register_bool(Property::Share, cb); + self.register_bool(Property::Options, cb); } /// Provide a callback to be called when any dpad button is pressed @@ -165,6 +171,30 @@ impl DualSense { self.register_symbols(Property::Symbols, cb); } + /// Provide a callback to be called when the mute button is pressed + pub fn on_mute_changed(&mut self, cb: &'static F) + where + F: Fn(bool) + Send + Sync, + { + self.register_bool(Property::Mute, cb); + } + + /// Provide a callback to be called when the touchpad is pressed + pub fn on_touchpad_changed(&mut self, cb: &'static F) + where + F: Fn(bool) + Send + Sync, + { + self.register_bool(Property::TouchPad, cb); + } + + /// Provide a callback to be called when the playstation button is pressed + pub fn on_playstation_pressed(&mut self, cb: &'static F) + where + F: Fn(bool) + Send + Sync, + { + self.register_bool(Property::PlayStation, cb); + } + /// Provide a callback to be called when the gyroscope X axis is changed pub fn on_gyro_x_changed(&mut self, cb: &'static F) where @@ -212,7 +242,7 @@ impl DualSense { { self.register_u16(Property::AccelerationZ, cb); } - + /// Provide a callback to be called when the touchpad is touched pub fn on_touchpad1_pressed(&mut self, cb: &'static F) where @@ -251,7 +281,7 @@ impl DualSense { { self.register_u16(Property::TouchPad1X, cb); } - + /// Provide a callback to be called when the touchpad input from the first finger /// on the Y axis is changed pub fn on_touchpad1_y_changed(&mut self, cb: &'static F) @@ -260,7 +290,7 @@ impl DualSense { { self.register_u16(Property::TouchPad1Y, cb); } - + /// Provide a callback to be called when the touchpad input from the second finger /// on the X axis is changed pub fn on_touchpad2_x_changed(&mut self, cb: &'static F) @@ -269,7 +299,7 @@ impl DualSense { { self.register_u16(Property::TouchPad2X, cb); } - + /// Provide a callback to be called when the touchpad input from the second finger /// on the Y axis is changed pub fn on_touchpad2_y_changed(&mut self, cb: &'static F) @@ -285,7 +315,7 @@ impl DualSense { { self.callbacks .entry(prop) - .or_insert_with(|| vec![]) + .or_default() .push(Box::new(move |x| cb(x.to_u8()))); } @@ -295,7 +325,7 @@ impl DualSense { { self.callbacks .entry(prop) - .or_insert_with(|| vec![]) + .or_default() .push(Box::new(move |x| cb(x.to_u16()))); } @@ -305,7 +335,7 @@ impl DualSense { { self.callbacks .entry(prop) - .or_insert_with(|| vec![]) + .or_default() .push(Box::new(move |x| cb(x.to_dpad()))); } @@ -315,7 +345,7 @@ impl DualSense { { self.callbacks .entry(prop) - .or_insert_with(|| vec![]) + .or_default() .push(Box::new(move |x| cb(x.to_symbol()))); } @@ -325,7 +355,7 @@ impl DualSense { { self.callbacks .entry(prop) - .or_insert_with(|| vec![]) + .or_default() .push(Box::new(move |x| cb(x.to_bool()))); } @@ -350,6 +380,17 @@ impl DualSense { }) } + #[allow(dead_code)] + fn debug_print_packet(data: &[u8; PACKET_SIZE]) { + data.chunks(8).for_each(|w| { + for b in w { + print!("{:#04x} ", b) + } + println!(); + }); + println!() + } + fn extract_bytes(prop: &Property, data: &[u8; 64]) -> ValueType { if prop.offset().bits == (0..8) { prop.convert(&data.as_slice()[prop.offset().bytes]) @@ -361,7 +402,7 @@ impl DualSense { for i in prop.offset().bits { let offset = i - prop.offset().bits.start; let current_bit = (val & (1 << i)) >> i; - out = out | (current_bit << offset); + out |= current_bit << offset; } prop.convert(&[out]) } else { @@ -369,3 +410,9 @@ impl DualSense { } } } + +impl Default for DualSense { + fn default() -> Self { + DualSense::new() + } +} diff --git a/src/lib.rs b/src/lib.rs index 4a078eb..6770394 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,35 +1,30 @@ //! Rust programmatic wrapper over HID messages sent and received by the PS5 DualSense controller. All communication is done via callback functions that are ran in a separate thread. -//! +//! //! ## Usage -//! +//! //! ### Read -//! +//! //! Log details about the left and right sticks' positions -//! +//! //! ```rust -//! use dualsense_rs::DualSense; -//! -//! -//! fn main() { -//! let mut controller = DualSense::new(); -//! -//! controller.on_left_pad_x_changed(&|lpx| println!("left pad x: {lpx}")); -//! controller.on_left_pad_x_changed(&|lpx| { -//! if lpx > 127 { -//! println!("left pad x in right region: {lpx}") -//! } -//! }); -//! controller.on_left_pad_y_changed(&|lpy| println!("left pad y: {lpy}")); -//! controller.on_right_pad_x_changed(&|rpx| println!("right pad x: {rpx}")); -//! controller.on_right_pad_y_changed(&|rpy| println!("right pad y: {rpy}")); -//! -//! let handle = controller.run(); -//! handle.join().ok(); -//! } +//!let mut controller = DualSense::new(); +//! +//! controller.on_left_pad_x_changed(&|lpx| println!("left pad x: {lpx}")); +//! controller.on_left_pad_x_changed(&|lpx| { +//! if lpx > 127 { +//! println!("left pad x in right region: {lpx}") +//! } +//! }); +//!controller.on_left_pad_y_changed(&|lpy| println!("left pad y: {lpy}")); +//!controller.on_right_pad_x_changed(&|rpx| println!("right pad x: {rpx}")); +//!controller.on_right_pad_y_changed(&|rpy| println!("right pad y: {rpy}")); +//! +//! let handle = controller.run(); +//! handle.join().ok(); //! ``` -//! +//! //! Run the complete example with: -//! +//! //! ```sh //! cargo run --example log_all //! ```