Input.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Context.h"
  24. #include "CoreEvents.h"
  25. #include "Graphics.h"
  26. #include "GraphicsEvents.h"
  27. #include "Input.h"
  28. #include "Log.h"
  29. #include "Profiler.h"
  30. #include <cstring>
  31. #ifndef USE_OPENGL
  32. #include <Windows.h>
  33. #else
  34. #include <GL/glfw.h>
  35. static Input* inputInstance = 0;
  36. #endif
  37. #include "DebugNew.h"
  38. OBJECTTYPESTATIC(Input);
  39. Input::Input(Context* context) :
  40. Object(context),
  41. showCursor_(true),
  42. toggleFullscreen_(true),
  43. active_(false),
  44. minimized_(false),
  45. activated_(false),
  46. suppressNextChar_(false),
  47. initialized_(false)
  48. {
  49. // Zero the initial state
  50. mouseButtonDown_ = 0;
  51. mouseButtonPress_ = 0;
  52. lastCursorPosition_ = IntVector2::ZERO;
  53. #ifndef USE_OPENGL
  54. SubscribeToEvent(E_WINDOWMESSAGE, HANDLER(Input, HandleWindowMessage));
  55. #else
  56. // GLFW callbacks do not include userdata, so a static instance pointer is necessary
  57. inputInstance = this;
  58. #endif
  59. SubscribeToEvent(E_SCREENMODE, HANDLER(Input, HandleScreenMode));
  60. SubscribeToEvent(E_BEGINFRAME, HANDLER(Input, HandleBeginFrame));
  61. // Try to initialize right now, but skip if screen mode is not yet set
  62. Initialize();
  63. }
  64. Input::~Input()
  65. {
  66. }
  67. void Input::Update()
  68. {
  69. PROFILE(UpdateInput);
  70. if (!graphics_)
  71. return;
  72. // Reset current state
  73. keyPress_.Clear();
  74. mouseButtonPress_ = 0;
  75. mouseMove_ = IntVector2::ZERO;
  76. mouseMoveWheel_ = 0;
  77. #ifndef USE_OPENGL
  78. // Pump Win32 events
  79. MSG msg;
  80. while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  81. {
  82. TranslateMessage(&msg);
  83. DispatchMessage(&msg);
  84. }
  85. #else
  86. // Pump GLFW events
  87. glfwPollEvents();
  88. // Check state
  89. if (glfwGetWindowParam(GLFW_ACTIVE))
  90. {
  91. if (!active_)
  92. activated_ = true;
  93. }
  94. else
  95. {
  96. if (active_)
  97. MakeInactive();
  98. }
  99. #endif
  100. // Activate application now if necessary
  101. if (activated_)
  102. MakeActive();
  103. // Finally send the mouse move event if motion has been accumulated
  104. if (active_)
  105. {
  106. #ifndef USE_OPENGL
  107. IntVector2 mousePos = GetCursorPosition();
  108. mouseMove_ = mousePos - lastCursorPosition_;
  109. // Recenter the mouse cursor manually if it moved
  110. if (mouseMove_ != IntVector2::ZERO)
  111. {
  112. IntVector2 center(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2);
  113. SetCursorPosition(center);
  114. lastCursorPosition_ = GetCursorPosition();
  115. }
  116. else
  117. lastCursorPosition_ = mousePos;
  118. #else
  119. IntVector2 mousePos = GetCursorPosition();
  120. mouseMove_ = mousePos - lastCursorPosition_;
  121. lastCursorPosition_ = mousePos;
  122. #endif
  123. if (mouseMove_ != IntVector2::ZERO)
  124. {
  125. using namespace MouseMove;
  126. VariantMap eventData;
  127. eventData[P_DX] = mouseMove_.x_;
  128. eventData[P_DY] = mouseMove_.y_;
  129. eventData[P_BUTTONS] = mouseButtonDown_;
  130. eventData[P_QUALIFIERS] = GetQualifiers();
  131. SendEvent(E_MOUSEMOVE, eventData);
  132. }
  133. }
  134. }
  135. void Input::SetToggleFullscreen(bool enable)
  136. {
  137. toggleFullscreen_ = enable;
  138. }
  139. void Input::SuppressNextChar()
  140. {
  141. suppressNextChar_ = true;
  142. }
  143. bool Input::GetKeyDown(int key) const
  144. {
  145. return keyDown_.Contains(key);
  146. }
  147. bool Input::GetKeyPress(int key) const
  148. {
  149. return keyPress_.Contains(key);
  150. }
  151. bool Input::GetMouseButtonDown(int button) const
  152. {
  153. return (mouseButtonDown_ & button) != 0;
  154. }
  155. bool Input::GetMouseButtonPress(int button) const
  156. {
  157. return (mouseButtonPress_ & button) != 0;
  158. }
  159. bool Input::GetQualifierDown(int qualifier) const
  160. {
  161. #ifndef USE_OPENGL
  162. if (qualifier == QUAL_SHIFT)
  163. return GetKeyDown(KEY_SHIFT);
  164. if (qualifier == QUAL_CTRL)
  165. return GetKeyDown(KEY_CTRL);
  166. if (qualifier == QUAL_ALT)
  167. return GetKeyDown(KEY_ALT);
  168. #else
  169. if (qualifier == QUAL_SHIFT)
  170. return GetKeyDown(KEY_LSHIFT) || GetKeyDown(KEY_RSHIFT);
  171. if (qualifier == QUAL_CTRL)
  172. return GetKeyDown(KEY_LCTRL) || GetKeyDown(KEY_RCTRL);
  173. if (qualifier == QUAL_ALT)
  174. return GetKeyDown(KEY_LALT) || GetKeyDown(KEY_RALT);
  175. #endif
  176. return false;
  177. }
  178. bool Input::GetQualifierPress(int qualifier) const
  179. {
  180. #ifndef USE_OPENGL
  181. if (qualifier == QUAL_SHIFT)
  182. return GetKeyPress(KEY_SHIFT);
  183. if (qualifier == QUAL_CTRL)
  184. return GetKeyPress(KEY_CTRL);
  185. if (qualifier == QUAL_ALT)
  186. return GetKeyPress(KEY_ALT);
  187. #else
  188. if (qualifier == QUAL_SHIFT)
  189. return GetKeyPress(KEY_LSHIFT) || GetKeyPress(KEY_RSHIFT);
  190. if (qualifier == QUAL_CTRL)
  191. return GetKeyPress(KEY_LCTRL) || GetKeyPress(KEY_RCTRL);
  192. if (qualifier == QUAL_ALT)
  193. return GetKeyPress(KEY_LALT) || GetKeyPress(KEY_RALT);
  194. #endif
  195. return false;
  196. }
  197. int Input::GetQualifiers() const
  198. {
  199. int ret = 0;
  200. if (GetQualifierDown(QUAL_SHIFT))
  201. ret |= QUAL_SHIFT;
  202. if (GetQualifierDown(QUAL_CTRL))
  203. ret |= QUAL_CTRL;
  204. if (GetQualifierDown(QUAL_ALT))
  205. ret |= QUAL_ALT;
  206. return ret;
  207. }
  208. void Input::Initialize()
  209. {
  210. Graphics* graphics = GetSubsystem<Graphics>();
  211. if (!graphics || !graphics->IsInitialized())
  212. return;
  213. graphics_ = graphics;
  214. // Set the initial activation
  215. MakeActive();
  216. #ifndef USE_OPENGL
  217. SetClipCursor(true);
  218. SetCursorVisible(false);
  219. #endif
  220. initialized_ = true;
  221. LOGINFO("Initialized input");
  222. }
  223. void Input::MakeActive()
  224. {
  225. if (!graphics_)
  226. return;
  227. ResetState();
  228. active_ = true;
  229. activated_ = false;
  230. // Re-establish mouse cursor clipping as necessary
  231. #ifndef USE_OPENGL
  232. SetClipCursor(true);
  233. SetCursorVisible(false);
  234. #else
  235. // Get the current mouse position as a base for movement calculations
  236. lastCursorPosition_ = GetCursorPosition();
  237. #endif
  238. using namespace Activation;
  239. VariantMap eventData;
  240. eventData[P_ACTIVE] = active_;
  241. eventData[P_MINIMIZED] = minimized_;
  242. SendEvent(E_ACTIVATION, eventData);
  243. }
  244. void Input::MakeInactive()
  245. {
  246. if (!graphics_)
  247. return;
  248. ResetState();
  249. active_ = false;
  250. activated_ = false;
  251. // Free and show the mouse cursor
  252. #ifndef USE_OPENGL
  253. ReleaseCapture();
  254. ClipCursor(0);
  255. SetCursorVisible(true);
  256. #endif
  257. using namespace Activation;
  258. VariantMap eventData;
  259. eventData[P_ACTIVE] = active_;
  260. eventData[P_MINIMIZED] = minimized_;
  261. SendEvent(E_ACTIVATION, eventData);
  262. }
  263. void Input::ResetState()
  264. {
  265. keyDown_.Clear();
  266. keyPress_.Clear();
  267. // Use SetMouseButton() to reset the state so that mouse events will be sent properly
  268. SetMouseButton(MOUSEB_LEFT, false);
  269. SetMouseButton(MOUSEB_RIGHT, false);
  270. SetMouseButton(MOUSEB_MIDDLE, false);
  271. mouseMove_ = IntVector2::ZERO;
  272. mouseMoveWheel_ = 0;
  273. mouseButtonPress_ = 0;
  274. }
  275. void Input::SetMouseButton(int button, bool newState)
  276. {
  277. // If we are not active yet, do not react to the mouse button down
  278. if (newState && !active_)
  279. return;
  280. if (newState)
  281. {
  282. if (!(mouseButtonDown_ & button))
  283. mouseButtonPress_ |= button;
  284. mouseButtonDown_ |= button;
  285. }
  286. else
  287. {
  288. if (!(mouseButtonDown_ & button))
  289. return;
  290. mouseButtonDown_ &= ~button;
  291. }
  292. using namespace MouseButtonDown;
  293. VariantMap eventData;
  294. eventData[P_BUTTON] = button;
  295. eventData[P_BUTTONS] = mouseButtonDown_;
  296. eventData[P_QUALIFIERS] = GetQualifiers();
  297. SendEvent(newState ? E_MOUSEBUTTONDOWN : E_MOUSEBUTTONUP, eventData);
  298. }
  299. void Input::SetKey(int key, bool newState)
  300. {
  301. // If we are not active yet, do not react to the key down
  302. if (newState && !active_)
  303. return;
  304. bool repeat = false;
  305. if (newState)
  306. {
  307. if (!keyDown_.Contains(key))
  308. {
  309. keyDown_.Insert(key);
  310. keyPress_.Insert(key);
  311. }
  312. else
  313. repeat = true;
  314. }
  315. else
  316. {
  317. if (!keyDown_.Erase(key))
  318. return;
  319. }
  320. using namespace KeyDown;
  321. VariantMap eventData;
  322. eventData[P_KEY] = key;
  323. eventData[P_BUTTONS] = mouseButtonDown_;
  324. eventData[P_QUALIFIERS] = GetQualifiers();
  325. if (newState)
  326. eventData[P_REPEAT] = repeat;
  327. SendEvent(newState ? E_KEYDOWN : E_KEYUP, eventData);
  328. }
  329. void Input::SetMouseWheel(int delta)
  330. {
  331. // If we are not active yet, do not react to the wheel
  332. if (!active_)
  333. return;
  334. if (delta)
  335. {
  336. mouseMoveWheel_ += delta;
  337. using namespace MouseWheel;
  338. VariantMap eventData;
  339. eventData[P_WHEEL] = delta;
  340. eventData[P_BUTTONS] = mouseButtonDown_;
  341. eventData[P_QUALIFIERS] = GetQualifiers();
  342. SendEvent(E_MOUSEWHEEL, eventData);
  343. }
  344. }
  345. #ifndef USE_OPENGL
  346. void Input::SetClipCursor(bool enable)
  347. {
  348. if (!graphics_)
  349. return;
  350. if (!graphics_->GetFullscreen() && active_ && enable)
  351. {
  352. SetCursorPosition(IntVector2(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2));
  353. lastCursorPosition_ = GetCursorPosition();
  354. RECT clipRect;
  355. GetWindowRect((HWND)graphics_->GetWindowHandle(), &clipRect);
  356. ClipCursor(&clipRect);
  357. }
  358. else
  359. {
  360. if (graphics_->GetFullscreen() && active_ && enable)
  361. {
  362. SetCursorPosition(IntVector2(graphics_->GetWidth() / 2, graphics_->GetHeight() / 2));
  363. lastCursorPosition_ = GetCursorPosition();
  364. }
  365. ClipCursor(0);
  366. }
  367. }
  368. void Input::SetCursorPosition(const IntVector2& position)
  369. {
  370. if (!graphics_)
  371. return;
  372. POINT point;
  373. point.x = position.x_;
  374. point.y = position.y_;
  375. ClientToScreen((HWND)graphics_->GetWindowHandle(), &point);
  376. SetCursorPos(point.x, point.y);
  377. }
  378. void Input::SetCursorVisible(bool enable)
  379. {
  380. if (!graphics_)
  381. return;
  382. // When inactive, always show the cursor
  383. if (!active_)
  384. enable = true;
  385. if (showCursor_ == enable)
  386. return;
  387. ShowCursor(enable ? TRUE : FALSE);
  388. showCursor_ = enable;
  389. }
  390. void Input::HandleWindowMessage(StringHash eventType, VariantMap& eventData)
  391. {
  392. using namespace WindowMessage;
  393. int msg = eventData[P_MSG].GetInt();
  394. int wParam = eventData[P_WPARAM].GetInt();
  395. int lParam = eventData[P_LPARAM].GetInt();
  396. switch (msg)
  397. {
  398. case WM_LBUTTONDOWN:
  399. SetMouseButton(MOUSEB_LEFT, true);
  400. eventData[P_HANDLED] = true;
  401. break;
  402. case WM_NCLBUTTONUP:
  403. case WM_LBUTTONUP:
  404. SetMouseButton(MOUSEB_LEFT, false);
  405. eventData[P_HANDLED] = true;
  406. break;
  407. case WM_RBUTTONDOWN:
  408. SetMouseButton(MOUSEB_RIGHT, true);
  409. eventData[P_HANDLED] = true;
  410. break;
  411. case WM_NCRBUTTONUP:
  412. case WM_RBUTTONUP:
  413. SetMouseButton(MOUSEB_RIGHT, false);
  414. eventData[P_HANDLED] = true;
  415. break;
  416. case WM_MBUTTONDOWN:
  417. SetMouseButton(MOUSEB_MIDDLE, true);
  418. eventData[P_HANDLED] = true;
  419. break;
  420. case WM_NCMBUTTONUP:
  421. case WM_MBUTTONUP:
  422. SetMouseButton(MOUSEB_MIDDLE, false);
  423. eventData[P_HANDLED] = true;
  424. break;
  425. case WM_MOUSEWHEEL:
  426. SetMouseWheel(wParam >> 16);
  427. eventData[P_HANDLED] = true;
  428. break;
  429. case WM_ACTIVATE:
  430. minimized_ = HIWORD(wParam) != 0;
  431. if (LOWORD(wParam) == WA_INACTIVE)
  432. MakeInactive();
  433. else
  434. {
  435. if (!minimized_)
  436. activated_ = true;
  437. }
  438. eventData[P_HANDLED] = true;
  439. break;
  440. case WM_KEYDOWN:
  441. SetKey(wParam, true);
  442. eventData[P_HANDLED] = true;
  443. break;
  444. case WM_SYSKEYDOWN:
  445. SetKey(wParam, true);
  446. if (wParam == KEY_RETURN && toggleFullscreen_)
  447. graphics_->ToggleFullscreen();
  448. if (wParam != KEY_F4)
  449. eventData[P_HANDLED] = true;
  450. break;
  451. case WM_KEYUP:
  452. SetKey(wParam, false);
  453. eventData[P_HANDLED] = true;
  454. break;
  455. case WM_SYSKEYUP:
  456. SetKey(wParam, false);
  457. eventData[P_HANDLED] = true;
  458. break;
  459. case WM_CHAR:
  460. if (!suppressNextChar_)
  461. {
  462. using namespace Char;
  463. VariantMap keyEventData;
  464. keyEventData[P_CHAR] = wParam;
  465. keyEventData[P_BUTTONS] = mouseButtonDown_;
  466. keyEventData[P_QUALIFIERS] = GetQualifiers();
  467. SendEvent(E_CHAR, keyEventData);
  468. }
  469. suppressNextChar_ = false;
  470. eventData[P_HANDLED] = true;
  471. break;
  472. }
  473. }
  474. #else
  475. void KeyCallback(int key, int action)
  476. {
  477. inputInstance->SetKey(key, action & GLFW_PRESS);
  478. }
  479. void CharCallback(int key, int action)
  480. {
  481. if (key < 256 && action == GLFW_PRESS)
  482. {
  483. using namespace Char;
  484. VariantMap keyEventData;
  485. keyEventData[P_CHAR] = key;
  486. keyEventData[P_BUTTONS] = inputInstance->mouseButtonDown_;
  487. keyEventData[P_QUALIFIERS] = inputInstance->GetQualifiers();
  488. inputInstance->SendEvent(E_CHAR, keyEventData);
  489. }
  490. }
  491. void MouseButtonCallback(int button, int action)
  492. {
  493. inputInstance->SetMouseButton(1 << button, action == GLFW_PRESS);
  494. }
  495. void MouseWheelCallback(int wheel)
  496. {
  497. /// \todo Implement
  498. }
  499. #endif
  500. IntVector2 Input::GetCursorPosition() const
  501. {
  502. IntVector2 ret(0, 0);
  503. if (!graphics_)
  504. return ret;
  505. #ifndef USE_OPENGL
  506. POINT mouse;
  507. GetCursorPos(&mouse);
  508. ScreenToClient((HWND)graphics_->GetWindowHandle(), &mouse);
  509. ret.x_ = mouse.x;
  510. ret.y_ = mouse.y;
  511. #else
  512. glfwGetMousePos(&ret.x_, &ret.y_);
  513. #endif
  514. return ret;
  515. }
  516. void Input::HandleScreenMode(StringHash eventType, VariantMap& eventData)
  517. {
  518. if (!initialized_)
  519. Initialize();
  520. // Screen mode change may affect the cursor clipping behaviour. Also re-center the cursor (if needed) to the new screen size,
  521. // so that there is no erroneous mouse move event
  522. #ifndef USE_OPENGL
  523. SetClipCursor(true);
  524. #else
  525. // Re-enable GLFW callbacks each time the window has been recreated
  526. glfwDisable(GLFW_MOUSE_CURSOR);
  527. glfwSetKeyCallback(&KeyCallback);
  528. glfwSetCharCallback(&CharCallback);
  529. glfwSetMouseButtonCallback(&MouseButtonCallback);
  530. glfwSetMouseWheelCallback(&MouseWheelCallback);
  531. #endif
  532. }
  533. void Input::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
  534. {
  535. // Update input right at the beginning of the frame
  536. if (initialized_)
  537. Update();
  538. }