BsWin32Keyboard.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Input/BsKeyboard.h"
  4. #include "Input/BsInput.h"
  5. #include "Win32/BsWin32Input.h"
  6. #include "Error/BsException.h"
  7. namespace bs
  8. {
  9. /** Contains private data for the Win32 Keyboard implementation. */
  10. struct Keyboard::Pimpl
  11. {
  12. IDirectInput8* directInput;
  13. IDirectInputDevice8* keyboard;
  14. DWORD coopSettings;
  15. HWND hWnd;
  16. UINT8 keyBuffer[256];
  17. };
  18. /**
  19. * Initializes DirectInput keyboard device for a window with the specified handle. Only input from that window will be
  20. * reported.
  21. */
  22. void initializeDirectInput(Keyboard::Pimpl* m, HWND hWnd)
  23. {
  24. DIPROPDWORD dipdw;
  25. dipdw.diph.dwSize = sizeof(DIPROPDWORD);
  26. dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  27. dipdw.diph.dwObj = 0;
  28. dipdw.diph.dwHow = DIPH_DEVICE;
  29. dipdw.dwData = DI_BUFFER_SIZE_KEYBOARD;
  30. if (FAILED(m->directInput->CreateDevice(GUID_SysKeyboard, &m->keyboard, nullptr)))
  31. BS_EXCEPT(InternalErrorException, "DirectInput keyboard init: Failed to create device.");
  32. if (FAILED(m->keyboard->SetDataFormat(&c_dfDIKeyboard)))
  33. BS_EXCEPT(InternalErrorException, "DirectInput keyboard init: Failed to set format.");
  34. if (FAILED(m->keyboard->SetCooperativeLevel(hWnd, m->coopSettings)))
  35. BS_EXCEPT(InternalErrorException, "DirectInput keyboard init: Failed to set coop level.");
  36. if (FAILED(m->keyboard->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
  37. BS_EXCEPT(InternalErrorException, "DirectInput keyboard init: Failed to set property.");
  38. HRESULT hr = m->keyboard->Acquire();
  39. if (FAILED(hr) && hr != DIERR_OTHERAPPHASPRIO)
  40. BS_EXCEPT(InternalErrorException, "DirectInput keyboard init: Failed to acquire device.");
  41. m->hWnd = hWnd;
  42. }
  43. /** Releases DirectInput resources for the provided device */
  44. void releaseDirectInput(Keyboard::Pimpl* m)
  45. {
  46. if(m->keyboard)
  47. {
  48. m->keyboard->Unacquire();
  49. m->keyboard->Release();
  50. m->keyboard = nullptr;
  51. }
  52. }
  53. Keyboard::Keyboard(const String& name, Input* owner)
  54. : mName(name), mOwner(owner)
  55. {
  56. InputPrivateData* pvtData = owner->_getPrivateData();
  57. m = bs_new<Pimpl>();
  58. m->directInput = pvtData->directInput;
  59. m->coopSettings = pvtData->kbSettings;
  60. m->keyboard = nullptr;
  61. bs_zero_out(m->keyBuffer);
  62. initializeDirectInput(m, (HWND)owner->_getWindowHandle());
  63. }
  64. Keyboard::~Keyboard()
  65. {
  66. releaseDirectInput(m);
  67. bs_delete(m);
  68. }
  69. void Keyboard::capture()
  70. {
  71. DIDEVICEOBJECTDATA diBuff[DI_BUFFER_SIZE_KEYBOARD];
  72. DWORD numEntries = DI_BUFFER_SIZE_KEYBOARD;
  73. // Note: Only one keyboard per app due to this static (which is fine)
  74. static bool verifyAfterAltTab = false;
  75. HRESULT hr = m->keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diBuff, &numEntries, 0);
  76. if (hr != DI_OK)
  77. {
  78. hr = m->keyboard->Acquire();
  79. if (hr == E_ACCESSDENIED)
  80. verifyAfterAltTab = true;
  81. while (hr == DIERR_INPUTLOST)
  82. hr = m->keyboard->Acquire();
  83. return;
  84. }
  85. if (FAILED(hr))
  86. {
  87. LOGERR("Failed to read keyboard input. Internal error. ");
  88. return;
  89. }
  90. for (UINT32 i = 0; i < numEntries; ++i)
  91. {
  92. ButtonCode buttonCode = (ButtonCode)diBuff[i].dwOfs;
  93. m->keyBuffer[buttonCode] = (UINT8)(diBuff[i].dwData);
  94. if (diBuff[i].dwData & 0x80)
  95. mOwner->_notifyButtonPressed(0, buttonCode, diBuff[i].dwTimeStamp);
  96. else
  97. mOwner->_notifyButtonReleased(0, buttonCode, diBuff[i].dwTimeStamp);
  98. }
  99. // If a lost device/access denied was detected, recover
  100. if (verifyAfterAltTab)
  101. {
  102. // Store old buffer
  103. UINT8 keyBufferCopy[256];
  104. memcpy(keyBufferCopy, m->keyBuffer, 256);
  105. // Read immediate state
  106. hr = m->keyboard->GetDeviceState(sizeof(m->keyBuffer), &m->keyBuffer);
  107. if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
  108. {
  109. hr = m->keyboard->Acquire();
  110. if (hr != DIERR_OTHERAPPHASPRIO)
  111. m->keyboard->GetDeviceState(sizeof(m->keyBuffer), &m->keyBuffer);
  112. }
  113. for (UINT32 i = 0; i < 256; i++)
  114. {
  115. if (keyBufferCopy[i] != m->keyBuffer[i])
  116. {
  117. if (m->keyBuffer[i])
  118. mOwner->_notifyButtonPressed(0, (ButtonCode)i, GetTickCount64());
  119. else
  120. mOwner->_notifyButtonReleased(0, (ButtonCode)i, GetTickCount64());
  121. }
  122. }
  123. verifyAfterAltTab = false;
  124. }
  125. }
  126. void Keyboard::changeCaptureContext(UINT64 windowHandle)
  127. {
  128. HWND newhWnd = (HWND)windowHandle;
  129. if(m->hWnd != newhWnd)
  130. {
  131. releaseDirectInput(m);
  132. initializeDirectInput(m, newhWnd);
  133. }
  134. }
  135. }