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)
|
set(SOURCE_FILES src/KeyboardHook.cpp)
|
||||||
if (MSVC)
|
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)
|
add_definitions(-DUSING_UV_SHARED)
|
||||||
else()
|
else()
|
||||||
add_definitions(-DHAVE_X11)
|
add_definitions(-DHAVE_X11)
|
||||||
|
@ -8,7 +8,12 @@ using namespace std;
|
|||||||
|
|
||||||
#include "include/NanException.h"
|
#include "include/NanException.h"
|
||||||
#include "include/NanEventCallback.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;
|
std::mutex callback_lock;
|
||||||
@ -62,7 +67,7 @@ NAN_METHOD(UnregisterCallback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NAN_MODULE_INIT(init) {
|
NAN_MODULE_INIT(init) {
|
||||||
hook = make_unique<KeyboardHook>();
|
hook = make_unique<hooks::Win32RawHook>();
|
||||||
if(!hook->attach()) {
|
if(!hook->attach()) {
|
||||||
NAN_THROW_EXCEPTION(Error, "Failed to attach hook!");
|
NAN_THROW_EXCEPTION(Error, "Failed to attach hook!");
|
||||||
return;
|
return;
|
||||||
|
@ -1,6 +1,26 @@
|
|||||||
#include "KeyboardHook.h"
|
#include "./KeyboardHook.h"
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using namespace std;
|
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) {
|
void KeyboardHook::trigger_key_event(const enum KeyEvent::type& type, const std::string &key) {
|
||||||
if(!this->callback_event) return;
|
if(!this->callback_event) return;
|
||||||
|
|
||||||
|
@ -4,19 +4,28 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#ifdef HAVE_X11
|
//#define HOOK_X11
|
||||||
|
|
||||||
|
#if defined(HOOK_X11)
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#elif defined(WIN32)
|
|
||||||
#include <Windows.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum struct KeyboardHookType {
|
||||||
|
X11,
|
||||||
|
|
||||||
|
RAW_INPUT,
|
||||||
|
SYSTEM_HOOK
|
||||||
|
};
|
||||||
|
|
||||||
class KeyboardHook {
|
class KeyboardHook {
|
||||||
#if defined(WIN32)
|
#ifdef HOOK_WIN32_LL
|
||||||
friend LRESULT CALLBACK _keyboard_hook_callback(int, WPARAM, LPARAM);
|
friend LRESULT CALLBACK _keyboard_hook_callback(int, WPARAM, LPARAM);
|
||||||
friend LRESULT CALLBACK _mouse_hook_callback(int, WPARAM, LPARAM);
|
friend LRESULT CALLBACK _mouse_hook_callback(int, WPARAM, LPARAM);
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
struct KeyType {
|
typedef unsigned int KeyID;
|
||||||
enum value {
|
|
||||||
|
enum struct KeyType {
|
||||||
KEY_UNKNOWN,
|
KEY_UNKNOWN,
|
||||||
|
|
||||||
KEY_NORMAL,
|
KEY_NORMAL,
|
||||||
@ -25,7 +34,6 @@ class KeyboardHook {
|
|||||||
KEY_WIN,
|
KEY_WIN,
|
||||||
KEY_CTRL
|
KEY_CTRL
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
struct KeyEvent {
|
struct KeyEvent {
|
||||||
enum type {
|
enum type {
|
||||||
@ -45,17 +53,23 @@ class KeyboardHook {
|
|||||||
};
|
};
|
||||||
typedef std::function<void(const std::shared_ptr<KeyEvent>& /* event */)> callback_event_t;
|
typedef std::function<void(const std::shared_ptr<KeyEvent>& /* event */)> callback_event_t;
|
||||||
|
|
||||||
KeyboardHook();
|
KeyboardHook(KeyboardHookType);
|
||||||
virtual ~KeyboardHook();
|
virtual ~KeyboardHook();
|
||||||
|
|
||||||
bool attach();
|
[[nodiscard]] inline KeyboardHookType type() const { return this->type_; }
|
||||||
inline bool attached() { return this->_attached; }
|
[[nodiscard]] virtual bool keytype_supported() const = 0;
|
||||||
void detach();
|
|
||||||
|
[[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 */);
|
void trigger_key_event(const enum KeyEvent::type&, const std::string& /* key */);
|
||||||
callback_event_t callback_event;
|
callback_event_t callback_event;
|
||||||
private:
|
protected:
|
||||||
#ifdef HAVE_X11
|
const KeyboardHookType type_;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#if defined(HOOK_X11)
|
||||||
typedef int KeyID;
|
typedef int KeyID;
|
||||||
Display* display = nullptr;
|
Display* display = nullptr;
|
||||||
Window window_root = 0;
|
Window window_root = 0;
|
||||||
@ -63,20 +77,20 @@ class KeyboardHook {
|
|||||||
int focus_revert;
|
int focus_revert;
|
||||||
|
|
||||||
long end_id = 0;
|
long end_id = 0;
|
||||||
#elif defined(WIN32)
|
#elseif defined(HOOK_WIN32_LL)
|
||||||
typedef UINT KeyID;
|
typedef UINT KeyID;
|
||||||
HHOOK keyboad_hook_id{nullptr};
|
HHOOK keyboad_hook_id{nullptr};
|
||||||
HHOOK mouse_hook_id{nullptr};
|
|
||||||
|
|
||||||
bool keyboard_hook_callback(int, WPARAM, LPARAM);
|
bool keyboard_hook_callback(int, WPARAM, LPARAM);
|
||||||
|
|
||||||
|
#ifdef USE_MOUSE_HOOK
|
||||||
|
HHOOK mouse_hook_id{nullptr};
|
||||||
bool mouse_hook_callback(int, WPARAM, LPARAM);
|
bool mouse_hook_callback(int, WPARAM, LPARAM);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
std::map<KeyID, bool> map_key;
|
std::map<KeyID, bool> map_key;
|
||||||
std::map<KeyID, KeyID> map_special;
|
std::map<KeyType, KeyID> map_special;
|
||||||
|
|
||||||
bool _attached = false;
|
bool _attached = false;
|
||||||
bool active = false;
|
|
||||||
std::thread poll_thread;
|
|
||||||
void poll_events();
|
|
||||||
};
|
};
|
@ -1,193 +1,11 @@
|
|||||||
#include <iostream>
|
//
|
||||||
#include <cassert>
|
// Created by WolverinDEV on 01/05/2020.
|
||||||
#include <string>
|
//
|
||||||
#include <mutex>
|
|
||||||
#include "KeyboardHook.h"
|
|
||||||
|
|
||||||
using namespace std;
|
#include "./Win32KeyboardHook.h"
|
||||||
|
|
||||||
typedef KBDLLHOOKSTRUCT KeyboardHookStruct;
|
namespace hooks {
|
||||||
typedef MSLLHOOKSTRUCT MouseHookStruct;
|
std::string key_string_from_vk(DWORD code, bool extended) {
|
||||||
thread_local KeyboardHook* thread_hook{nullptr};
|
|
||||||
|
|
||||||
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() {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSG msg;
|
|
||||||
while(!GetMessage(&msg, nullptr, 0, 0) && this->active) {
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessage(&msg);
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds{1});
|
|
||||||
|
|
||||||
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);
|
auto scan_code = MapVirtualKey(code, MAPVK_VK_TO_VSC);
|
||||||
if(extended)
|
if(extended)
|
||||||
scan_code |= KF_EXTENDED;
|
scan_code |= KF_EXTENDED;
|
||||||
@ -197,16 +15,22 @@ inline std::string key_code(DWORD code, bool extended = false) {
|
|||||||
if(length == 0)
|
if(length == 0)
|
||||||
return "error";
|
return "error";
|
||||||
else
|
else
|
||||||
return string(key_buffer, length);
|
return std::string{key_buffer, (size_t) length};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string key_code(KeyboardHookStruct* keyboard) {
|
std::string key_string_from_sc(USHORT code) {
|
||||||
return key_code(keyboard->vkCode, (keyboard->flags & LLKHF_EXTENDED) > 0);
|
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};
|
||||||
|
}
|
||||||
|
|
||||||
|
//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;
|
||||||
|
|
||||||
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) {
|
switch(vk_code) {
|
||||||
case VK_CONTROL:
|
case VK_CONTROL:
|
||||||
case VK_LCONTROL:
|
case VK_LCONTROL:
|
||||||
@ -226,96 +50,5 @@ inline KeyType::value key_type(DWORD vk_code) {
|
|||||||
default:
|
default:
|
||||||
return KeyType::KEY_NORMAL;
|
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;
|
|
||||||
}
|
|
||||||
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/KeyboardHook.h"
|
||||||
|
#include "../src/Win32KeyboardHook.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using namespace std;
|
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() {
|
int main() {
|
||||||
KeyboardHook hook;
|
#if 1
|
||||||
hook.callback_event = [](const shared_ptr<KeyboardHook::KeyEvent>& event) {
|
KeyboardHook* hook = new hooks::Win32RawHook{};
|
||||||
|
hook->callback_event = [](const shared_ptr<KeyboardHook::KeyEvent>& event) {
|
||||||
if(event->type == KeyboardHook::KeyEvent::PRESS)
|
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;
|
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)
|
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;
|
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;
|
cerr << "failed to attach!" << endl;
|
||||||
return 0;
|
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;
|
return 0;
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user