ScpControl/BusDevice.cs (265 lines of code) (raw):

using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; using ScpControl.Driver; using ScpControl.ScpCore; using ScpControl.Shared.Core; using ScpControl.Shared.Utilities; using ScpControl.Shared.XInput; namespace ScpControl { public class BusDevice : ScpDevice { #region Private fields private const int BusWidth = 4; private readonly List<int> _pluggedInDevices = new List<int>(); private int _busOffset; private DsState _busState = DsState.Disconnected; #endregion #region Public properties private static Guid DeviceClassGuid { get { return Guid.Parse("{F679F562-3164-42CE-A4DB-E7DDBE723909}"); } } public DsState State { get { return _busState; } } #endregion #region Ctor public BusDevice() : base(DeviceClassGuid) { } #endregion #region Public methods /// <summary> /// Translates Pad ID to bus device offset. /// </summary> /// <param name="index">The Pad ID to translate.</param> /// <returns>The bus device serial.</returns> public int IndexToSerial(byte index) { return index + _busOffset + 1; } public override bool Open(int instance = 0) { if (State == DsState.Disconnected) { _busOffset = instance*BusWidth; Log.DebugFormat("-- Bus Open: Offset {0}", _busOffset); if (!base.Open(0)) { Log.ErrorFormat("-- Bus Open: Offset {0} failed", _busOffset); } } return State == DsState.Reserved; } public override bool Open(string devicePath) { if (State == DsState.Disconnected) { Path = devicePath; Log.DebugFormat("-- Bus Open: Path {0}", Path); if (GetDeviceHandle(Path)) { IsActive = true; _busState = DsState.Reserved; } } return State == DsState.Reserved; } public override bool Start() { if (State == DsState.Reserved) { _busState = DsState.Connected; } return State == DsState.Connected; } public override bool Stop() { if (State == DsState.Connected) { var items = new Queue<int>(); lock (_pluggedInDevices) { foreach (var serial in _pluggedInDevices) items.Enqueue(serial - _busOffset); } while (items.Count > 0) Unplug(items.Dequeue()); // send un-plug-all to clean the bus from devices stuck in error state XOutputWrapper.Instance.UnPlugAll(); _busState = DsState.Reserved; } return State == DsState.Reserved; } public override bool Close() { if (base.Stop()) { _busState = DsState.Reserved; } if (State != DsState.Reserved) { if (base.Close()) { _busState = DsState.Disconnected; } } return State == DsState.Disconnected; } public bool Suspend() { return Stop(); } public bool Resume() { return Start(); } /// <summary> /// Translates an <see cref="ScpHidReport"/> to an Xbox 360 compatible byte array. /// </summary> /// <param name="inputReport">The <see cref="ScpHidReport"/> to translate.</param> /// <returns>The translated data as <see cref="XINPUT_GAMEPAD"/> structure.</returns> public XINPUT_GAMEPAD Parse(ScpHidReport inputReport) { var xButton = X360Button.None; var output = new XINPUT_GAMEPAD(); switch (inputReport.Model) { case DsModel.DS3: { // select & start if (inputReport[Ds3Button.Select].IsPressed) xButton |= X360Button.Back; if (inputReport[Ds3Button.Start].IsPressed) xButton |= X360Button.Start; // d-pad if (inputReport[Ds3Button.Up].IsPressed) xButton |= X360Button.Up; if (inputReport[Ds3Button.Right].IsPressed) xButton |= X360Button.Right; if (inputReport[Ds3Button.Down].IsPressed) xButton |= X360Button.Down; if (inputReport[Ds3Button.Left].IsPressed) xButton |= X360Button.Left; // shoulders if (inputReport[Ds3Button.L1].IsPressed) xButton |= X360Button.LB; if (inputReport[Ds3Button.R1].IsPressed) xButton |= X360Button.RB; // face buttons if (inputReport[Ds3Button.Triangle].IsPressed) xButton |= X360Button.Y; if (inputReport[Ds3Button.Circle].IsPressed) xButton |= X360Button.B; if (inputReport[Ds3Button.Cross].IsPressed) xButton |= X360Button.A; if (inputReport[Ds3Button.Square].IsPressed) xButton |= X360Button.X; // PS/Guide if (inputReport[Ds3Button.Ps].IsPressed) xButton |= X360Button.Guide; // thumbs if (inputReport[Ds3Button.L3].IsPressed) xButton |= X360Button.LS; if (inputReport[Ds3Button.R3].IsPressed) xButton |= X360Button.RS; // face buttons output.wButtons = (ushort) xButton; // trigger output.bLeftTrigger = inputReport[Ds3Axis.L2].Value; output.bRightTrigger = inputReport[Ds3Axis.R2].Value; if (!DsMath.DeadZone(GlobalConfiguration.Instance.DeadZoneL, inputReport[Ds3Axis.Lx].Value, inputReport[Ds3Axis.Ly].Value)) // Left Stick DeadZone { output.sThumbLX = (short) +DsMath.Scale(inputReport[Ds3Axis.Lx].Value, GlobalConfiguration.Instance.FlipLX); output.sThumbLY = (short) -DsMath.Scale(inputReport[Ds3Axis.Ly].Value, GlobalConfiguration.Instance.FlipLY); } if (!DsMath.DeadZone(GlobalConfiguration.Instance.DeadZoneR, inputReport[Ds3Axis.Rx].Value, inputReport[Ds3Axis.Ry].Value)) // Right Stick DeadZone { output.sThumbRX = (short) +DsMath.Scale(inputReport[Ds3Axis.Rx].Value, GlobalConfiguration.Instance.FlipRX); output.sThumbRY = (short) -DsMath.Scale(inputReport[Ds3Axis.Ry].Value, GlobalConfiguration.Instance.FlipRY); } } break; case DsModel.DS4: { if (inputReport[Ds4Button.Share].IsPressed) xButton |= X360Button.Back; if (inputReport[Ds4Button.Options].IsPressed) xButton |= X360Button.Start; if (inputReport[Ds4Button.Up].IsPressed) xButton |= X360Button.Up; if (inputReport[Ds4Button.Right].IsPressed) xButton |= X360Button.Right; if (inputReport[Ds4Button.Down].IsPressed) xButton |= X360Button.Down; if (inputReport[Ds4Button.Left].IsPressed) xButton |= X360Button.Left; if (inputReport[Ds4Button.L1].IsPressed) xButton |= X360Button.LB; if (inputReport[Ds4Button.R1].IsPressed) xButton |= X360Button.RB; if (inputReport[Ds4Button.Triangle].IsPressed) xButton |= X360Button.Y; if (inputReport[Ds4Button.Circle].IsPressed) xButton |= X360Button.B; if (inputReport[Ds4Button.Cross].IsPressed) xButton |= X360Button.A; if (inputReport[Ds4Button.Square].IsPressed) xButton |= X360Button.X; if (inputReport[Ds4Button.Ps].IsPressed) xButton |= X360Button.Guide; if (inputReport[Ds4Button.L3].IsPressed) xButton |= X360Button.LS; if (inputReport[Ds4Button.R3].IsPressed) xButton |= X360Button.RS; // face buttons output.wButtons = (ushort) xButton; // trigger output.bLeftTrigger = inputReport[Ds4Axis.L2].Value; output.bRightTrigger = inputReport[Ds4Axis.R2].Value; if (!DsMath.DeadZone(GlobalConfiguration.Instance.DeadZoneL, inputReport[Ds4Axis.Lx].Value, inputReport[Ds4Axis.Ly].Value)) // Left Stick DeadZone { output.sThumbLX = (short) +DsMath.Scale(inputReport[Ds4Axis.Lx].Value, GlobalConfiguration.Instance.FlipLX); output.sThumbLY = (short) -DsMath.Scale(inputReport[Ds4Axis.Ly].Value, GlobalConfiguration.Instance.FlipLY); } if (!DsMath.DeadZone(GlobalConfiguration.Instance.DeadZoneR, inputReport[Ds4Axis.Rx].Value, inputReport[Ds4Axis.Ry].Value)) // Right Stick DeadZone { output.sThumbRX = (short) +DsMath.Scale(inputReport[Ds4Axis.Rx].Value, GlobalConfiguration.Instance.FlipRX); output.sThumbRY = (short) -DsMath.Scale(inputReport[Ds4Axis.Ry].Value, GlobalConfiguration.Instance.FlipRY); } } break; } return output; } public bool Plugin(int serial) { if (GlobalConfiguration.Instance.IsVBusDisabled) return true; var retVal = false; if (serial < 1 || serial > BusWidth) return false; serial += _busOffset; if (State != DsState.Connected) return false; lock (_pluggedInDevices) { if (!_pluggedInDevices.Contains(serial)) { if (XOutputWrapper.Instance.PlugIn(serial - 1)) { _pluggedInDevices.Add(serial); retVal = true; Log.DebugFormat("-- Bus Plugin : Serial {0}", serial); } else { Log.ErrorFormat("Couldn't plug in virtual device {0}: {1}", serial, new Win32Exception(Marshal.GetLastWin32Error())); } } else retVal = true; } return retVal; } public bool Unplug(int serial) { if (GlobalConfiguration.Instance.IsVBusDisabled) return true; var retVal = false; serial += _busOffset; if (State != DsState.Connected) return false; lock (_pluggedInDevices) { if (_pluggedInDevices.Contains(serial)) { if (XOutputWrapper.Instance.UnPlug(serial - 1)) { _pluggedInDevices.Remove(serial); retVal = true; Log.DebugFormat("-- Bus Unplug : Serial {0}", serial); } else { Log.ErrorFormat("Couldn't unplug virtual device {0}: {1}", serial, new Win32Exception(Marshal.GetLastWin32Error())); } } else retVal = true; } return retVal; } #endregion } }