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…
Reference in New Issue
Block a user