/ src / Ryujinx.HLE / UI / Input / NpadReader.cs
NpadReader.cs
  1  using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
  2  using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
  3  
  4  namespace Ryujinx.HLE.UI.Input
  5  {
  6      /// <summary>
  7      /// Class that converts Hid entries for the Npad into pressed / released events.
  8      /// </summary>
  9      class NpadReader
 10      {
 11          private readonly Switch _device;
 12          private readonly NpadCommonState[] _lastStates;
 13  
 14          public event NpadButtonHandler NpadButtonUpEvent;
 15          public event NpadButtonHandler NpadButtonDownEvent;
 16  
 17          public NpadReader(Switch device)
 18          {
 19              _device = device;
 20              _lastStates = new NpadCommonState[_device.Hid.SharedMemory.Npads.Length];
 21          }
 22  
 23          public NpadButton GetCurrentButtonsOfNpad(int npadIndex)
 24          {
 25              return _lastStates[npadIndex].Buttons;
 26          }
 27  
 28          public NpadButton GetCurrentButtonsOfAllNpads()
 29          {
 30              NpadButton buttons = 0;
 31  
 32              foreach (var state in _lastStates)
 33              {
 34                  buttons |= state.Buttons;
 35              }
 36  
 37              return buttons;
 38          }
 39  
 40          private static ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad)
 41          {
 42              switch (npad.StyleSet)
 43              {
 44                  case NpadStyleTag.FullKey:
 45                      return ref npad.FullKey;
 46                  case NpadStyleTag.Handheld:
 47                      return ref npad.Handheld;
 48                  case NpadStyleTag.JoyDual:
 49                      return ref npad.JoyDual;
 50                  case NpadStyleTag.JoyLeft:
 51                      return ref npad.JoyLeft;
 52                  case NpadStyleTag.JoyRight:
 53                      return ref npad.JoyRight;
 54                  case NpadStyleTag.Palma:
 55                      return ref npad.Palma;
 56                  default:
 57                      return ref npad.SystemExt;
 58              }
 59          }
 60  
 61          public void Update(bool supressEvents = false)
 62          {
 63              ref var npads = ref _device.Hid.SharedMemory.Npads;
 64  
 65              // Process each input individually.
 66              for (int npadIndex = 0; npadIndex < npads.Length; npadIndex++)
 67              {
 68                  UpdateNpad(npadIndex, supressEvents);
 69              }
 70          }
 71  
 72          private void UpdateNpad(int npadIndex, bool supressEvents)
 73          {
 74              const int MaxEntries = 1024;
 75  
 76              ref var npadState = ref _device.Hid.SharedMemory.Npads[npadIndex];
 77              ref var lastEntry = ref _lastStates[npadIndex];
 78  
 79              var fullKeyEntries = GetCommonStateLifo(ref npadState.InternalState).ReadEntries(MaxEntries);
 80  
 81              int firstEntryNum;
 82  
 83              // Scan the LIFO for the first entry that is newer that what's already processed.
 84              for (firstEntryNum = fullKeyEntries.Length - 1;
 85                   firstEntryNum >= 0 && fullKeyEntries[firstEntryNum].Object.SamplingNumber <= lastEntry.SamplingNumber;
 86                   firstEntryNum--)
 87              {
 88              }
 89  
 90              if (firstEntryNum == -1)
 91              {
 92                  return;
 93              }
 94  
 95              for (; firstEntryNum >= 0; firstEntryNum--)
 96              {
 97                  var entry = fullKeyEntries[firstEntryNum];
 98  
 99                  // The interval of valid entries should be contiguous.
100                  if (entry.SamplingNumber < lastEntry.SamplingNumber)
101                  {
102                      break;
103                  }
104  
105                  if (!supressEvents)
106                  {
107                      ProcessNpadButtons(npadIndex, entry.Object.Buttons);
108                  }
109  
110                  lastEntry = entry.Object;
111              }
112          }
113  
114          private void ProcessNpadButtons(int npadIndex, NpadButton buttons)
115          {
116              NpadButton lastButtons = _lastStates[npadIndex].Buttons;
117  
118              for (ulong buttonMask = 1; buttonMask != 0; buttonMask <<= 1)
119              {
120                  NpadButton currentButton = (NpadButton)buttonMask & buttons;
121                  NpadButton lastButton = (NpadButton)buttonMask & lastButtons;
122  
123                  if (lastButton != 0)
124                  {
125                      if (currentButton == 0)
126                      {
127                          NpadButtonUpEvent?.Invoke(npadIndex, lastButton);
128                      }
129                  }
130                  else
131                  {
132                      if (currentButton != 0)
133                      {
134                          NpadButtonDownEvent?.Invoke(npadIndex, currentButton);
135                      }
136                  }
137              }
138          }
139      }
140  }