use std::{fmt, mem, ptr};
#[cfg(feature = "unstable_xtarget_notification")]
use std::{marker, pin, thread};
use std::borrow::Borrow;
use winapi::um::xinput::XINPUT_GAMEPAD;
use winapi::shared::winerror;
use crate::*;
#[derive(Copy, Clone, Default, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct XButtons {
pub raw: u16,
}
#[allow(non_snake_case)]
#[inline]
pub const fn XButtons(raw: u16) -> XButtons {
XButtons { raw }
}
#[macro_export]
macro_rules! XButtons {
(UP) => { $crate::XButtons { raw: $crate::XButtons::UP } };
(DOWN) => { $crate::XButtons { raw: $crate::XButtons::DOWN } };
(LEFT) => { $crate::XButtons { raw: $crate::XButtons::LEFT } };
(RIGHT) => { $crate::XButtons { raw: $crate::XButtons::RIGHT } };
(START) => { $crate::XButtons { raw: $crate::XButtons::START } };
(BACK) => { $crate::XButtons { raw: $crate::XButtons::BACK } };
(LTHUMB) => { $crate::XButtons { raw: $crate::XButtons::LTHUMB } };
(RTHUMB) => { $crate::XButtons { raw: $crate::XButtons::RTHUMB } };
(LB) => { $crate::XButtons { raw: $crate::XButtons::LB } };
(RB) => { $crate::XButtons { raw: $crate::XButtons::RB } };
(GUIDE) => { $crate::XButtons { raw: $crate::XButtons::GUIDE } };
(A) => { $crate::XButtons { raw: $crate::XButtons::A } };
(B) => { $crate::XButtons { raw: $crate::XButtons::B } };
(X) => { $crate::XButtons { raw: $crate::XButtons::X } };
(Y) => { $crate::XButtons { raw: $crate::XButtons::Y } };
($($face:ident)|*) => {
$crate::XButtons { raw: 0 $(| $crate::XButtons!($face).raw)* }
};
}
impl XButtons {
pub const UP: u16 = 0x0001;
pub const DOWN: u16 = 0x0002;
pub const LEFT: u16 = 0x0004;
pub const RIGHT: u16 = 0x0008;
pub const START: u16 = 0x0010;
pub const BACK: u16 = 0x0020;
pub const LTHUMB: u16 = 0x0040;
pub const RTHUMB: u16 = 0x0080;
pub const LB: u16 = 0x0100;
pub const RB: u16 = 0x0200;
pub const GUIDE: u16 = 0x0400;
pub const A: u16 = 0x1000;
pub const B: u16 = 0x2000;
pub const X: u16 = 0x4000;
pub const Y: u16 = 0x8000;
}
impl From<u16> for XButtons {
#[inline]
fn from(raw: u16) -> Self {
XButtons { raw }
}
}
impl From<XButtons> for u16 {
#[inline]
fn from(buttons: XButtons) -> Self {
buttons.raw
}
}
impl AsRef<u16> for XButtons {
#[inline]
fn as_ref(&self) -> &u16 {
&self.raw
}
}
impl AsMut<u16> for XButtons {
#[inline]
fn as_mut(&mut self) -> &mut u16 {
&mut self.raw
}
}
impl fmt::Debug for XButtons {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if f.alternate() {
const NAMES: [&'static str; 16] = [
"UP", "DOWN", "LEFT", "RIGHT",
"START", "BACK", "LTHUMB", "RTHUMB",
"LB", "RB", "GUIDE", "?",
"A", "B", "X", "Y",
];
let mut comma = false;
for index in 0..16 {
if self.raw & (1 << index) != 0 {
if comma {
f.write_str("|")?;
comma = true;
}
f.write_str(NAMES[index])?;
}
}
Ok(())
}
else {
write!(f, "XButtons({:#x})", self.raw)
}
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct XGamepad {
pub buttons: XButtons,
pub left_trigger: u8,
pub right_trigger: u8,
pub thumb_lx: i16,
pub thumb_ly: i16,
pub thumb_rx: i16,
pub thumb_ry: i16,
}
impl From<XINPUT_GAMEPAD> for XGamepad {
#[inline]
fn from(gamepad: XINPUT_GAMEPAD) -> Self {
unsafe { mem::transmute(gamepad) }
}
}
impl From<XGamepad> for XINPUT_GAMEPAD {
#[inline]
fn from(report: XGamepad) -> XINPUT_GAMEPAD {
unsafe { mem::transmute(report) }
}
}
impl AsRef<XINPUT_GAMEPAD> for XGamepad {
#[inline]
fn as_ref(&self) -> &XINPUT_GAMEPAD {
unsafe { mem::transmute(self) }
}
}
impl AsMut<XINPUT_GAMEPAD> for XGamepad {
#[inline]
fn as_mut(&mut self) -> &mut XINPUT_GAMEPAD {
unsafe { mem::transmute(self) }
}
}
#[cfg(feature = "unstable_xtarget_notification")]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct XNotification {
pub large_motor: u8,
pub small_motor: u8,
pub led_number: u8,
}
#[cfg(feature = "unstable_xtarget_notification")]
pub struct XRequestNotification {
client: Client,
xurn: bus::RequestNotification<bus::XUsbRequestNotification>,
_unpin: marker::PhantomPinned,
}
#[cfg(feature = "unstable_xtarget_notification")]
impl XRequestNotification {
#[inline]
pub fn is_attached(&self) -> bool {
self.xurn.buffer.SerialNo != 0
}
#[inline]
pub fn spawn_thread<F: FnMut(&XRequestNotification, XNotification) + Send + 'static>(self, mut f: F) -> thread::JoinHandle<()> {
thread::spawn(move || {
let mut reqn = self;
let mut reqn = unsafe { pin::Pin::new_unchecked(&mut reqn) };
loop {
reqn.as_mut().request();
let result = reqn.as_mut().poll(true);
match result {
Ok(None) => {},
Ok(Some(data)) => f(&reqn, data),
Err(_) => break,
}
}
})
}
#[inline(never)]
pub fn request(self: pin::Pin<&mut Self>) {
unsafe {
let device = self.client.device;
let xurn = &mut self.get_unchecked_mut().xurn;
if xurn.buffer.SerialNo != 0 {
xurn.ioctl(device);
}
}
}
#[inline(never)]
pub fn poll(self: pin::Pin<&mut Self>, wait: bool) -> Result<Option<XNotification>, Error> {
unsafe {
let device = self.client.device;
let xurn = &mut self.get_unchecked_mut().xurn;
match xurn.poll(device, wait) {
Ok(()) => Ok(Some(XNotification {
large_motor: xurn.buffer.LargeMotor,
small_motor: xurn.buffer.SmallMotor,
led_number: xurn.buffer.LedNumber,
})),
Err(winerror::ERROR_IO_INCOMPLETE) => Ok(None),
Err(winerror::ERROR_OPERATION_ABORTED) => {
xurn.buffer.SerialNo = 0;
Err(Error::OperationAborted)
},
Err(err) => Err(Error::WinError(err)),
}
}
}
}
#[cfg(feature = "unstable_xtarget_notification")]
unsafe impl Sync for XRequestNotification {}
#[cfg(feature = "unstable_xtarget_notification")]
unsafe impl Send for XRequestNotification {}
#[cfg(feature = "unstable_xtarget_notification")]
impl fmt::Debug for XRequestNotification {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("XRequestNotification")
.field("client", &format_args!("{:?}", self.client))
.field("serial_no", &self.xurn.buffer.SerialNo)
.finish()
}
}
#[cfg(feature = "unstable_xtarget_notification")]
impl Drop for XRequestNotification {
fn drop(&mut self) {
unsafe {
let this = pin::Pin::new_unchecked(self);
if this.xurn.buffer.SerialNo != 0 {
let device = this.client.device;
let xurn = &mut this.get_unchecked_mut().xurn;
let _ = xurn.cancel(device);
}
}
}
}
pub type XTarget = Xbox360Wired<Client>;
pub struct Xbox360Wired<CL: Borrow<Client>> {
client: CL,
event: Event,
serial_no: u32,
id: TargetId,
}
impl<CL: Borrow<Client>> Xbox360Wired<CL> {
#[inline]
pub fn new(client: CL, id: TargetId) -> Xbox360Wired<CL> {
let event = Event::new(false, false);
Xbox360Wired { client, event, serial_no: 0, id }
}
#[inline]
pub fn is_attached(&self) -> bool {
self.serial_no != 0
}
#[inline]
pub fn id(&self) -> TargetId {
self.id
}
#[inline]
pub fn client(&self) -> &CL {
&self.client
}
#[inline]
pub fn drop(mut self) -> CL {
let _ = self.unplug();
unsafe {
let client = (&self.client as *const CL).read();
ptr::drop_in_place(&mut self.event);
mem::forget(self);
client
}
}
#[inline(never)]
pub fn plugin(&mut self) -> Result<(), Error> {
if self.is_attached() {
return Err(Error::AlreadyConnected);
}
let mut plugin = bus::PluginTarget::x360_wired(1, self.id.vendor, self.id.product);
let device = self.client.borrow().device;
while unsafe { plugin.ioctl(device, self.event.handle) }.is_err() {
plugin.SerialNo += 1;
if plugin.SerialNo >= u16::MAX as u32 {
return Err(Error::NoFreeSlot);
}
}
self.serial_no = plugin.SerialNo;
Ok(())
}
#[inline(never)]
pub fn unplug(&mut self) -> Result<(), Error> {
if !self.is_attached() {
return Err(Error::NotPluggedIn);
}
unsafe {
let mut unplug = bus::UnplugTarget::new(self.serial_no);
let device = self.client.borrow().device;
unplug.ioctl(device, self.event.handle)?;
}
self.serial_no = 0;
Ok(())
}
#[inline(never)]
pub fn wait_ready(&mut self) -> Result<(), Error> {
if !self.is_attached() {
return Err(Error::NotPluggedIn);
}
unsafe {
let mut wait = bus::WaitDeviceReady::new(self.serial_no);
let device = self.client.borrow().device;
wait.ioctl(device, self.event.handle)?;
}
Ok(())
}
#[inline(never)]
pub fn get_user_index(&mut self) -> Result<u32, Error> {
if !self.is_attached() {
return Err(Error::NotPluggedIn);
}
let user_index = unsafe {
let mut gui = bus::XUsbGetUserIndex::new(self.serial_no);
let device = self.client.borrow().device;
match gui.ioctl(device, self.event.handle) {
Ok(()) => (),
Err(winerror::ERROR_INVALID_DEVICE_OBJECT_PARAMETER) => return Err(Error::UserIndexOutOfRange),
Err(err) => return Err(Error::WinError(err)),
}
gui.UserIndex
};
Ok(user_index)
}
#[inline(never)]
pub fn update(&mut self, gamepad: &XGamepad) -> Result<(), Error> {
if !self.is_attached() {
return Err(Error::NotPluggedIn);
}
unsafe {
let mut xsr = bus::XUsbSubmitReport::new(self.serial_no, *gamepad);
let device = self.client.borrow().device;
match xsr.ioctl(device, self.event.handle) {
Ok(()) => Ok(()),
Err(winerror::ERROR_DEV_NOT_EXIST) => Err(Error::TargetNotReady),
Err(err) => Err(Error::WinError(err)),
}
}
}
#[cfg(feature = "unstable_xtarget_notification")]
#[inline(never)]
pub fn request_notification(&mut self) -> Result<XRequestNotification, Error> {
if !self.is_attached() {
return Err(Error::NotPluggedIn);
}
let client = self.client.borrow().try_clone()?;
let xurn = bus::RequestNotification::new(
bus::XUsbRequestNotification::new(self.serial_no));
Ok(XRequestNotification { client, xurn, _unpin: marker::PhantomPinned })
}
}
impl<CL: Borrow<Client>> fmt::Debug for Xbox360Wired<CL> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Xbox360Wired")
.field("client", &format_args!("{:?}", self.client.borrow()))
.field("event", &format_args!("{:?}", self.event))
.field("serial_no", &self.serial_no)
.field("vendor_id", &self.id.vendor)
.field("product_id", &self.id.product)
.finish()
}
}
impl<CL: Borrow<Client>> Drop for Xbox360Wired<CL> {
#[inline]
fn drop(&mut self) {
let _ = self.unplug();
}
}