dsi2key/server/core/input.cpp
2023-07-26 01:05:40 +10:00

437 lines
9.5 KiB
C++

// Virtual input, includes Keyboard, Mouse, and Joystick support
#ifdef _WIN32
#ifdef __GNUC__
#define _WIN32_WINNT 0x0500
#endif
#include <windows.h>
#include <winuser.h>
#include "vjoy.h"
#endif
#include "common/key.h"
#include "common/easylogging++Wrapper.h"
#include "input.h"
#include "common/misc.h"
namespace D2K {namespace Input {
#ifdef __linux__
Display* g_display;
#endif
void Init()
{
#ifdef __linux__
g_display = XOpenDisplay(nullptr);
#endif
}
void DeInit()
{
#ifdef _WIN32
for(uint8_t i = 1; i < Joystick::MAX_JOYSTICKS; i++)
if(Joystick::IsActive(i))
Joystick::DeInit(i);
#endif
}
// TODO: check and verify for every extended key
// Checks if (key) is an "extended" key
// param: key Platform specific value
// return: true if (key) is an "extended" key
bool IsExtended(uint16_t key)
{
switch(key)
{
#ifdef _WIN32
case VK_INSERT:
case VK_DELETE:
case VK_HOME:
case VK_END:
case VK_PRIOR:
case VK_NEXT:
case VK_NUMLOCK:
case VK_PRINT:
case VK_SCROLL:
case VK_PAUSE:
case VK_DIVIDE:
case VK_LMENU:
case VK_RMENU:
case VK_LWIN:
case VK_RWIN:
case VK_LCONTROL:
case VK_RCONTROL:
case VK_UP:
case VK_DOWN:
case VK_LEFT:
case VK_RIGHT:
case VK_MEDIA_NEXT_TRACK:
case VK_MEDIA_PLAY_PAUSE:
case VK_MEDIA_PREV_TRACK:
case VK_MEDIA_STOP:
// next are untested
case VK_EXECUTE:
case VK_SNAPSHOT:
case VK_APPS:
return true;
#endif
default:
return false;
}
}
enum KeyState
{
pressed = false,
released = true,
};
enum MouseMovement
{
relative = 0,
absolute = 1,
joystick = 2,
};
static uint16_t s_press_counter[65535]{}; // this allows 1 or more profile to press the same key, instead of going crazy
static uint16_t s_turbo_status [65535]{};
// Presses or releases (key) depending on (state)
// param: key Platform specific value
// param: state true = released, false = pressed
void Keyboard(uint16_t key, KeyState state)
{
#ifdef _WIN32
INPUT input{};
switch(key)
{
case VK_LBUTTON:
{
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
if(state == KeyState::released)
input.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
else
input.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
break;
}
case VK_RBUTTON:
{
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
if(state == KeyState::released)
input.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
else
input.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
break;
}
case VK_MBUTTON:
{
input.type = INPUT_MOUSE;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
if(state == KeyState::released)
input.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
else
input.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
break;
}
default:
{
input.type = INPUT_KEYBOARD;
input.ki.wVk = key;
input.ki.dwFlags = KEYEVENTF_SCANCODE;
if(IsExtended(key))
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
if(state == KeyState::released)
input.ki.dwFlags |= KEYEVENTF_KEYUP;
input.ki.wScan = (WORD)MapVirtualKey(key, MAPVK_VK_TO_VSC);
break;
}
}
SendInput(1, (LPINPUT)&input, sizeof(INPUT));
#elif defined(__linux__)
unsigned long int code;
if (key >= 0x8001 && key <= 0x8003) {
code = (key & 0x3);
//LOG(TRACE) << "XTestFakeButtonEvent:: code: " << code << ", state: " << state;
XTestFakeButtonEvent(g_display, code, !state, 0);
XFlush(g_display);
return;
}
code = XKeysymToKeycode(g_display, key);
if (code == 0) {
LOG(ERROR) << "XKeysymToKeycode returned 0 (NULL)" << std::endl;
return;
}
XTestFakeKeyEvent(g_display, code, !state, 0);
XFlush(g_display);
#endif
}
// Moves cursor position
// param: type true = absolute, false = relative
void Mouse(MouseMovement type, signed long int x, signed long int y)
{
#ifdef _WIN32
INPUT input{};
input.type = INPUT_MOUSE;
input.mi.dx = x;
input.mi.dy = y;
input.mi.dwFlags = (type ? MOUSEEVENTF_ABSOLUTE : 0) | MOUSEEVENTF_MOVE;
input.mi.dwExtraInfo = 0;
input.mi.mouseData = 0;
input.mi.time = 0;
SendInput(1, (LPINPUT)&input, sizeof(INPUT));
#elif defined(__linux__)
Window dummyWin;
int dummySignedInt;
unsigned int width, height, dummyInt;
int screen = DefaultScreen(g_display);
Window rootwindow = RootWindow(g_display, screen);
if(XGetGeometry(g_display, rootwindow, &dummyWin, &dummySignedInt, &dummySignedInt, &width, &height, &dummyInt, &dummyInt))
{
//LOG(DEBUG) << x << ", " << y << std::endl;
//return;
if(type)
XTestFakeMotionEvent(g_display, screen, x, y, 0);
else
XTestFakeRelativeMotionEvent(g_display, x, y, 0);
XFlush(g_display);
}
#endif
}
void MouseJoystick(uint8_t x, uint8_t y)
{
#ifdef _WIN32
Joystick::SetAxisPercent(1, HID_USAGE_X, x);
Joystick::SetAxisPercent(1, HID_USAGE_Y, y);
Joystick::Update(1);
#endif
}
void Tap(uint16_t key, uint8_t joy)
{
if(s_press_counter[key] == 0)
{
if(!s_turbo_status[key])
Press(key, joy);
else
Release(key, joy);
}
else
{
if(!s_turbo_status[key])
Release(key, joy);
else
Press(key, joy);
}
}
void Press(uint16_t key, uint8_t joy)
{
if(s_press_counter[key] == 0)
{
// GamePad Buttons
if(key >= Key::JOY && key <= Key::JOY_MAX) // virtual gamepad buttons
{
#ifdef _WIN32
Joystick::SetButton(joy, (uint8_t)(key - Key::JOY), true);
Joystick::Update(joy);
#endif
}
// GamePad DPad
else if(key >= Key::JOY_HAT && key <= Key::JOY_HAT_MAX) // virtual gamepad buttons
{
#ifdef _WIN32
Joystick::SetHat(joy, (uint8_t)(key - Key::JOY_HAT), true);
Joystick::Update(joy);
#endif
}
// Analog Axis
else if(key >= Key::JOY_AXIS1 && key <= Key::JOY_AXIS5_MAX) // virtual analog axis
{
#ifdef _WIN32
switch(key)
{
case Key::JOY_AXIS_X_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_X, 0);
break;
case Key::JOY_AXIS_X_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_X, 100);
break;
case Key::JOY_AXIS_Y_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_Y, 0);
break;
case Key::JOY_AXIS_Y_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_Y, 100);
break;
case Key::JOY_AXIS_Z_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_Z, 0);
break;
case Key::JOY_AXIS_Z_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_Z, 100);
break;
case Key::JOY_AXIS_RX_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RX, 0);
break;
case Key::JOY_AXIS_RX_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RX, 100);
break;
case Key::JOY_AXIS_RY_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RY, 0);
break;
case Key::JOY_AXIS_RY_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RY, 100);
break;
case Key::JOY_AXIS_RZ_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RZ, 0);
break;
case Key::JOY_AXIS_RZ_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RZ, 100);
break;
case Key::JOY_AXIS_SL0_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_SL0, 0);
break;
case Key::JOY_AXIS_SL0_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_SL0, 100);
break;
case Key::JOY_AXIS_SL1_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_SL1, 0);
break;
case Key::JOY_AXIS_SL1_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_SL1, 100);
break;
case Key::JOY_AXIS_WHL_MINUS:
Joystick::SetAxisPercent(joy, HID_USAGE_WHL, 0);
break;
case Key::JOY_AXIS_WHL_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_WHL, 100);
break;
default:
break;
}
Joystick::Update(joy);
#endif
}
// Keyboard
else
{
Keyboard(key, KeyState::pressed);
LOG(TRACE) << "Key Pressed:" << key;
}
}
if(s_press_counter[key] < 65535)
s_press_counter[key]++;
}
void Release(uint16_t key, uint8_t joy)
{
if(s_press_counter[key] == 1)
{
// GamePad Buttons
if(key >= Key::JOY && key <= Key::JOY_MAX)
{
#ifdef _WIN32
Joystick::SetButton(joy, (uint8_t)(key - Key::JOY), false);
Joystick::Update(joy);
#endif
}
// GamePad DPad
else if(key >= Key::JOY_HAT && key <= Key::JOY_HAT_MAX)
{
#ifdef _WIN32
Joystick::SetHat(joy, (uint8_t)( key - Key::JOY_HAT), false);
Joystick::Update(joy);
#endif
}
// Analog Axis
else if(key >= Key::JOY_AXIS1 && key <= Key::JOY_AXIS5_MAX)
{
#ifdef _WIN32
switch(key)
{
case Key::JOY_AXIS_X_MINUS:
case Key::JOY_AXIS_X_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_X, 50);
break;
case Key::JOY_AXIS_Y_MINUS:
case Key::JOY_AXIS_Y_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_Y, 50);
break;
case Key::JOY_AXIS_Z_MINUS:
case Key::JOY_AXIS_Z_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_Z, 50);
break;
case Key::JOY_AXIS_RX_MINUS:
case Key::JOY_AXIS_RX_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RX, 50);
break;
case Key::JOY_AXIS_RY_MINUS:
case Key::JOY_AXIS_RY_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RY, 50);
break;
case Key::JOY_AXIS_RZ_MINUS:
case Key::JOY_AXIS_RZ_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_RZ, 50);
break;
case Key::JOY_AXIS_SL0_MINUS:
case Key::JOY_AXIS_SL0_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_SL0, 50);
break;
case Key::JOY_AXIS_SL1_MINUS:
case Key::JOY_AXIS_SL1_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_SL1, 50);
break;
case Key::JOY_AXIS_WHL_MINUS:
case Key::JOY_AXIS_WHL_PLUS:
Joystick::SetAxisPercent(joy, HID_USAGE_WHL, 50);
break;
default:
break;
}
Joystick::Update(joy);
#endif
}
// Keyboard
else
{
Keyboard(key, KeyState::released);
}
}
if(s_press_counter[key] > 0)
s_press_counter[key]--;
}
void Move(signed long int x, signed long int y)
{
Mouse(MouseMovement::relative, x, y);
}
void MoveAbsolute(signed long int x, signed long int y)
{
Mouse(MouseMovement::absolute, x, y);
}
void MoveJoystick(uint8_t x, uint8_t y)
{
MouseJoystick(x, y);
}
}} // namespace D2K::Input