Esempio n. 1
0
        public bool DisconnectDongle(bool remove = false)
        {
            bool result = false;

            byte[] disconnectReport = new byte[65];
            disconnectReport[0] = 0xe2;
            disconnectReport[1] = 0x02;
            Array.Clear(disconnectReport, 2, 63);

            lock (outputReport)
            {
                result = hDevice.WriteFeatureReport(disconnectReport);
            }

            if (result && remove)
            {
                isDisconnecting = true;
                StopOutputUpdate();

                uiContext.Send(new SendOrPostCallback(delegate(object state4)
                {
                    Removal?.Invoke(this, EventArgs.Empty);
                }), null);

                //System.Threading.Tasks.Task.Factory.StartNew(() => { Removal?.Invoke(this, EventArgs.Empty); });
                //Removal?.Invoke(this, EventArgs.Empty);
            }
            else if (result && !remove)
            {
                isRemoved = true;
            }

            return(result);
        }
Esempio n. 2
0
 public override void BeingRemoved()
 {
     if (Removal != null)
     {
         Removal.Invoke(this, new EventArgs());
     }
     base.BeingRemoved();
 }
Esempio n. 3
0
        private void performDs4Input()
        {
            firstActive = DateTime.UtcNow;
            NativeMethods.HidD_SetNumInputBuffers(hDevice.safeReadHandle.DangerousGetHandle(), 2);
            //List<long> latencyList = new List<long>(51); // Set capacity at max + 1 to avoid any list resizing
            Queue <long> latencyQueue     = new Queue <long>(51); // 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;

            int maxBatteryValue = 0;
            int tempBattery     = 0;

            while (!exitInputThread)
            {
                oldCharging          = charging;
                currerror            = string.Empty;
                curtime              = sw.ElapsedMilliseconds;
                this.lastTimeElapsed = curtime - oldtime;
                //latencyList.Add(this.lastTimeElapsed);
                latencyQueue.Enqueue(this.lastTimeElapsed);
                tempLatencyCount++;
                oldtime = curtime;

                if (tempLatencyCount > 50)
                {
                    //latencyList.RemoveAt(0);
                    latencyQueue.Dequeue();
                    tempLatencyCount--;
                }

                //Latency = latencyList.Average();
                //latencyList.Average();
                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;
                    //HidDevice.ReadStatus res = hDevice.ReadFileOverlapped(btInputReport, READ_STREAM_TIMEOUT);
                    if (res == HidDevice.ReadStatus.Success)
                    {
                        Array.Copy(btInputReport, 2, inputReport, 0, inputReport.Length);
                    }
                    else
                    {
                        if (res == HidDevice.ReadStatus.WaitTimedOut)
                        {
                            Log.LogToGui(Mac.ToString() + " disconnected due to timeout", true);
                        }
                        else
                        {
                            int winError = Marshal.GetLastWin32Error();
                            Console.WriteLine(Mac.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " + winError);
                            //Log.LogToGui(Mac.ToString() + " disconnected due to read failure: " + winError, true);
                        }

                        sendOutputReport(true); // Kick Windows into noticing the disconnection.
                        StopOutputUpdate();
                        isDisconnecting = true;
                        uiContext.Send(new SendOrPostCallback(delegate(object state4)
                        {
                            Removal?.Invoke(this, EventArgs.Empty);
                        }), null);

                        //System.Threading.Tasks.Task.Factory.StartNew(() => { Removal?.Invoke(this, EventArgs.Empty); });
                        //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);
                    //HidDevice.ReadStatus res = hDevice.ReadFileOverlapped(inputReport, READ_STREAM_TIMEOUT);
                    if (res != HidDevice.ReadStatus.Success)
                    {
                        if (res == HidDevice.ReadStatus.WaitTimedOut)
                        {
                            Log.LogToGui(Mac.ToString() + " disconnected due to timeout", true);
                        }
                        else
                        {
                            int winError = Marshal.GetLastWin32Error();
                            Console.WriteLine(Mac.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "> disconnect due to read failure: " + winError);
                            //Log.LogToGui(Mac.ToString() + " disconnected due to read failure: " + winError, true);
                        }

                        StopOutputUpdate();
                        isDisconnecting = true;
                        uiContext.Send(new SendOrPostCallback(delegate(object state4)
                        {
                            Removal?.Invoke(this, EventArgs.Empty);
                        }), null);

                        //System.Threading.Tasks.Task.Factory.StartNew(() => { Removal?.Invoke(this, EventArgs.Empty); });
                        //Removal?.Invoke(this, EventArgs.Empty);

                        timeoutExecuted = true;
                        return;
                    }
                    else
                    {
                        //Array.Copy(inputReport2, 0, inputReport, 0, inputReport.Length);
                    }
                }

                if (conType == ConnectionType.BT && btInputReport[0] != 0x11)
                {
                    //Received incorrect report, skip it
                    continue;
                }

                DateTime 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;

                // First 4 bits denote dpad state. Clock representation
                // with 8 meaning centered and 0 meaning DpadUp.
                byte dpad_state = (byte)(inputReport[5] & 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;
                }

                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;
                    maxBatteryValue = charging ? BATTERY_MAX_USB : BATTERY_MAX;
                    tempBattery     = (inputReport[30] & 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 (inputReport[30] != priorInputReport30)
                    {
                        priorInputReport30 = inputReport[30];
                        //Console.WriteLine(MacAddress.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "> power subsystem octet: 0x" + inputReport[30].ToString("x02"));
                    }
                }
                catch { currerror = "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
                {
                    // 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; // >= 1 touch 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; // 2 touches detected
                        cState.Touch2Identifier   = (byte)(inputReport[4 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f);
                        cState.TouchLeft          = (inputReport[1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] + ((inputReport[2 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) * 255) >= 1920 * 2 / 5) ? false : true;
                        cState.TouchRight         = (inputReport[1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] + ((inputReport[2 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) * 255) < 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"; }

                /* 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)
                    {
                        synced = controllerSynced;
                        SyncChange?.Invoke(this, EventArgs.Empty);
                    }
                }

                bool ds4Idle = cState.FrameCounter == pState.FrameCounter;
                if (!ds4Idle)
                {
                    isRemoved = false;
                }

                if (conType == ConnectionType.USB)
                {
                    lastActive = utcNow;
                }
                else
                {
                    bool shouldDisconnect = false;
                    int  idleTime         = idleTimeout;
                    if (!isRemoved && idleTime > 0)
                    {
                        bool idleInput = isDS4Idle();
                        if (idleInput)
                        {
                            DateTime timeout = lastActive + TimeSpan.FromSeconds(idleTime);
                            if (!charging)
                            {
                                shouldDisconnect = utcNow >= timeout;
                            }
                        }
                        else
                        {
                            lastActive = utcNow;
                        }
                    }
                    else
                    {
                        lastActive = utcNow;
                    }

                    if (shouldDisconnect)
                    {
                        Log.LogToGui(Mac.ToString() + " disconnecting due to idle disconnect", false);

                        if (conType == ConnectionType.BT)
                        {
                            if (DisconnectBT(true))
                            {
                                timeoutExecuted = true;
                                return; // all done
                            }
                        }
                        else if (conType == ConnectionType.SONYWA)
                        {
                            DisconnectDongle();
                        }
                    }
                }

                if (oldCharging != charging && conType == ConnectionType.BT)
                {
                    if (Global.getQuickCharge() && charging)
                    {
                        DisconnectBT(true);
                        timeoutExecuted = true;
                        return;
                    }
                }

                if (Report != null)
                {
                    Report(this, EventArgs.Empty);
                }

                bool syncWriteReport = true;
                if (conType == ConnectionType.BT)
                {
                    syncWriteReport = false;
                }
                sendOutputReport(syncWriteReport);

                if (!string.IsNullOrEmpty(currerror))
                {
                    error = currerror;
                }
                else if (!string.IsNullOrEmpty(error))
                {
                    error = string.Empty;
                }

                cState.CopyTo(pState);

                lock (eventQueueLock)
                {
                    Action tempAct = null;
                    for (int actInd = 0, actLen = eventQueue.Count; actInd < actLen; actInd++)
                    //foreach (Action tempAct in eventQueue)
                    {
                        tempAct = eventQueue.Dequeue();
                        tempAct.Invoke();
                    }

                    //eventQueue.Clear();
                }
            }

            timeoutExecuted = true;
        }
Esempio n. 4
0
 public void runRemoval()
 {
     Removal?.Invoke(this, EventArgs.Empty);
 }
Esempio n. 5
0
        public bool DisconnectBT(bool callRemoval = false)
        {
            if (Mac != null)
            {
                Console.WriteLine("Trying to disconnect BT device " + Mac);
                IntPtr btHandle = IntPtr.Zero;
                int    IOCTL_BTH_DISCONNECT_DEVICE = 0x41000c;

                byte[]   btAddr = new byte[8];
                string[] sbytes = Mac.Split(':');
                for (int i = 0; i < 6; i++)
                {
                    // parse hex byte in reverse order
                    btAddr[5 - i] = Convert.ToByte(sbytes[i], 16);
                }

                long lbtAddr = BitConverter.ToInt64(btAddr, 0);

                bool success = false;
                // Wait for output report to be written
                lock (outputReport)
                {
                    NativeMethods.BLUETOOTH_FIND_RADIO_PARAMS p = new NativeMethods.BLUETOOTH_FIND_RADIO_PARAMS();
                    p.dwSize = Marshal.SizeOf(typeof(NativeMethods.BLUETOOTH_FIND_RADIO_PARAMS));
                    IntPtr searchHandle  = NativeMethods.BluetoothFindFirstRadio(ref p, ref btHandle);
                    int    bytesReturned = 0;

                    while (!success && btHandle != IntPtr.Zero)
                    {
                        success = NativeMethods.DeviceIoControl(btHandle, IOCTL_BTH_DISCONNECT_DEVICE, ref lbtAddr, 8, IntPtr.Zero, 0, ref bytesReturned, IntPtr.Zero);
                        NativeMethods.CloseHandle(btHandle);
                        if (!success)
                        {
                            if (!NativeMethods.BluetoothFindNextRadio(searchHandle, ref btHandle))
                            {
                                btHandle = IntPtr.Zero;
                            }
                        }
                    }

                    NativeMethods.BluetoothFindRadioClose(searchHandle);
                    Console.WriteLine("Disconnect successful: " + success);
                }

                success = true; // XXX return value indicates failure, but it still works?
                if (success)
                {
                    IsDisconnecting = true;
                    StopOutputUpdate();

                    if (callRemoval)
                    {
                        uiContext.Send(new SendOrPostCallback(delegate(object state)
                        {
                            Removal?.Invoke(this, EventArgs.Empty);
                        }), null);

                        //System.Threading.Tasks.Task.Factory.StartNew(() => { Removal?.Invoke(this, EventArgs.Empty); });
                    }
                }

                return(success);
            }

            return(false);
        }
Esempio n. 6
0
        private void performDs4Input()
        {
            firstActive = DateTime.UtcNow;
            System.Timers.Timer readTimeout = new System.Timers.Timer(); // Await 30 seconds for the initial packet, then 3 seconds thereafter.
            readTimeout.Elapsed += delegate { HidDevice.CancelIO(); };
            List <long> Latency = new List <long>();
            long        oldtime = 0;
            Stopwatch   sw      = new Stopwatch();

            sw.Start();
            while (true)
            {
                string currerror = string.Empty;
                Latency.Add(sw.ElapsedMilliseconds - oldtime);
                oldtime = sw.ElapsedMilliseconds;

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

                this.Latency = Latency.Average();

                if (this.Latency > 10 && !warn && sw.ElapsedMilliseconds > 4000)
                {
                    warn = true;
                    //System.Diagnostics.Trace.WriteLine(System.DateTime.UtcNow.ToString("o") + "> " + "Controller " + /*this.DeviceNum*/ + 1 + " (" + this.MacAddress + ") is experiencing latency issues. Currently at " + Math.Round(this.Latency, 2).ToString() + "ms of recomended maximum 10ms");
                }
                else if (this.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 (conType != ConnectionType.USB)
                {
                    HidDevice.ReadStatus res = hDevice.ReadFile(btInputReport);
                    readTimeout.Enabled = false;
                    if (res == HidDevice.ReadStatus.Success)
                    {
                        Array.Copy(btInputReport, 2, inputReport, 0, inputReport.Length);
                    }
                    else
                    {
                        Console.WriteLine(MacAddress.ToString() + " " + System.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
                {
                    HidDevice.ReadStatus res = hDevice.ReadFile(inputReport);
                    readTimeout.Enabled = false;
                    if (res != HidDevice.ReadStatus.Success)
                    {
                        Console.WriteLine(MacAddress.ToString() + " " + System.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;
                }
                DateTime utcNow = System.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  = ((byte)inputReport[5] & (1 << 7)) != 0;
                cState.Circle    = ((byte)inputReport[5] & (1 << 6)) != 0;
                cState.Cross     = ((byte)inputReport[5] & (1 << 5)) != 0;
                cState.Square    = ((byte)inputReport[5] & (1 << 4)) != 0;
                cState.DpadUp    = ((byte)inputReport[5] & (1 << 3)) != 0;
                cState.DpadDown  = ((byte)inputReport[5] & (1 << 2)) != 0;
                cState.DpadLeft  = ((byte)inputReport[5] & (1 << 1)) != 0;
                cState.DpadRight = ((byte)inputReport[5] & (1 << 0)) != 0;

                //Convert dpad into individual On/Off bits instead of a clock representation
                byte dpad_state = 0;

                dpad_state = (byte)(
                    ((cState.DpadRight ? 1 : 0) << 0) |
                    ((cState.DpadLeft ? 1 : 0) << 1) |
                    ((cState.DpadDown ? 1 : 0) << 2) |
                    ((cState.DpadUp ? 1 : 0) << 3));

                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: cState.DpadUp = false; cState.DpadDown = false; cState.DpadLeft = false; cState.DpadRight = false; break;
                }

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

                cState.PS           = ((byte)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.ToString() + " " + System.DateTime.UtcNow.ToString("o") + "> power subsystem octet: 0x" + inputReport[30].ToString("x02"));
                    }
                }
                catch { currerror = "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 + 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; // >= 1 touch 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; // 2 touches detected
                        cState.Touch2Identifier   = (byte)(inputReport[4 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0x7f);
                        cState.TouchLeft          = (inputReport[1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] + ((inputReport[2 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) * 255) >= 1920 * 2 / 5) ? false : true;
                        cState.TouchRight         = (inputReport[1 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] + ((inputReport[2 + DS4Touchpad.TOUCHPAD_DATA_OFFSET + touchOffset] & 0xF) * 255) < 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"; }

                /* 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 (!isDS4Idle())
                {
                    lastActive = utcNow;
                }
                if (conType == ConnectionType.BT)
                {
                    bool shouldDisconnect = false;
                    if (IdleTimeout > 0)
                    {
                        if (isDS4Idle())
                        {
                            DateTime 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
                if (Report != null)
                {
                    Report(this, EventArgs.Empty);
                }
                sendOutputReport(false);
                if (!string.IsNullOrEmpty(error))
                {
                    error = string.Empty;
                }
                if (!string.IsNullOrEmpty(currerror))
                {
                    error = currerror;
                }
                cState.CopyTo(pState);

                Thread.Sleep(WHILE_LOOP_TIMEOUT);
            }
        }
Esempio n. 7
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);
            }
        }