mirror of
https://github.com/cemu-project/DS4Windows.git
synced 2024-12-26 08:31:49 +01:00
7d7d5d7391
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
362 lines
12 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|