KeyDelay.h
1 #pragma once 2 #include <functional> 3 #include <thread> 4 #include <queue> 5 #include <mutex> 6 7 #include <common/hooks/LowlevelKeyboardEvent.h> 8 // Available states for the KeyDelay state machine. 9 enum class KeyDelayState 10 { 11 RELEASED, 12 ON_HOLD, 13 ON_HOLD_TIMEOUT, 14 }; 15 16 // Virtual key + timestamp (in millis since Windows startup) 17 struct KeyTimedEvent 18 { 19 DWORD64 time; 20 WPARAM message; 21 }; 22 23 // Handles delayed key inputs. 24 // Implemented as a state machine running on its own thread. 25 // Thread stops on destruction. 26 class KeyDelay 27 { 28 public: 29 KeyDelay( 30 DWORD key, 31 std::function<void(DWORD)> onShortPress, 32 std::function<void(DWORD)> onLongPressDetected, 33 std::function<void(DWORD)> onLongPressReleased) : 34 _quit(false), 35 _state(KeyDelayState::RELEASED), 36 _initialHoldKeyDown(0), 37 _key(key), 38 _onShortPress(onShortPress), 39 _onLongPressDetected(onLongPressDetected), 40 _onLongPressReleased(onLongPressReleased), 41 _delayThread(&KeyDelay::DelayThread, this){}; 42 43 // Enqueue new KeyTimedEvent and notify the condition variable. 44 void KeyEvent(LowlevelKeyboardEvent* ev); 45 ~KeyDelay(); 46 47 private: 48 // Runs the state machine, waits if there is no events to process. 49 // Checks for _quit condition. 50 void DelayThread(); 51 52 // Manage state transitions and trigger callbacks on certain events. 53 // Returns whether or not the thread should wait on new events. 54 bool HandleRelease(); 55 bool HandleOnHold(std::unique_lock<std::mutex>& cvLock); 56 bool HandleOnHoldTimeout(); 57 58 // Get next key event in queue. 59 KeyTimedEvent NextEvent(); 60 bool HasNextEvent(); 61 62 // Check if <duration> milliseconds passed since <first> millisecond. 63 // Also checks for overflow conditions. 64 bool CheckIfMillisHaveElapsed(DWORD64 first, DWORD64 last, DWORD64 duration); 65 66 bool _quit; 67 KeyDelayState _state; 68 69 // Callback functions, the key provided in the constructor is passed as an argument. 70 std::function<void(DWORD)> _onLongPressDetected; 71 std::function<void(DWORD)> _onLongPressReleased; 72 std::function<void(DWORD)> _onShortPress; 73 74 // Queue holding key events that are not processed yet. Should be kept synchronized 75 // using _queueMutex 76 std::queue<KeyTimedEvent> _queue; 77 std::mutex _queueMutex; 78 79 // DelayThread waits on this condition variable when there is no events to process. 80 std::condition_variable _cv; 81 82 // Keeps track of the time at which the initial KEY_DOWN event happened. 83 DWORD64 _initialHoldKeyDown; 84 85 // Virtual Key provided in the constructor. Passed to callback functions. 86 DWORD _key; 87 88 // Declare _delayThread after all other members so that it is the last to be initialized by the constructor 89 std::thread _delayThread; 90 91 static const DWORD64 LONG_PRESS_DELAY_MILLIS = 900; 92 static const DWORD64 ON_HOLD_WAIT_TIMEOUT_MILLIS = 50; 93 };