ScpXInputBridge/XInputDll.Exports.cs (192 lines of code) (raw):

using System; using System.Runtime.InteropServices; using RGiesecke.DllExport; #if !EXPERIMENTAL using System.Threading; using ScpControl.Shared.Core; using ScpControl.Shared.Utilities; #endif using ScpControl.Shared.XInput; namespace ScpXInputBridge { public partial class XInputDll { #region XInput proxy functions /// <summary> /// Sets the reporting state of XInput. /// </summary> /// <param name="enable"> /// If enable is FALSE, XInput will only send neutral data in response to /// <see cref="XInputGetState" /> (all buttons up, axes centered, and triggers at 0). <see cref="XInputSetState" /> /// calls will be registered but not sent to the device. Sending any value other than FALSE will restore reading and /// writing functionality to normal. /// </param> [DllExport("XInputEnable", CallingConvention.StdCall)] public static void XInputEnable(bool enable) { OriginalXInputEnableFunction.Value(enable); } /// <summary> /// Retrieves the current state of the specified controller. /// </summary> /// <param name="dwUserIndex">Index of the user's controller. Can be a value from 0 to 3.</param> /// <param name="pState"> /// Pointer to an <see cref="XINPUT_STATE" /> structure that receives the current state of the /// controller. /// </param> /// <returns> /// If the function succeeds, the return value is ERROR_SUCCESS. If the controller is not connected, the return /// value is ERROR_DEVICE_NOT_CONNECTED. /// </returns> [DllExport("XInputGetState", CallingConvention.StdCall)] public static uint XInputGetState(uint dwUserIndex, ref XINPUT_STATE pState) { #if !EXPERIMENTAL return OriginalXInputGetStateFunction.Value(dwUserIndex, ref pState); #else if (OriginalXInputGetStateFunction.Value(dwUserIndex, ref pState) == ResultWin32.ERROR_SUCCESS) { return ResultWin32.ERROR_SUCCESS; } try { ScpHidReport report = null; while (dwUserIndex == 0 && (report = Proxy.GetReport(dwUserIndex)) == null) { Thread.Sleep(100); } if (report == null || report.PadState != DsState.Connected) { return ResultWin32.ERROR_DEVICE_NOT_CONNECTED; } var xPad = new XINPUT_GAMEPAD(); pState.dwPacketNumber = report.PacketCounter; switch (report.Model) { case DsModel.DS3: { // select & start xPad.wButtons |= (ushort) report[Ds3Button.Select].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.Start].Xbox360Button; // d-pad xPad.wButtons |= (ushort) report[Ds3Button.Up].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.Right].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.Down].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.Left].Xbox360Button; // shoulders xPad.wButtons |= (ushort) report[Ds3Button.L1].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.R1].Xbox360Button; // face buttons xPad.wButtons |= (ushort) report[Ds3Button.Triangle].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.Circle].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.Cross].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.Square].Xbox360Button; // PS/Guide xPad.wButtons |= (ushort) report[Ds3Button.Ps].Xbox360Button; // thumbs xPad.wButtons |= (ushort) report[Ds3Button.L3].Xbox360Button; xPad.wButtons |= (ushort) report[Ds3Button.R3].Xbox360Button; // triggers xPad.bLeftTrigger = report[Ds3Axis.L2].Value; xPad.bRightTrigger = report[Ds3Axis.R2].Value; // thumb axes xPad.sThumbLX = (short) +DsMath.Scale(report[Ds3Axis.Lx].Value, false); xPad.sThumbLY = (short) -DsMath.Scale(report[Ds3Axis.Ly].Value, false); xPad.sThumbRX = (short) +DsMath.Scale(report[Ds3Axis.Rx].Value, false); xPad.sThumbRY = (short) -DsMath.Scale(report[Ds3Axis.Ry].Value, false); } break; case DsModel.DS4: { // select & start xPad.wButtons |= (ushort) report[Ds4Button.Share].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.Options].Xbox360Button; // d-pad xPad.wButtons |= (ushort) report[Ds4Button.Up].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.Right].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.Down].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.Left].Xbox360Button; // shoulders xPad.wButtons |= (ushort) report[Ds4Button.L1].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.R1].Xbox360Button; // face buttons xPad.wButtons |= (ushort) report[Ds4Button.Triangle].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.Circle].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.Cross].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.Square].Xbox360Button; // PS/Guide xPad.wButtons |= (ushort) report[Ds4Button.Ps].Xbox360Button; // thumbs xPad.wButtons |= (ushort) report[Ds4Button.L3].Xbox360Button; xPad.wButtons |= (ushort) report[Ds4Button.R3].Xbox360Button; // triggers xPad.bLeftTrigger = report[Ds4Axis.L2].Value; xPad.bRightTrigger = report[Ds4Axis.R2].Value; // thumb axes xPad.sThumbLX = (short) +DsMath.Scale(report[Ds4Axis.Lx].Value, false); xPad.sThumbLY = (short) -DsMath.Scale(report[Ds4Axis.Ly].Value, false); xPad.sThumbRX = (short) +DsMath.Scale(report[Ds4Axis.Rx].Value, false); xPad.sThumbRY = (short) -DsMath.Scale(report[Ds4Axis.Ry].Value, false); } break; } pState.Gamepad = xPad; } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); return ResultWin32.ERROR_DEVICE_NOT_CONNECTED; } return ResultWin32.ERROR_SUCCESS; #endif } /// <summary> /// Sends data to a connected controller. This function is used to activate the vibration function of a controller. /// </summary> /// <param name="dwUserIndex">Index of the user's controller. Can be a value from 0 to 3.</param> /// <param name="pVibration"> /// Pointer to an <see cref="XINPUT_VIBRATION" /> structure containing the vibration information /// to send to the controller. /// </param> /// <returns> /// If the function succeeds, the return value is ERROR_SUCCESS. If the controller is not connected, the return /// value is ERROR_DEVICE_NOT_CONNECTED. /// </returns> [DllExport("XInputSetState", CallingConvention.StdCall)] public static uint XInputSetState(uint dwUserIndex, ref XINPUT_VIBRATION pVibration) { return OriginalXInputSetStateFunction.Value(dwUserIndex, ref pVibration); } /// <summary> /// Retrieves the capabilities and features of a connected controller. /// </summary> /// <param name="dwUserIndex">Index of the user's controller. Can be a value from 0 to 3.</param> /// <param name="dwFlags"> /// Input flags that identify the controller type. If this value is 0, then the capabilities of all /// controllers connected to the system are returned. /// </param> /// <param name="pCapabilities"> /// Pointer to an <see cref="XINPUT_CAPABILITIES" /> structure that receives the controller /// capabilities. /// </param> /// <returns> /// If the function succeeds, the return value is ERROR_SUCCESS. If the controller is not connected, the return /// value is ERROR_DEVICE_NOT_CONNECTED. /// </returns> [DllExport("XInputGetCapabilities", CallingConvention.StdCall)] public static uint XInputGetCapabilities(uint dwUserIndex, uint dwFlags, ref XINPUT_CAPABILITIES pCapabilities) { #if !EXPERIMENTAL return OriginalXInputGetCapabilitiesFunction.Value(dwUserIndex, dwFlags, ref pCapabilities); #else Log.DebugFormat("dwUserIndex = {0}", dwUserIndex); if (OriginalXInputGetCapabilitiesFunction.Value(dwUserIndex, dwFlags, ref pCapabilities) == ResultWin32.ERROR_SUCCESS) { return ResultWin32.ERROR_SUCCESS; } try { ScpHidReport report = Proxy.GetReport(dwUserIndex); if (report == null || report.PadState != DsState.Connected) { return ResultWin32.ERROR_DEVICE_NOT_CONNECTED; } pCapabilities.Type = XInputConstants.XINPUT_DEVTYPE_GAMEPAD; pCapabilities.SubType = XInputConstants.XINPUT_DEVSUBTYPE_GAMEPAD; pCapabilities.Flags = (ushort) (XInputConstants.CapabilityFlags.XINPUT_CAPS_FFB_SUPPORTED | XInputConstants.CapabilityFlags.XINPUT_CAPS_WIRELESS); pCapabilities.Gamepad = new XINPUT_GAMEPAD() { wButtons = 0xFFFF, bLeftTrigger = 0xFF, bRightTrigger = 0xFF }; } catch (Exception ex) { Log.ErrorFormat("Unexpected error: {0}", ex); return ResultWin32.ERROR_DEVICE_NOT_CONNECTED; } return ResultWin32.ERROR_SUCCESS; #endif } /// <summary> /// Gets the sound rendering and sound capture device GUIDs that are associated with the headset connected to the /// specified controller. /// </summary> /// <param name="dwUserIndex">Index of the user's controller. Can be a value from 0 to 3.</param> /// <param name="pDSoundRenderGuid">Pointer that receives the GUID of the headset sound rendering device.</param> /// <param name="pDSoundCaptureGuid">Pointer that receives the GUID of the headset sound capture device.</param> /// <returns> /// If the function successfully retrieves the device IDs for render and capture, the return code is /// ERROR_SUCCESS. If there is no headset connected to the controller, the function also retrieves ERROR_SUCCESS with /// GUID_NULL as the values for pDSoundRenderGuid and pDSoundCaptureGuid. If the controller port device is not /// physically connected, the function returns ERROR_DEVICE_NOT_CONNECTED. If the function fails, it returns a valid /// Win32 error code. /// </returns> /// <remarks>XInputGetDSoundAudioDeviceGuids is deprecated because it isn't supported by Windows 8 (XInput 1.4).</remarks> [DllExport("XInputGetDSoundAudioDeviceGuids", CallingConvention.StdCall)] public static uint XInputGetDSoundAudioDeviceGuids(uint dwUserIndex, ref Guid pDSoundRenderGuid, ref Guid pDSoundCaptureGuid) { return OriginalXInputGetDSoundAudioDeviceGuidsFunction.Value(dwUserIndex, ref pDSoundRenderGuid, ref pDSoundCaptureGuid); } /// <summary> /// Retrieves the battery type and charge status of a wireless controller. /// </summary> /// <param name="dwUserIndex">Index of the user's controller. Can be a value from 0 to 3.</param> /// <param name="devType"> /// Specifies which device associated with this user index should be queried. Must be /// BATTERY_DEVTYPE_GAMEPAD or BATTERY_DEVTYPE_HEADSET. /// </param> /// <param name="pBatteryInformation"> /// Pointer to an <see cref="XINPUT_BATTERY_INFORMATION" /> structure that receives the /// battery information. /// </param> /// <returns>If the function succeeds, the return value is ERROR_SUCCESS.</returns> [DllExport("XInputGetBatteryInformation", CallingConvention.StdCall)] public static uint XInputGetBatteryInformation(uint dwUserIndex, byte devType, ref XINPUT_BATTERY_INFORMATION pBatteryInformation) { return OriginalXInputGetBatteryInformationFunction.Value(dwUserIndex, devType, ref pBatteryInformation); } /// <summary> /// Retrieves a gamepad input event. /// </summary> /// <param name="dwUserIndex">Index of the user's controller. Can be a value from 0 to 3.</param> /// <param name="dwReserved">Reserved</param> /// <param name="pKeystroke">Pointer to an <see cref="XINPUT_KEYSTROKE" /> structure that receives an input event.</param> /// <returns> /// If the function succeeds, the return value is ERROR_SUCCESS. If no new keys have been pressed, the return /// value is ERROR_EMPTY. If the controller is not connected or the user has not activated it, the return value is /// ERROR_DEVICE_NOT_CONNECTED. See the Remarks section below. If the function fails, the return value is an error code /// defined in Winerror.h. The function does not use SetLastError to set the calling thread's last-error code. /// </returns> [DllExport("XInputGetKeystroke", CallingConvention.StdCall)] public static uint XInputGetKeystroke(uint dwUserIndex, uint dwReserved, ref XINPUT_KEYSTROKE pKeystroke) { return OriginalXInputGetKeystrokeFunction.Value(dwUserIndex, dwReserved, ref pKeystroke); } #region Undocumented functions [DllExport("XInputGetStateEx", CallingConvention.StdCall)] public static uint XInputGetStateEx(uint dwUserIndex, ref XINPUT_STATE pState) { throw new NotImplementedException(); } [DllExport("XInputWaitForGuideButton", CallingConvention.StdCall)] public static uint XInputWaitForGuideButton(uint dwUserIndex, uint dwFlag, IntPtr pVoid) { throw new NotImplementedException(); } [DllExport("XInputCancelGuideButtonWait", CallingConvention.StdCall)] public static uint XInputCancelGuideButtonWait(uint dwUserIndex) { throw new NotImplementedException(); } [DllExport("XInputPowerOffController", CallingConvention.StdCall)] public static uint XInputPowerOffController(uint dwUserIndex) { throw new NotImplementedException(); } #endregion #endregion } }