diff --git a/DS4Windows/DS4Library/DS4Device.cs b/DS4Windows/DS4Library/DS4Device.cs index bc603a1..dcdc40f 100644 --- a/DS4Windows/DS4Library/DS4Device.cs +++ b/DS4Windows/DS4Library/DS4Device.cs @@ -706,390 +706,393 @@ namespace DS4Windows private unsafe void performDs4Input() { - firstActive = DateTime.UtcNow; - NativeMethods.HidD_SetNumInputBuffers(hDevice.safeReadHandle.DangerousGetHandle(), 2); - Queue latencyQueue = new Queue(21); // Set capacity at max + 1 to avoid any resizing - int tempLatencyCount = 0; - long oldtime = 0; - string currerror = string.Empty; - long curtime = 0; - Stopwatch sw = new Stopwatch(); - sw.Start(); - timeoutEvent = false; - ds4InactiveFrame = true; - idleInput = true; - bool syncWriteReport = conType != ConnectionType.BT; - - int maxBatteryValue = 0; - int tempBattery = 0; - uint tempStamp = 0; - double elapsedDeltaTime = 0.0; - uint tempDelta = 0; - byte tempByte = 0; - int CRC32_POS_1 = BT_INPUT_REPORT_CRC32_POS + 1, - CRC32_POS_2 = BT_INPUT_REPORT_CRC32_POS + 2, - CRC32_POS_3 = BT_INPUT_REPORT_CRC32_POS + 3; - int crcpos = BT_INPUT_REPORT_CRC32_POS; - int crcoffset = 0; - - while (!exitInputThread) + unchecked { - oldCharging = charging; - currerror = string.Empty; + firstActive = DateTime.UtcNow; + NativeMethods.HidD_SetNumInputBuffers(hDevice.safeReadHandle.DangerousGetHandle(), 2); + Queue latencyQueue = new Queue(21); // Set capacity at max + 1 to avoid any resizing + int tempLatencyCount = 0; + long oldtime = 0; + string currerror = string.Empty; + long curtime = 0; + Stopwatch sw = new Stopwatch(); + sw.Start(); + timeoutEvent = false; + ds4InactiveFrame = true; + idleInput = true; + bool syncWriteReport = conType != ConnectionType.BT; - if (tempLatencyCount >= 20) + int maxBatteryValue = 0; + int tempBattery = 0; + uint tempStamp = 0; + double elapsedDeltaTime = 0.0; + uint tempDelta = 0; + byte tempByte = 0; + int CRC32_POS_1 = BT_INPUT_REPORT_CRC32_POS + 1, + CRC32_POS_2 = BT_INPUT_REPORT_CRC32_POS + 2, + CRC32_POS_3 = BT_INPUT_REPORT_CRC32_POS + 3; + int crcpos = BT_INPUT_REPORT_CRC32_POS; + int crcoffset = 0; + + while (!exitInputThread) { - latencyQueue.Dequeue(); - tempLatencyCount--; - } + oldCharging = charging; + currerror = string.Empty; - latencyQueue.Enqueue(this.lastTimeElapsed); - tempLatencyCount++; - - Latency = latencyQueue.Average(); - - if (conType == ConnectionType.BT) - { - //HidDevice.ReadStatus res = hDevice.ReadFile(btInputReport); - //HidDevice.ReadStatus res = hDevice.ReadAsyncWithFileStream(btInputReport, READ_STREAM_TIMEOUT); - HidDevice.ReadStatus res = hDevice.ReadWithFileStream(btInputReport); - timeoutEvent = false; - if (res == HidDevice.ReadStatus.Success) + if (tempLatencyCount >= 20) { - //Array.Copy(btInputReport, 2, inputReport, 0, inputReport.Length); - fixed (byte* byteP = &btInputReport[2], imp = inputReport) + latencyQueue.Dequeue(); + tempLatencyCount--; + } + + latencyQueue.Enqueue(this.lastTimeElapsed); + tempLatencyCount++; + + Latency = latencyQueue.Average(); + + if (conType == ConnectionType.BT) + { + //HidDevice.ReadStatus res = hDevice.ReadFile(btInputReport); + //HidDevice.ReadStatus res = hDevice.ReadAsyncWithFileStream(btInputReport, READ_STREAM_TIMEOUT); + HidDevice.ReadStatus res = hDevice.ReadWithFileStream(btInputReport); + timeoutEvent = false; + if (res == HidDevice.ReadStatus.Success) { - for (int j = 0; j < BT_INPUT_REPORT_LENGTH-2;j++) + //Array.Copy(btInputReport, 2, inputReport, 0, inputReport.Length); + fixed (byte* byteP = &btInputReport[2], imp = inputReport) { - imp[j] = byteP[j]; + for (int j = 0; j < BT_INPUT_REPORT_LENGTH - 2; j++) + { + imp[j] = byteP[j]; + } + } + + //uint recvCrc32 = BitConverter.ToUInt32(btInputReport, BT_INPUT_REPORT_CRC32_POS); + uint recvCrc32 = btInputReport[BT_INPUT_REPORT_CRC32_POS] | + (uint)(btInputReport[CRC32_POS_1] << 8) | + (uint)(btInputReport[CRC32_POS_2] << 16) | + (uint)(btInputReport[CRC32_POS_3] << 24); + + uint calcCrc32 = ~Crc32Algorithm.CalculateFasterBTHash(ref HamSeed, ref btInputReport, ref crcoffset, ref crcpos); + if (recvCrc32 != calcCrc32) + { + //Log.LogToGui("Crc check failed", true); + //Console.WriteLine(MacAddress.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "" + + // "> invalid CRC32 in BT input report: 0x" + recvCrc32.ToString("X8") + " expected: 0x" + calcCrc32.ToString("X8")); + + cState.PacketCounter = pState.PacketCounter + 1; //still increase so we know there were lost packets + continue; } } - - //uint recvCrc32 = BitConverter.ToUInt32(btInputReport, BT_INPUT_REPORT_CRC32_POS); - uint recvCrc32 = btInputReport[BT_INPUT_REPORT_CRC32_POS] | - (uint)(btInputReport[CRC32_POS_1] << 8) | - (uint)(btInputReport[CRC32_POS_2] << 16) | - (uint)(btInputReport[CRC32_POS_3] << 24); - - uint calcCrc32 = ~Crc32Algorithm.CalculateFasterBTHash(ref HamSeed, ref btInputReport, ref crcoffset, ref crcpos); - if (recvCrc32 != calcCrc32) + else { - //Log.LogToGui("Crc check failed", true); - //Console.WriteLine(MacAddress.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "" + - // "> invalid CRC32 in BT input report: 0x" + recvCrc32.ToString("X8") + " expected: 0x" + calcCrc32.ToString("X8")); + if (res == HidDevice.ReadStatus.WaitTimedOut) + { + AppLogger.LogToGui(Mac.ToString() + " disconnected due to timeout", true); + } + else + { + int winError = Marshal.GetLastWin32Error(); + Console.WriteLine(Mac.ToString() + " " + DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " + winError); + //Log.LogToGui(Mac.ToString() + " disconnected due to read failure: " + winError, true); + } - cState.PacketCounter = pState.PacketCounter + 1; //still increase so we know there were lost packets - continue; + sendOutputReport(true, true); // Kick Windows into noticing the disconnection. + StopOutputUpdate(); + isDisconnecting = true; + Removal?.Invoke(this, EventArgs.Empty); + + timeoutExecuted = true; + return; } } else { - if (res == HidDevice.ReadStatus.WaitTimedOut) + //HidDevice.ReadStatus res = hDevice.ReadFile(inputReport); + //Array.Clear(inputReport, 0, inputReport.Length); + //HidDevice.ReadStatus res = hDevice.ReadAsyncWithFileStream(inputReport, READ_STREAM_TIMEOUT); + HidDevice.ReadStatus res = hDevice.ReadWithFileStream(inputReport); + if (res != HidDevice.ReadStatus.Success) { - AppLogger.LogToGui(Mac.ToString() + " disconnected due to timeout", true); + if (res == HidDevice.ReadStatus.WaitTimedOut) + { + AppLogger.LogToGui(Mac.ToString() + " disconnected due to timeout", true); + } + else + { + int winError = Marshal.GetLastWin32Error(); + Console.WriteLine(Mac.ToString() + " " + DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " + winError); + //Log.LogToGui(Mac.ToString() + " disconnected due to read failure: " + winError, true); + } + + StopOutputUpdate(); + isDisconnecting = true; + Removal?.Invoke(this, EventArgs.Empty); + + timeoutExecuted = true; + return; } - else - { - int winError = Marshal.GetLastWin32Error(); - Console.WriteLine(Mac.ToString() + " " + DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " + winError); - //Log.LogToGui(Mac.ToString() + " disconnected due to read failure: " + winError, true); - } - - sendOutputReport(true, true); // Kick Windows into noticing the disconnection. - StopOutputUpdate(); - isDisconnecting = true; - Removal?.Invoke(this, EventArgs.Empty); - - timeoutExecuted = true; - return; - } - } - else - { - //HidDevice.ReadStatus res = hDevice.ReadFile(inputReport); - //Array.Clear(inputReport, 0, inputReport.Length); - //HidDevice.ReadStatus res = hDevice.ReadAsyncWithFileStream(inputReport, READ_STREAM_TIMEOUT); - HidDevice.ReadStatus res = hDevice.ReadWithFileStream(inputReport); - if (res != HidDevice.ReadStatus.Success) - { - if (res == HidDevice.ReadStatus.WaitTimedOut) - { - AppLogger.LogToGui(Mac.ToString() + " disconnected due to timeout", true); - } - else - { - int winError = Marshal.GetLastWin32Error(); - Console.WriteLine(Mac.ToString() + " " + DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " + winError); - //Log.LogToGui(Mac.ToString() + " disconnected due to read failure: " + winError, true); - } - - StopOutputUpdate(); - isDisconnecting = true; - Removal?.Invoke(this, EventArgs.Empty); - - timeoutExecuted = true; - return; - } - } - - curTimeDouble = sw.Elapsed.TotalMilliseconds; - curtime = sw.ElapsedMilliseconds; - - lastTimeElapsed = curtime - oldtime; - lastTimeElapsedDouble = (curTimeDouble - oldTimeDouble); - - oldtime = curtime; - oldTimeDouble = curTimeDouble; - - if (conType == ConnectionType.BT && btInputReport[0] != 0x11) - { - //Received incorrect report, skip it - continue; - } - - utcNow = DateTime.UtcNow; // timestamp with UTC in case system time zone changes - resetHapticState(); - cState.PacketCounter = pState.PacketCounter + 1; - cState.ReportTimeStamp = utcNow; - cState.LX = inputReport[1]; - cState.LY = inputReport[2]; - cState.RX = inputReport[3]; - cState.RY = inputReport[4]; - cState.L2 = inputReport[8]; - cState.R2 = inputReport[9]; - - tempByte = inputReport[5]; - cState.Triangle = (tempByte & (1 << 7)) != 0; - cState.Circle = (tempByte & (1 << 6)) != 0; - cState.Cross = (tempByte & (1 << 5)) != 0; - cState.Square = (tempByte & (1 << 4)) != 0; - - // First 4 bits denote dpad state. Clock representation - // with 8 meaning centered and 0 meaning DpadUp. - byte dpad_state = (byte)(tempByte & 0x0F); - - switch (dpad_state) - { - case 0: cState.DpadUp = true; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = false; break; - case 1: cState.DpadUp = true; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = true; break; - case 2: cState.DpadUp = false; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = true; break; - case 3: cState.DpadUp = false; cState.DpadDown = true; cState.DpadLeft = false; cState.DpadRight = true; break; - case 4: cState.DpadUp = false; cState.DpadDown = true; cState.DpadLeft = false; cState.DpadRight = false; break; - case 5: cState.DpadUp = false; cState.DpadDown = true; cState.DpadLeft = true; cState.DpadRight = false; break; - case 6: cState.DpadUp = false; cState.DpadDown = false; cState.DpadLeft = true; cState.DpadRight = false; break; - case 7: cState.DpadUp = true; cState.DpadDown = false; cState.DpadLeft = true; cState.DpadRight = false; break; - case 8: - default: cState.DpadUp = false; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = false; break; - } - - tempByte = inputReport[6]; - cState.R3 = (tempByte & (1 << 7)) != 0; - cState.L3 = (tempByte & (1 << 6)) != 0; - cState.Options = (tempByte & (1 << 5)) != 0; - cState.Share = (tempByte & (1 << 4)) != 0; - cState.R2Btn = (inputReport[6] & (1 << 3)) != 0; - cState.L2Btn = (inputReport[6] & (1 << 2)) != 0; - cState.R1 = (tempByte & (1 << 1)) != 0; - cState.L1 = (tempByte & (1 << 0)) != 0; - - tempByte = inputReport[7]; - cState.PS = (tempByte & (1 << 0)) != 0; - cState.TouchButton = (tempByte & 0x02) != 0; - cState.FrameCounter = (byte)(tempByte >> 2); - - tempByte = inputReport[30]; - charging = (tempByte & 0x10) != 0; - maxBatteryValue = charging ? BATTERY_MAX_USB : BATTERY_MAX; - tempBattery = (tempByte & 0x0f) * 100 / maxBatteryValue; - battery = Math.Min((byte)tempBattery, (byte)100); - cState.Battery = (byte)battery; - //System.Diagnostics.Debug.WriteLine("CURRENT BATTERY: " + (inputReport[30] & 0x0f) + " | " + tempBattery + " | " + battery); - if (tempByte != priorInputReport30) - { - priorInputReport30 = tempByte; - //Console.WriteLine(MacAddress.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "> power subsystem octet: 0x" + inputReport[30].ToString("x02")); - } - - tempStamp = (uint)((ushort)(inputReport[11] << 8) | inputReport[10]); - if (timeStampInit == false) - { - timeStampInit = true; - deltaTimeCurrent = tempStamp * 16u / 3u; - } - else if (timeStampPrevious > tempStamp) - { - tempDelta = ushort.MaxValue - timeStampPrevious + tempStamp + 1u; - deltaTimeCurrent = tempDelta * 16u / 3u; - } - else - { - tempDelta = tempStamp - timeStampPrevious; - deltaTimeCurrent = tempDelta * 16u / 3u; - } - - timeStampPrevious = tempStamp; - elapsedDeltaTime = 0.000001 * deltaTimeCurrent; // Convert from microseconds to seconds - cState.elapsedTime = elapsedDeltaTime; - cState.totalMicroSec = pState.totalMicroSec + deltaTimeCurrent; - - //Simpler touch storing - cState.TrackPadTouch0.Id = (byte)(inputReport[35] & 0x7f); - cState.TrackPadTouch0.IsActive = (inputReport[35] & 0x80) == 0; - cState.TrackPadTouch0.X = (short)(((ushort)(inputReport[37] & 0x0f) << 8) | (ushort)(inputReport[36])); - cState.TrackPadTouch0.Y = (short)(((ushort)(inputReport[38]) << 4) | ((ushort)(inputReport[37] & 0xf0) >> 4)); - - cState.TrackPadTouch1.Id = (byte)(inputReport[39] & 0x7f); - cState.TrackPadTouch1.IsActive = (inputReport[39] & 0x80) == 0; - cState.TrackPadTouch1.X = (short)(((ushort)(inputReport[41] & 0x0f) << 8) | (ushort)(inputReport[40])); - cState.TrackPadTouch1.Y = (short)(((ushort)(inputReport[42]) << 4) | ((ushort)(inputReport[41] & 0xf0) >> 4)); - - // XXX DS4State mapping needs fixup, turn touches into an array[4] of structs. And include the touchpad details there instead. - try - { - // Only care if one touch packet is detected. Other touch packets - // don't seem to contain relevant data. ds4drv does not use them either. - for (int touches = Math.Max((int)(inputReport[-1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET - 1]), 1), touchOffset = 0; touches > 0; touches--, touchOffset += 9) - //for (int touches = inputReport[-1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET - 1], touchOffset = 0; touches > 0; touches--, touchOffset += 9) - { - cState.TouchPacketCounter = inputReport[-1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset]; - cState.Touch1 = (inputReport[0 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] >> 7) != 0 ? false : true; // finger 1 detected - cState.Touch1Identifier = (byte)(inputReport[0 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f); - cState.Touch2 = (inputReport[4 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] >> 7) != 0 ? false : true; // finger 2 detected - cState.Touch2Identifier = (byte)(inputReport[4 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f); - cState.Touch1Finger = cState.Touch1 || cState.Touch2; // >= 1 touch detected - cState.Touch2Fingers = cState.Touch1 && cState.Touch2; // 2 touches detected - int touchX = (((inputReport[2 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) << 8) | inputReport[1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset]); - cState.TouchLeft = touchX >= 1920 * 2 / 5 ? false : true; - cState.TouchRight = touchX < 1920 * 2 / 5 ? false : true; - // Even when idling there is still a touch packet indicating no touch 1 or 2 - touchpad.handleTouchpad(inputReport, cState, touchOffset); - } - } - catch { currerror = "Index out of bounds: touchpad"; } - - // Store Gyro and Accel values - //Array.Copy(inputReport, 13, gyro, 0, 6); - //Array.Copy(inputReport, 19, accel, 0, 6); - fixed (byte* pbInput = &inputReport[13], pbGyro = gyro, pbAccel = accel) - { - for (int i = 0; i < 6; i++) - { - pbGyro[i] = pbInput[i]; } - for (int i = 6; i < 12; i++) + curTimeDouble = sw.Elapsed.TotalMilliseconds; + curtime = sw.ElapsedMilliseconds; + + lastTimeElapsed = curtime - oldtime; + lastTimeElapsedDouble = (curTimeDouble - oldTimeDouble); + + oldtime = curtime; + oldTimeDouble = curTimeDouble; + + if (conType == ConnectionType.BT && btInputReport[0] != 0x11) { - pbAccel[i-6] = pbInput[i]; + //Received incorrect report, skip it + continue; } - sixAxis.handleSixaxis(pbGyro, pbAccel, cState, elapsedDeltaTime); - } + utcNow = DateTime.UtcNow; // timestamp with UTC in case system time zone changes + resetHapticState(); + cState.PacketCounter = pState.PacketCounter + 1; + cState.ReportTimeStamp = utcNow; + cState.LX = inputReport[1]; + cState.LY = inputReport[2]; + cState.RX = inputReport[3]; + cState.RY = inputReport[4]; + cState.L2 = inputReport[8]; + cState.R2 = inputReport[9]; - /* Debug output of incoming HID data: - if (cState.L2 == 0xff && cState.R2 == 0xff) - { - Console.Write(MacAddress.ToString() + " " + System.DateTime.UtcNow.ToString("o") + ">"); - for (int i = 0; i < inputReport.Length; i++) - Console.Write(" " + inputReport[i].ToString("x2")); - Console.WriteLine(); - } - */ + tempByte = inputReport[5]; + cState.Triangle = (tempByte & (1 << 7)) != 0; + cState.Circle = (tempByte & (1 << 6)) != 0; + cState.Cross = (tempByte & (1 << 5)) != 0; + cState.Square = (tempByte & (1 << 4)) != 0; - if (conType == ConnectionType.SONYWA) - { - bool controllerSynced = inputReport[31] == 0; - if (controllerSynced != synced) + // First 4 bits denote dpad state. Clock representation + // with 8 meaning centered and 0 meaning DpadUp. + byte dpad_state = (byte)(tempByte & 0x0F); + + switch (dpad_state) { - runCalib = synced = controllerSynced; - SyncChange?.Invoke(this, EventArgs.Empty); - sendOutputReport(true, true); + case 0: cState.DpadUp = true; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = false; break; + case 1: cState.DpadUp = true; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = true; break; + case 2: cState.DpadUp = false; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = true; break; + case 3: cState.DpadUp = false; cState.DpadDown = true; cState.DpadLeft = false; cState.DpadRight = true; break; + case 4: cState.DpadUp = false; cState.DpadDown = true; cState.DpadLeft = false; cState.DpadRight = false; break; + case 5: cState.DpadUp = false; cState.DpadDown = true; cState.DpadLeft = true; cState.DpadRight = false; break; + case 6: cState.DpadUp = false; cState.DpadDown = false; cState.DpadLeft = true; cState.DpadRight = false; break; + case 7: cState.DpadUp = true; cState.DpadDown = false; cState.DpadLeft = true; cState.DpadRight = false; break; + case 8: + default: cState.DpadUp = false; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = false; break; } - } - ds4InactiveFrame = cState.FrameCounter == pState.FrameCounter; - if (!ds4InactiveFrame) - { - isRemoved = false; - } + tempByte = inputReport[6]; + cState.R3 = (tempByte & (1 << 7)) != 0; + cState.L3 = (tempByte & (1 << 6)) != 0; + cState.Options = (tempByte & (1 << 5)) != 0; + cState.Share = (tempByte & (1 << 4)) != 0; + cState.R2Btn = (inputReport[6] & (1 << 3)) != 0; + cState.L2Btn = (inputReport[6] & (1 << 2)) != 0; + cState.R1 = (tempByte & (1 << 1)) != 0; + cState.L1 = (tempByte & (1 << 0)) != 0; - if (conType == ConnectionType.USB) - { - if (idleTimeout == 0) + tempByte = inputReport[7]; + cState.PS = (tempByte & (1 << 0)) != 0; + cState.TouchButton = (tempByte & 0x02) != 0; + cState.FrameCounter = (byte)(tempByte >> 2); + + tempByte = inputReport[30]; + charging = (tempByte & 0x10) != 0; + maxBatteryValue = charging ? BATTERY_MAX_USB : BATTERY_MAX; + tempBattery = (tempByte & 0x0f) * 100 / maxBatteryValue; + battery = Math.Min((byte)tempBattery, (byte)100); + cState.Battery = (byte)battery; + //System.Diagnostics.Debug.WriteLine("CURRENT BATTERY: " + (inputReport[30] & 0x0f) + " | " + tempBattery + " | " + battery); + if (tempByte != priorInputReport30) { - lastActive = utcNow; + priorInputReport30 = tempByte; + //Console.WriteLine(MacAddress.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "> power subsystem octet: 0x" + inputReport[30].ToString("x02")); + } + + tempStamp = (uint)((ushort)(inputReport[11] << 8) | inputReport[10]); + if (timeStampInit == false) + { + timeStampInit = true; + deltaTimeCurrent = tempStamp * 16u / 3u; + } + else if (timeStampPrevious > tempStamp) + { + tempDelta = ushort.MaxValue - timeStampPrevious + tempStamp + 1u; + deltaTimeCurrent = tempDelta * 16u / 3u; } else { - idleInput = isDS4Idle(); - if (!idleInput) + tempDelta = tempStamp - timeStampPrevious; + deltaTimeCurrent = tempDelta * 16u / 3u; + } + + timeStampPrevious = tempStamp; + elapsedDeltaTime = 0.000001 * deltaTimeCurrent; // Convert from microseconds to seconds + cState.elapsedTime = elapsedDeltaTime; + cState.totalMicroSec = pState.totalMicroSec + deltaTimeCurrent; + + //Simpler touch storing + cState.TrackPadTouch0.Id = (byte)(inputReport[35] & 0x7f); + cState.TrackPadTouch0.IsActive = (inputReport[35] & 0x80) == 0; + cState.TrackPadTouch0.X = (short)(((ushort)(inputReport[37] & 0x0f) << 8) | (ushort)(inputReport[36])); + cState.TrackPadTouch0.Y = (short)(((ushort)(inputReport[38]) << 4) | ((ushort)(inputReport[37] & 0xf0) >> 4)); + + cState.TrackPadTouch1.Id = (byte)(inputReport[39] & 0x7f); + cState.TrackPadTouch1.IsActive = (inputReport[39] & 0x80) == 0; + cState.TrackPadTouch1.X = (short)(((ushort)(inputReport[41] & 0x0f) << 8) | (ushort)(inputReport[40])); + cState.TrackPadTouch1.Y = (short)(((ushort)(inputReport[42]) << 4) | ((ushort)(inputReport[41] & 0xf0) >> 4)); + + // XXX DS4State mapping needs fixup, turn touches into an array[4] of structs. And include the touchpad details there instead. + try + { + // Only care if one touch packet is detected. Other touch packets + // don't seem to contain relevant data. ds4drv does not use them either. + for (int touches = Math.Max((int)(inputReport[-1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET - 1]), 1), touchOffset = 0; touches > 0; touches--, touchOffset += 9) + //for (int touches = inputReport[-1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET - 1], touchOffset = 0; touches > 0; touches--, touchOffset += 9) + { + cState.TouchPacketCounter = inputReport[-1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset]; + cState.Touch1 = (inputReport[0 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] >> 7) != 0 ? false : true; // finger 1 detected + cState.Touch1Identifier = (byte)(inputReport[0 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f); + cState.Touch2 = (inputReport[4 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] >> 7) != 0 ? false : true; // finger 2 detected + cState.Touch2Identifier = (byte)(inputReport[4 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f); + cState.Touch1Finger = cState.Touch1 || cState.Touch2; // >= 1 touch detected + cState.Touch2Fingers = cState.Touch1 && cState.Touch2; // 2 touches detected + int touchX = (((inputReport[2 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) << 8) | inputReport[1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset]); + cState.TouchLeft = touchX >= 1920 * 2 / 5 ? false : true; + cState.TouchRight = touchX < 1920 * 2 / 5 ? false : true; + // Even when idling there is still a touch packet indicating no touch 1 or 2 + touchpad.handleTouchpad(inputReport, cState, touchOffset); + } + } + catch { currerror = "Index out of bounds: touchpad"; } + + // Store Gyro and Accel values + //Array.Copy(inputReport, 13, gyro, 0, 6); + //Array.Copy(inputReport, 19, accel, 0, 6); + fixed (byte* pbInput = &inputReport[13], pbGyro = gyro, pbAccel = accel) + { + for (int i = 0; i < 6; i++) + { + pbGyro[i] = pbInput[i]; + } + + for (int i = 6; i < 12; i++) + { + pbAccel[i - 6] = pbInput[i]; + } + + sixAxis.handleSixaxis(pbGyro, pbAccel, cState, elapsedDeltaTime); + } + + /* Debug output of incoming HID data: + if (cState.L2 == 0xff && cState.R2 == 0xff) + { + Console.Write(MacAddress.ToString() + " " + System.DateTime.UtcNow.ToString("o") + ">"); + for (int i = 0; i < inputReport.Length; i++) + Console.Write(" " + inputReport[i].ToString("x2")); + Console.WriteLine(); + } + */ + + if (conType == ConnectionType.SONYWA) + { + bool controllerSynced = inputReport[31] == 0; + if (controllerSynced != synced) + { + runCalib = synced = controllerSynced; + SyncChange?.Invoke(this, EventArgs.Empty); + sendOutputReport(true, true); + } + } + + ds4InactiveFrame = cState.FrameCounter == pState.FrameCounter; + if (!ds4InactiveFrame) + { + isRemoved = false; + } + + if (conType == ConnectionType.USB) + { + if (idleTimeout == 0) { lastActive = utcNow; } - } - } - else - { - bool shouldDisconnect = false; - if (!isRemoved && idleTimeout > 0) - { - idleInput = isDS4Idle(); - if (idleInput) + else { - DateTime timeout = lastActive + TimeSpan.FromSeconds(idleTimeout); - if (!charging) - shouldDisconnect = utcNow >= timeout; + idleInput = isDS4Idle(); + if (!idleInput) + { + lastActive = utcNow; + } + } + } + else + { + bool shouldDisconnect = false; + if (!isRemoved && idleTimeout > 0) + { + idleInput = isDS4Idle(); + if (idleInput) + { + DateTime timeout = lastActive + TimeSpan.FromSeconds(idleTimeout); + if (!charging) + shouldDisconnect = utcNow >= timeout; + } + else + { + lastActive = utcNow; + } } else { lastActive = utcNow; } - } - else - { - lastActive = utcNow; - } - if (shouldDisconnect) - { - AppLogger.LogToGui(Mac.ToString() + " disconnecting due to idle disconnect", false); - - if (conType == ConnectionType.BT) + if (shouldDisconnect) { - if (DisconnectBT(true)) + AppLogger.LogToGui(Mac.ToString() + " disconnecting due to idle disconnect", false); + + if (conType == ConnectionType.BT) { - timeoutExecuted = true; - return; // all done + if (DisconnectBT(true)) + { + timeoutExecuted = true; + return; // all done + } + } + else if (conType == ConnectionType.SONYWA) + { + DisconnectDongle(); } } - else if (conType == ConnectionType.SONYWA) + } + + if (conType == ConnectionType.BT && oldCharging != charging) + { + if (Global.getQuickCharge() && charging) { - DisconnectDongle(); + DisconnectBT(true); + timeoutExecuted = true; + return; } } + + if (Report != null) + Report(this, EventArgs.Empty); + + sendOutputReport(syncWriteReport); + + if (!string.IsNullOrEmpty(currerror)) + error = currerror; + else if (!string.IsNullOrEmpty(error)) + error = string.Empty; + + cState.CopyTo(pState); } - if (conType == ConnectionType.BT && oldCharging != charging) - { - if (Global.getQuickCharge() && charging) - { - DisconnectBT(true); - timeoutExecuted = true; - return; - } - } - - if (Report != null) - Report(this, EventArgs.Empty); - - sendOutputReport(syncWriteReport); - - if (!string.IsNullOrEmpty(currerror)) - error = currerror; - else if (!string.IsNullOrEmpty(error)) - error = string.Empty; - - cState.CopyTo(pState); - lock (eventQueueLock) { Action tempAct = null; diff --git a/DS4Windows/HidLibrary/NativeMethods.cs b/DS4Windows/HidLibrary/NativeMethods.cs index 647cdd1..8ee0adf 100644 --- a/DS4Windows/HidLibrary/NativeMethods.cs +++ b/DS4Windows/HidLibrary/NativeMethods.cs @@ -1,8 +1,10 @@ using System; using System.Runtime.InteropServices; +using System.Security; using Microsoft.Win32.SafeHandles; namespace DS4Windows { + [SuppressUnmanagedCodeSecurity] internal static class NativeMethods { [StructLayout(LayoutKind.Sequential)]