BsWin32Keyboard.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. if (m->keyboard == nullptr)
  72. return;
  73. DIDEVICEOBJECTDATA diBuff[DI_BUFFER_SIZE_KEYBOARD];
  74. DWORD numEntries = DI_BUFFER_SIZE_KEYBOARD;
  75. // Note: Only one keyboard per app due to this static (which is fine)
  76. static bool verifyAfterAltTab = false;
  77. HRESULT hr = m->keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diBuff, &numEntries, 0);
  78. if (hr != DI_OK)
  79. {
  80. hr = m->keyboard->Acquire();
  81. if (hr == E_ACCESSDENIED)
  82. verifyAfterAltTab = true;
  83. while (hr == DIERR_INPUTLOST)
  84. hr = m->keyboard->Acquire();
  85. return;
  86. }
  87. if (FAILED(hr))
  88. {
  89. LOGERR("Failed to read keyboard input. Internal error. ");
  90. return;
  91. }
  92. for (UINT32 i = 0; i < numEntries; ++i)
  93. {
  94. ButtonCode buttonCode = (ButtonCode)diBuff[i].dwOfs;
  95. m->keyBuffer[buttonCode] = (UINT8)(diBuff[i].dwData);
  96. if (diBuff[i].dwData & 0x80)
  97. mOwner->_notifyButtonPressed(0, buttonCode, diBuff[i].dwTimeStamp);
  98. else
  99. mOwner->_notifyButtonReleased(0, buttonCode, diBuff[i].dwTimeStamp);
  100. }
  101. // If a lost device/access denied was detected, recover
  102. if (verifyAfterAltTab)
  103. {
  104. // Store old buffer
  105. UINT8 keyBufferCopy[256];
  106. memcpy(keyBufferCopy, m->keyBuffer, 256);
  107. // Read immediate state
  108. hr = m->keyboard->GetDeviceState(sizeof(m->keyBuffer), &m->keyBuffer);
  109. if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED)
  110. {
  111. hr = m->keyboard->Acquire();
  112. if (hr != DIERR_OTHERAPPHASPRIO)
  113. m->keyboard->GetDeviceState(sizeof(m->keyBuffer), &m->keyBuffer);
  114. }
  115. for (UINT32 i = 0; i < 256; i++)
  116. {
  117. if (keyBufferCopy[i] != m->keyBuffer[i])
  118. {
  119. if (m->keyBuffer[i])
  120. mOwner->_notifyButtonPressed(0, (ButtonCode)i, GetTickCount64());
  121. else
  122. mOwner->_notifyButtonReleased(0, (ButtonCode)i, GetTickCount64());
  123. }
  124. }
  125. verifyAfterAltTab = false;
  126. }
  127. }
  128. void Keyboard::changeCaptureContext(UINT64 windowHandle)
  129. {
  130. HWND newhWnd = (HWND)windowHandle;
  131. if(m->hWnd != newhWnd)
  132. {
  133. releaseDirectInput(m);
  134. if (windowHandle != (UINT64)-1)
  135. initializeDirectInput(m, newhWnd);
  136. else
  137. m->hWnd = (HWND)-1;
  138. }
  139. }
  140. }