cemu-DS4Windows/HidLibrary/HidDevice.cs
jays2kings 7d7d5d7391 Version 1.4.23
Extended range needed for touchpad swipes actions to register
UI adjustments in profile settings, such as a color box for flashing
color, alignment adjustments, and the Sixaxis reading dot staying in
bounds of the box
Recording a macro for special actions now open up in a new window,
allowing for ctrl+tab to be used
When controller's latency passes 10ms, the log will show and the
controller will flash red until the latency is under 10ms
Hovering over the mac address shows the latency of said controller, if
it's connected via bluetooth
Option to choose when at low battery for the light to flash or pulse
Much cleaner/neater hotkeys/about window
Option to download language packs if your PC is not set to an english
language
Finished Italian Translations (Thanks again Giulio)
Finished German Translations (Thanks Ammonjak)
Updated Italian & Russian Translations
Reorganized the the code so all cs files are under the same project
2015-02-08 16:51:52 -05:00

362 lines
12 KiB
C#

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
namespace DS4Windows
{
public class HidDevice : IDisposable
{
public enum ReadStatus
{
Success = 0,
WaitTimedOut = 1,
WaitFail = 2,
NoDataRead = 3,
ReadError = 4,
NotConnected = 5
}
private readonly string _description;
private readonly string _devicePath;
private readonly HidDeviceAttributes _deviceAttributes;
private readonly HidDeviceCapabilities _deviceCapabilities;
private bool _monitorDeviceEvents;
private string serial = null;
internal HidDevice(string devicePath, string description = null)
{
_devicePath = devicePath;
_description = description;
try
{
var hidHandle = OpenHandle(_devicePath, false);
_deviceAttributes = GetDeviceAttributes(hidHandle);
_deviceCapabilities = GetDeviceCapabilities(hidHandle);
hidHandle.Close();
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
throw new Exception(string.Format("Error querying HID device '{0}'.", devicePath), exception);
}
}
public SafeFileHandle safeReadHandle { get; private set; }
public FileStream fileStream { get; private set; }
public bool IsOpen { get; private set; }
public bool IsExclusive { get; private set; }
public bool IsConnected { get { return HidDevices.IsConnected(_devicePath); } }
public string Description { get { return _description; } }
public HidDeviceCapabilities Capabilities { get { return _deviceCapabilities; } }
public HidDeviceAttributes Attributes { get { return _deviceAttributes; } }
public string DevicePath { get { return _devicePath; } }
public override string ToString()
{
return string.Format("VendorID={0}, ProductID={1}, Version={2}, DevicePath={3}",
_deviceAttributes.VendorHexId,
_deviceAttributes.ProductHexId,
_deviceAttributes.Version,
_devicePath);
}
public void OpenDevice(bool isExclusive)
{
if (IsOpen) return;
try
{
if (safeReadHandle == null || safeReadHandle.IsInvalid)
safeReadHandle = OpenHandle(_devicePath, isExclusive);
}
catch (Exception exception)
{
IsOpen = false;
throw new Exception("Error opening HID device.", exception);
}
IsOpen = !safeReadHandle.IsInvalid;
IsExclusive = isExclusive;
}
public void CloseDevice()
{
if (!IsOpen) return;
closeFileStreamIO();
IsOpen = false;
}
public void Dispose()
{
CancelIO();
CloseDevice();
}
public void CancelIO()
{
if (IsOpen)
NativeMethods.CancelIoEx(safeReadHandle.DangerousGetHandle(), IntPtr.Zero);
}
public bool ReadInputReport(byte[] data)
{
if (safeReadHandle == null)
safeReadHandle = OpenHandle(_devicePath, true);
return NativeMethods.HidD_GetInputReport(safeReadHandle, data, data.Length);
}
private static HidDeviceAttributes GetDeviceAttributes(SafeFileHandle hidHandle)
{
var deviceAttributes = default(NativeMethods.HIDD_ATTRIBUTES);
deviceAttributes.Size = Marshal.SizeOf(deviceAttributes);
NativeMethods.HidD_GetAttributes(hidHandle.DangerousGetHandle(), ref deviceAttributes);
return new HidDeviceAttributes(deviceAttributes);
}
private static HidDeviceCapabilities GetDeviceCapabilities(SafeFileHandle hidHandle)
{
var capabilities = default(NativeMethods.HIDP_CAPS);
var preparsedDataPointer = default(IntPtr);
if (NativeMethods.HidD_GetPreparsedData(hidHandle.DangerousGetHandle(), ref preparsedDataPointer))
{
NativeMethods.HidP_GetCaps(preparsedDataPointer, ref capabilities);
NativeMethods.HidD_FreePreparsedData(preparsedDataPointer);
}
return new HidDeviceCapabilities(capabilities);
}
private void closeFileStreamIO()
{
if (fileStream != null)
fileStream.Close();
fileStream = null;
Console.WriteLine("Close fs");
if (safeReadHandle != null && !safeReadHandle.IsInvalid)
{
safeReadHandle.Close();
Console.WriteLine("Close sh");
}
safeReadHandle = null;
}
public void flush_Queue()
{
if (safeReadHandle != null)
{
NativeMethods.HidD_FlushQueue(safeReadHandle);
}
}
private ReadStatus ReadWithFileStreamTask(byte[] inputBuffer)
{
try
{
if (fileStream.Read(inputBuffer, 0, inputBuffer.Length) > 0)
{
return ReadStatus.Success;
}
else
{
return ReadStatus.NoDataRead;
}
}
catch (Exception)
{
return ReadStatus.ReadError;
}
}
public ReadStatus ReadFile(byte[] inputBuffer)
{
if (safeReadHandle == null)
safeReadHandle = OpenHandle(_devicePath, true);
try
{
uint bytesRead;
if (NativeMethods.ReadFile(safeReadHandle.DangerousGetHandle(), inputBuffer, (uint)inputBuffer.Length, out bytesRead, IntPtr.Zero))
{
return ReadStatus.Success;
}
else
{
return ReadStatus.NoDataRead;
}
}
catch (Exception)
{
return ReadStatus.ReadError;
}
}
public ReadStatus ReadWithFileStream(byte[] inputBuffer, int timeout)
{
try
{
if (safeReadHandle == null)
safeReadHandle = OpenHandle(_devicePath, true);
if (fileStream == null && !safeReadHandle.IsInvalid)
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, inputBuffer.Length, false);
if (!safeReadHandle.IsInvalid && fileStream.CanRead)
{
Task<ReadStatus> readFileTask = new Task<ReadStatus>(() => ReadWithFileStreamTask(inputBuffer));
readFileTask.Start();
bool success = readFileTask.Wait(timeout);
if (success)
{
if (readFileTask.Result == ReadStatus.Success)
{
return ReadStatus.Success;
}
else if (readFileTask.Result == ReadStatus.ReadError)
{
return ReadStatus.ReadError;
}
else if (readFileTask.Result == ReadStatus.NoDataRead)
{
return ReadStatus.NoDataRead;
}
}
else
return ReadStatus.WaitTimedOut;
}
}
catch (Exception e)
{
if (e is AggregateException)
{
Console.WriteLine(e.Message);
return ReadStatus.WaitFail;
}
else
{
return ReadStatus.ReadError;
}
}
return ReadStatus.ReadError;
}
public bool WriteOutputReportViaControl(byte[] outputBuffer)
{
if (safeReadHandle == null)
{
safeReadHandle = OpenHandle(_devicePath, true);
}
if (NativeMethods.HidD_SetOutputReport(safeReadHandle, outputBuffer, outputBuffer.Length))
return true;
else
return false;
}
private bool WriteOutputReportViaInterruptTask(byte[] outputBuffer)
{
try
{
fileStream.Write(outputBuffer, 0, outputBuffer.Length);
return true;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
}
}
public bool WriteOutputReportViaInterrupt(byte[] outputBuffer, int timeout)
{
try
{
if (safeReadHandle == null)
{
safeReadHandle = OpenHandle(_devicePath, true);
}
if (fileStream == null && !safeReadHandle.IsInvalid)
{
fileStream = new FileStream(safeReadHandle, FileAccess.ReadWrite, outputBuffer.Length, false);
}
if (fileStream != null && fileStream.CanWrite && !safeReadHandle.IsInvalid)
{
fileStream.Write(outputBuffer, 0, outputBuffer.Length);
return true;
}
else
{
return false;
}
}
catch (Exception)
{
return false;
}
}
private SafeFileHandle OpenHandle(String devicePathName, Boolean isExclusive)
{
SafeFileHandle hidHandle;
try
{
if (isExclusive)
{
hidHandle = NativeMethods.CreateFile(devicePathName, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OpenExisting, 0, 0);
}
else
{
hidHandle = NativeMethods.CreateFile(devicePathName, NativeMethods.GENERIC_READ | NativeMethods.GENERIC_WRITE, NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethods.OpenExisting, 0, 0);
}
}
catch (Exception)
{
throw;
}
return hidHandle;
}
public bool readFeatureData(byte[] inputBuffer)
{
return NativeMethods.HidD_GetFeature(safeReadHandle.DangerousGetHandle(), inputBuffer, inputBuffer.Length);
}
public string readSerial()
{
if (serial != null)
return serial;
if (Capabilities.InputReportByteLength == 64)
{
byte[] buffer = new byte[16];
buffer[0] = 18;
readFeatureData(buffer);
serial = String.Format("{0:X02}:{1:X02}:{2:X02}:{3:X02}:{4:X02}:{5:X02}", buffer[6], buffer[5], buffer[4], buffer[3], buffer[2], buffer[1]);
return serial;
}
else
{
byte[] buffer = new byte[126];
NativeMethods.HidD_GetSerialNumberString(safeReadHandle.DangerousGetHandle(), buffer, (ulong)buffer.Length);
string MACAddr = System.Text.Encoding.Unicode.GetString(buffer).Replace("\0", string.Empty).ToUpper();
MACAddr = String.Format("{0}{1}:{2}{3}:{4}{5}:{6}{7}:{8}{9}:{10}{11}",
MACAddr[0], MACAddr[1], MACAddr[2], MACAddr[3], MACAddr[4],
MACAddr[5], MACAddr[6], MACAddr[7], MACAddr[8],
MACAddr[9], MACAddr[10], MACAddr[11]);
serial = MACAddr;
return serial;
}
}
}
}