mirror of
https://github.com/cemu-project/DS4Windows.git
synced 2024-12-27 09:01:48 +01:00
164fbd5154
Used to assist with persisting linked profile setting
1668 lines
69 KiB
C#
1668 lines
69 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using System.Threading;
|
|
using System.Diagnostics;
|
|
using static DS4Windows.Global;
|
|
using Nefarius.ViGEm.Client;
|
|
|
|
namespace DS4Windows
|
|
{
|
|
public class ControlService
|
|
{
|
|
public ViGEmClient vigemTestClient = null;
|
|
public const int DS4_CONTROLLER_COUNT = 4;
|
|
public DS4Device[] DS4Controllers = new DS4Device[DS4_CONTROLLER_COUNT];
|
|
public Mouse[] touchPad = new Mouse[DS4_CONTROLLER_COUNT];
|
|
public bool running = false;
|
|
private DS4State[] MappedState = new DS4State[DS4_CONTROLLER_COUNT];
|
|
private DS4State[] CurrentState = new DS4State[DS4_CONTROLLER_COUNT];
|
|
private DS4State[] PreviousState = new DS4State[DS4_CONTROLLER_COUNT];
|
|
private DS4State[] TempState = new DS4State[DS4_CONTROLLER_COUNT];
|
|
public DS4StateExposed[] ExposedState = new DS4StateExposed[DS4_CONTROLLER_COUNT];
|
|
public ControllerSlotManager slotManager = new ControllerSlotManager();
|
|
public bool recordingMacro = false;
|
|
public event EventHandler<DebugEventArgs> Debug = null;
|
|
bool[] buttonsdown = new bool[4] { false, false, false, false };
|
|
bool[] held = new bool[DS4_CONTROLLER_COUNT];
|
|
int[] oldmouse = new int[DS4_CONTROLLER_COUNT] { -1, -1, -1, -1 };
|
|
public OutputDevice[] outputDevices = new OutputDevice[4] { null, null, null, null };
|
|
//public Xbox360Controller[] x360controls = new Xbox360Controller[4] { null, null, null, null };
|
|
/*private Xbox360Report[] x360reports = new Xbox360Report[4] { new Xbox360Report(), new Xbox360Report(),
|
|
new Xbox360Report(), new Xbox360Report()
|
|
};
|
|
*/
|
|
Thread tempThread;
|
|
Thread tempBusThread;
|
|
public List<string> affectedDevs = new List<string>()
|
|
{
|
|
@"HID\VID_054C&PID_05C4",
|
|
@"HID\VID_054C&PID_09CC&MI_03",
|
|
@"HID\VID_054C&PID_0BA0&MI_03",
|
|
@"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&05c4",
|
|
@"HID\{00001124-0000-1000-8000-00805f9b34fb}_VID&0002054c_PID&09cc",
|
|
};
|
|
public bool suspending;
|
|
//SoundPlayer sp = new SoundPlayer();
|
|
private UdpServer _udpServer;
|
|
private OutputSlotManager outputslotMan;
|
|
|
|
public event EventHandler ServiceStarted;
|
|
public event EventHandler PreServiceStop;
|
|
public event EventHandler ServiceStopped;
|
|
public event EventHandler RunningChanged;
|
|
//public event EventHandler HotplugFinished;
|
|
public delegate void HotplugControllerHandler(ControlService sender, DS4Device device, int index);
|
|
public event HotplugControllerHandler HotplugController;
|
|
|
|
private class X360Data
|
|
{
|
|
public byte[] Report = new byte[28];
|
|
public byte[] Rumble = new byte[8];
|
|
}
|
|
|
|
private X360Data[] processingData = new X360Data[4];
|
|
private byte[][] udpOutBuffers = new byte[4][]
|
|
{
|
|
new byte[100], new byte[100],
|
|
new byte[100], new byte[100]
|
|
};
|
|
|
|
|
|
void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta)
|
|
{
|
|
//meta = new DualShockPadMeta();
|
|
meta.PadId = (byte) padIdx;
|
|
meta.Model = DsModel.DS4;
|
|
|
|
var d = DS4Controllers[padIdx];
|
|
if (d == null)
|
|
{
|
|
meta.PadMacAddress = null;
|
|
meta.PadState = DsState.Disconnected;
|
|
meta.ConnectionType = DsConnection.None;
|
|
meta.Model = DsModel.None;
|
|
meta.BatteryStatus = 0;
|
|
meta.IsActive = false;
|
|
return;
|
|
//return meta;
|
|
}
|
|
|
|
bool isValidSerial = false;
|
|
//if (d.isValidSerial())
|
|
//{
|
|
string stringMac = d.getMacAddress();
|
|
if (!string.IsNullOrEmpty(stringMac))
|
|
{
|
|
stringMac = string.Join("", stringMac.Split(':'));
|
|
//stringMac = stringMac.Replace(":", "").Trim();
|
|
meta.PadMacAddress = System.Net.NetworkInformation.PhysicalAddress.Parse(stringMac);
|
|
isValidSerial = d.isValidSerial();
|
|
}
|
|
//}
|
|
|
|
if (!isValidSerial)
|
|
{
|
|
//meta.PadMacAddress = null;
|
|
meta.PadState = DsState.Disconnected;
|
|
}
|
|
else
|
|
{
|
|
if (d.isSynced() || d.IsAlive())
|
|
meta.PadState = DsState.Connected;
|
|
else
|
|
meta.PadState = DsState.Reserved;
|
|
}
|
|
|
|
meta.ConnectionType = (d.getConnectionType() == ConnectionType.USB) ? DsConnection.Usb : DsConnection.Bluetooth;
|
|
meta.IsActive = !d.isDS4Idle();
|
|
|
|
if (d.isCharging() && d.getBattery() >= 100)
|
|
meta.BatteryStatus = DsBattery.Charged;
|
|
else
|
|
{
|
|
if (d.getBattery() >= 95)
|
|
meta.BatteryStatus = DsBattery.Full;
|
|
else if (d.getBattery() >= 70)
|
|
meta.BatteryStatus = DsBattery.High;
|
|
else if (d.getBattery() >= 50)
|
|
meta.BatteryStatus = DsBattery.Medium;
|
|
else if (d.getBattery() >= 20)
|
|
meta.BatteryStatus = DsBattery.Low;
|
|
else if (d.getBattery() >= 5)
|
|
meta.BatteryStatus = DsBattery.Dying;
|
|
else
|
|
meta.BatteryStatus = DsBattery.None;
|
|
}
|
|
|
|
//return meta;
|
|
}
|
|
|
|
private object busThrLck = new object();
|
|
private bool busThrRunning = false;
|
|
private Queue<Action> busEvtQueue = new Queue<Action>();
|
|
private object busEvtQueueLock = new object();
|
|
public ControlService()
|
|
{
|
|
Crc32Algorithm.InitializeTable(DS4Device.DefaultPolynomial);
|
|
|
|
//sp.Stream = DS4WinWPF.Properties.Resources.EE;
|
|
// Cause thread affinity to not be tied to main GUI thread
|
|
tempBusThread = new Thread(() => {
|
|
//_udpServer = new UdpServer(GetPadDetailForIdx);
|
|
busThrRunning = true;
|
|
|
|
while (busThrRunning)
|
|
{
|
|
lock (busEvtQueueLock)
|
|
{
|
|
Action tempAct = null;
|
|
for (int actInd = 0, actLen = busEvtQueue.Count; actInd < actLen; actInd++)
|
|
{
|
|
tempAct = busEvtQueue.Dequeue();
|
|
tempAct.Invoke();
|
|
}
|
|
}
|
|
|
|
lock (busThrLck)
|
|
Monitor.Wait(busThrLck);
|
|
}
|
|
});
|
|
tempBusThread.Priority = ThreadPriority.Normal;
|
|
tempBusThread.IsBackground = true;
|
|
tempBusThread.Start();
|
|
//while (_udpServer == null)
|
|
//{
|
|
// Thread.SpinWait(500);
|
|
//}
|
|
|
|
if (Global.IsHidGuardianInstalled())
|
|
{
|
|
ProcessStartInfo startInfo =
|
|
new ProcessStartInfo(Global.exedirpath + "\\HidGuardHelper.exe");
|
|
startInfo.Verb = "runas";
|
|
startInfo.Arguments = Process.GetCurrentProcess().Id.ToString();
|
|
startInfo.WorkingDirectory = Global.exedirpath;
|
|
try
|
|
{ Process tempProc = Process.Start(startInfo); tempProc.Dispose(); }
|
|
catch { }
|
|
}
|
|
|
|
for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++)
|
|
{
|
|
processingData[i] = new X360Data();
|
|
MappedState[i] = new DS4State();
|
|
CurrentState[i] = new DS4State();
|
|
TempState[i] = new DS4State();
|
|
PreviousState[i] = new DS4State();
|
|
ExposedState[i] = new DS4StateExposed(CurrentState[i]);
|
|
}
|
|
|
|
outputslotMan = new OutputSlotManager();
|
|
}
|
|
|
|
private void TestQueueBus(Action temp)
|
|
{
|
|
lock (busEvtQueueLock)
|
|
{
|
|
busEvtQueue.Enqueue(temp);
|
|
}
|
|
|
|
lock (busThrLck)
|
|
Monitor.Pulse(busThrLck);
|
|
}
|
|
|
|
public void ChangeUDPStatus(bool state, bool openPort=true)
|
|
{
|
|
if (state && _udpServer == null)
|
|
{
|
|
udpChangeStatus = true;
|
|
TestQueueBus(() =>
|
|
{
|
|
_udpServer = new UdpServer(GetPadDetailForIdx);
|
|
if (openPort)
|
|
{
|
|
// Change thread affinity of object to have normal priority
|
|
Task.Run(() =>
|
|
{
|
|
var UDP_SERVER_PORT = Global.getUDPServerPortNum();
|
|
var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress();
|
|
|
|
try
|
|
{
|
|
_udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS);
|
|
LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}");
|
|
}
|
|
catch (System.Net.Sockets.SocketException ex)
|
|
{
|
|
var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode);
|
|
|
|
LogDebug(errMsg, true);
|
|
AppLogger.LogToTray(errMsg, true, true);
|
|
}
|
|
}).Wait();
|
|
}
|
|
|
|
udpChangeStatus = false;
|
|
});
|
|
}
|
|
else if (!state && _udpServer != null)
|
|
{
|
|
TestQueueBus(() =>
|
|
{
|
|
udpChangeStatus = true;
|
|
_udpServer.Stop();
|
|
_udpServer = null;
|
|
AppLogger.LogToGui("Closed UDP server", false);
|
|
udpChangeStatus = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
public void ChangeMotionEventStatus(bool state)
|
|
{
|
|
IEnumerable<DS4Device> devices = DS4Devices.getDS4Controllers();
|
|
if (state)
|
|
{
|
|
foreach (DS4Device dev in devices)
|
|
{
|
|
dev.queueEvent(() =>
|
|
{
|
|
dev.Report += dev.MotionEvent;
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (DS4Device dev in devices)
|
|
{
|
|
dev.queueEvent(() =>
|
|
{
|
|
dev.Report -= dev.MotionEvent;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool udpChangeStatus = false;
|
|
public bool changingUDPPort = false;
|
|
public async void UseUDPPort()
|
|
{
|
|
changingUDPPort = true;
|
|
IEnumerable<DS4Device> devices = DS4Devices.getDS4Controllers();
|
|
foreach (DS4Device dev in devices)
|
|
{
|
|
dev.queueEvent(() =>
|
|
{
|
|
dev.Report -= dev.MotionEvent;
|
|
});
|
|
}
|
|
|
|
await Task.Delay(100);
|
|
|
|
var UDP_SERVER_PORT = Global.getUDPServerPortNum();
|
|
var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress();
|
|
|
|
try
|
|
{
|
|
_udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS);
|
|
foreach (DS4Device dev in devices)
|
|
{
|
|
dev.queueEvent(() =>
|
|
{
|
|
dev.Report += dev.MotionEvent;
|
|
});
|
|
}
|
|
LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}");
|
|
}
|
|
catch (System.Net.Sockets.SocketException ex)
|
|
{
|
|
var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode);
|
|
|
|
LogDebug(errMsg, true);
|
|
AppLogger.LogToTray(errMsg, true, true);
|
|
}
|
|
|
|
changingUDPPort = false;
|
|
}
|
|
|
|
private void WarnExclusiveModeFailure(DS4Device device)
|
|
{
|
|
if (DS4Devices.isExclusiveMode && !device.isExclusive())
|
|
{
|
|
string message = DS4WinWPF.Properties.Resources.CouldNotOpenDS4.Replace("*Mac address*", device.getMacAddress()) + " " +
|
|
DS4WinWPF.Properties.Resources.QuitOtherPrograms;
|
|
LogDebug(message, true);
|
|
AppLogger.LogToTray(message, true);
|
|
}
|
|
}
|
|
|
|
private void startViGEm()
|
|
{
|
|
tempThread = new Thread(() => { try { vigemTestClient = new ViGEmClient(); } catch { } });
|
|
tempThread.Priority = ThreadPriority.AboveNormal;
|
|
tempThread.IsBackground = true;
|
|
tempThread.Start();
|
|
while (tempThread.IsAlive)
|
|
{
|
|
Thread.SpinWait(500);
|
|
}
|
|
|
|
tempThread = null;
|
|
}
|
|
|
|
private void stopViGEm()
|
|
{
|
|
if (vigemTestClient != null)
|
|
{
|
|
vigemTestClient.Dispose();
|
|
vigemTestClient = null;
|
|
}
|
|
}
|
|
|
|
public void PluginOutDev(int index, DS4Device device)
|
|
{
|
|
OutContType contType = Global.OutContType[index];
|
|
if (useDInputOnly[index])
|
|
{
|
|
if (contType == OutContType.X360)
|
|
{
|
|
LogDebug("Plugging in X360 Controller for input #" + (index + 1));
|
|
activeOutDevType[index] = OutContType.X360;
|
|
|
|
//Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient);
|
|
Xbox360OutDevice tempXbox = outputslotMan.AllocateController(OutContType.X360, vigemTestClient)
|
|
as Xbox360OutDevice;
|
|
//outputDevices[index] = tempXbox;
|
|
int devIndex = index;
|
|
tempXbox.cont.FeedbackReceived += (sender, args) =>
|
|
{
|
|
SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
|
|
};
|
|
|
|
outputslotMan.DeferredPlugin(tempXbox, index, outputDevices);
|
|
//tempXbox.Connect();
|
|
//LogDebug("X360 Controller #" + (index + 1) + " connected");
|
|
}
|
|
else if (contType == OutContType.DS4)
|
|
{
|
|
LogDebug("Plugging in DS4 Controller for input #" + (index + 1));
|
|
activeOutDevType[index] = OutContType.DS4;
|
|
//DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient);
|
|
DS4OutDevice tempDS4 = outputslotMan.AllocateController(OutContType.DS4, vigemTestClient)
|
|
as DS4OutDevice;
|
|
//outputDevices[index] = tempDS4;
|
|
int devIndex = index;
|
|
tempDS4.cont.FeedbackReceived += (sender, args) =>
|
|
{
|
|
SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
|
|
};
|
|
|
|
outputslotMan.DeferredPlugin(tempDS4, index, outputDevices);
|
|
//tempDS4.Connect();
|
|
//LogDebug("DS4 Controller #" + (index + 1) + " connected");
|
|
}
|
|
}
|
|
|
|
useDInputOnly[index] = false;
|
|
}
|
|
|
|
public void UnplugOutDev(int index, DS4Device device, bool immediate = false)
|
|
{
|
|
if (!useDInputOnly[index])
|
|
{
|
|
//OutContType contType = Global.OutContType[index];
|
|
string tempType = outputDevices[index].GetDeviceType();
|
|
LogDebug("Unplugging " + tempType + " Controller for input #" + (index + 1), false);
|
|
OutputDevice dev = outputDevices[index];
|
|
outputDevices[index] = null;
|
|
activeOutDevType[index] = OutContType.None;
|
|
outputslotMan.DeferredRemoval(dev, index, outputDevices, immediate);
|
|
//dev.Disconnect();
|
|
//LogDebug(tempType + " Controller # " + (index + 1) + " unplugged");
|
|
useDInputOnly[index] = true;
|
|
}
|
|
}
|
|
|
|
public bool Start(bool showlog = true)
|
|
{
|
|
startViGEm();
|
|
if (vigemTestClient != null)
|
|
//if (x360Bus.Open() && x360Bus.Start())
|
|
{
|
|
if (showlog)
|
|
LogDebug(DS4WinWPF.Properties.Resources.Starting);
|
|
|
|
LogDebug($"Connection to ViGEmBus {Global.vigembusVersion} established");
|
|
|
|
DS4Devices.isExclusiveMode = getUseExclusiveMode();
|
|
//uiContext = tempui as SynchronizationContext;
|
|
if (showlog)
|
|
{
|
|
LogDebug(DS4WinWPF.Properties.Resources.SearchingController);
|
|
LogDebug(DS4Devices.isExclusiveMode ? DS4WinWPF.Properties.Resources.UsingExclusive : DS4WinWPF.Properties.Resources.UsingShared);
|
|
}
|
|
|
|
if (isUsingUDPServer() && _udpServer == null)
|
|
{
|
|
ChangeUDPStatus(true, false);
|
|
while (udpChangeStatus == true)
|
|
{
|
|
Thread.SpinWait(500);
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
DS4Devices.findControllers();
|
|
IEnumerable<DS4Device> devices = DS4Devices.getDS4Controllers();
|
|
//int ind = 0;
|
|
DS4LightBar.defaultLight = false;
|
|
//foreach (DS4Device device in devices)
|
|
|
|
//for (int i = 0, devCount = devices.Count(); i < devCount; i++)
|
|
int i = 0;
|
|
for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext(); i++)
|
|
{
|
|
DS4Device device = devEnum.Current;
|
|
//DS4Device device = devices.ElementAt(i);
|
|
if (showlog)
|
|
LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" +
|
|
device.DisplayName + ")");
|
|
|
|
Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
|
|
task.Start();
|
|
|
|
DS4Controllers[i] = device;
|
|
slotManager.AddController(device, i);
|
|
device.Removal += this.On_DS4Removal;
|
|
device.Removal += DS4Devices.On_Removal;
|
|
device.SyncChange += this.On_SyncChange;
|
|
device.SyncChange += DS4Devices.UpdateSerial;
|
|
device.SerialChange += this.On_SerialChange;
|
|
device.ChargingChanged += CheckQuickCharge;
|
|
|
|
touchPad[i] = new Mouse(i, device);
|
|
|
|
if (!useTempProfile[i])
|
|
{
|
|
if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress()))
|
|
{
|
|
ProfilePath[i] = getLinkedProfile(device.getMacAddress());
|
|
Global.linkedProfileCheck[i] = true;
|
|
}
|
|
else
|
|
{
|
|
ProfilePath[i] = OlderProfilePath[i];
|
|
Global.linkedProfileCheck[i] = false;
|
|
}
|
|
|
|
LoadProfile(i, false, this, false, false);
|
|
}
|
|
|
|
device.LightBarColor = getMainColor(i);
|
|
|
|
if (!getDInputOnly(i) && device.isSynced())
|
|
{
|
|
//useDInputOnly[i] = false;
|
|
PluginOutDev(i, device);
|
|
|
|
}
|
|
else
|
|
{
|
|
useDInputOnly[i] = true;
|
|
Global.activeOutDevType[i] = OutContType.None;
|
|
}
|
|
|
|
int tempIdx = i;
|
|
device.Report += (sender, e) =>
|
|
{
|
|
this.On_Report(sender, e, tempIdx);
|
|
};
|
|
|
|
DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
|
|
{
|
|
DualShockPadMeta padDetail = new DualShockPadMeta();
|
|
GetPadDetailForIdx(tempIdx, ref padDetail);
|
|
_udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]);
|
|
};
|
|
device.MotionEvent = tempEvnt;
|
|
|
|
if (_udpServer != null)
|
|
{
|
|
device.Report += tempEvnt;
|
|
}
|
|
|
|
TouchPadOn(i, device);
|
|
CheckProfileOptions(i, device, true);
|
|
device.StartUpdate();
|
|
//string filename = ProfilePath[ind];
|
|
//ind++;
|
|
if (showlog)
|
|
{
|
|
if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[i] + ".xml"))
|
|
{
|
|
string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (i + 1).ToString()).Replace("*Profile name*", ProfilePath[i]);
|
|
LogDebug(prolog);
|
|
AppLogger.LogToTray(prolog);
|
|
}
|
|
else
|
|
{
|
|
string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (i + 1).ToString());
|
|
LogDebug(prolog);
|
|
AppLogger.LogToTray(prolog);
|
|
}
|
|
}
|
|
|
|
if (i >= 4) // out of Xinput devices!
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
LogDebug(e.Message);
|
|
AppLogger.LogToTray(e.Message);
|
|
}
|
|
|
|
running = true;
|
|
|
|
if (_udpServer != null)
|
|
{
|
|
//var UDP_SERVER_PORT = 26760;
|
|
var UDP_SERVER_PORT = Global.getUDPServerPortNum();
|
|
var UDP_SERVER_LISTEN_ADDRESS = Global.getUDPServerListenAddress();
|
|
|
|
try
|
|
{
|
|
_udpServer.Start(UDP_SERVER_PORT, UDP_SERVER_LISTEN_ADDRESS);
|
|
LogDebug($"UDP server listening on address {UDP_SERVER_LISTEN_ADDRESS} port {UDP_SERVER_PORT}");
|
|
}
|
|
catch (System.Net.Sockets.SocketException ex)
|
|
{
|
|
var errMsg = String.Format("Couldn't start UDP server on address {0}:{1}, outside applications won't be able to access pad data ({2})", UDP_SERVER_LISTEN_ADDRESS, UDP_SERVER_PORT, ex.SocketErrorCode);
|
|
|
|
LogDebug(errMsg, true);
|
|
AppLogger.LogToTray(errMsg, true, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string logMessage = string.Empty;
|
|
if (!vigemInstalled)
|
|
{
|
|
logMessage = "ViGEmBus is not installed";
|
|
}
|
|
else
|
|
{
|
|
logMessage = "Could not connect to ViGEmBus. Please check the status of the System device in Device Manager and if Visual C++ 2017 Redistributable is installed.";
|
|
}
|
|
|
|
LogDebug(logMessage);
|
|
AppLogger.LogToTray(logMessage);
|
|
}
|
|
|
|
runHotPlug = true;
|
|
ServiceStarted?.Invoke(this, EventArgs.Empty);
|
|
RunningChanged?.Invoke(this, EventArgs.Empty);
|
|
return true;
|
|
}
|
|
|
|
private void CheckQuickCharge(object sender, EventArgs e)
|
|
{
|
|
DS4Device device = sender as DS4Device;
|
|
if (device.ConnectionType == ConnectionType.BT && getQuickCharge() &&
|
|
device.Charging)
|
|
{
|
|
device.DisconnectBT();
|
|
}
|
|
}
|
|
|
|
public bool Stop(bool showlog = true)
|
|
{
|
|
if (running)
|
|
{
|
|
running = false;
|
|
runHotPlug = false;
|
|
PreServiceStop?.Invoke(this, EventArgs.Empty);
|
|
|
|
if (showlog)
|
|
LogDebug(DS4WinWPF.Properties.Resources.StoppingX360);
|
|
|
|
LogDebug("Closing connection to ViGEmBus");
|
|
|
|
for (int i = 0, arlength = DS4Controllers.Length; i < arlength; i++)
|
|
{
|
|
DS4Device tempDevice = DS4Controllers[i];
|
|
if (tempDevice != null)
|
|
{
|
|
if ((DCBTatStop && !tempDevice.isCharging()) || suspending)
|
|
{
|
|
if (tempDevice.getConnectionType() == ConnectionType.BT)
|
|
{
|
|
tempDevice.StopUpdate();
|
|
tempDevice.DisconnectBT(true);
|
|
}
|
|
else if (tempDevice.getConnectionType() == ConnectionType.SONYWA)
|
|
{
|
|
tempDevice.StopUpdate();
|
|
tempDevice.DisconnectDongle(true);
|
|
}
|
|
else
|
|
{
|
|
tempDevice.StopUpdate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DS4LightBar.forcelight[i] = false;
|
|
DS4LightBar.forcedFlash[i] = 0;
|
|
DS4LightBar.defaultLight = true;
|
|
DS4LightBar.updateLightBar(DS4Controllers[i], i);
|
|
tempDevice.IsRemoved = true;
|
|
tempDevice.StopUpdate();
|
|
DS4Devices.RemoveDevice(tempDevice);
|
|
Thread.Sleep(50);
|
|
}
|
|
|
|
CurrentState[i].Battery = PreviousState[i].Battery = 0; // Reset for the next connection's initial status change.
|
|
OutputDevice tempout = outputDevices[i];
|
|
if (tempout != null)
|
|
{
|
|
UnplugOutDev(i, tempDevice, true);
|
|
}
|
|
|
|
//outputDevices[i] = null;
|
|
//useDInputOnly[i] = true;
|
|
//Global.activeOutDevType[i] = OutContType.None;
|
|
DS4Controllers[i] = null;
|
|
touchPad[i] = null;
|
|
lag[i] = false;
|
|
inWarnMonitor[i] = false;
|
|
}
|
|
}
|
|
|
|
if (showlog)
|
|
LogDebug(DS4WinWPF.Properties.Resources.StoppingDS4);
|
|
|
|
DS4Devices.stopControllers();
|
|
slotManager.ClearControllerList();
|
|
|
|
if (_udpServer != null)
|
|
ChangeUDPStatus(false);
|
|
//_udpServer.Stop();
|
|
|
|
if (showlog)
|
|
LogDebug(DS4WinWPF.Properties.Resources.StoppedDS4Windows);
|
|
|
|
while (outputslotMan.RunningQueue)
|
|
{
|
|
Thread.SpinWait(500);
|
|
}
|
|
|
|
stopViGEm();
|
|
}
|
|
|
|
runHotPlug = false;
|
|
ServiceStopped?.Invoke(this, EventArgs.Empty);
|
|
RunningChanged?.Invoke(this, EventArgs.Empty);
|
|
return true;
|
|
}
|
|
|
|
public bool HotPlug()
|
|
{
|
|
if (running)
|
|
{
|
|
DS4Devices.findControllers();
|
|
IEnumerable<DS4Device> devices = DS4Devices.getDS4Controllers();
|
|
//foreach (DS4Device device in devices)
|
|
//for (int i = 0, devlen = devices.Count(); i < devlen; i++)
|
|
for (var devEnum = devices.GetEnumerator(); devEnum.MoveNext();)
|
|
{
|
|
DS4Device device = devEnum.Current;
|
|
//DS4Device device = devices.ElementAt(i);
|
|
|
|
if (device.isDisconnectingStatus())
|
|
continue;
|
|
|
|
if (((Func<bool>)delegate
|
|
{
|
|
for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++)
|
|
{
|
|
if (DS4Controllers[Index] != null &&
|
|
DS4Controllers[Index].getMacAddress() == device.getMacAddress())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
})())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (Int32 Index = 0, arlength = DS4Controllers.Length; Index < arlength; Index++)
|
|
{
|
|
if (DS4Controllers[Index] == null)
|
|
{
|
|
//LogDebug(DS4WinWPF.Properties.Resources.FoundController + device.getMacAddress() + " (" + device.getConnectionType() + ")");
|
|
LogDebug(DS4WinWPF.Properties.Resources.FoundController + " " + device.getMacAddress() + " (" + device.getConnectionType() + ") (" +
|
|
device.DisplayName + ")");
|
|
Task task = new Task(() => { Thread.Sleep(5); WarnExclusiveModeFailure(device); });
|
|
task.Start();
|
|
DS4Controllers[Index] = device;
|
|
slotManager.AddController(device, Index);
|
|
device.Removal += this.On_DS4Removal;
|
|
device.Removal += DS4Devices.On_Removal;
|
|
device.SyncChange += this.On_SyncChange;
|
|
device.SyncChange += DS4Devices.UpdateSerial;
|
|
device.SerialChange += this.On_SerialChange;
|
|
device.ChargingChanged += CheckQuickCharge;
|
|
|
|
touchPad[Index] = new Mouse(Index, device);
|
|
|
|
if (!useTempProfile[Index])
|
|
{
|
|
if (device.isValidSerial() && containsLinkedProfile(device.getMacAddress()))
|
|
{
|
|
ProfilePath[Index] = getLinkedProfile(device.getMacAddress());
|
|
Global.linkedProfileCheck[Index] = true;
|
|
}
|
|
else
|
|
{
|
|
ProfilePath[Index] = OlderProfilePath[Index];
|
|
Global.linkedProfileCheck[Index] = false;
|
|
}
|
|
|
|
LoadProfile(Index, false, this, false, false);
|
|
}
|
|
|
|
device.LightBarColor = getMainColor(Index);
|
|
|
|
int tempIdx = Index;
|
|
device.Report += (sender, e) =>
|
|
{
|
|
this.On_Report(sender, e, tempIdx);
|
|
};
|
|
|
|
DS4Device.ReportHandler<EventArgs> tempEvnt = (sender, args) =>
|
|
{
|
|
DualShockPadMeta padDetail = new DualShockPadMeta();
|
|
GetPadDetailForIdx(tempIdx, ref padDetail);
|
|
_udpServer.NewReportIncoming(ref padDetail, CurrentState[tempIdx], udpOutBuffers[tempIdx]);
|
|
};
|
|
device.MotionEvent = tempEvnt;
|
|
|
|
if (_udpServer != null)
|
|
{
|
|
device.Report += tempEvnt;
|
|
}
|
|
|
|
if (!getDInputOnly(Index) && device.isSynced())
|
|
{
|
|
//useDInputOnly[Index] = false;
|
|
PluginOutDev(Index, device);
|
|
}
|
|
else
|
|
{
|
|
useDInputOnly[Index] = true;
|
|
Global.activeOutDevType[Index] = OutContType.None;
|
|
}
|
|
|
|
TouchPadOn(Index, device);
|
|
CheckProfileOptions(Index, device);
|
|
device.StartUpdate();
|
|
|
|
//string filename = Path.GetFileName(ProfilePath[Index]);
|
|
if (File.Exists(appdatapath + "\\Profiles\\" + ProfilePath[Index] + ".xml"))
|
|
{
|
|
string prolog = DS4WinWPF.Properties.Resources.UsingProfile.Replace("*number*", (Index + 1).ToString()).Replace("*Profile name*", ProfilePath[Index]);
|
|
LogDebug(prolog);
|
|
AppLogger.LogToTray(prolog);
|
|
}
|
|
else
|
|
{
|
|
string prolog = DS4WinWPF.Properties.Resources.NotUsingProfile.Replace("*number*", (Index + 1).ToString());
|
|
LogDebug(prolog);
|
|
AppLogger.LogToTray(prolog);
|
|
}
|
|
|
|
HotplugController?.Invoke(this, device, Index);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*private void testNewReport(ref Xbox360Report xboxreport, DS4State state,
|
|
int device)
|
|
{
|
|
Xbox360Buttons tempButtons = 0;
|
|
|
|
unchecked
|
|
{
|
|
if (state.Share) tempButtons |= Xbox360Buttons.Back;
|
|
if (state.L3) tempButtons |= Xbox360Buttons.LeftThumb;
|
|
if (state.R3) tempButtons |= Xbox360Buttons.RightThumb;
|
|
if (state.Options) tempButtons |= Xbox360Buttons.Start;
|
|
|
|
if (state.DpadUp) tempButtons |= Xbox360Buttons.Up;
|
|
if (state.DpadRight) tempButtons |= Xbox360Buttons.Right;
|
|
if (state.DpadDown) tempButtons |= Xbox360Buttons.Down;
|
|
if (state.DpadLeft) tempButtons |= Xbox360Buttons.Left;
|
|
|
|
if (state.L1) tempButtons |= Xbox360Buttons.LeftShoulder;
|
|
if (state.R1) tempButtons |= Xbox360Buttons.RightShoulder;
|
|
|
|
if (state.Triangle) tempButtons |= Xbox360Buttons.Y;
|
|
if (state.Circle) tempButtons |= Xbox360Buttons.B;
|
|
if (state.Cross) tempButtons |= Xbox360Buttons.A;
|
|
if (state.Square) tempButtons |= Xbox360Buttons.X;
|
|
if (state.PS) tempButtons |= Xbox360Buttons.Guide;
|
|
xboxreport.SetButtonsFull(tempButtons);
|
|
}
|
|
|
|
xboxreport.LeftTrigger = state.L2;
|
|
xboxreport.RightTrigger = state.R2;
|
|
|
|
SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(device);
|
|
switch (steeringWheelMappedAxis)
|
|
{
|
|
case SASteeringWheelEmulationAxisType.None:
|
|
xboxreport.LeftThumbX = AxisScale(state.LX, false);
|
|
xboxreport.LeftThumbY = AxisScale(state.LY, true);
|
|
xboxreport.RightThumbX = AxisScale(state.RX, false);
|
|
xboxreport.RightThumbY = AxisScale(state.RY, true);
|
|
break;
|
|
|
|
case SASteeringWheelEmulationAxisType.LX:
|
|
xboxreport.LeftThumbX = (short)state.SASteeringWheelEmulationUnit;
|
|
xboxreport.LeftThumbY = AxisScale(state.LY, true);
|
|
xboxreport.RightThumbX = AxisScale(state.RX, false);
|
|
xboxreport.RightThumbY = AxisScale(state.RY, true);
|
|
break;
|
|
|
|
case SASteeringWheelEmulationAxisType.LY:
|
|
xboxreport.LeftThumbX = AxisScale(state.LX, false);
|
|
xboxreport.LeftThumbY = (short)state.SASteeringWheelEmulationUnit;
|
|
xboxreport.RightThumbX = AxisScale(state.RX, false);
|
|
xboxreport.RightThumbY = AxisScale(state.RY, true);
|
|
break;
|
|
|
|
case SASteeringWheelEmulationAxisType.RX:
|
|
xboxreport.LeftThumbX = AxisScale(state.LX, false);
|
|
xboxreport.LeftThumbY = AxisScale(state.LY, true);
|
|
xboxreport.RightThumbX = (short)state.SASteeringWheelEmulationUnit;
|
|
xboxreport.RightThumbY = AxisScale(state.RY, true);
|
|
break;
|
|
|
|
case SASteeringWheelEmulationAxisType.RY:
|
|
xboxreport.LeftThumbX = AxisScale(state.LX, false);
|
|
xboxreport.LeftThumbY = AxisScale(state.LY, true);
|
|
xboxreport.RightThumbX = AxisScale(state.RX, false);
|
|
xboxreport.RightThumbY = (short)state.SASteeringWheelEmulationUnit;
|
|
break;
|
|
|
|
case SASteeringWheelEmulationAxisType.L2R2:
|
|
xboxreport.LeftTrigger = xboxreport.RightTrigger = 0;
|
|
if (state.SASteeringWheelEmulationUnit >= 0) xboxreport.LeftTrigger = (Byte)state.SASteeringWheelEmulationUnit;
|
|
else xboxreport.RightTrigger = (Byte)state.SASteeringWheelEmulationUnit;
|
|
goto case SASteeringWheelEmulationAxisType.None;
|
|
|
|
case SASteeringWheelEmulationAxisType.VJoy1X:
|
|
case SASteeringWheelEmulationAxisType.VJoy2X:
|
|
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X);
|
|
goto case SASteeringWheelEmulationAxisType.None;
|
|
|
|
case SASteeringWheelEmulationAxisType.VJoy1Y:
|
|
case SASteeringWheelEmulationAxisType.VJoy2Y:
|
|
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y);
|
|
goto case SASteeringWheelEmulationAxisType.None;
|
|
|
|
case SASteeringWheelEmulationAxisType.VJoy1Z:
|
|
case SASteeringWheelEmulationAxisType.VJoy2Z:
|
|
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(state.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z);
|
|
goto case SASteeringWheelEmulationAxisType.None;
|
|
|
|
default:
|
|
// Should never come here but just in case use the NONE case as default handler....
|
|
goto case SASteeringWheelEmulationAxisType.None;
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*private short AxisScale(Int32 Value, Boolean Flip)
|
|
{
|
|
unchecked
|
|
{
|
|
Value -= 0x80;
|
|
|
|
//float temp = (Value - (-128)) / (float)inputResolution;
|
|
float temp = (Value - (-128)) * reciprocalInputResolution;
|
|
if (Flip) temp = (temp - 0.5f) * -1.0f + 0.5f;
|
|
|
|
return (short)(temp * outputResolution + (-32768));
|
|
}
|
|
}
|
|
*/
|
|
|
|
private void CheckProfileOptions(int ind, DS4Device device, bool startUp=false)
|
|
{
|
|
device.setIdleTimeout(getIdleDisconnectTimeout(ind));
|
|
device.setBTPollRate(getBTPollRate(ind));
|
|
touchPad[ind].ResetTrackAccel(getTrackballFriction(ind));
|
|
|
|
if (!startUp)
|
|
{
|
|
CheckLauchProfileOption(ind, device);
|
|
}
|
|
}
|
|
|
|
private void CheckLauchProfileOption(int ind, DS4Device device)
|
|
{
|
|
string programPath = LaunchProgram[ind];
|
|
if (programPath != string.Empty)
|
|
{
|
|
System.Diagnostics.Process[] localAll = System.Diagnostics.Process.GetProcesses();
|
|
bool procFound = false;
|
|
for (int procInd = 0, procsLen = localAll.Length; !procFound && procInd < procsLen; procInd++)
|
|
{
|
|
try
|
|
{
|
|
string temp = localAll[procInd].MainModule.FileName;
|
|
if (temp == programPath)
|
|
{
|
|
procFound = true;
|
|
}
|
|
}
|
|
// Ignore any process for which this information
|
|
// is not exposed
|
|
catch { }
|
|
}
|
|
|
|
if (!procFound)
|
|
{
|
|
Task processTask = new Task(() =>
|
|
{
|
|
Thread.Sleep(5000);
|
|
System.Diagnostics.Process tempProcess = new System.Diagnostics.Process();
|
|
tempProcess.StartInfo.FileName = programPath;
|
|
tempProcess.StartInfo.WorkingDirectory = new FileInfo(programPath).Directory.ToString();
|
|
//tempProcess.StartInfo.UseShellExecute = false;
|
|
try { tempProcess.Start(); }
|
|
catch { }
|
|
});
|
|
|
|
processTask.Start();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void TouchPadOn(int ind, DS4Device device)
|
|
{
|
|
Mouse tPad = touchPad[ind];
|
|
//ITouchpadBehaviour tPad = touchPad[ind];
|
|
device.Touchpad.TouchButtonDown += tPad.touchButtonDown;
|
|
device.Touchpad.TouchButtonUp += tPad.touchButtonUp;
|
|
device.Touchpad.TouchesBegan += tPad.touchesBegan;
|
|
device.Touchpad.TouchesMoved += tPad.touchesMoved;
|
|
device.Touchpad.TouchesEnded += tPad.touchesEnded;
|
|
device.Touchpad.TouchUnchanged += tPad.touchUnchanged;
|
|
//device.Touchpad.PreTouchProcess += delegate { touchPad[ind].populatePriorButtonStates(); };
|
|
device.Touchpad.PreTouchProcess += (sender, args) => { touchPad[ind].populatePriorButtonStates(); };
|
|
device.SixAxis.SixAccelMoved += tPad.sixaxisMoved;
|
|
//LogDebug("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString());
|
|
//Log.LogToTray("Touchpad mode for " + device.MacAddress + " is now " + tmode.ToString());
|
|
}
|
|
|
|
public string getDS4ControllerInfo(int index)
|
|
{
|
|
DS4Device d = DS4Controllers[index];
|
|
if (d != null)
|
|
{
|
|
if (!d.IsAlive())
|
|
{
|
|
return DS4WinWPF.Properties.Resources.Connecting;
|
|
}
|
|
|
|
string battery;
|
|
if (d.isCharging())
|
|
{
|
|
if (d.getBattery() >= 100)
|
|
battery = DS4WinWPF.Properties.Resources.Charged;
|
|
else
|
|
battery = DS4WinWPF.Properties.Resources.Charging.Replace("*number*", d.getBattery().ToString());
|
|
}
|
|
else
|
|
{
|
|
battery = DS4WinWPF.Properties.Resources.Battery.Replace("*number*", d.getBattery().ToString());
|
|
}
|
|
|
|
return d.getMacAddress() + " (" + d.getConnectionType() + "), " + battery;
|
|
//return d.MacAddress + " (" + d.ConnectionType + "), Battery is " + battery + ", Touchpad in " + modeSwitcher[index].ToString();
|
|
}
|
|
else
|
|
return string.Empty;
|
|
}
|
|
|
|
public string getDS4MacAddress(int index)
|
|
{
|
|
DS4Device d = DS4Controllers[index];
|
|
if (d != null)
|
|
{
|
|
if (!d.IsAlive())
|
|
{
|
|
return DS4WinWPF.Properties.Resources.Connecting;
|
|
}
|
|
|
|
return d.getMacAddress();
|
|
}
|
|
else
|
|
return string.Empty;
|
|
}
|
|
|
|
public string getShortDS4ControllerInfo(int index)
|
|
{
|
|
DS4Device d = DS4Controllers[index];
|
|
if (d != null)
|
|
{
|
|
string battery;
|
|
if (!d.IsAlive())
|
|
battery = "...";
|
|
|
|
if (d.isCharging())
|
|
{
|
|
if (d.getBattery() >= 100)
|
|
battery = DS4WinWPF.Properties.Resources.Full;
|
|
else
|
|
battery = d.getBattery() + "%+";
|
|
}
|
|
else
|
|
{
|
|
battery = d.getBattery() + "%";
|
|
}
|
|
|
|
return (d.getConnectionType() + " " + battery);
|
|
}
|
|
else
|
|
return DS4WinWPF.Properties.Resources.NoneText;
|
|
}
|
|
|
|
public string getDS4Battery(int index)
|
|
{
|
|
DS4Device d = DS4Controllers[index];
|
|
if (d != null)
|
|
{
|
|
string battery;
|
|
if (!d.IsAlive())
|
|
battery = "...";
|
|
|
|
if (d.isCharging())
|
|
{
|
|
if (d.getBattery() >= 100)
|
|
battery = DS4WinWPF.Properties.Resources.Full;
|
|
else
|
|
battery = d.getBattery() + "%+";
|
|
}
|
|
else
|
|
{
|
|
battery = d.getBattery() + "%";
|
|
}
|
|
|
|
return battery;
|
|
}
|
|
else
|
|
return DS4WinWPF.Properties.Resources.NA;
|
|
}
|
|
|
|
public string getDS4Status(int index)
|
|
{
|
|
DS4Device d = DS4Controllers[index];
|
|
if (d != null)
|
|
{
|
|
return d.getConnectionType() + "";
|
|
}
|
|
else
|
|
return DS4WinWPF.Properties.Resources.NoneText;
|
|
}
|
|
|
|
protected void On_SerialChange(object sender, EventArgs e)
|
|
{
|
|
DS4Device device = (DS4Device)sender;
|
|
int ind = -1;
|
|
for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++)
|
|
{
|
|
DS4Device tempDev = DS4Controllers[i];
|
|
if (tempDev != null && device == tempDev)
|
|
ind = i;
|
|
}
|
|
|
|
if (ind >= 0)
|
|
{
|
|
OnDeviceSerialChange(this, ind, device.getMacAddress());
|
|
}
|
|
}
|
|
|
|
protected void On_SyncChange(object sender, EventArgs e)
|
|
{
|
|
DS4Device device = (DS4Device)sender;
|
|
int ind = -1;
|
|
for (int i = 0, arlength = DS4_CONTROLLER_COUNT; ind == -1 && i < arlength; i++)
|
|
{
|
|
DS4Device tempDev = DS4Controllers[i];
|
|
if (tempDev != null && device == tempDev)
|
|
ind = i;
|
|
}
|
|
|
|
if (ind >= 0)
|
|
{
|
|
bool synced = device.isSynced();
|
|
|
|
if (!synced)
|
|
{
|
|
if (!useDInputOnly[ind])
|
|
{
|
|
//string tempType = outputDevices[ind].GetDeviceType();
|
|
//outputDevices[ind].Disconnect();
|
|
//outputDevices[ind] = null;
|
|
//useDInputOnly[ind] = true;
|
|
//LogDebug(tempType + " Controller #" + (ind + 1) + " unplugged");
|
|
Global.activeOutDevType[ind] = OutContType.None;
|
|
UnplugOutDev(ind, device);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!getDInputOnly(ind))
|
|
{
|
|
PluginOutDev(ind, device);
|
|
/*OutContType conType = Global.OutContType[ind];
|
|
if (conType == OutContType.X360)
|
|
{
|
|
LogDebug("Plugging in X360 Controller #" + (ind + 1));
|
|
Global.activeOutDevType[ind] = OutContType.X360;
|
|
Xbox360OutDevice tempXbox = new Xbox360OutDevice(vigemTestClient);
|
|
outputDevices[ind] = tempXbox;
|
|
tempXbox.cont.FeedbackReceived += (eventsender, args) =>
|
|
{
|
|
SetDevRumble(device, args.LargeMotor, args.SmallMotor, ind);
|
|
};
|
|
|
|
tempXbox.Connect();
|
|
LogDebug("X360 Controller #" + (ind + 1) + " connected");
|
|
}
|
|
else if (conType == OutContType.DS4)
|
|
{
|
|
LogDebug("Plugging in DS4 Controller #" + (ind + 1));
|
|
Global.activeOutDevType[ind] = OutContType.DS4;
|
|
DS4OutDevice tempDS4 = new DS4OutDevice(vigemTestClient);
|
|
outputDevices[ind] = tempDS4;
|
|
int devIndex = ind;
|
|
tempDS4.cont.FeedbackReceived += (eventsender, args) =>
|
|
{
|
|
SetDevRumble(device, args.LargeMotor, args.SmallMotor, devIndex);
|
|
};
|
|
|
|
tempDS4.Connect();
|
|
LogDebug("DS4 Controller #" + (ind + 1) + " connected");
|
|
}
|
|
*/
|
|
|
|
//useDInputOnly[ind] = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Called when DS4 is disconnected or timed out
|
|
protected virtual void On_DS4Removal(object sender, EventArgs e)
|
|
{
|
|
DS4Device device = (DS4Device)sender;
|
|
int ind = -1;
|
|
for (int i = 0, arlength = DS4Controllers.Length; ind == -1 && i < arlength; i++)
|
|
{
|
|
if (DS4Controllers[i] != null && device.getMacAddress() == DS4Controllers[i].getMacAddress())
|
|
ind = i;
|
|
}
|
|
|
|
if (ind != -1)
|
|
{
|
|
bool removingStatus = false;
|
|
lock (device.removeLocker)
|
|
{
|
|
if (!device.IsRemoving)
|
|
{
|
|
removingStatus = true;
|
|
device.IsRemoving = true;
|
|
}
|
|
}
|
|
|
|
if (removingStatus)
|
|
{
|
|
CurrentState[ind].Battery = PreviousState[ind].Battery = 0; // Reset for the next connection's initial status change.
|
|
if (!useDInputOnly[ind])
|
|
{
|
|
UnplugOutDev(ind, device);
|
|
}
|
|
|
|
// Use Task to reset device synth state and commit it
|
|
Task.Run(() =>
|
|
{
|
|
Mapping.Commit(ind);
|
|
}).Wait();
|
|
|
|
string removed = DS4WinWPF.Properties.Resources.ControllerWasRemoved.Replace("*Mac address*", (ind + 1).ToString());
|
|
if (device.getBattery() <= 20 &&
|
|
device.getConnectionType() == ConnectionType.BT && !device.isCharging())
|
|
{
|
|
removed += ". " + DS4WinWPF.Properties.Resources.ChargeController;
|
|
}
|
|
|
|
LogDebug(removed);
|
|
AppLogger.LogToTray(removed);
|
|
/*Stopwatch sw = new Stopwatch();
|
|
sw.Start();
|
|
while (sw.ElapsedMilliseconds < XINPUT_UNPLUG_SETTLE_TIME)
|
|
{
|
|
// Use SpinWait to keep control of current thread. Using Sleep could potentially
|
|
// cause other events to get run out of order
|
|
System.Threading.Thread.SpinWait(500);
|
|
}
|
|
sw.Stop();
|
|
*/
|
|
|
|
device.IsRemoved = true;
|
|
device.Synced = false;
|
|
DS4Controllers[ind] = null;
|
|
slotManager.RemoveController(device, ind);
|
|
touchPad[ind] = null;
|
|
lag[ind] = false;
|
|
inWarnMonitor[ind] = false;
|
|
useDInputOnly[ind] = true;
|
|
Global.activeOutDevType[ind] = OutContType.None;
|
|
/*uiContext?.Post(new SendOrPostCallback((state) =>
|
|
{
|
|
OnControllerRemoved(this, ind);
|
|
}), null);
|
|
*/
|
|
//Thread.Sleep(XINPUT_UNPLUG_SETTLE_TIME);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool[] lag = new bool[4] { false, false, false, false };
|
|
public bool[] inWarnMonitor = new bool[4] { false, false, false, false };
|
|
private byte[] currentBattery = new byte[4] { 0, 0, 0, 0 };
|
|
private bool[] charging = new bool[4] { false, false, false, false };
|
|
private string[] tempStrings = new string[4] { string.Empty, string.Empty, string.Empty, string.Empty };
|
|
|
|
// Called every time a new input report has arrived
|
|
//protected virtual void On_Report(object sender, EventArgs e, int ind)
|
|
protected virtual void On_Report(DS4Device device, EventArgs e, int ind)
|
|
{
|
|
//DS4Device device = (DS4Device)sender;
|
|
|
|
if (ind != -1)
|
|
{
|
|
if (getFlushHIDQueue(ind))
|
|
device.FlushHID();
|
|
|
|
string devError = tempStrings[ind] = device.error;
|
|
if (!string.IsNullOrEmpty(devError))
|
|
{
|
|
LogDebug(devError);
|
|
/*uiContext?.Post(new SendOrPostCallback(delegate (object state)
|
|
{
|
|
LogDebug(devError);
|
|
}), null);
|
|
*/
|
|
}
|
|
|
|
if (inWarnMonitor[ind])
|
|
{
|
|
int flashWhenLateAt = getFlashWhenLateAt();
|
|
if (!lag[ind] && device.Latency >= flashWhenLateAt)
|
|
{
|
|
lag[ind] = true;
|
|
LagFlashWarning(ind, true);
|
|
/*uiContext?.Post(new SendOrPostCallback(delegate (object state)
|
|
{
|
|
LagFlashWarning(ind, true);
|
|
}), null);
|
|
*/
|
|
}
|
|
else if (lag[ind] && device.Latency < flashWhenLateAt)
|
|
{
|
|
lag[ind] = false;
|
|
LagFlashWarning(ind, false);
|
|
/*uiContext?.Post(new SendOrPostCallback(delegate (object state)
|
|
{
|
|
LagFlashWarning(ind, false);
|
|
}), null);
|
|
*/
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (DateTime.UtcNow - device.firstActive > TimeSpan.FromSeconds(5))
|
|
{
|
|
inWarnMonitor[ind] = true;
|
|
}
|
|
}
|
|
|
|
device.getCurrentState(CurrentState[ind]);
|
|
DS4State cState = CurrentState[ind];
|
|
DS4State pState = device.getPreviousStateRef();
|
|
//device.getPreviousState(PreviousState[ind]);
|
|
//DS4State pState = PreviousState[ind];
|
|
|
|
if (device.firstReport && device.IsAlive())
|
|
{
|
|
device.firstReport = false;
|
|
/*uiContext?.Post(new SendOrPostCallback(delegate (object state)
|
|
{
|
|
OnDeviceStatusChanged(this, ind);
|
|
}), null);
|
|
*/
|
|
}
|
|
//else if (pState.Battery != cState.Battery || device.oldCharging != device.isCharging())
|
|
//{
|
|
// byte tempBattery = currentBattery[ind] = cState.Battery;
|
|
// bool tempCharging = charging[ind] = device.isCharging();
|
|
// /*uiContext?.Post(new SendOrPostCallback(delegate (object state)
|
|
// {
|
|
// OnBatteryStatusChange(this, ind, tempBattery, tempCharging);
|
|
// }), null);
|
|
// */
|
|
//}
|
|
|
|
if (getEnableTouchToggle(ind))
|
|
CheckForTouchToggle(ind, cState, pState);
|
|
|
|
cState = Mapping.SetCurveAndDeadzone(ind, cState, TempState[ind]);
|
|
|
|
if (!recordingMacro && (useTempProfile[ind] ||
|
|
containsCustomAction(ind) || containsCustomExtras(ind) ||
|
|
getProfileActionCount(ind) > 0 ||
|
|
GetSASteeringWheelEmulationAxis(ind) >= SASteeringWheelEmulationAxisType.VJoy1X))
|
|
{
|
|
Mapping.MapCustom(ind, cState, MappedState[ind], ExposedState[ind], touchPad[ind], this);
|
|
cState = MappedState[ind];
|
|
}
|
|
|
|
if (!useDInputOnly[ind])
|
|
{
|
|
outputDevices[ind]?.ConvertandSendReport(cState, ind);
|
|
//testNewReport(ref x360reports[ind], cState, ind);
|
|
//x360controls[ind]?.SendReport(x360reports[ind]);
|
|
|
|
//x360Bus.Parse(cState, processingData[ind].Report, ind);
|
|
// We push the translated Xinput state, and simultaneously we
|
|
// pull back any possible rumble data coming from Xinput consumers.
|
|
/*if (x360Bus.Report(processingData[ind].Report, processingData[ind].Rumble))
|
|
{
|
|
byte Big = processingData[ind].Rumble[3];
|
|
byte Small = processingData[ind].Rumble[4];
|
|
|
|
if (processingData[ind].Rumble[1] == 0x08)
|
|
{
|
|
SetDevRumble(device, Big, Small, ind);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
// UseDInputOnly profile may re-map sixaxis gyro sensor values as a VJoy joystick axis (steering wheel emulation mode using VJoy output device). Handle this option because VJoy output works even in USeDInputOnly mode.
|
|
// If steering wheel emulation uses LS/RS/R2/L2 output axies then the profile should NOT use UseDInputOnly option at all because those require a virtual output device.
|
|
SASteeringWheelEmulationAxisType steeringWheelMappedAxis = Global.GetSASteeringWheelEmulationAxis(ind);
|
|
switch (steeringWheelMappedAxis)
|
|
{
|
|
case SASteeringWheelEmulationAxisType.None: break;
|
|
|
|
case SASteeringWheelEmulationAxisType.VJoy1X:
|
|
case SASteeringWheelEmulationAxisType.VJoy2X:
|
|
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_X);
|
|
break;
|
|
|
|
case SASteeringWheelEmulationAxisType.VJoy1Y:
|
|
case SASteeringWheelEmulationAxisType.VJoy2Y:
|
|
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Y);
|
|
break;
|
|
|
|
case SASteeringWheelEmulationAxisType.VJoy1Z:
|
|
case SASteeringWheelEmulationAxisType.VJoy2Z:
|
|
DS4Windows.VJoyFeeder.vJoyFeeder.FeedAxisValue(cState.SASteeringWheelEmulationUnit, ((((uint)steeringWheelMappedAxis) - ((uint)SASteeringWheelEmulationAxisType.VJoy1X)) / 3) + 1, DS4Windows.VJoyFeeder.HID_USAGES.HID_USAGE_Z);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Output any synthetic events.
|
|
Mapping.Commit(ind);
|
|
|
|
// Update the GUI/whatever.
|
|
DS4LightBar.updateLightBar(device, ind);
|
|
}
|
|
}
|
|
|
|
public void LagFlashWarning(int ind, bool on)
|
|
{
|
|
if (on)
|
|
{
|
|
lag[ind] = true;
|
|
LogDebug(DS4WinWPF.Properties.Resources.LatencyOverTen.Replace("*number*", (ind + 1).ToString()), true);
|
|
if (getFlashWhenLate())
|
|
{
|
|
DS4Color color = new DS4Color { red = 50, green = 0, blue = 0 };
|
|
DS4LightBar.forcedColor[ind] = color;
|
|
DS4LightBar.forcedFlash[ind] = 2;
|
|
DS4LightBar.forcelight[ind] = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lag[ind] = false;
|
|
LogDebug(DS4WinWPF.Properties.Resources.LatencyNotOverTen.Replace("*number*", (ind + 1).ToString()));
|
|
DS4LightBar.forcelight[ind] = false;
|
|
DS4LightBar.forcedFlash[ind] = 0;
|
|
}
|
|
}
|
|
|
|
public DS4Controls GetActiveInputControl(int ind)
|
|
{
|
|
DS4State cState = CurrentState[ind];
|
|
DS4StateExposed eState = ExposedState[ind];
|
|
Mouse tp = touchPad[ind];
|
|
DS4Controls result = DS4Controls.None;
|
|
|
|
if (DS4Controllers[ind] != null)
|
|
{
|
|
if (Mapping.getBoolButtonMapping(cState.Cross))
|
|
result = DS4Controls.Cross;
|
|
else if (Mapping.getBoolButtonMapping(cState.Circle))
|
|
result = DS4Controls.Circle;
|
|
else if (Mapping.getBoolButtonMapping(cState.Triangle))
|
|
result = DS4Controls.Triangle;
|
|
else if (Mapping.getBoolButtonMapping(cState.Square))
|
|
result = DS4Controls.Square;
|
|
else if (Mapping.getBoolButtonMapping(cState.L1))
|
|
result = DS4Controls.L1;
|
|
else if (Mapping.getBoolTriggerMapping(cState.L2))
|
|
result = DS4Controls.L2;
|
|
else if (Mapping.getBoolButtonMapping(cState.L3))
|
|
result = DS4Controls.L3;
|
|
else if (Mapping.getBoolButtonMapping(cState.R1))
|
|
result = DS4Controls.R1;
|
|
else if (Mapping.getBoolTriggerMapping(cState.R2))
|
|
result = DS4Controls.R2;
|
|
else if (Mapping.getBoolButtonMapping(cState.R3))
|
|
result = DS4Controls.R3;
|
|
else if (Mapping.getBoolButtonMapping(cState.DpadUp))
|
|
result = DS4Controls.DpadUp;
|
|
else if (Mapping.getBoolButtonMapping(cState.DpadDown))
|
|
result = DS4Controls.DpadDown;
|
|
else if (Mapping.getBoolButtonMapping(cState.DpadLeft))
|
|
result = DS4Controls.DpadLeft;
|
|
else if (Mapping.getBoolButtonMapping(cState.DpadRight))
|
|
result = DS4Controls.DpadRight;
|
|
else if (Mapping.getBoolButtonMapping(cState.Share))
|
|
result = DS4Controls.Share;
|
|
else if (Mapping.getBoolButtonMapping(cState.Options))
|
|
result = DS4Controls.Options;
|
|
else if (Mapping.getBoolButtonMapping(cState.PS))
|
|
result = DS4Controls.PS;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.LX, true))
|
|
result = DS4Controls.LXPos;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.LX, false))
|
|
result = DS4Controls.LXNeg;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.LY, true))
|
|
result = DS4Controls.LYPos;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.LY, false))
|
|
result = DS4Controls.LYNeg;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.RX, true))
|
|
result = DS4Controls.RXPos;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.RX, false))
|
|
result = DS4Controls.RXNeg;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.RY, true))
|
|
result = DS4Controls.RYPos;
|
|
else if (Mapping.getBoolAxisDirMapping(cState.RY, false))
|
|
result = DS4Controls.RYNeg;
|
|
else if (Mapping.getBoolTouchMapping(tp.leftDown))
|
|
result = DS4Controls.TouchLeft;
|
|
else if (Mapping.getBoolTouchMapping(tp.rightDown))
|
|
result = DS4Controls.TouchRight;
|
|
else if (Mapping.getBoolTouchMapping(tp.multiDown))
|
|
result = DS4Controls.TouchMulti;
|
|
else if (Mapping.getBoolTouchMapping(tp.upperDown))
|
|
result = DS4Controls.TouchUpper;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public bool[] touchreleased = new bool[4] { true, true, true, true },
|
|
touchslid = new bool[4] { false, false, false, false };
|
|
|
|
protected virtual void CheckForTouchToggle(int deviceID, DS4State cState, DS4State pState)
|
|
{
|
|
if (!getUseTPforControls(deviceID) && cState.Touch1 && pState.PS)
|
|
{
|
|
if (GetTouchActive(deviceID) && touchreleased[deviceID])
|
|
{
|
|
TouchActive[deviceID] = false;
|
|
LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOff);
|
|
AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOff);
|
|
touchreleased[deviceID] = false;
|
|
}
|
|
else if (touchreleased[deviceID])
|
|
{
|
|
TouchActive[deviceID] = true;
|
|
LogDebug(DS4WinWPF.Properties.Resources.TouchpadMovementOn);
|
|
AppLogger.LogToTray(DS4WinWPF.Properties.Resources.TouchpadMovementOn);
|
|
touchreleased[deviceID] = false;
|
|
}
|
|
}
|
|
else
|
|
touchreleased[deviceID] = true;
|
|
}
|
|
|
|
public virtual void StartTPOff(int deviceID)
|
|
{
|
|
if (deviceID < 4)
|
|
{
|
|
TouchActive[deviceID] = false;
|
|
}
|
|
}
|
|
|
|
public virtual string TouchpadSlide(int ind)
|
|
{
|
|
DS4State cState = CurrentState[ind];
|
|
string slidedir = "none";
|
|
if (DS4Controllers[ind] != null && cState.Touch2 &&
|
|
!(touchPad[ind].dragging || touchPad[ind].dragging2))
|
|
{
|
|
if (touchPad[ind].slideright && !touchslid[ind])
|
|
{
|
|
slidedir = "right";
|
|
touchslid[ind] = true;
|
|
}
|
|
else if (touchPad[ind].slideleft && !touchslid[ind])
|
|
{
|
|
slidedir = "left";
|
|
touchslid[ind] = true;
|
|
}
|
|
else if (!touchPad[ind].slideleft && !touchPad[ind].slideright)
|
|
{
|
|
slidedir = "";
|
|
touchslid[ind] = false;
|
|
}
|
|
}
|
|
|
|
return slidedir;
|
|
}
|
|
|
|
public virtual void LogDebug(String Data, bool warning = false)
|
|
{
|
|
//Console.WriteLine(System.DateTime.Now.ToString("G") + "> " + Data);
|
|
if (Debug != null)
|
|
{
|
|
DebugEventArgs args = new DebugEventArgs(Data, warning);
|
|
OnDebug(this, args);
|
|
}
|
|
}
|
|
|
|
public virtual void OnDebug(object sender, DebugEventArgs args)
|
|
{
|
|
if (Debug != null)
|
|
Debug(this, args);
|
|
}
|
|
|
|
// sets the rumble adjusted with rumble boost. General use method
|
|
public virtual void setRumble(byte heavyMotor, byte lightMotor, int deviceNum)
|
|
{
|
|
if (deviceNum < 4)
|
|
{
|
|
DS4Device device = DS4Controllers[deviceNum];
|
|
if (device != null)
|
|
SetDevRumble(device, heavyMotor, lightMotor, deviceNum);
|
|
//device.setRumble((byte)lightBoosted, (byte)heavyBoosted);
|
|
}
|
|
}
|
|
|
|
// sets the rumble adjusted with rumble boost. Method more used for
|
|
// report handling. Avoid constant checking for a device.
|
|
public void SetDevRumble(DS4Device device,
|
|
byte heavyMotor, byte lightMotor, int deviceNum)
|
|
{
|
|
byte boost = getRumbleBoost(deviceNum);
|
|
uint lightBoosted = ((uint)lightMotor * (uint)boost) / 100;
|
|
if (lightBoosted > 255)
|
|
lightBoosted = 255;
|
|
uint heavyBoosted = ((uint)heavyMotor * (uint)boost) / 100;
|
|
if (heavyBoosted > 255)
|
|
heavyBoosted = 255;
|
|
|
|
device.setRumble((byte)lightBoosted, (byte)heavyBoosted);
|
|
}
|
|
|
|
public DS4State getDS4State(int ind)
|
|
{
|
|
return CurrentState[ind];
|
|
}
|
|
|
|
public DS4State getDS4StateMapped(int ind)
|
|
{
|
|
return MappedState[ind];
|
|
}
|
|
|
|
public DS4State getDS4StateTemp(int ind)
|
|
{
|
|
return TempState[ind];
|
|
}
|
|
}
|
|
}
|