mirror of
https://github.com/cemu-project/DS4Windows.git
synced 2024-12-12 10:24:19 +01:00
6074445c03
Use SynchronizationContext to run some tasks in main thread as opposed to input thread
225 lines
10 KiB
C#
225 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Principal;
|
|
|
|
namespace DS4Windows
|
|
{
|
|
public class DS4Devices
|
|
{
|
|
private static Dictionary<string, DS4Device> Devices = new Dictionary<string, DS4Device>();
|
|
private static HashSet<String> DevicePaths = new HashSet<String>();
|
|
public static bool isExclusiveMode = false;
|
|
|
|
private static string devicePathToInstanceId(string devicePath)
|
|
{
|
|
string deviceInstanceId = devicePath;
|
|
deviceInstanceId = deviceInstanceId.Remove(0, deviceInstanceId.LastIndexOf('\\') + 1);
|
|
deviceInstanceId = deviceInstanceId.Remove(deviceInstanceId.LastIndexOf('{'));
|
|
deviceInstanceId = deviceInstanceId.Replace('#', '\\');
|
|
if (deviceInstanceId.EndsWith("\\"))
|
|
{
|
|
deviceInstanceId = deviceInstanceId.Remove(deviceInstanceId.Length - 1);
|
|
}
|
|
|
|
return deviceInstanceId;
|
|
}
|
|
|
|
// enumerates ds4 controllers in the system
|
|
public static void findControllers()
|
|
{
|
|
lock (Devices)
|
|
{
|
|
int[] pid = { 0xBA0, 0x5C4, 0x09CC };
|
|
IEnumerable<HidDevice> hDevices = HidDevices.Enumerate(0x054C, pid);
|
|
// Sort Bluetooth first in case USB is also connected on the same controller.
|
|
hDevices = hDevices.OrderBy<HidDevice, ConnectionType>((HidDevice d) => { return DS4Device.HidConnectionType(d); });
|
|
|
|
List<HidDevice> tempList = hDevices.ToList();
|
|
int devCount = tempList.Count();
|
|
string devicePlural = "device" + (devCount == 0 || devCount > 1 ? "s" : "");
|
|
//Log.LogToGui("Found " + devCount + " possible " + devicePlural + ". Examining " + devicePlural + ".", false);
|
|
|
|
for (int i = 0; i < devCount; i++)
|
|
//foreach (HidDevice hDevice in hDevices)
|
|
{
|
|
HidDevice hDevice = tempList[i];
|
|
if (DevicePaths.Contains(hDevice.DevicePath))
|
|
continue; // BT/USB endpoint already open once
|
|
|
|
if (!hDevice.IsOpen)
|
|
{
|
|
hDevice.OpenDevice(isExclusiveMode);
|
|
if (!hDevice.IsOpen && isExclusiveMode)
|
|
{
|
|
try
|
|
{
|
|
WindowsIdentity identity = WindowsIdentity.GetCurrent();
|
|
WindowsPrincipal principal = new WindowsPrincipal(identity);
|
|
bool elevated = principal.IsInRole(WindowsBuiltInRole.Administrator);
|
|
|
|
if (!elevated)
|
|
{
|
|
// Launches an elevated child process to re-enable device
|
|
string exeName = Process.GetCurrentProcess().MainModule.FileName;
|
|
ProcessStartInfo startInfo = new ProcessStartInfo(exeName);
|
|
startInfo.Verb = "runas";
|
|
startInfo.Arguments = "re-enabledevice " + devicePathToInstanceId(hDevice.DevicePath);
|
|
Process child = Process.Start(startInfo);
|
|
|
|
if (!child.WaitForExit(5000))
|
|
{
|
|
child.Kill();
|
|
}
|
|
else if (child.ExitCode == 0)
|
|
{
|
|
hDevice.OpenDevice(isExclusiveMode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
reEnableDevice(devicePathToInstanceId(hDevice.DevicePath));
|
|
hDevice.OpenDevice(isExclusiveMode);
|
|
}
|
|
}
|
|
catch (Exception) { }
|
|
}
|
|
|
|
// TODO in exclusive mode, try to hold both open when both are connected
|
|
if (isExclusiveMode && !hDevice.IsOpen)
|
|
hDevice.OpenDevice(false);
|
|
}
|
|
|
|
if (hDevice.IsOpen)
|
|
{
|
|
string serial = hDevice.readSerial();
|
|
bool validSerial = !serial.Equals("00:00:00:00:00:00");
|
|
if (Devices.ContainsKey(serial))
|
|
continue; // happens when the BT endpoint already is open and the USB is plugged into the same host
|
|
else
|
|
{
|
|
DS4Device ds4Device = new DS4Device(hDevice);
|
|
//ds4Device.Removal += On_Removal;
|
|
Devices.Add(ds4Device.MacAddress, ds4Device);
|
|
DevicePaths.Add(hDevice.DevicePath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//allows to get DS4Device by specifying unique MAC address
|
|
//format for MAC address is XX:XX:XX:XX:XX:XX
|
|
public static DS4Device getDS4Controller(string mac)
|
|
{
|
|
lock (Devices)
|
|
{
|
|
DS4Device device = null;
|
|
try
|
|
{
|
|
Devices.TryGetValue(mac, out device);
|
|
}
|
|
catch (ArgumentNullException) { }
|
|
return device;
|
|
}
|
|
}
|
|
|
|
//returns DS4 controllers that were found and are running
|
|
public static IEnumerable<DS4Device> getDS4Controllers()
|
|
{
|
|
lock (Devices)
|
|
{
|
|
DS4Device[] controllers = new DS4Device[Devices.Count];
|
|
Devices.Values.CopyTo(controllers, 0);
|
|
return controllers;
|
|
}
|
|
}
|
|
|
|
public static void stopControllers()
|
|
{
|
|
lock (Devices)
|
|
{
|
|
IEnumerable<DS4Device> devices = getDS4Controllers();
|
|
//foreach (DS4Device device in devices)
|
|
for (int i = 0, devCount = devices.Count(); i < devCount; i++)
|
|
{
|
|
DS4Device device = devices.ElementAt(i);
|
|
device.StopUpdate();
|
|
//device.runRemoval();
|
|
device.HidDevice.CloseDevice();
|
|
}
|
|
|
|
Devices.Clear();
|
|
DevicePaths.Clear();
|
|
}
|
|
}
|
|
|
|
//called when devices is diconnected, timed out or has input reading failure
|
|
public static void On_Removal(object sender, EventArgs e)
|
|
{
|
|
lock (Devices)
|
|
{
|
|
DS4Device device = (DS4Device)sender;
|
|
device.HidDevice.CloseDevice();
|
|
Devices.Remove(device.MacAddress);
|
|
DevicePaths.Remove(device.HidDevice.DevicePath);
|
|
}
|
|
}
|
|
|
|
public static void reEnableDevice(string deviceInstanceId)
|
|
{
|
|
bool success;
|
|
Guid hidGuid = new Guid();
|
|
NativeMethods.HidD_GetHidGuid(ref hidGuid);
|
|
IntPtr deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidGuid, deviceInstanceId, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE);
|
|
NativeMethods.SP_DEVINFO_DATA deviceInfoData = new NativeMethods.SP_DEVINFO_DATA();
|
|
deviceInfoData.cbSize = Marshal.SizeOf(deviceInfoData);
|
|
success = NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData);
|
|
if (!success)
|
|
{
|
|
throw new Exception("Error getting device info data, error code = " + Marshal.GetLastWin32Error());
|
|
}
|
|
success = NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 1, ref deviceInfoData); // Checks that we have a unique device
|
|
if (success)
|
|
{
|
|
throw new Exception("Can't find unique device");
|
|
}
|
|
|
|
NativeMethods.SP_PROPCHANGE_PARAMS propChangeParams = new NativeMethods.SP_PROPCHANGE_PARAMS();
|
|
propChangeParams.classInstallHeader.cbSize = Marshal.SizeOf(propChangeParams.classInstallHeader);
|
|
propChangeParams.classInstallHeader.installFunction = NativeMethods.DIF_PROPERTYCHANGE;
|
|
propChangeParams.stateChange = NativeMethods.DICS_DISABLE;
|
|
propChangeParams.scope = NativeMethods.DICS_FLAG_GLOBAL;
|
|
propChangeParams.hwProfile = 0;
|
|
success = NativeMethods.SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInfoData, ref propChangeParams, Marshal.SizeOf(propChangeParams));
|
|
if (!success)
|
|
{
|
|
throw new Exception("Error setting class install params, error code = " + Marshal.GetLastWin32Error());
|
|
}
|
|
success = NativeMethods.SetupDiCallClassInstaller(NativeMethods.DIF_PROPERTYCHANGE, deviceInfoSet, ref deviceInfoData);
|
|
if (!success)
|
|
{
|
|
throw new Exception("Error disabling device, error code = " + Marshal.GetLastWin32Error());
|
|
}
|
|
|
|
System.Threading.Thread.Sleep(50);
|
|
|
|
propChangeParams.stateChange = NativeMethods.DICS_ENABLE;
|
|
success = NativeMethods.SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInfoData, ref propChangeParams, Marshal.SizeOf(propChangeParams));
|
|
if (!success)
|
|
{
|
|
throw new Exception("Error setting class install params, error code = " + Marshal.GetLastWin32Error());
|
|
}
|
|
success = NativeMethods.SetupDiCallClassInstaller(NativeMethods.DIF_PROPERTYCHANGE, deviceInfoSet, ref deviceInfoData);
|
|
if (!success)
|
|
{
|
|
throw new Exception("Error enabling device, error code = " + Marshal.GetLastWin32Error());
|
|
}
|
|
|
|
NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
|
}
|
|
}
|
|
}
|