Improved mouse hook performance (not slowing down the system anymore)
This commit is contained in:
		
							parent
							
								
									383c9fbda9
								
							
						
					
					
						commit
						b92f584247
					
				| @ -2,7 +2,7 @@ set(MODULE_NAME "teaclient_ppt") | ||||
| 
 | ||||
| set(SOURCE_FILES src/KeyboardHook.cpp) | ||||
| if (MSVC) | ||||
| 	set(SOURCE_FILES ${SOURCE_FILES} src/Win32KeyboardHook.cpp) | ||||
| 	set(SOURCE_FILES ${SOURCE_FILES} src/Win32KeyboardHook.cpp src/Win32KeyboardHookLL.cpp src/Win32KeyboardRawInput.cpp) | ||||
| 	add_definitions(-DUSING_UV_SHARED) | ||||
| else() | ||||
| 	add_definitions(-DHAVE_X11) | ||||
|  | ||||
| @ -8,7 +8,12 @@ using namespace std; | ||||
| 
 | ||||
| #include "include/NanException.h" | ||||
| #include "include/NanEventCallback.h" | ||||
| #include "src/KeyboardHook.h" | ||||
| 
 | ||||
| #ifdef WIN32 | ||||
| 	#include "src/Win32KeyboardHook.h" | ||||
| #else | ||||
| 	#include "src/KeyboardHook.h" | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| std::mutex callback_lock; | ||||
| @ -62,7 +67,7 @@ NAN_METHOD(UnregisterCallback) { | ||||
| } | ||||
| 
 | ||||
| NAN_MODULE_INIT(init) { | ||||
| 	hook = make_unique<KeyboardHook>(); | ||||
| 	hook = make_unique<hooks::Win32RawHook>(); | ||||
| 	if(!hook->attach()) { | ||||
| 		NAN_THROW_EXCEPTION(Error, "Failed to attach hook!"); | ||||
| 		return; | ||||
|  | ||||
| @ -1,6 +1,26 @@ | ||||
| #include "KeyboardHook.h" | ||||
| #include "./KeyboardHook.h" | ||||
| #include <cassert> | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| KeyboardHook::KeyboardHook(KeyboardHookType type) : type_{type} {}; | ||||
| 
 | ||||
| KeyboardHook::~KeyboardHook() { | ||||
|     if(this->_attached) | ||||
|         this->detach(); | ||||
| } | ||||
| 
 | ||||
| bool KeyboardHook::attach() { | ||||
|     assert(!this->_attached); | ||||
|     this->_attached = true; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void KeyboardHook::detach() { | ||||
|     assert(this->_attached); | ||||
|     this->_attached = false; | ||||
| } | ||||
| 
 | ||||
| void KeyboardHook::trigger_key_event(const enum KeyEvent::type& type, const std::string &key) { | ||||
| 	if(!this->callback_event) return; | ||||
| 
 | ||||
|  | ||||
| @ -4,27 +4,35 @@ | ||||
| #include <thread> | ||||
| #include <map> | ||||
| 
 | ||||
| #ifdef HAVE_X11 | ||||
| //#define HOOK_X11
 | ||||
| 
 | ||||
| #if defined(HOOK_X11) | ||||
| 	#include <X11/Xlib.h> | ||||
| #elif defined(WIN32) | ||||
| 	#include <Windows.h> | ||||
| #endif | ||||
| 
 | ||||
| enum struct KeyboardHookType { | ||||
| 	X11, | ||||
| 
 | ||||
| 	RAW_INPUT, | ||||
| 	SYSTEM_HOOK | ||||
| }; | ||||
| 
 | ||||
| class KeyboardHook { | ||||
| 	#if defined(WIN32) | ||||
| 	#ifdef HOOK_WIN32_LL | ||||
| 		friend LRESULT CALLBACK _keyboard_hook_callback(int, WPARAM, LPARAM); | ||||
|         friend LRESULT CALLBACK _mouse_hook_callback(int, WPARAM, LPARAM); | ||||
| 	#endif | ||||
| 	public: | ||||
| 		struct KeyType { | ||||
| 			enum value { | ||||
| 				KEY_UNKNOWN, | ||||
| 		typedef unsigned int KeyID; | ||||
| 
 | ||||
| 				KEY_NORMAL, | ||||
| 				KEY_SHIFT, | ||||
| 				KEY_ALT, | ||||
| 				KEY_WIN, | ||||
| 				KEY_CTRL | ||||
| 			}; | ||||
| 		enum struct KeyType { | ||||
| 			KEY_UNKNOWN, | ||||
| 
 | ||||
| 			KEY_NORMAL, | ||||
| 			KEY_SHIFT, | ||||
| 			KEY_ALT, | ||||
| 			KEY_WIN, | ||||
| 			KEY_CTRL | ||||
| 		}; | ||||
| 
 | ||||
| 		struct KeyEvent { | ||||
| @ -45,17 +53,23 @@ class KeyboardHook { | ||||
| 		}; | ||||
| 		typedef std::function<void(const std::shared_ptr<KeyEvent>& /* event */)> callback_event_t; | ||||
| 
 | ||||
| 		KeyboardHook(); | ||||
| 		KeyboardHook(KeyboardHookType); | ||||
| 		virtual ~KeyboardHook(); | ||||
| 
 | ||||
| 		bool attach(); | ||||
| 		inline bool attached() { return this->_attached; } | ||||
| 		void detach(); | ||||
| 		[[nodiscard]] inline KeyboardHookType type() const { return this->type_; } | ||||
| 		[[nodiscard]] virtual bool keytype_supported() const = 0; | ||||
| 
 | ||||
| 		[[nodiscard]] virtual bool attach(); | ||||
| 		[[nodiscard]] inline bool attached() const { return this->_attached; } | ||||
| 		virtual void detach(); | ||||
| 
 | ||||
| 		void trigger_key_event(const enum KeyEvent::type&, const std::string& /* key */); | ||||
| 		callback_event_t callback_event; | ||||
| 	private: | ||||
| 	#ifdef HAVE_X11 | ||||
| 	protected: | ||||
| 		const KeyboardHookType type_; | ||||
| 
 | ||||
| #if 0 | ||||
| 	#if defined(HOOK_X11) | ||||
| 		typedef int KeyID; | ||||
| 		Display* display = nullptr; | ||||
| 		Window window_root = 0; | ||||
| @ -63,20 +77,20 @@ class KeyboardHook { | ||||
| 		int focus_revert; | ||||
| 
 | ||||
| 		long end_id = 0; | ||||
| 	#elif defined(WIN32) | ||||
|     #elseif defined(HOOK_WIN32_LL) | ||||
| 		typedef UINT KeyID; | ||||
| 		HHOOK keyboad_hook_id{nullptr}; | ||||
|         HHOOK mouse_hook_id{nullptr}; | ||||
| 
 | ||||
|         bool keyboard_hook_callback(int, WPARAM, LPARAM); | ||||
| 
 | ||||
| #ifdef USE_MOUSE_HOOK | ||||
|         HHOOK mouse_hook_id{nullptr}; | ||||
|         bool mouse_hook_callback(int, WPARAM, LPARAM); | ||||
| #endif | ||||
|     #endif | ||||
| #endif | ||||
| 
 | ||||
| 		std::map<KeyID, bool> map_key; | ||||
| 		std::map<KeyID, KeyID> map_special; | ||||
| 		std::map<KeyType, KeyID> map_special; | ||||
| 
 | ||||
| 		bool _attached = false; | ||||
| 		bool active = false; | ||||
| 		std::thread poll_thread; | ||||
| 		void poll_events(); | ||||
| }; | ||||
| @ -1,321 +1,54 @@ | ||||
| #include <iostream> | ||||
| #include <cassert> | ||||
| #include <string> | ||||
| #include <mutex> | ||||
| #include "KeyboardHook.h" | ||||
| //
 | ||||
| // Created by WolverinDEV on 01/05/2020.
 | ||||
| //
 | ||||
| 
 | ||||
| using namespace std; | ||||
| #include "./Win32KeyboardHook.h" | ||||
| 
 | ||||
| typedef KBDLLHOOKSTRUCT KeyboardHookStruct; | ||||
| typedef MSLLHOOKSTRUCT MouseHookStruct; | ||||
| thread_local KeyboardHook* thread_hook{nullptr}; | ||||
| namespace hooks { | ||||
|     std::string key_string_from_vk(DWORD code, bool extended) { | ||||
|         auto scan_code = MapVirtualKey(code, MAPVK_VK_TO_VSC); | ||||
|         if(extended) | ||||
|             scan_code |= KF_EXTENDED; | ||||
| 
 | ||||
| struct MouseButtonEventEntry { | ||||
|     MouseButtonEventEntry* next; | ||||
|     KeyboardHook* hook; | ||||
| 
 | ||||
|     enum KeyboardHook::KeyEvent::type type; | ||||
|     std::string key; | ||||
| }; | ||||
| 
 | ||||
| inline MouseButtonEventEntry* allocate_mb_event() { | ||||
|     return new MouseButtonEventEntry{}; | ||||
| } | ||||
| 
 | ||||
| inline void delete_mb_event(MouseButtonEventEntry* event) { | ||||
|     delete event; | ||||
| } | ||||
| 
 | ||||
| struct MouseButtonEventDispatcher { | ||||
|     bool active{true}; | ||||
| 
 | ||||
|     std::thread dispatcher{}; | ||||
| 
 | ||||
|     CRITICAL_SECTION mutex; | ||||
|     CONDITION_VARIABLE cv_flushed; | ||||
|     CONDITION_VARIABLE cv_work; | ||||
| 
 | ||||
|     MouseButtonEventEntry* event_head{nullptr}; | ||||
|     MouseButtonEventEntry** event_tail{&event_head}; | ||||
| }; | ||||
| 
 | ||||
| MouseButtonEventDispatcher* global_event_dispatcher{}; | ||||
| size_t global_ed_ref_count{0}; | ||||
| 
 | ||||
| void init_global_ed() { | ||||
|     if(global_event_dispatcher) { | ||||
|         global_ed_ref_count++; | ||||
|         return; | ||||
|         char key_buffer[255]; | ||||
|         auto length = GetKeyNameTextA(scan_code << 16, key_buffer, 255); | ||||
|         if(length == 0) | ||||
|             return "error"; | ||||
|         else | ||||
|             return std::string{key_buffer, (size_t) length}; | ||||
|     } | ||||
| 
 | ||||
|     global_event_dispatcher = new MouseButtonEventDispatcher{}; | ||||
|     InitializeCriticalSection(&global_event_dispatcher->mutex); | ||||
|     InitializeConditionVariable(&global_event_dispatcher->cv_flushed); | ||||
|     InitializeConditionVariable(&global_event_dispatcher->cv_work); | ||||
| 
 | ||||
|     global_event_dispatcher->dispatcher = std::thread([]{ | ||||
|         auto ed = global_event_dispatcher; | ||||
| 
 | ||||
|         while(ed->active) { | ||||
|             MouseButtonEventEntry* entry{nullptr}; | ||||
|             { | ||||
|                 EnterCriticalSection(&ed->mutex); | ||||
|                 while(!global_event_dispatcher->event_head && ed->active) { | ||||
|                     WakeAllConditionVariable(&ed->cv_flushed); | ||||
|                     SleepConditionVariableCS(&ed->cv_work, &ed->mutex, INFINITE); | ||||
|                 } | ||||
| 
 | ||||
|                 entry = global_event_dispatcher->event_head; | ||||
|                 global_event_dispatcher->event_head = nullptr; | ||||
|                 global_event_dispatcher->event_tail = &global_event_dispatcher->event_head; | ||||
|                 LeaveCriticalSection(&ed->mutex); | ||||
|             } | ||||
| 
 | ||||
|             while(entry) { | ||||
|                 entry->hook->trigger_key_event(entry->type, std::string{entry->key}); | ||||
| 
 | ||||
|                 auto next = entry->next; | ||||
|                 delete_mb_event(entry); | ||||
|                 entry = next; | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| void shutdown_global_ed() { | ||||
|     if(--global_ed_ref_count > 0) return; | ||||
| 
 | ||||
|     auto ed = std::exchange(global_event_dispatcher, nullptr); | ||||
|     ed->active = false; | ||||
|     WakeAllConditionVariable(&ed->cv_work); | ||||
| 
 | ||||
|     if(ed->dispatcher.joinable()) | ||||
|         ed->dispatcher.join(); | ||||
| 
 | ||||
|     DeleteCriticalSection(&ed->mutex); | ||||
|     delete ed; | ||||
| } | ||||
| 
 | ||||
| KeyboardHook::KeyboardHook() = default; | ||||
| 
 | ||||
| KeyboardHook::~KeyboardHook() { | ||||
| 	if(this->_attached) | ||||
| 		this->detach(); | ||||
| } | ||||
| 
 | ||||
| bool KeyboardHook::attach() { | ||||
| 	assert(!this->_attached); | ||||
| 	this->active = true; | ||||
| 
 | ||||
|     init_global_ed(); | ||||
| 	this->poll_thread = std::thread(std::bind(&KeyboardHook::poll_events, this)); | ||||
| 
 | ||||
| 	this->_attached = true; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void KeyboardHook::detach() { | ||||
| 	assert(this->_attached); | ||||
| 	this->active = false; | ||||
| 	{ | ||||
| 		//TODO trigger no message!
 | ||||
| 	} | ||||
| 	if(this->poll_thread.joinable()) | ||||
| 		this->poll_thread.join(); | ||||
| 
 | ||||
| 	/* all events flushed */ | ||||
|     EnterCriticalSection(&global_event_dispatcher->mutex); | ||||
|     WakeAllConditionVariable(&global_event_dispatcher->cv_work); | ||||
|     SleepConditionVariableCS(&global_event_dispatcher->cv_flushed, &global_event_dispatcher->mutex, INFINITE); | ||||
|     LeaveCriticalSection(&global_event_dispatcher->mutex); | ||||
|     shutdown_global_ed(); | ||||
| 
 | ||||
| 	this->_attached = false; | ||||
| } | ||||
| 
 | ||||
| LRESULT _keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { | ||||
|     assert(thread_hook); | ||||
|     auto consume = thread_hook->keyboard_hook_callback(nCode, event, ptr_keyboard); | ||||
|     if(consume) | ||||
|         return 1; | ||||
|     return CallNextHookEx(nullptr, nCode, event, ptr_keyboard); | ||||
| } | ||||
| 
 | ||||
| LRESULT _mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { | ||||
|     assert(thread_hook); | ||||
|     auto consume = thread_hook->mouse_hook_callback(nCode, event, ptr_keyboard); | ||||
|     auto end = std::chrono::high_resolution_clock::now(); | ||||
|     if(consume) | ||||
|         return 1; | ||||
|     return CallNextHookEx(nullptr, nCode, event, ptr_keyboard); | ||||
| } | ||||
| 
 | ||||
| void KeyboardHook::poll_events() { | ||||
|     thread_hook = this; | ||||
| 
 | ||||
|     if(!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) | ||||
|         std::cerr << "Failed to set priority class to realtime!" << std::endl; | ||||
| 
 | ||||
| #if 0 | ||||
|     int    cur_priority		  = GetThreadPriority(GetCurrentThread()); | ||||
|     DWORD  cur_priority_class = GetPriorityClass(GetCurrentProcess()); | ||||
|     std::cout << "P: " << cur_priority << " C: " << cur_priority_class << std::endl; | ||||
| #endif | ||||
| 
 | ||||
| 	this->keyboad_hook_id = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboard_hook_callback, GetModuleHandle(nullptr), 0); | ||||
| 	if(!this->keyboad_hook_id) { | ||||
| 		cerr << "Failed to register keyboard hook" << endl; | ||||
| 		return; | ||||
| 	} | ||||
| 	 | ||||
|     this->mouse_hook_id = SetWindowsHookEx(WH_MOUSE_LL, _mouse_hook_callback, GetModuleHandle(nullptr), 0); | ||||
|     if(!this->keyboad_hook_id) { | ||||
|         UnhookWindowsHookEx(this->keyboad_hook_id); | ||||
|         cerr << "Failed to register mouse hook" << endl; | ||||
|         return; | ||||
|     std::string key_string_from_sc(USHORT code) { | ||||
|         char key_buffer[255]; | ||||
|         auto length = GetKeyNameTextA(code << 16, key_buffer, 255); | ||||
|         if(length == 0) | ||||
|             return "error"; | ||||
|         else | ||||
|             return std::string{key_buffer, (size_t) length}; | ||||
|     } | ||||
| 
 | ||||
| 	MSG msg; | ||||
| 	while(!GetMessage(&msg, nullptr, 0, 0) && this->active) { | ||||
| 		TranslateMessage(&msg); | ||||
| 		DispatchMessage(&msg); | ||||
| 	} | ||||
|     std::this_thread::sleep_for(std::chrono::seconds{1}); | ||||
|     //https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes
 | ||||
|     KeyboardHook::KeyType key_type_from_vk(DWORD vk_code) { | ||||
|         using KeyType = KeyboardHook::KeyType; | ||||
| 
 | ||||
| 	UnhookWindowsHookEx(this->mouse_hook_id); | ||||
|     UnhookWindowsHookEx(this->keyboad_hook_id); | ||||
|     thread_hook = nullptr; | ||||
| } | ||||
| 
 | ||||
| inline std::string key_code(DWORD code, bool extended = false) { | ||||
| 	auto scan_code = MapVirtualKey(code, MAPVK_VK_TO_VSC); | ||||
| 	if(extended) | ||||
| 		scan_code |= KF_EXTENDED; | ||||
| 
 | ||||
| 	char key_buffer[255]; | ||||
| 	auto length = GetKeyNameTextA(scan_code << 16, key_buffer, 255); | ||||
| 	if(length == 0) | ||||
| 		return "error"; | ||||
| 	else | ||||
| 		return string(key_buffer, length); | ||||
| } | ||||
| 
 | ||||
| inline std::string key_code(KeyboardHookStruct* keyboard) { | ||||
| 	return key_code(keyboard->vkCode, (keyboard->flags & LLKHF_EXTENDED) > 0); | ||||
| }  | ||||
| 
 | ||||
| using KeyType = KeyboardHook::KeyType; | ||||
| //https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes
 | ||||
| inline KeyType::value key_type(DWORD vk_code) { | ||||
| 	switch(vk_code) { | ||||
| 		case VK_CONTROL: | ||||
| 		case VK_LCONTROL: | ||||
| 		case VK_RCONTROL: | ||||
| 			return KeyType::KEY_CTRL; | ||||
| 		case VK_MENU: | ||||
| 		case VK_RMENU: | ||||
| 		case VK_LMENU: | ||||
| 			return KeyType::KEY_ALT; | ||||
| 		case VK_SHIFT: | ||||
| 		case VK_RSHIFT: | ||||
| 		case VK_LSHIFT: | ||||
| 			return KeyType::KEY_SHIFT; | ||||
| 		case VK_LWIN: | ||||
| 		case VK_RWIN: | ||||
| 			return KeyType::KEY_WIN; | ||||
| 		default: | ||||
| 			return KeyType::KEY_NORMAL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool KeyboardHook::keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { | ||||
| 	auto keyboard = (KeyboardHookStruct*) ptr_keyboard; | ||||
| 	if(event == WM_KEYDOWN || event == WM_SYSKEYDOWN) { | ||||
| 		auto& state = this->map_key[keyboard->vkCode]; | ||||
| 		bool typed = state; | ||||
| 		state = true; | ||||
| 
 | ||||
| 		auto type = key_type(keyboard->vkCode); | ||||
| 		if(type != KeyType::KEY_NORMAL)  | ||||
| 			this->map_special[type] = true; | ||||
| 		else | ||||
| 			this->map_special[type] = keyboard->vkCode; | ||||
| 
 | ||||
|         if(!typed) | ||||
|             this->trigger_key_event(KeyEvent::PRESS, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_code(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
|         this->trigger_key_event(KeyEvent::TYPE, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_code(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
| 	} else if(event == WM_KEYUP || event == WM_SYSKEYUP) { | ||||
| 		auto& state = this->map_key[keyboard->vkCode]; | ||||
| 		if(!state) return false; //Duplicate
 | ||||
| 		state = false; | ||||
| 
 | ||||
| 		auto type = key_type(keyboard->vkCode); | ||||
| 		if(type != KeyType::KEY_NORMAL) | ||||
| 			this->map_special[type] = false; | ||||
| 		else if(this->map_special[KeyType::KEY_NORMAL] == keyboard->vkCode) | ||||
| 			this->map_special[KeyType::KEY_NORMAL] = 0; | ||||
| 
 | ||||
|         this->trigger_key_event(KeyEvent::RELEASE, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_code(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
| 	} | ||||
| 
 | ||||
| 	//Consume the event: return 1
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool KeyboardHook::mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_mouse) { | ||||
|     MouseButtonEventEntry* mb_event; | ||||
|     switch (event) { | ||||
|         case WM_LBUTTONDOWN: | ||||
|             mb_event = allocate_mb_event(); | ||||
|             mb_event->type = KeyEvent::PRESS; | ||||
|             mb_event->key = "MOUSE1"; | ||||
|             break; | ||||
|         case WM_LBUTTONUP: | ||||
|             mb_event = allocate_mb_event(); | ||||
|             mb_event->type = KeyEvent::RELEASE; | ||||
|             mb_event->key = "MOUSE1"; | ||||
|             break; | ||||
| 
 | ||||
|         case WM_RBUTTONDOWN: | ||||
|             mb_event = allocate_mb_event(); | ||||
|             mb_event->type = KeyEvent::PRESS; | ||||
|             mb_event->key = "MOUSE3"; | ||||
|             break; | ||||
|         case WM_RBUTTONUP: | ||||
|             mb_event = allocate_mb_event(); | ||||
|             mb_event->type = KeyEvent::RELEASE; | ||||
|             mb_event->key = "MOUSE3"; | ||||
|             break; | ||||
| 
 | ||||
|         case WM_XBUTTONDOWN: { | ||||
|             auto mouse = (MouseHookStruct*) ptr_mouse; | ||||
|             auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData); | ||||
| 
 | ||||
|             mb_event = allocate_mb_event(); | ||||
|             mb_event->type = KeyEvent::PRESS; | ||||
|             mb_event->key = "MOUSEX" + std::to_string(x_index); | ||||
|             break; | ||||
|         switch(vk_code) { | ||||
|             case VK_CONTROL: | ||||
|             case VK_LCONTROL: | ||||
|             case VK_RCONTROL: | ||||
|                 return KeyType::KEY_CTRL; | ||||
|             case VK_MENU: | ||||
|             case VK_RMENU: | ||||
|             case VK_LMENU: | ||||
|                 return KeyType::KEY_ALT; | ||||
|             case VK_SHIFT: | ||||
|             case VK_RSHIFT: | ||||
|             case VK_LSHIFT: | ||||
|                 return KeyType::KEY_SHIFT; | ||||
|             case VK_LWIN: | ||||
|             case VK_RWIN: | ||||
|                 return KeyType::KEY_WIN; | ||||
|             default: | ||||
|                 return KeyType::KEY_NORMAL; | ||||
|         } | ||||
|         case WM_XBUTTONUP: { | ||||
|             auto mouse = (MouseHookStruct*) ptr_mouse; | ||||
|             auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData); | ||||
| 
 | ||||
|             mb_event = allocate_mb_event(); | ||||
|             mb_event->type = KeyEvent::RELEASE; | ||||
|             mb_event->key = "MOUSEX" + std::to_string(x_index); | ||||
|             break; | ||||
|         } | ||||
|         default: | ||||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     mb_event->next = nullptr; | ||||
|     mb_event->hook = thread_hook; | ||||
| 
 | ||||
|     EnterCriticalSection(&global_event_dispatcher->mutex); | ||||
|     *global_event_dispatcher->event_tail = mb_event; | ||||
|     global_event_dispatcher->event_tail = &mb_event->next; | ||||
|     WakeAllConditionVariable(&global_event_dispatcher->cv_work); | ||||
|     LeaveCriticalSection(&global_event_dispatcher->mutex); | ||||
|     return false; | ||||
| } | ||||
							
								
								
									
										68
									
								
								native/ppt/src/Win32KeyboardHook.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								native/ppt/src/Win32KeyboardHook.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "./KeyboardHook.h" | ||||
| #include <condition_variable> | ||||
| #include <Windows.h> | ||||
| 
 | ||||
| namespace hooks { | ||||
|     extern KeyboardHook::KeyType key_type_from_vk(DWORD vk_code); | ||||
|     extern std::string key_string_from_vk(DWORD code, bool extended); | ||||
|     extern std::string key_string_from_sc(USHORT code); | ||||
| 
 | ||||
|     class Win32SystemHook : public KeyboardHook { | ||||
|         public: | ||||
|             Win32SystemHook(); | ||||
| 
 | ||||
|             bool attach() override; | ||||
|             void detach() override; | ||||
| 
 | ||||
|             bool keytype_supported() const override { return true; } | ||||
|         private: | ||||
|             static LRESULT CALLBACK _keyboard_hook_callback(int, WPARAM, LPARAM); | ||||
|             static LRESULT CALLBACK _mouse_hook_callback(int, WPARAM, LPARAM); | ||||
| 
 | ||||
|             HHOOK keyboad_hook_id{nullptr}; | ||||
|             bool keyboard_hook_callback(int, WPARAM, LPARAM); | ||||
| 
 | ||||
|             HHOOK mouse_hook_id{nullptr}; | ||||
|             bool mouse_hook_callback(int, WPARAM, LPARAM); | ||||
| 
 | ||||
|             bool active{false}; | ||||
|             std::thread poll_thread; | ||||
|             void poll_events(); | ||||
|     }; | ||||
| 
 | ||||
|     class Win32RawHook : public KeyboardHook { | ||||
|         public: | ||||
|             Win32RawHook(); | ||||
| 
 | ||||
|             bool attach() override; | ||||
|             void detach() override; | ||||
| 
 | ||||
|             bool keytype_supported() const override { return true; } | ||||
|         private: | ||||
|             static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); | ||||
| 
 | ||||
|             std::thread wthread; | ||||
|             void wloop(); | ||||
| 
 | ||||
|             enum struct WorkerStatus { | ||||
|                 STOPPED, | ||||
|                 DIED, | ||||
| 
 | ||||
|                 INITIALIZING, | ||||
|                 RUNNING | ||||
|             }; | ||||
| 
 | ||||
|             bool wactive{false}; | ||||
|             WorkerStatus wstatus{WorkerStatus::STOPPED}; | ||||
|             std::mutex wstatus_mutex{}; | ||||
|             std::condition_variable wstatus_changed_cv{}; | ||||
|             std::string worker_died_reason{}; | ||||
| 
 | ||||
|             void set_wstatus(WorkerStatus); | ||||
|             void handle_raw_input(RAWINPUT&); | ||||
| 
 | ||||
|             HWND hwnd{0}; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										301
									
								
								native/ppt/src/Win32KeyboardHookLL.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								native/ppt/src/Win32KeyboardHookLL.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,301 @@ | ||||
| #include <iostream> | ||||
| #include <cassert> | ||||
| #include <string> | ||||
| #include <mutex> | ||||
| #include "./Win32KeyboardHook.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| namespace hooks { | ||||
| 	void init_global_ed(); | ||||
| 	void shutdown_global_ed(); | ||||
| 
 | ||||
| 	typedef KBDLLHOOKSTRUCT KeyboardHookStruct; | ||||
| 	thread_local Win32SystemHook* thread_hook{nullptr}; | ||||
| 
 | ||||
| 	Win32SystemHook::Win32SystemHook() : KeyboardHook{KeyboardHookType::SYSTEM_HOOK} {} | ||||
| 
 | ||||
| 	bool Win32SystemHook::attach() { | ||||
| 		if(!KeyboardHook::attach()) | ||||
| 			return false; | ||||
| 
 | ||||
| 		init_global_ed(); | ||||
| 
 | ||||
| 		this->active = true; | ||||
| 		this->poll_thread = std::thread(std::bind(&Win32SystemHook::poll_events, this)); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	void Win32SystemHook::detach() { | ||||
| 		this->active = false; | ||||
| 		{ | ||||
| 			//TODO trigger no message!
 | ||||
| 		} | ||||
| 		if(this->poll_thread.joinable()) | ||||
| 			this->poll_thread.join(); | ||||
| 
 | ||||
| 		/* will flush all events */ | ||||
| 		shutdown_global_ed(); | ||||
| 
 | ||||
|         KeyboardHook::detach(); | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	LRESULT Win32SystemHook::_mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { | ||||
| 		assert(thread_hook); | ||||
| 		auto consume = thread_hook->mouse_hook_callback(nCode, event, ptr_keyboard); | ||||
| 		auto end = std::chrono::high_resolution_clock::now(); | ||||
| 		if(consume) | ||||
| 			return 1; | ||||
| 		return CallNextHookEx(nullptr, nCode, event, ptr_keyboard); | ||||
| 	} | ||||
| 
 | ||||
| 	LRESULT Win32SystemHook::_keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { | ||||
| 		assert(thread_hook); | ||||
| 		auto consume = thread_hook->keyboard_hook_callback(nCode, event, ptr_keyboard); | ||||
| 		if(consume) | ||||
| 			return 1; | ||||
| 		return CallNextHookEx(nullptr, nCode, event, ptr_keyboard); | ||||
| 	} | ||||
| 
 | ||||
| 	void Win32SystemHook::poll_events() { | ||||
| 		thread_hook = this; | ||||
| 
 | ||||
| 		if(!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) | ||||
| 			std::cerr << "Failed to set priority class to realtime!" << std::endl; | ||||
| 
 | ||||
| 	#if 0 | ||||
| 		std::string msg_box{"Priority Class: "}; | ||||
| 		msg_box += std::to_string((int) GetPriorityClass(GetCurrentProcess())); | ||||
| 		msg_box += " Priority: "; | ||||
| 		msg_box += std::to_string((int) GetThreadPriority(GetCurrentProcess())); | ||||
| 		MessageBox(nullptr, msg_box.c_str(), "PPT-Thread", MB_OK); | ||||
| 	#endif | ||||
| 	#if 0 | ||||
| 		int    cur_priority		  = GetThreadPriority(GetCurrentThread()); | ||||
| 		DWORD  cur_priority_class = GetPriorityClass(GetCurrentProcess()); | ||||
| 		std::cout << "P: " << cur_priority << " C: " << cur_priority_class << std::endl; | ||||
| 	#endif | ||||
| 
 | ||||
| 		this->keyboad_hook_id = SetWindowsHookEx(WH_KEYBOARD_LL, &Win32SystemHook::_keyboard_hook_callback, GetModuleHandle(nullptr), 0); | ||||
| 		if(!this->keyboad_hook_id) { | ||||
| 			cerr << "Failed to register keyboard hook" << endl; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| #if 1 | ||||
| 		this->mouse_hook_id = SetWindowsHookEx(WH_MOUSE_LL, &Win32SystemHook::_mouse_hook_callback, GetModuleHandle(nullptr), 0); | ||||
| 		if(!this->keyboad_hook_id) { | ||||
| 			UnhookWindowsHookEx(this->keyboad_hook_id); | ||||
| 			cerr << "Failed to register mouse hook" << endl; | ||||
| 			return; | ||||
| 		} | ||||
| #else | ||||
| 		this->mouse_hook_id = 0; | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| 		MSG msg; | ||||
| 		while(!GetMessage(&msg, nullptr, 0, 0) && this->active) { | ||||
| 			TranslateMessage(&msg); | ||||
| 			DispatchMessage(&msg); | ||||
| 		} | ||||
| 
 | ||||
| 		if(this->mouse_hook_id > 0) | ||||
| 			UnhookWindowsHookEx(this->mouse_hook_id); | ||||
| 		UnhookWindowsHookEx(this->keyboad_hook_id); | ||||
| 		thread_hook = nullptr; | ||||
| 	} | ||||
| 
 | ||||
| 	inline std::string key_code(KeyboardHookStruct* keyboard) { | ||||
| 		return key_string_from_vk(keyboard->vkCode, (keyboard->flags & LLKHF_EXTENDED) > 0); | ||||
| 	} | ||||
| 
 | ||||
| 	using KeyType = KeyboardHook::KeyType; | ||||
| 
 | ||||
| 	bool Win32SystemHook::keyboard_hook_callback(int nCode, WPARAM event, LPARAM ptr_keyboard) { | ||||
| 		auto keyboard = (KeyboardHookStruct*) ptr_keyboard; | ||||
| 		if(event == WM_KEYDOWN || event == WM_SYSKEYDOWN) { | ||||
| 			auto& state = this->map_key[keyboard->vkCode]; | ||||
| 			bool typed = state; | ||||
| 			state = true; | ||||
| 
 | ||||
| 			auto type = key_type_from_vk(keyboard->vkCode); | ||||
| 			if(type != KeyType::KEY_NORMAL) | ||||
| 				this->map_special[type] = true; | ||||
| 			else | ||||
| 				this->map_special[type] = keyboard->vkCode; | ||||
| 
 | ||||
| 			if(!typed) | ||||
| 				this->trigger_key_event(KeyEvent::PRESS, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
| 			this->trigger_key_event(KeyEvent::TYPE, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
| 		} else if(event == WM_KEYUP || event == WM_SYSKEYUP) { | ||||
| 			auto& state = this->map_key[keyboard->vkCode]; | ||||
| 			if(!state) return false; //Duplicate
 | ||||
| 			state = false; | ||||
| 
 | ||||
| 			auto type = key_type_from_vk(keyboard->vkCode); | ||||
| 			if(type != KeyType::KEY_NORMAL) | ||||
| 				this->map_special[type] = false; | ||||
| 			else if(this->map_special[KeyType::KEY_NORMAL] == keyboard->vkCode) | ||||
| 				this->map_special[KeyType::KEY_NORMAL] = 0; | ||||
| 
 | ||||
| 			this->trigger_key_event(KeyEvent::RELEASE, type == KeyType::KEY_NORMAL ? key_code(keyboard) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
| 		} | ||||
| 
 | ||||
| 		//Consume the event: return 1
 | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	typedef MSLLHOOKSTRUCT MouseLLHookStruct; | ||||
| 
 | ||||
| 	struct MouseButtonEventEntry { | ||||
| 		MouseButtonEventEntry* next; | ||||
| 		KeyboardHook* hook; | ||||
| 
 | ||||
| 		enum KeyboardHook::KeyEvent::type type; | ||||
| 		std::string key; | ||||
| 	}; | ||||
| 
 | ||||
| 	inline MouseButtonEventEntry* allocate_mb_event() { | ||||
| 		return new MouseButtonEventEntry{}; | ||||
| 	} | ||||
| 
 | ||||
| 	inline void delete_mb_event(MouseButtonEventEntry* event) { | ||||
| 		delete event; | ||||
| 	} | ||||
| 
 | ||||
| 	struct MouseButtonEventDispatcher { | ||||
| 		bool active{true}; | ||||
| 
 | ||||
| 		std::thread dispatcher{}; | ||||
| 
 | ||||
| 		CRITICAL_SECTION mutex; | ||||
| 		CONDITION_VARIABLE cv_flushed; | ||||
| 		CONDITION_VARIABLE cv_work; | ||||
| 
 | ||||
| 		MouseButtonEventEntry* event_head{nullptr}; | ||||
| 		MouseButtonEventEntry** event_tail{&event_head}; | ||||
| 	}; | ||||
| 
 | ||||
| 	MouseButtonEventDispatcher* global_event_dispatcher{}; | ||||
| 	size_t global_ed_ref_count{0}; | ||||
| 
 | ||||
| 	void init_global_ed() { | ||||
| 		if(global_event_dispatcher) { | ||||
| 			global_ed_ref_count++; | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		global_event_dispatcher = new MouseButtonEventDispatcher{}; | ||||
| 		InitializeCriticalSection(&global_event_dispatcher->mutex); | ||||
| 		InitializeConditionVariable(&global_event_dispatcher->cv_flushed); | ||||
| 		InitializeConditionVariable(&global_event_dispatcher->cv_work); | ||||
| 
 | ||||
| 		global_event_dispatcher->dispatcher = std::thread([]{ | ||||
| 			auto ed = global_event_dispatcher; | ||||
| 
 | ||||
| 			while(ed->active) { | ||||
| 				MouseButtonEventEntry* entry{nullptr}; | ||||
| 				{ | ||||
| 					EnterCriticalSection(&ed->mutex); | ||||
| 					while(!global_event_dispatcher->event_head && ed->active) { | ||||
| 						WakeAllConditionVariable(&ed->cv_flushed); | ||||
| 						SleepConditionVariableCS(&ed->cv_work, &ed->mutex, INFINITE); | ||||
| 					} | ||||
| 
 | ||||
| 					entry = global_event_dispatcher->event_head; | ||||
| 					global_event_dispatcher->event_head = nullptr; | ||||
| 					global_event_dispatcher->event_tail = &global_event_dispatcher->event_head; | ||||
| 					LeaveCriticalSection(&ed->mutex); | ||||
| 				} | ||||
| 
 | ||||
| 				while(entry) { | ||||
| 					entry->hook->trigger_key_event(entry->type, std::string{entry->key}); | ||||
| 
 | ||||
| 					auto next = entry->next; | ||||
| 					delete_mb_event(entry); | ||||
| 					entry = next; | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	void shutdown_global_ed() { | ||||
| 		/* flush all events */ | ||||
| 		EnterCriticalSection(&global_event_dispatcher->mutex); | ||||
| 		WakeAllConditionVariable(&global_event_dispatcher->cv_work); | ||||
| 		SleepConditionVariableCS(&global_event_dispatcher->cv_flushed, &global_event_dispatcher->mutex, INFINITE); | ||||
| 		LeaveCriticalSection(&global_event_dispatcher->mutex); | ||||
| 
 | ||||
| 		if(--global_ed_ref_count > 0) return; | ||||
| 
 | ||||
| 		auto ed = std::exchange(global_event_dispatcher, nullptr); | ||||
| 		ed->active = false; | ||||
| 		WakeAllConditionVariable(&ed->cv_work); | ||||
| 
 | ||||
| 		if(ed->dispatcher.joinable()) | ||||
| 			ed->dispatcher.join(); | ||||
| 
 | ||||
| 		DeleteCriticalSection(&ed->mutex); | ||||
| 		delete ed; | ||||
| 	} | ||||
| 
 | ||||
| 	bool Win32SystemHook::mouse_hook_callback(int nCode, WPARAM event, LPARAM ptr_mouse) { | ||||
| 		MouseButtonEventEntry* mb_event; | ||||
| 		switch (event) { | ||||
| 			case WM_LBUTTONDOWN: | ||||
| 				mb_event = allocate_mb_event(); | ||||
| 				mb_event->type = KeyEvent::PRESS; | ||||
| 				mb_event->key = "MOUSE1"; | ||||
| 				break; | ||||
| 			case WM_LBUTTONUP: | ||||
| 				mb_event = allocate_mb_event(); | ||||
| 				mb_event->type = KeyEvent::RELEASE; | ||||
| 				mb_event->key = "MOUSE1"; | ||||
| 				break; | ||||
| 
 | ||||
| 			case WM_RBUTTONDOWN: | ||||
| 				mb_event = allocate_mb_event(); | ||||
| 				mb_event->type = KeyEvent::PRESS; | ||||
| 				mb_event->key = "MOUSE3"; | ||||
| 				break; | ||||
| 			case WM_RBUTTONUP: | ||||
| 				mb_event = allocate_mb_event(); | ||||
| 				mb_event->type = KeyEvent::RELEASE; | ||||
| 				mb_event->key = "MOUSE3"; | ||||
| 				break; | ||||
| 
 | ||||
| 			case WM_XBUTTONDOWN: { | ||||
| 				auto mouse = (MouseLLHookStruct*) ptr_mouse; | ||||
| 				auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData); | ||||
| 
 | ||||
| 				mb_event = allocate_mb_event(); | ||||
| 				mb_event->type = KeyEvent::PRESS; | ||||
| 				mb_event->key = "MOUSEX" + std::to_string(x_index); | ||||
| 				break; | ||||
| 			} | ||||
| 			case WM_XBUTTONUP: { | ||||
| 				auto mouse = (MouseLLHookStruct*) ptr_mouse; | ||||
| 				auto x_index = GET_XBUTTON_WPARAM(mouse->mouseData); | ||||
| 
 | ||||
| 				mb_event = allocate_mb_event(); | ||||
| 				mb_event->type = KeyEvent::RELEASE; | ||||
| 				mb_event->key = "MOUSEX" + std::to_string(x_index); | ||||
| 				break; | ||||
| 			} | ||||
| 			default: | ||||
| 				return false; | ||||
| 		} | ||||
| 
 | ||||
| 		mb_event->next = nullptr; | ||||
| 		mb_event->hook = thread_hook; | ||||
| 
 | ||||
| 		EnterCriticalSection(&global_event_dispatcher->mutex); | ||||
| 		*global_event_dispatcher->event_tail = mb_event; | ||||
| 		global_event_dispatcher->event_tail = &mb_event->next; | ||||
| 		WakeAllConditionVariable(&global_event_dispatcher->cv_work); | ||||
| 		LeaveCriticalSection(&global_event_dispatcher->mutex); | ||||
| 		return false; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										200
									
								
								native/ppt/src/Win32KeyboardRawInput.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								native/ppt/src/Win32KeyboardRawInput.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,200 @@ | ||||
| //
 | ||||
| // Created by WolverinDEV on 01/05/2020.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "./Win32KeyboardHook.h" | ||||
| #include <iostream> | ||||
| #include <WinUser.h> | ||||
| 
 | ||||
| namespace hooks { | ||||
|     Win32RawHook::Win32RawHook() : KeyboardHook{KeyboardHookType::RAW_INPUT} {} | ||||
| 
 | ||||
|     bool Win32RawHook::attach() { | ||||
|         if(!KeyboardHook::attach()) | ||||
|             return false; | ||||
| 
 | ||||
|         this->wactive = true; | ||||
|         this->set_wstatus(WorkerStatus::INITIALIZING); | ||||
|         this->wthread = std::thread(std::bind(&Win32RawHook::wloop, this)); | ||||
| 
 | ||||
|         std::unique_lock ws_lock{this->wstatus_mutex}; | ||||
|         this->wstatus_changed_cv.wait(ws_lock, [&]{ | ||||
|             return this->wstatus == WorkerStatus::RUNNING || this->wstatus == WorkerStatus::DIED; | ||||
|         }); | ||||
| 
 | ||||
|         return this->wstatus == WorkerStatus::RUNNING; | ||||
|     } | ||||
| 
 | ||||
|     void Win32RawHook::detach() { | ||||
|         this->wactive = false; | ||||
|         { | ||||
|             //TODO trigger no message!
 | ||||
|         } | ||||
| 
 | ||||
|         if(this->wthread.joinable()) | ||||
|             this->wthread.join(); | ||||
|         this->set_wstatus(WorkerStatus::STOPPED); | ||||
| 
 | ||||
|         KeyboardHook::detach(); | ||||
|     } | ||||
| 
 | ||||
|     void Win32RawHook::set_wstatus(WorkerStatus status) { | ||||
|         std::lock_guard ws_lock{this->wstatus_mutex}; | ||||
|         if(this->wstatus == status) return; | ||||
|         this->wstatus = status; | ||||
|         this->wstatus_changed_cv.notify_all(); | ||||
|     } | ||||
| 
 | ||||
| #define WORKER_CLASS_NAME ("TeaClient - KeyHook worker") | ||||
|     void Win32RawHook::wloop() { | ||||
|         this->set_wstatus(WorkerStatus::INITIALIZING); | ||||
| 
 | ||||
|         /* setup */ | ||||
|         { | ||||
|             { | ||||
|                 WNDCLASS wc = {0}; | ||||
|                 wc.lpfnWndProc = window_proc; | ||||
|                 wc.cbWndExtra = sizeof(void*); | ||||
|                 wc.hInstance = 0; | ||||
|                 wc.lpszClassName = WORKER_CLASS_NAME; | ||||
|                 RegisterClass(&wc); | ||||
|             } | ||||
| 
 | ||||
|             this->hwnd = CreateWindow(WORKER_CLASS_NAME, "TeaClient - KeyHook worker window", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, this); | ||||
|             if(!this->hwnd) { | ||||
|                 this->worker_died_reason = "Failed to create window"; | ||||
|                 this->set_wstatus(WorkerStatus::DIED); | ||||
|                 goto cleanup; | ||||
|             } | ||||
| 
 | ||||
|             RAWINPUTDEVICE devices[2]; | ||||
|             devices[0].usUsagePage  = 0x01; //HID_USAGE_PAGE_GENERIC;
 | ||||
|             devices[0].usUsage      = 0x02; //HID_USAGE_GENERIC_MOUSE;
 | ||||
|             devices[0].dwFlags      = RIDEV_NOLEGACY | RIDEV_INPUTSINK; | ||||
|             devices[0].hwndTarget   = hwnd; | ||||
| 
 | ||||
|             devices[1].usUsagePage  = 0x01; //HID_USAGE_PAGE_GENERIC;
 | ||||
|             devices[1].usUsage      = 0x06; //HID_USAGE_GENERIC_KEYBOARD;
 | ||||
|             devices[1].dwFlags      = RIDEV_NOLEGACY | RIDEV_INPUTSINK; | ||||
|             devices[1].hwndTarget   = hwnd; | ||||
| 
 | ||||
|             if(!RegisterRawInputDevices(devices, 2, sizeof *devices)) { | ||||
|                 this->worker_died_reason = "failed to register raw input devices"; | ||||
|                 this->set_wstatus(WorkerStatus::DIED); | ||||
|                 goto cleanup; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         this->set_wstatus(WorkerStatus::RUNNING); | ||||
|         BOOL ret; | ||||
|         MSG msg; | ||||
|         while (this->wactive) { | ||||
|             ret = GetMessage(&msg, this->hwnd, 0, 0); | ||||
|             if(ret == 0) | ||||
|                 break; | ||||
|             if (ret == -1) { | ||||
|                 this->worker_died_reason = "GetMessage() threw an error"; | ||||
|                 this->set_wstatus(WorkerStatus::DIED); | ||||
|                 goto cleanup; | ||||
|             } | ||||
|             else { | ||||
|                 TranslateMessage(&msg); | ||||
|                 DispatchMessage(&msg); | ||||
|             } | ||||
|         } | ||||
|         this->set_wstatus(WorkerStatus::STOPPED); | ||||
| 
 | ||||
|         cleanup: | ||||
|         if(this->hwnd > 0) { | ||||
|             DestroyWindow(this->hwnd); | ||||
|             this->hwnd = 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     LRESULT Win32RawHook::window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { | ||||
|         auto hook = reinterpret_cast<Win32RawHook *>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); | ||||
| 
 | ||||
|         switch (msg) { | ||||
|             case WM_CREATE: { | ||||
|                 CREATESTRUCT *s = reinterpret_cast<CREATESTRUCT *>(lp); | ||||
|                 SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) s->lpCreateParams); | ||||
|                 return 0; | ||||
|             } | ||||
|             case WM_CLOSE: | ||||
|                 DestroyWindow(hwnd); | ||||
|                 PostQuitMessage(0); | ||||
|                 return 0; | ||||
| 
 | ||||
|             case WM_INPUT: { | ||||
|                 UINT target_size{0}; | ||||
|                 GetRawInputData((HRAWINPUT) lp, RID_INPUT, NULL, &target_size, sizeof(RAWINPUTHEADER)); | ||||
|                 if(target_size > sizeof(RAWINPUT)) { | ||||
|                     std::cerr << "Failed to retrieve input (Target size is longer than expected)" << std::endl; | ||||
|                     return 0; | ||||
|                 } | ||||
|                 RAWINPUT input{}; | ||||
|                 GetRawInputData((HRAWINPUT) lp, RID_INPUT, &input, &target_size, sizeof(RAWINPUTHEADER)); | ||||
| 
 | ||||
|                 hook->handle_raw_input(input); | ||||
|                 return 0; | ||||
|             } | ||||
|             default: | ||||
|                 return DefWindowProc(hwnd, msg, wp, lp); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void Win32RawHook::handle_raw_input(RAWINPUT &input) { | ||||
|         if(input.header.dwType == RIM_TYPEMOUSE) { | ||||
|             auto& data = input.data.mouse; | ||||
|             if(data.ulButtons == 0) return; /* mouse move event */ | ||||
| 
 | ||||
| #define BUTTON_EVENT(number, name) \ | ||||
|             case RI_MOUSE_BUTTON_ ##number ##_DOWN: \ | ||||
|                 this->trigger_key_event(KeyEvent::PRESS, name); \ | ||||
|                 break; \ | ||||
|             case RI_MOUSE_BUTTON_ ##number ##_UP: \ | ||||
|                 this->trigger_key_event(KeyEvent::RELEASE, name); \ | ||||
|                 break | ||||
| 
 | ||||
|             switch (data.ulButtons) { | ||||
|                 BUTTON_EVENT(1, "MOUSE1"); | ||||
|                 BUTTON_EVENT(2, "MOUSE2"); | ||||
|                 BUTTON_EVENT(3, "MOUSE3"); | ||||
|                 BUTTON_EVENT(4, "MOUSEX1"); | ||||
|                 BUTTON_EVENT(5, "MOUSEX2"); | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|         } else if(input.header.dwType == RIM_TYPEKEYBOARD) { | ||||
|             auto& data = input.data.keyboard; | ||||
| 
 | ||||
|             if(data.Message == WM_KEYDOWN || data.Message == WM_SYSKEYDOWN) { | ||||
|                 auto& state = this->map_key[data.VKey]; | ||||
|                 bool typed = state; | ||||
|                 state = true; | ||||
| 
 | ||||
|                 auto type = key_type_from_vk(data.VKey); | ||||
|                 if(type != KeyType::KEY_NORMAL) | ||||
|                     this->map_special[type] = true; | ||||
|                 else | ||||
|                     this->map_special[type] = data.VKey; | ||||
| 
 | ||||
|                 if(!typed) | ||||
|                     this->trigger_key_event(KeyEvent::PRESS, type == KeyType::KEY_NORMAL ? key_string_from_sc(data.MakeCode) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
|                 this->trigger_key_event(KeyEvent::TYPE, type == KeyType::KEY_NORMAL ? key_string_from_sc(data.MakeCode) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
|             } else if(data.Message == WM_KEYUP || data.Message == WM_SYSKEYUP) { | ||||
|                 auto& state = this->map_key[data.VKey]; | ||||
|                 if(!state) return; //Duplicate
 | ||||
|                 state = false; | ||||
| 
 | ||||
|                 auto type = key_type_from_vk(data.VKey); | ||||
|                 if(type != KeyType::KEY_NORMAL) | ||||
|                     this->map_special[type] = false; | ||||
|                 else if(this->map_special[KeyType::KEY_NORMAL] == data.VKey) | ||||
|                     this->map_special[KeyType::KEY_NORMAL] = 0; | ||||
| 
 | ||||
|                 this->trigger_key_event(KeyEvent::RELEASE, type == KeyType::KEY_NORMAL ? key_string_from_sc(data.MakeCode) : key_string_from_vk(this->map_special[KeyType::KEY_NORMAL], false)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,13 +1,83 @@ | ||||
| #include "../src/KeyboardHook.h" | ||||
| #include "../src/Win32KeyboardHook.h" | ||||
| #include <iostream> | ||||
| #include <thread> | ||||
| #include <Windows.h> | ||||
| 
 | ||||
| using namespace std::chrono; | ||||
| using namespace std; | ||||
| 
 | ||||
| #if 0 | ||||
| LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { | ||||
| 	// Retrieve "this" pointer
 | ||||
| 	// Guaranteed to be garbage until WM_CREATE finishes, but
 | ||||
| 	// we don't actually use this value until WM_CREATE writes a valid one
 | ||||
| 	//vrpn_DirectXRumblePad *me = reinterpret_cast<vrpn_DirectXRumblePad *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
 | ||||
| 
 | ||||
| 	switch (msg) { | ||||
| 		// Window is being created; store "this" pointer for future retrieval
 | ||||
| 		case WM_CREATE: { | ||||
| 			CREATESTRUCT *s = reinterpret_cast<CREATESTRUCT *>(lp); | ||||
| 			SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) s->lpCreateParams); | ||||
| 			return 0; | ||||
| 		} | ||||
| 			// Something (most likely ~vrpn_DirectXRumblePad) wants to close the window
 | ||||
| 			// Go ahead and signal shutdown
 | ||||
| 		case WM_CLOSE: | ||||
| 			DestroyWindow(hwnd); | ||||
| 			PostQuitMessage(0); | ||||
| 			break; | ||||
| 
 | ||||
| 		case WM_INPUT: { | ||||
| 			UINT dwSize; | ||||
| 			UINT buffer_size = sizeof(RAWINPUT); | ||||
| 			GetRawInputData((HRAWINPUT) lp, RID_INPUT, NULL, &dwSize,sizeof(RAWINPUTHEADER)); | ||||
| 			if(dwSize > buffer_size) { | ||||
| 				std::cerr << "Failed to retreive input" << std::endl; | ||||
| 				return 0; | ||||
| 			} | ||||
| 			RAWINPUT input{}; | ||||
| 			GetRawInputData((HRAWINPUT) lp, RID_INPUT, &input, &buffer_size, sizeof(RAWINPUTHEADER)); | ||||
| 
 | ||||
| 			if(input.header.dwType != RIM_TYPEMOUSE) | ||||
| 				return 0; | ||||
| 
 | ||||
| 			auto& mouse_data = input.data.mouse; | ||||
| 			std::cout << "Input" << std::endl; | ||||
| 			std::cout << "Buttons: " << (int) mouse_data.ulButtons << std::endl; | ||||
| 		} | ||||
| 
 | ||||
| 			// Everything not explicitly handled goes to DefWindowProc as per usual
 | ||||
| 		default: | ||||
| 			return DefWindowProc(hwnd, msg, wp, lp); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| std::string GetLastErrorAsString() { | ||||
| 	//Get the error message, if any.
 | ||||
| 	DWORD errorMessageID = ::GetLastError(); | ||||
| 	if(errorMessageID == 0) | ||||
| 		return std::string(); //No error message has been recorded
 | ||||
| 
 | ||||
| 	LPSTR messageBuffer = nullptr; | ||||
| 	size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | ||||
| 								 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); | ||||
| 
 | ||||
| 	std::string message(messageBuffer, size); | ||||
| 
 | ||||
| 	//Free the buffer.
 | ||||
| 	LocalFree(messageBuffer); | ||||
| 
 | ||||
| 	return message; | ||||
| } | ||||
| 
 | ||||
| int main() { | ||||
| 	KeyboardHook hook; | ||||
| 	hook.callback_event = [](const shared_ptr<KeyboardHook::KeyEvent>& event) { | ||||
| #if 1 | ||||
| 	KeyboardHook* hook = new hooks::Win32RawHook{}; | ||||
| 	hook->callback_event = [](const shared_ptr<KeyboardHook::KeyEvent>& event) { | ||||
| 		if(event->type == KeyboardHook::KeyEvent::PRESS) | ||||
| 			cout << "press   " << event->code.c_str() << ": shift: " << event->key_shift << ", alt: " << event->key_alt << ", ctrl: " << event->key_ctrl << ", win: " << event->key_windows << endl; | ||||
| 		else if(event->type == KeyboardHook::KeyEvent::TYPE) | ||||
| @ -16,11 +86,45 @@ int main() { | ||||
| 			cout << "release " << event->code.c_str() << ": shift: " << event->key_shift << ", alt: " << event->key_alt << ", ctrl: " << event->key_ctrl << ", win: " << event->key_windows << endl; | ||||
| 	}; | ||||
| 
 | ||||
| 	if(!hook.attach()) { | ||||
| 	if(!hook->attach()) { | ||||
| 		cerr << "failed to attach!" << endl; | ||||
| 		return 0; | ||||
| 	} | ||||
| #else | ||||
| #define CLASS_NAME "TeaClient - Hook" | ||||
| 	WNDCLASS wc = {0}; | ||||
| 	wc.lpfnWndProc = window_proc; | ||||
| 	wc.cbWndExtra = sizeof(void*); | ||||
| 	wc.hInstance = 0; | ||||
| 	wc.lpszClassName = CLASS_NAME; | ||||
| 	RegisterClass(&wc); | ||||
| 
 | ||||
| 	this_thread::sleep_for(seconds(100)); | ||||
| 	auto hwnd = CreateWindow(CLASS_NAME, "TeaClient - PPT hook", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, 0); | ||||
| 
 | ||||
| 	RAWINPUTDEVICE device; | ||||
| 	device.usUsagePage  = 0x01; //HID_USAGE_PAGE_GENERIC;
 | ||||
| 	device.usUsage      = 0x02; //HID_USAGE_GENERIC_MOUSE;
 | ||||
| 	//device.usUsage      = 0x06; //HID_USAGE_GENERIC_KEYBOARD;
 | ||||
| 	device.dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK; | ||||
| 	device.hwndTarget = hwnd; | ||||
| 	if(!RegisterRawInputDevices(&device, 1, sizeof device)) { | ||||
| 		std::cerr << "Invalid: " << GetLastErrorAsString() << std::endl; | ||||
| 	} | ||||
| 
 | ||||
| 	BOOL ret; | ||||
| 	MSG msg; | ||||
| 	while ((ret = GetMessage(&msg, hwnd, 0, 0)) != 0) { | ||||
| 		if (ret == -1) { | ||||
| 			std::cerr << "GetMessage() threw an error." << std::endl; | ||||
| 			return 1; | ||||
| 		} | ||||
| 		else { | ||||
| 			TranslateMessage(&msg); | ||||
| 			DispatchMessage(&msg); | ||||
| 		} | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	this_thread::sleep_for(seconds(10)); | ||||
| 	return 0; | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user