Beispiel #1
0
        public Device(HidDevice hidDevice)
        {
            HidDevice      = hidDevice;
            ConnectionType = HidConnectionType(HidDevice);
            MacAddress     = HidDevice.readSerial();

            if (ConnectionType == ConnectionType.USB)
            {
                _inputReport        = new byte[64];
                _outputReport       = new byte[HidDevice.Capabilities.OutputReportByteLength];
                _outputReportBuffer = new byte[HidDevice.Capabilities.OutputReportByteLength];
            }
            else
            {
                _btInputReport      = new byte[BtInputReportLength];
                _inputReport        = new byte[_btInputReport.Length - 2];
                _outputReport       = new byte[BtOutputReportLength];
                _outputReportBuffer = new byte[BtOutputReportLength];
            }

            Touchpad = new Touchpad();
            SixAxis  = new SixAxis();
        }
Beispiel #2
0
        private void PerformDs4Input()
        {
            FirstActive = DateTime.UtcNow;

            var readTimeout = new System.Timers.Timer(); // Await 30 seconds for the initial packet, then 3 seconds thereafter.

            readTimeout.Elapsed += (sender, args) => HidDevice.CancelIO();

            var  latencies = new List <long>();
            long oldtime   = 0;

            var sw = Stopwatch.StartNew();

            while (true)
            {
                var currentError = string.Empty;

                latencies.Add(sw.ElapsedMilliseconds - oldtime);
                oldtime = sw.ElapsedMilliseconds;

                if (latencies.Count > 100)
                {
                    latencies.RemoveAt(0);
                }

                Latency = latencies.Average();

                if (Latency > 10 && !warn && sw.ElapsedMilliseconds > 4000)
                {
                    warn = true;
                }
                else if (Latency <= 10 && warn)
                {
                    warn = false;
                }

                if (readTimeout.Interval != 3000.0)
                {
                    if (readTimeout.Interval != 30000.0)
                    {
                        readTimeout.Interval = 30000.0;
                    }
                    else
                    {
                        readTimeout.Interval = 3000.0;
                    }
                }
                readTimeout.Enabled = true;
                if (ConnectionType != ConnectionType.USB)
                {
                    var res = HidDevice.ReadFile(_btInputReport);
                    readTimeout.Enabled = false;
                    if (res == HidDevice.ReadStatus.Success)
                    {
                        Array.Copy(_btInputReport, 2, _inputReport, 0, _inputReport.Length);
                    }
                    else
                    {
                        Console.WriteLine(MacAddress + " " + DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " +
                                          Marshal.GetLastWin32Error());
                        SendOutputReport(true); // Kick Windows into noticing the disconnection.
                        StopOutputUpdate();
                        IsDisconnecting = true;
                        Removal?.Invoke(this, EventArgs.Empty);
                        return;
                    }
                }
                else
                {
                    var res = HidDevice.ReadFile(_inputReport);
                    readTimeout.Enabled = false;
                    if (res != HidDevice.ReadStatus.Success)
                    {
                        Console.WriteLine(MacAddress + " " + DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " +
                                          Marshal.GetLastWin32Error());
                        StopOutputUpdate();
                        IsDisconnecting = true;
                        Removal?.Invoke(this, EventArgs.Empty);
                        return;
                    }
                }
                if (ConnectionType == ConnectionType.BT && _btInputReport[0] != 0x11)
                {
                    //Received incorrect report, skip it
                    continue;
                }
                var utcNow = DateTime.UtcNow; // timestamp with UTC in case system time zone changes
                resetHapticState();
                _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];

                _cState.Triangle  = (_inputReport[5] & (1 << 7)) != 0;
                _cState.Circle    = (_inputReport[5] & (1 << 6)) != 0;
                _cState.Cross     = (_inputReport[5] & (1 << 5)) != 0;
                _cState.Square    = (_inputReport[5] & (1 << 4)) != 0;
                _cState.DpadUp    = (_inputReport[5] & (1 << 3)) != 0;
                _cState.DpadDown  = (_inputReport[5] & (1 << 2)) != 0;
                _cState.DpadLeft  = (_inputReport[5] & (1 << 1)) != 0;
                _cState.DpadRight = (_inputReport[5] & (1 << 0)) != 0;

                //Convert dpad into individual On/Off bits instead of a clock representation
                var dpadState = (byte)(
                    ((_cState.DpadRight ? 1 : 0) << 0) |
                    ((_cState.DpadLeft ? 1 : 0) << 1) |
                    ((_cState.DpadDown ? 1 : 0) << 2) |
                    ((_cState.DpadUp ? 1 : 0) << 3));

                switch (dpadState)
                {
                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:
                    _cState.DpadUp    = false;
                    _cState.DpadDown  = false;
                    _cState.DpadLeft  = false;
                    _cState.DpadRight = false;
                    break;
                }

                _cState.R3      = (_inputReport[6] & (1 << 7)) != 0;
                _cState.L3      = (_inputReport[6] & (1 << 6)) != 0;
                _cState.Options = (_inputReport[6] & (1 << 5)) != 0;
                _cState.Share   = (_inputReport[6] & (1 << 4)) != 0;
                _cState.R1      = (_inputReport[6] & (1 << 1)) != 0;
                _cState.L1      = (_inputReport[6] & (1 << 0)) != 0;

                _cState.PS           = (_inputReport[7] & (1 << 0)) != 0;
                _cState.TouchButton  = (_inputReport[7] & (1 << 2 - 1)) != 0;
                _cState.FrameCounter = (byte)(_inputReport[7] >> 2);

                // Store Gyro and Accel values
                Array.Copy(_inputReport, 14, _accel, 0, 6);
                Array.Copy(_inputReport, 20, _gyro, 0, 6);
                SixAxis.handleSixaxis(_gyro, _accel, _cState);

                try
                {
                    Charging        = (_inputReport[30] & 0x10) != 0;
                    Battery         = (_inputReport[30] & 0x0f) * 10;
                    _cState.Battery = (byte)Battery;
                    if (_inputReport[30] != priorInputReport30)
                    {
                        priorInputReport30 = _inputReport[30];
                        Console.WriteLine(MacAddress + " " + DateTime.UtcNow.ToString("o") + "> power subsystem octet: 0x" +
                                          _inputReport[30].ToString("x02"));
                    }
                }
                catch
                {
                    currentError = "Index out of bounds: battery";
                }
                // XXX DS4State mapping needs fixup, turn touches into an array[4] of structs.  And include the touchpad details there instead.
                try
                {
                    for (int touches = _inputReport[-1 + Touchpad.TOUCHPAD_DATA_OFFSET - 1], touchOffset = 0; touches > 0; touches--, touchOffset += 9)
                    {
                        _cState.TouchPacketCounter = _inputReport[-1 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset];
                        _cState.Touch1             = _inputReport[0 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] >> 7 == 0; // >= 1 touch detected
                        _cState.Touch1Identifier   = (byte)(_inputReport[0 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f);
                        _cState.Touch2             = _inputReport[4 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] >> 7 == 0; // 2 touches detected
                        _cState.Touch2Identifier   = (byte)(_inputReport[4 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f);
                        _cState.TouchLeft          = _inputReport[1 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] +
                                                     (_inputReport[2 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) * 255 < 1920 * 2 / 5;
                        _cState.TouchRight = _inputReport[1 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] +
                                             (_inputReport[2 + Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) * 255 >= 1920 * 2 / 5;
                        // Even when idling there is still a touch packet indicating no touch 1 or 2
                        Touchpad.handleTouchpad(_inputReport, _cState, touchOffset);
                    }
                }
                catch
                {
                    currentError = "Index out of bounds: touchpad";
                }

                /* 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 (!IsIdle())
                {
                    LastActive = utcNow;
                }
                if (ConnectionType == ConnectionType.BT)
                {
                    var shouldDisconnect = false;
                    if (IdleTimeout > 0)
                    {
                        if (IsIdle())
                        {
                            var timeout = LastActive + TimeSpan.FromSeconds(IdleTimeout);
                            if (!Charging)
                            {
                                shouldDisconnect = utcNow >= timeout;
                            }
                        }
                    }
                    if (shouldDisconnect && DisconnectBT())
                    {
                        return; // all done
                    }
                }

                // XXX fix initialization ordering so the null checks all go away
                Report?.Invoke(this, EventArgs.Empty);

                SendOutputReport(false);
                if (!string.IsNullOrEmpty(error))
                {
                    error = string.Empty;
                }
                if (!string.IsNullOrEmpty(currentError))
                {
                    error = currentError;
                }
                _cState.CopyTo(_pState);
            }
        }