mirror of
https://github.com/robotn/gohook.git
synced 2024-11-25 22:06:54 +08:00
334 lines
10 KiB
C
334 lines
10 KiB
C
/* Copyright (C) 2006-2017 Alexander Barker.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include "../iohook.h"
|
|
#include <windows.h>
|
|
|
|
// #include "logger.h"
|
|
#include "input.h"
|
|
|
|
// Some buggy versions of MinGW and MSys do not include these constants in winuser.h.
|
|
#ifndef MAPVK_VK_TO_VSC
|
|
#define MAPVK_VK_TO_VSC 0
|
|
#define MAPVK_VSC_TO_VK 1
|
|
#define MAPVK_VK_TO_CHAR 2
|
|
#define MAPVK_VSC_TO_VK_EX 3
|
|
#endif
|
|
// Some buggy versions of MinGW and MSys only define this value for Windows
|
|
// versions >= 0x0600 (Windows Vista) when it should be 0x0500 (Windows 2000).
|
|
#ifndef MAPVK_VK_TO_VSC_EX
|
|
#define MAPVK_VK_TO_VSC_EX 4
|
|
#endif
|
|
|
|
#ifndef KEYEVENTF_SCANCODE
|
|
#define KEYEVENTF_EXTENDEDKEY 0x0001
|
|
#define KEYEVENTF_KEYUP 0x0002
|
|
#define KEYEVENTF_UNICODE 0x0004
|
|
#define KEYEVENTF_SCANCODE 0x0008
|
|
#endif
|
|
|
|
#ifndef KEYEVENTF_KEYDOWN
|
|
#define KEYEVENTF_KEYDOWN 0x0000
|
|
#endif
|
|
|
|
#define MAX_WINDOWS_COORD_VALUE 65535
|
|
|
|
static UINT keymask_lookup[8] = {
|
|
VK_LSHIFT,
|
|
VK_LCONTROL,
|
|
VK_LWIN,
|
|
VK_LMENU,
|
|
|
|
VK_RSHIFT,
|
|
VK_RCONTROL,
|
|
VK_RWIN,
|
|
VK_RMENU
|
|
};
|
|
|
|
IOHOOK_API void hook_post_event(iohook_event * const event) {
|
|
//FIXME implement multiple monitor support
|
|
uint16_t screen_width = GetSystemMetrics( SM_CXSCREEN );
|
|
uint16_t screen_height = GetSystemMetrics( SM_CYSCREEN );
|
|
|
|
unsigned char events_size = 0, events_max = 28;
|
|
INPUT *events = malloc(sizeof(INPUT) * events_max);
|
|
|
|
if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
|
|
unsigned int i;
|
|
for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
|
|
if (event->mask & 1 << i) {
|
|
events[events_size].type = INPUT_KEYBOARD;
|
|
events[events_size].ki.wVk = keymask_lookup[i];
|
|
events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN;
|
|
events[events_size].ki.time = 0; // Use current system time.
|
|
events_size++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
|
|
events[events_size].type = INPUT_MOUSE;
|
|
events[events_size].mi.dx = 0; // Relative mouse movement due to
|
|
events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
|
|
events[events_size].mi.mouseData = 0x00;
|
|
events[events_size].mi.time = 0; // Use current system time.
|
|
|
|
if (event->mask & MASK_BUTTON1) {
|
|
events[events_size].mi.mouseData |= MOUSEEVENTF_LEFTDOWN;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON2) {
|
|
events[events_size].mi.mouseData |= MOUSEEVENTF_RIGHTDOWN;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON3) {
|
|
events[events_size].mi.mouseData |= MOUSEEVENTF_MIDDLEDOWN;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON4) {
|
|
events[events_size].mi.mouseData = XBUTTON1;
|
|
events[events_size].mi.mouseData |= MOUSEEVENTF_XDOWN;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON5) {
|
|
events[events_size].mi.mouseData = XBUTTON2;
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_XDOWN;
|
|
}
|
|
|
|
events_size++;
|
|
}
|
|
|
|
|
|
switch (event->type) {
|
|
case EVENT_KEY_PRESSED:
|
|
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
|
|
if (events[events_size].ki.wVk != 0x0000) {
|
|
events[events_size].type = INPUT_KEYBOARD;
|
|
events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN; // |= KEYEVENTF_SCANCODE;
|
|
events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
|
|
events[events_size].ki.time = 0; // GetSystemTime()
|
|
events_size++;
|
|
} else {
|
|
logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
|
|
__FUNCTION__, __LINE__,
|
|
event->data.keyboard.keycode);
|
|
}
|
|
break;
|
|
|
|
case EVENT_KEY_RELEASED:
|
|
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
|
|
if (events[events_size].ki.wVk != 0x0000) {
|
|
events[events_size].type = INPUT_KEYBOARD;
|
|
events[events_size].ki.dwFlags = KEYEVENTF_KEYUP; // |= KEYEVENTF_SCANCODE;
|
|
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
|
|
events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
|
|
events[events_size].ki.time = 0; // GetSystemTime()
|
|
events_size++;
|
|
} else {
|
|
logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
|
|
__FUNCTION__, __LINE__,
|
|
event->data.keyboard.keycode);
|
|
}
|
|
break;
|
|
|
|
|
|
case EVENT_MOUSE_PRESSED:
|
|
events[events_size].type = INPUT_MOUSE;
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_XDOWN;
|
|
|
|
switch (event->data.mouse.button) {
|
|
case MOUSE_BUTTON1:
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
|
|
break;
|
|
|
|
case MOUSE_BUTTON2:
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
|
|
break;
|
|
|
|
case MOUSE_BUTTON3:
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
|
|
break;
|
|
|
|
case MOUSE_BUTTON4:
|
|
events[events_size].mi.mouseData = XBUTTON1;
|
|
break;
|
|
|
|
case MOUSE_BUTTON5:
|
|
events[events_size].mi.mouseData = XBUTTON2;
|
|
break;
|
|
|
|
default:
|
|
// Extra buttons.
|
|
if (event->data.mouse.button > 3) {
|
|
events[events_size].mi.mouseData = event->data.mouse.button - 3;
|
|
}
|
|
}
|
|
|
|
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
|
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
|
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
|
events[events_size].mi.time = 0; // GetSystemTime()
|
|
|
|
events_size++;
|
|
break;
|
|
|
|
case EVENT_MOUSE_RELEASED:
|
|
events[events_size].type = INPUT_MOUSE;
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_XUP;
|
|
|
|
switch (event->data.mouse.button) {
|
|
case MOUSE_BUTTON1:
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTUP;
|
|
break;
|
|
|
|
case MOUSE_BUTTON2:
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
|
|
break;
|
|
|
|
case MOUSE_BUTTON3:
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
|
|
break;
|
|
|
|
case MOUSE_BUTTON4:
|
|
events[events_size].mi.mouseData = XBUTTON1;
|
|
break;
|
|
|
|
case MOUSE_BUTTON5:
|
|
events[events_size].mi.mouseData = XBUTTON2;
|
|
break;
|
|
|
|
default:
|
|
// Extra buttons.
|
|
if (event->data.mouse.button > 3) {
|
|
events[events_size].mi.mouseData = event->data.mouse.button - 3;
|
|
}
|
|
}
|
|
|
|
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
|
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
|
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
|
events[events_size].mi.time = 0; // GetSystemTime()
|
|
events_size++;
|
|
break;
|
|
|
|
|
|
case EVENT_MOUSE_WHEEL:
|
|
events[events_size].type = INPUT_MOUSE;
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_WHEEL;
|
|
|
|
// type, amount and rotation?
|
|
events[events_size].mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
|
|
|
|
events[events_size].mi.dx = event->data.wheel.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
|
events[events_size].mi.dy = event->data.wheel.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
|
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
|
events[events_size].mi.time = 0; // GetSystemTime()
|
|
events_size++;
|
|
break;
|
|
|
|
|
|
case EVENT_MOUSE_DRAGGED:
|
|
// The button masks are all applied with the modifier masks.
|
|
|
|
case EVENT_MOUSE_MOVED:
|
|
events[events_size].type = INPUT_MOUSE;
|
|
events[events_size].mi.dwFlags = MOUSEEVENTF_MOVE;
|
|
|
|
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
|
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
|
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
|
events[events_size].mi.time = 0; // GetSystemTime()
|
|
events_size++;
|
|
break;
|
|
|
|
|
|
case EVENT_MOUSE_CLICKED:
|
|
case EVENT_KEY_TYPED:
|
|
// Ignore clicked and typed events.
|
|
|
|
case EVENT_HOOK_ENABLED:
|
|
case EVENT_HOOK_DISABLED:
|
|
// Ignore hook enabled / disabled events.
|
|
|
|
default:
|
|
// Ignore any other garbage.
|
|
logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
|
|
__FUNCTION__, __LINE__, event->type);
|
|
break;
|
|
}
|
|
|
|
// Release the previously held modifier keys used to fake the event mask.
|
|
if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
|
|
unsigned int i;
|
|
for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
|
|
if (event->mask & 1 << i) {
|
|
events[events_size].type = INPUT_KEYBOARD;
|
|
events[events_size].ki.wVk = keymask_lookup[i];
|
|
events[events_size].ki.dwFlags = KEYEVENTF_KEYUP;
|
|
events[events_size].ki.time = 0; // Use current system time.
|
|
events_size++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
|
|
events[events_size].type = INPUT_MOUSE;
|
|
events[events_size].mi.dx = 0; // Relative mouse movement due to
|
|
events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
|
|
events[events_size].mi.mouseData = 0x00;
|
|
events[events_size].mi.time = 0; // Use current system time.
|
|
|
|
// If dwFlags does not contain MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN, or MOUSEEVENTF_XUP,
|
|
// then mouseData should be zero.
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273%28v=vs.85%29.aspx
|
|
if (event->mask & MASK_BUTTON1) {
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_LEFTUP;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON2) {
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON3) {
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON4) {
|
|
events[events_size].mi.mouseData = XBUTTON1;
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
|
|
}
|
|
|
|
if (event->mask & MASK_BUTTON5) {
|
|
events[events_size].mi.mouseData = XBUTTON2;
|
|
events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
|
|
}
|
|
|
|
events_size++;
|
|
}
|
|
|
|
// Create the key release input
|
|
// memcpy(key_events + 1, key_events, sizeof(INPUT));
|
|
// key_events[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
|
|
|
if (! SendInput(events_size, events, sizeof(INPUT)) ) {
|
|
logger(LOG_LEVEL_ERROR, "%s [%u]: SendInput() failed! (%#lX)\n",
|
|
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
|
|
}
|
|
|
|
free(events);
|
|
}
|