MockedInput.cpp
  1  #include "pch.h"
  2  #include "MockedInput.h"
  3  
  4  using namespace KeyboardManagerInput;
  5  
  6  // Set the keyboard hook procedure to be tested
  7  void MockedInput::SetHookProc(std::function<intptr_t(LowlevelKeyboardEvent*)> hookProcedure)
  8  {
  9      hookProc = hookProcedure;
 10  }
 11  
 12  // Function to simulate keyboard input - arguments and return value based on SendInput function (https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-sendinput)
 13  void MockedInput::SendVirtualInput(const std::vector<INPUT>& inputs)
 14  {
 15      // Iterate over inputs
 16      for (const INPUT& input : inputs)
 17      {
 18          LowlevelKeyboardEvent keyEvent{};
 19  
 20          // Distinguish between key and sys key by checking if the key is either F10 (for syskeydown) or if the key message is sent while Alt is held down. SYSKEY messages are also sent if there is no window in focus, but that has not been mocked since it would require many changes. More details on key messages at https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown
 21          if (input.ki.dwFlags & KEYEVENTF_KEYUP)
 22          {
 23              if (keyboardState[VK_MENU] == true)
 24              {
 25                  keyEvent.wParam = WM_SYSKEYUP;
 26              }
 27              else
 28              {
 29                  keyEvent.wParam = WM_KEYUP;
 30              }
 31          }
 32          else
 33          {
 34              if (input.ki.wVk == VK_F10 || keyboardState[VK_MENU] == true)
 35              {
 36                  keyEvent.wParam = WM_SYSKEYDOWN;
 37              }
 38              else
 39              {
 40                  keyEvent.wParam = WM_KEYDOWN;
 41              }
 42          }
 43          KBDLLHOOKSTRUCT lParam = {};
 44  
 45          // Set only vkCode and dwExtraInfo since other values are unused
 46          lParam.vkCode = input.ki.wVk;
 47          lParam.dwExtraInfo = input.ki.dwExtraInfo;
 48          keyEvent.lParam = &lParam;
 49  
 50          // If the SendVirtualInput call condition is true, increment the count. If no condition is set then always increment the count
 51          if (sendVirtualInputCallCondition == nullptr || sendVirtualInputCallCondition(&keyEvent))
 52          {
 53              sendVirtualInputCallCount++;
 54          }
 55  
 56          // Call low level hook handler
 57          intptr_t result = MockedKeyboardHook(&keyEvent);
 58  
 59          // Set keyboard state if the hook does not suppress the input
 60          if (result == 0)
 61          {
 62              // If key up flag is set, then set keyboard state to false
 63              keyboardState[input.ki.wVk] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
 64  
 65              // Handling modifier key codes
 66              switch (input.ki.wVk)
 67              {
 68              case VK_CONTROL:
 69                  if (input.ki.dwFlags & KEYEVENTF_KEYUP)
 70                  {
 71                      keyboardState[VK_LCONTROL] = false;
 72                      keyboardState[VK_RCONTROL] = false;
 73                  }
 74                  break;
 75              case VK_LCONTROL:
 76                  keyboardState[VK_CONTROL] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
 77                  break;
 78              case VK_RCONTROL:
 79                  keyboardState[VK_CONTROL] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
 80                  break;
 81              case VK_MENU:
 82                  if (input.ki.dwFlags & KEYEVENTF_KEYUP)
 83                  {
 84                      keyboardState[VK_LMENU] = false;
 85                      keyboardState[VK_RMENU] = false;
 86                  }
 87                  break;
 88              case VK_LMENU:
 89                  keyboardState[VK_MENU] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
 90                  break;
 91              case VK_RMENU:
 92                  keyboardState[VK_MENU] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
 93                  break;
 94              case VK_SHIFT:
 95                  if (input.ki.dwFlags & KEYEVENTF_KEYUP)
 96                  {
 97                      keyboardState[VK_LSHIFT] = false;
 98                      keyboardState[VK_RSHIFT] = false;
 99                  }
100                  break;
101              case VK_LSHIFT:
102                  keyboardState[VK_SHIFT] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
103                  break;
104              case VK_RSHIFT:
105                  keyboardState[VK_SHIFT] = (input.ki.dwFlags & KEYEVENTF_KEYUP) ? false : true;
106                  break;
107              }
108          }
109      }
110  }
111  
112  // Function to simulate keyboard hook behavior
113  intptr_t MockedInput::MockedKeyboardHook(LowlevelKeyboardEvent* data)
114  {
115      // If the hookProc is set to null, then skip the hook
116      if (hookProc != nullptr)
117      {
118          return hookProc(data);
119      }
120      else
121      {
122          return 0;
123      }
124  }
125  
126  // Function to get the state of a particular key
127  bool MockedInput::GetVirtualKeyState(int key)
128  {
129      return keyboardState[key];
130  }
131  
132  // Function to reset the mocked keyboard state
133  void MockedInput::ResetKeyboardState()
134  {
135      std::fill(keyboardState.begin(), keyboardState.end(), false);
136  }
137  
138  // Function to set SendVirtualInput call count condition
139  void MockedInput::SetSendVirtualInputTestHandler(std::function<bool(LowlevelKeyboardEvent*)> condition)
140  {
141      sendVirtualInputCallCount = 0;
142      sendVirtualInputCallCondition = condition;
143  }
144  
145  // Function to get SendVirtualInput call count
146  int MockedInput::GetSendVirtualInputCallCount()
147  {
148      return sendVirtualInputCallCount;
149  }
150  
151  // Function to get the foreground process name
152  void MockedInput::SetForegroundProcess(std::wstring process)
153  {
154      currentProcess = process;
155  }
156  
157  // Function to get the foreground process name
158  void MockedInput::GetForegroundProcess(_Out_ std::wstring& foregroundProcess)
159  {
160      foregroundProcess = currentProcess;
161  }