Linux: Gamepad support (using libevdev)

This commit is contained in:
Souryo 2016-12-18 20:39:31 -05:00
parent 760011b66d
commit bdbf830dd7
11 changed files with 6153 additions and 32 deletions

View file

@ -55,32 +55,61 @@
Left = InteropEmu.GetKeyCode("Left Arrow"), Right = InteropEmu.GetKeyCode("Right Arrow")
};
for(int i = 0; i < 2; i++) {
string prefix = "Pad" + (i+1).ToString() + " ";
_xboxLayouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "A"), B = InteropEmu.GetKeyCode(prefix + "X"),
TurboA = InteropEmu.GetKeyCode(prefix + "B"), TurboB = InteropEmu.GetKeyCode(prefix + "Y"),
Select = InteropEmu.GetKeyCode(prefix + "Back"), Start = InteropEmu.GetKeyCode(prefix + "Start"),
Up = InteropEmu.GetKeyCode(prefix + "Up"), Down = InteropEmu.GetKeyCode(prefix + "Down"),
Left = InteropEmu.GetKeyCode(prefix + "Left"), Right = InteropEmu.GetKeyCode(prefix + "Right")
};
if(Program.IsMono) {
for(int i = 0; i < 2; i++) {
string prefix = "Pad" + (i+1).ToString() + " ";
_xboxLayouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "A"), B = InteropEmu.GetKeyCode(prefix + "X"),
TurboA = InteropEmu.GetKeyCode(prefix + "B"), TurboB = InteropEmu.GetKeyCode(prefix + "Y"),
Select = InteropEmu.GetKeyCode(prefix + "Select"), Start = InteropEmu.GetKeyCode(prefix + "Start"),
Up = InteropEmu.GetKeyCode(prefix + "Up"), Down = InteropEmu.GetKeyCode(prefix + "Down"),
Left = InteropEmu.GetKeyCode(prefix + "Left"), Right = InteropEmu.GetKeyCode(prefix + "Right")
};
prefix = "Joy" + (i+1).ToString() + " ";
_ps4Layouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "But2"), B = InteropEmu.GetKeyCode(prefix + "But1"),
TurboA = InteropEmu.GetKeyCode(prefix + "But3"), TurboB = InteropEmu.GetKeyCode(prefix + "But4"),
Select = InteropEmu.GetKeyCode(prefix + "But9"), Start = InteropEmu.GetKeyCode(prefix + "But10"),
Up = InteropEmu.GetKeyCode(prefix + "DPad Up"), Down = InteropEmu.GetKeyCode(prefix + "DPad Down"),
Left = InteropEmu.GetKeyCode(prefix + "DPad Left"), Right = InteropEmu.GetKeyCode(prefix + "DPad Right")
};
_ps4Layouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "B"), B = InteropEmu.GetKeyCode(prefix + "A"),
TurboA = InteropEmu.GetKeyCode(prefix + "C"), TurboB = InteropEmu.GetKeyCode(prefix + "X"),
Select = InteropEmu.GetKeyCode(prefix + "L2"), Start = InteropEmu.GetKeyCode(prefix + "R2"),
Up = InteropEmu.GetKeyCode(prefix + "Up"), Down = InteropEmu.GetKeyCode(prefix + "Down"),
Left = InteropEmu.GetKeyCode(prefix + "Left"), Right = InteropEmu.GetKeyCode(prefix + "Right")
};
_snes30Layouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "But2"), B = InteropEmu.GetKeyCode(prefix + "But5"),
TurboA = InteropEmu.GetKeyCode(prefix + "But1"), TurboB = InteropEmu.GetKeyCode(prefix + "But4"),
Select = InteropEmu.GetKeyCode(prefix + "But11"), Start = InteropEmu.GetKeyCode(prefix + "But12"),
Up = InteropEmu.GetKeyCode(prefix + "Y+"), Down = InteropEmu.GetKeyCode(prefix + "Y-"),
Left = InteropEmu.GetKeyCode(prefix + "X-"), Right = InteropEmu.GetKeyCode(prefix + "X+")
};
_snes30Layouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "Thumb"), B = InteropEmu.GetKeyCode(prefix + "Top2"),
TurboA = InteropEmu.GetKeyCode(prefix + "Trigger"), TurboB = InteropEmu.GetKeyCode(prefix + "Top"),
Select = InteropEmu.GetKeyCode(prefix + "Base5"), Start = InteropEmu.GetKeyCode(prefix + "Base6"),
Up = InteropEmu.GetKeyCode(prefix + "Y-"), Down = InteropEmu.GetKeyCode(prefix + "Y+"),
Left = InteropEmu.GetKeyCode(prefix + "X-"), Right = InteropEmu.GetKeyCode(prefix + "X+")
};
}
} else {
for(int i = 0; i < 2; i++) {
string prefix = "Pad" + (i+1).ToString() + " ";
_xboxLayouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "A"), B = InteropEmu.GetKeyCode(prefix + "X"),
TurboA = InteropEmu.GetKeyCode(prefix + "B"), TurboB = InteropEmu.GetKeyCode(prefix + "Y"),
Select = InteropEmu.GetKeyCode(prefix + "Back"), Start = InteropEmu.GetKeyCode(prefix + "Start"),
Up = InteropEmu.GetKeyCode(prefix + "Up"), Down = InteropEmu.GetKeyCode(prefix + "Down"),
Left = InteropEmu.GetKeyCode(prefix + "Left"), Right = InteropEmu.GetKeyCode(prefix + "Right")
};
prefix = "Joy" + (i+1).ToString() + " ";
_ps4Layouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "But2"), B = InteropEmu.GetKeyCode(prefix + "But1"),
TurboA = InteropEmu.GetKeyCode(prefix + "But3"), TurboB = InteropEmu.GetKeyCode(prefix + "But4"),
Select = InteropEmu.GetKeyCode(prefix + "But9"), Start = InteropEmu.GetKeyCode(prefix + "But10"),
Up = InteropEmu.GetKeyCode(prefix + "DPad Up"), Down = InteropEmu.GetKeyCode(prefix + "DPad Down"),
Left = InteropEmu.GetKeyCode(prefix + "DPad Left"), Right = InteropEmu.GetKeyCode(prefix + "DPad Right")
};
_snes30Layouts[i] = new KeyMappings() {
A = InteropEmu.GetKeyCode(prefix + "But2"), B = InteropEmu.GetKeyCode(prefix + "But5"),
TurboA = InteropEmu.GetKeyCode(prefix + "But1"), TurboB = InteropEmu.GetKeyCode(prefix + "But4"),
Select = InteropEmu.GetKeyCode(prefix + "But11"), Start = InteropEmu.GetKeyCode(prefix + "But12"),
Up = InteropEmu.GetKeyCode(prefix + "Y+"), Down = InteropEmu.GetKeyCode(prefix + "Y-"),
Left = InteropEmu.GetKeyCode(prefix + "X-"), Right = InteropEmu.GetKeyCode(prefix + "X+")
};
}
}
}
}

View file

@ -0,0 +1,215 @@
#include "../Core/MessageManager.h"
#include "LinuxGameController.h"
#include "libevdev/libevdev.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
std::shared_ptr<LinuxGameController> LinuxGameController::GetController(int deviceID)
{
int fd = open(("/dev/input/event" + std::to_string(deviceID)).c_str(), O_RDONLY | O_NONBLOCK);
if(fd < 0) {
//fprintf(stderr, "error: %d %s\n", errno, strerror(errno));
return nullptr;
}
libevdev* device = nullptr;
int rc = libevdev_new_from_fd(fd, &device);
if(rc < 0) {
//fprintf(stderr, "error: %d %s\n", -rc, strerror(-rc));
return nullptr;
}
if(libevdev_has_event_type(device, EV_KEY) && libevdev_has_event_code(device, EV_KEY, BTN_GAMEPAD) ||
libevdev_has_event_type(device, EV_ABS) && libevdev_has_event_code(device, EV_ABS, ABS_X)) {
MessageManager::Log(std::string("[Input Connected] Name: ") + libevdev_get_name(device) + " Vendor: " + std::to_string(libevdev_get_id_vendor(device)) + " Product: " + std::to_string(libevdev_get_id_product(device)));
return std::shared_ptr<LinuxGameController>(new LinuxGameController(deviceID, fd, device));
}
return nullptr;
}
LinuxGameController::LinuxGameController(int deviceID, int fileDescriptor, libevdev* device)
{
_deviceID = deviceID;
_stopFlag = false;
_device = device;
_fd = fileDescriptor;
memset(_axisDefaultValue, 0, sizeof(_axisDefaultValue));
_eventThread = std::thread([=]() {
int rc;
bool calibrate = true;
do {
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(_fd, &readSet);
//Timeout after 0.1 seconds (to allow thread to be terminated quickly)
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
if(rc = select((int)_fd+1, &readSet, nullptr, nullptr, &timeout)) {
do {
struct input_event ev;
rc = libevdev_next_event(_device, LIBEVDEV_READ_FLAG_NORMAL, &ev);
if(rc == LIBEVDEV_READ_STATUS_SYNC) {
while (rc == LIBEVDEV_READ_STATUS_SYNC) {
rc = libevdev_next_event(_device, LIBEVDEV_READ_FLAG_SYNC, &ev);
}
} else if(rc == LIBEVDEV_READ_STATUS_SUCCESS) {
//print_event(&ev);
}
} while(rc == LIBEVDEV_READ_STATUS_SYNC || rc == LIBEVDEV_READ_STATUS_SUCCESS);
}
if(rc != LIBEVDEV_READ_STATUS_SYNC && rc != LIBEVDEV_READ_STATUS_SUCCESS && rc != -EAGAIN && rc != EWOULDBLOCK) {
//Device was disconnected
MessageManager::Log("[Input Device] Disconnected");
break;
}
if(calibrate) {
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(100));
Calibrate();
calibrate = false;
}
} while(!_stopFlag);
_disconnected = true;
});
}
LinuxGameController::~LinuxGameController()
{
_stopFlag = true;
_eventThread.join();
libevdev_free(_device);
close(_fd);
}
void LinuxGameController::Calibrate()
{
int axes[14] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y, ABS_HAT3X, ABS_HAT3Y };
for(int axis : axes) {
_axisDefaultValue[axis] = libevdev_get_event_value(_device, EV_ABS, axis);
//std::cout << "center values: " << std::to_string(_axisDefaultValue[axis]) << std::endl;
}
}
bool LinuxGameController::CheckAxis(unsigned int code, bool forPositive)
{
int deadZoneNegative = (_axisDefaultValue[code] - libevdev_get_abs_minimum(_device, code)) * 0.225;
int deadZonePositive = (libevdev_get_abs_maximum(_device, code) - _axisDefaultValue[code]) * 0.225;
if(forPositive) {
return libevdev_get_event_value(_device, EV_ABS, code) - _axisDefaultValue[code] > deadZonePositive;
} else {
return libevdev_get_event_value(_device, EV_ABS, code) - _axisDefaultValue[code] < -deadZoneNegative;
}
}
bool LinuxGameController::IsButtonPressed(int buttonNumber)
{
switch(buttonNumber) {
case 0: return libevdev_get_event_value(_device, EV_KEY, BTN_A) == 1;
case 1: return libevdev_get_event_value(_device, EV_KEY, BTN_B) == 1;
case 2: return libevdev_get_event_value(_device, EV_KEY, BTN_C) == 1;
case 3: return libevdev_get_event_value(_device, EV_KEY, BTN_X) == 1;
case 4: return libevdev_get_event_value(_device, EV_KEY, BTN_Y) == 1;
case 5: return libevdev_get_event_value(_device, EV_KEY, BTN_Z) == 1;
case 6: return libevdev_get_event_value(_device, EV_KEY, BTN_TL) == 1;
case 7: return libevdev_get_event_value(_device, EV_KEY, BTN_TR) == 1;
case 8: return libevdev_get_event_value(_device, EV_KEY, BTN_TL2) == 1;
case 9: return libevdev_get_event_value(_device, EV_KEY, BTN_TR2) == 1;
case 10: return libevdev_get_event_value(_device, EV_KEY, BTN_SELECT) == 1;
case 11: return libevdev_get_event_value(_device, EV_KEY, BTN_START) == 1;
case 12: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMBL) == 1;
case 13: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMBR) == 1;
case 14: return CheckAxis(ABS_X, true);
case 15: return CheckAxis(ABS_X, false);
case 16: return CheckAxis(ABS_Y, true);
case 17: return CheckAxis(ABS_Y, false);
case 18: return CheckAxis(ABS_Z, true);
case 19: return CheckAxis(ABS_Z, false);
case 20: return CheckAxis(ABS_RX, true);
case 21: return CheckAxis(ABS_RX, false);
case 22: return CheckAxis(ABS_RY, true);
case 23: return CheckAxis(ABS_RY, false);
case 24: return CheckAxis(ABS_RZ, true);
case 25: return CheckAxis(ABS_RZ, false);
case 26: return CheckAxis(ABS_HAT0X, true);
case 27: return CheckAxis(ABS_HAT0X, false);
case 28: return CheckAxis(ABS_HAT0Y, true);
case 29: return CheckAxis(ABS_HAT0Y, false);
case 30: return CheckAxis(ABS_HAT1X, true);
case 31: return CheckAxis(ABS_HAT1X, false);
case 32: return CheckAxis(ABS_HAT1Y, true);
case 33: return CheckAxis(ABS_HAT1Y, false);
case 34: return CheckAxis(ABS_HAT2X, true);
case 35: return CheckAxis(ABS_HAT2X, false);
case 36: return CheckAxis(ABS_HAT2Y, true);
case 37: return CheckAxis(ABS_HAT2Y, false);
case 38: return CheckAxis(ABS_HAT3X, true);
case 39: return CheckAxis(ABS_HAT3X, false);
case 40: return CheckAxis(ABS_HAT3Y, true);
case 41: return CheckAxis(ABS_HAT3Y, false);
case 42: return libevdev_get_event_value(_device, EV_KEY, BTN_TRIGGER) == 1;
case 43: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMB) == 1;
case 44: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMB2) == 1;
case 45: return libevdev_get_event_value(_device, EV_KEY, BTN_TOP) == 1;
case 46: return libevdev_get_event_value(_device, EV_KEY, BTN_TOP2) == 1;
case 47: return libevdev_get_event_value(_device, EV_KEY, BTN_PINKIE) == 1;
case 48: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE) == 1;
case 49: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE2) == 1;
case 50: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE3) == 1;
case 51: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE4) == 1;
case 52: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE5) == 1;
case 53: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE6) == 1;
case 54: return libevdev_get_event_value(_device, EV_KEY, BTN_DEAD) == 1;
}
return false;
}
bool LinuxGameController::IsDisconnected()
{
return _disconnected;
}
int LinuxGameController::GetDeviceID()
{
return _deviceID;
}
/*
static int print_event(struct input_event *ev)
{
if (ev->type == EV_SYN)
printf("Event: time %ld.%06ld, ++++++++++++++++++++ %s +++++++++++++++\n",
ev->time.tv_sec,
ev->time.tv_usec,
libevdev_event_type_get_name(ev->type));
else
printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %d\n",
ev->time.tv_sec,
ev->time.tv_usec,
ev->type,
libevdev_event_type_get_name(ev->type),
ev->code,
libevdev_event_code_get_name(ev->type, ev->code),
ev->value);
return 0;
}
*/

View file

@ -0,0 +1,30 @@
#pragma once
#include <thread>
#include <atomic>
struct libevdev;
class LinuxGameController
{
private:
int _fd = -1;
int _deviceID = -1;
libevdev *_device = nullptr;
bool _disconnected = false;
std::thread _eventThread;
std::atomic<bool> _stopFlag;
int _axisDefaultValue[0x100];
LinuxGameController(int deviceID, int fileDescriptor, libevdev *device);
bool CheckAxis(unsigned int code, bool forPositive);
void Calibrate();
public:
~LinuxGameController();
static std::shared_ptr<LinuxGameController> GetController(int deviceID);
bool IsDisconnected();
int GetDeviceID();
bool IsButtonPressed(int buttonNumber);
};

View file

@ -1,5 +1,8 @@
#include "LinuxKeyManager.h"
#include <algorithm>
#include "LinuxKeyManager.h"
#include "LinuxGameController.h"
#include "../Core/ControlManager.h"
#include "../Core/Console.h"
static vector<KeyDefinition> _keyDefinitions = {
{ "", 9, "Escape", "" },
@ -228,14 +231,45 @@ LinuxKeyManager::LinuxKeyManager()
{
ResetKeyState();
vector<string> buttonNames = {
"A", "B", "C", "X", "Y", "Z", "L1", "R1", "L2", "R2", "Select", "Start", "L3", "R3",
"X+", "X-", "Y+", "Y-", "Z+", "Z-",
"X2+", "X2-", "Y2+", "Y2-", "Z2+", "Z2-",
"Right", "Left", "Down", "Up",
"Right 2", "Left 2", "Down 2", "Up 2",
"Right 3", "Left 3", "Down 3", "Up 3",
"Right 4", "Left 4", "Down 4", "Up 4",
"Trigger", "Thumb", "Thumb2", "Top", "Top2",
"Pinkie", "Base", "Base2", "Base3", "Base4",
"Base5", "Base6", "Dead"
};
for(int i = 0; i < 20; i++) {
for(int j = 0; j < (int)buttonNames.size(); j++) {
_keyDefinitions.push_back({ "", (uint32_t)(0x10000 + i * 0x100 + j), "Pad" + std::to_string(i + 1) + " " + buttonNames[j] });
}
}
for(KeyDefinition &keyDef : _keyDefinitions) {
_keyNames[keyDef.keyCode] = keyDef.description;
_keyCodes[keyDef.description] = keyDef.keyCode;
}
for(int i = 0; i < 30; i++) {
std::shared_ptr<LinuxGameController> controller = LinuxGameController::GetController(i);
if(controller) {
_controllers.push_back(controller);
}
}
_stopUpdateDeviceThread = false;
StartUpdateDeviceThread();
}
LinuxKeyManager::~LinuxKeyManager()
{
_stopUpdateDeviceThread = true;
_updateDeviceThread.join();
}
void LinuxKeyManager::RefreshState()
@ -246,7 +280,13 @@ void LinuxKeyManager::RefreshState()
bool LinuxKeyManager::IsKeyPressed(uint32_t key)
{
if(key < 0x200) {
if(key >= 0x10000) {
uint8_t gamepadPort = (key - 0x10000) / 0x100;
uint8_t gamepadButton = (key - 0x10000) % 0x100;
if(_controllers.size() > gamepadPort) {
return _controllers[gamepadPort]->IsButtonPressed(gamepadButton);
}
} else if(key < 0x200) {
return _keyState[key & 0xFF] != 0;
}
return false;
@ -265,6 +305,14 @@ bool LinuxKeyManager::IsMouseButtonPressed(MouseButton button)
uint32_t LinuxKeyManager::GetPressedKey()
{
for(size_t i = 0; i < _controllers.size(); i++) {
for(int j = 0; j <= 54; j++) {
if(_controllers[i]->IsButtonPressed(j)) {
return 0x10000 + i * 0x100 + j;
}
}
}
for(int i = 0; i < 0x200; i++) {
if(_keyState[i]) {
return i;
@ -275,7 +323,7 @@ uint32_t LinuxKeyManager::GetPressedKey()
string LinuxKeyManager::GetKeyName(uint32_t key)
{
auto keyDef = _keyNames.find(key & 0xFF);
auto keyDef = _keyNames.find(key);
if(keyDef != _keyNames.end()) {
return keyDef->second;
}
@ -297,6 +345,47 @@ void LinuxKeyManager::UpdateDevices()
//Only needed to detect newly plugged in devices
}
void LinuxKeyManager::StartUpdateDeviceThread()
{
_updateDeviceThread = std::thread([=]() {
while(!_stopUpdateDeviceThread) {
//Check for newly plugged in controllers every 2 secs
vector<int> indexesToRemove;
vector<int> connectedIDs;
std::vector<shared_ptr<LinuxGameController>> controllersToAdd;
for(int i = _controllers.size() - 1; i >= 0; i--) {
if(_controllers[i]->IsDisconnected()) {
indexesToRemove.push_back(i);
} else {
connectedIDs.push_back(_controllers[i]->GetDeviceID());
}
}
for(int i = 0; i < 30; i++) {
if(std::find(connectedIDs.begin(), connectedIDs.end(), i) == connectedIDs.end()) {
std::shared_ptr<LinuxGameController> controller = LinuxGameController::GetController(i);
if(controller) {
controllersToAdd.push_back(controller);
}
}
}
if(!indexesToRemove.empty() || !controllersToAdd.empty()) {
Console::Pause();
for(int index : indexesToRemove) {
_controllers.erase(_controllers.begin()+index);
}
for(std::shared_ptr<LinuxGameController> controller : controllersToAdd) {
_controllers.push_back(controller);
}
Console::Resume();
}
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(2000));
}
});
}
void LinuxKeyManager::SetKeyState(uint16_t scanCode, bool state)
{
if(scanCode > 0x1FF) {

View file

@ -1,7 +1,11 @@
#pragma once
#include <unordered_map>
#include <vector>
#include <thread>
#include "../Core/IKeyManager.h"
class LinuxGameController;
struct KeyDefinition {
string name;
uint32_t keyCode;
@ -12,11 +16,17 @@ struct KeyDefinition {
class LinuxKeyManager : public IKeyManager
{
private:
std::vector<shared_ptr<LinuxGameController>> _controllers;
bool _keyState[0x200];
bool _mouseState[0x03];
std::unordered_map<uint32_t, string> _keyNames;
std::unordered_map<string, uint32_t> _keyCodes;
std::thread _updateDeviceThread;
atomic<bool> _stopUpdateDeviceThread;
void StartUpdateDeviceThread();
public:
LinuxKeyManager();
virtual ~LinuxKeyManager();

1425
Linux/libevdev/event-names.h Normal file

File diff suppressed because it is too large Load diff

341
Linux/libevdev/libevdev-int.h Executable file
View file

@ -0,0 +1,341 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef LIBEVDEV_INT_H
#define LIBEVDEV_INT_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include "libevdev.h"
#include "libevdev-util.h"
#define MAX_NAME 256
#define ABS_MT_MIN ABS_MT_SLOT
#define ABS_MT_MAX ABS_MT_TOOL_Y
#define ABS_MT_CNT (ABS_MT_MAX - ABS_MT_MIN + 1)
#define LIBEVDEV_EXPORT __attribute__((visibility("default")))
#define ALIAS(_to) __attribute__((alias(#_to)))
/**
* Sync state machine:
* default state: SYNC_NONE
*
* SYNC_NONE SYN_DROPPED or forced sync SYNC_NEEDED
* SYNC_NEEDED libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC) SYNC_IN_PROGRESS
* SYNC_NEEDED libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) SYNC_NONE
* SYNC_IN_PROGRESS libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) SYNC_NONE
* SYNC_IN_PROGRESS no sync events left SYNC_NONE
*
*/
enum SyncState {
SYNC_NONE,
SYNC_NEEDED,
SYNC_IN_PROGRESS,
};
struct mt_sync_state {
int code;
int val[];
};
/**
* Internal only: log data used to send messages to the respective log
* handler. We re-use the same struct for a global and inside
* struct libevdev.
* For the global, device_handler is NULL, for per-device instance
* global_handler is NULL.
*/
struct logdata {
enum libevdev_log_priority priority; /** minimum logging priority */
libevdev_log_func_t global_handler; /** global handler function */
libevdev_device_log_func_t device_handler; /** per-device handler function */
void *userdata; /** user-defined data pointer */
};
struct libevdev {
int fd;
bool initialized;
char *name;
char *phys;
char *uniq;
struct input_id ids;
int driver_version;
unsigned long bits[NLONGS(EV_CNT)];
unsigned long props[NLONGS(INPUT_PROP_CNT)];
unsigned long key_bits[NLONGS(KEY_CNT)];
unsigned long rel_bits[NLONGS(REL_CNT)];
unsigned long abs_bits[NLONGS(ABS_CNT)];
unsigned long led_bits[NLONGS(LED_CNT)];
unsigned long msc_bits[NLONGS(MSC_CNT)];
unsigned long sw_bits[NLONGS(SW_CNT)];
unsigned long rep_bits[NLONGS(REP_CNT)]; /* convenience, always 1 */
unsigned long ff_bits[NLONGS(FF_CNT)];
unsigned long snd_bits[NLONGS(SND_CNT)];
unsigned long key_values[NLONGS(KEY_CNT)];
unsigned long led_values[NLONGS(LED_CNT)];
unsigned long sw_values[NLONGS(SW_CNT)];
struct input_absinfo abs_info[ABS_CNT];
int *mt_slot_vals; /* [num_slots * ABS_MT_CNT] */
int num_slots; /**< valid slots in mt_slot_vals */
int current_slot;
int rep_values[REP_CNT];
enum SyncState sync_state;
enum libevdev_grab_mode grabbed;
struct input_event *queue;
size_t queue_size; /**< size of queue in elements */
size_t queue_next; /**< next event index */
size_t queue_nsync; /**< number of sync events */
struct timeval last_event_time;
struct {
struct mt_sync_state *mt_state;
size_t mt_state_sz; /* in bytes */
unsigned long *slot_update;
size_t slot_update_sz; /* in bytes */
unsigned long *tracking_id_changes;
size_t tracking_id_changes_sz; /* in bytes */
} mt_sync;
struct logdata log;
};
#define log_msg_cond(dev, priority, ...) \
do { \
if (_libevdev_log_priority(dev) >= priority) \
_libevdev_log_msg(dev, priority, __FILE__, __LINE__, __func__, __VA_ARGS__); \
} while(0)
#define log_error(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_ERROR, __VA_ARGS__)
#define log_info(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_INFO, __VA_ARGS__)
#define log_dbg(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_DEBUG, __VA_ARGS__)
#define log_bug(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_ERROR, "BUG: "__VA_ARGS__)
extern void
_libevdev_log_msg(const struct libevdev *dev,
enum libevdev_log_priority priority,
const char *file, int line, const char *func,
const char *format, ...) LIBEVDEV_ATTRIBUTE_PRINTF(6, 7);
extern enum libevdev_log_priority
_libevdev_log_priority(const struct libevdev *dev);
/**
* @return a pointer to the next element in the queue, or NULL if the queue
* is full.
*/
static inline struct input_event*
queue_push(struct libevdev *dev)
{
if (dev->queue_next >= dev->queue_size)
return NULL;
return &dev->queue[dev->queue_next++];
}
/**
* Set ev to the last element in the queue, removing it from the queue.
*
* @return 0 on success, 1 if the queue is empty.
*/
static inline int
queue_pop(struct libevdev *dev, struct input_event *ev)
{
if (dev->queue_next == 0)
return 1;
*ev = dev->queue[--dev->queue_next];
return 0;
}
static inline int
queue_peek(struct libevdev *dev, size_t idx, struct input_event *ev)
{
if (dev->queue_next == 0 || idx > dev->queue_next)
return 1;
*ev = dev->queue[idx];
return 0;
}
/**
* Shift the first n elements into ev and return the number of elements
* shifted.
* ev must be large enough to store n elements.
*
* @param ev The buffer to copy into, or NULL
* @return The number of elements in ev.
*/
static inline int
queue_shift_multiple(struct libevdev *dev, size_t n, struct input_event *ev)
{
size_t remaining;
if (dev->queue_next == 0)
return 0;
remaining = dev->queue_next;
n = min(n, remaining);
remaining -= n;
if (ev)
memcpy(ev, dev->queue, n * sizeof(*ev));
memmove(dev->queue, &dev->queue[n], remaining * sizeof(*dev->queue));
dev->queue_next = remaining;
return n;
}
/**
* Set ev to the first element in the queue, shifting everything else
* forward by one.
*
* @return 0 on success, 1 if the queue is empty.
*/
static inline int
queue_shift(struct libevdev *dev, struct input_event *ev)
{
return queue_shift_multiple(dev, 1, ev) == 1 ? 0 : 1;
}
static inline int
queue_alloc(struct libevdev *dev, size_t size)
{
if (size == 0)
return -ENOMEM;
dev->queue = calloc(size, sizeof(struct input_event));
if (!dev->queue)
return -ENOMEM;
dev->queue_size = size;
dev->queue_next = 0;
return 0;
}
static inline void
queue_free(struct libevdev *dev)
{
free(dev->queue);
dev->queue_size = 0;
dev->queue_next = 0;
}
static inline size_t
queue_num_elements(struct libevdev *dev)
{
return dev->queue_next;
}
static inline size_t
queue_size(struct libevdev *dev)
{
return dev->queue_size;
}
static inline size_t
queue_num_free_elements(struct libevdev *dev)
{
if (dev->queue_size == 0)
return 0;
return dev->queue_size - dev->queue_next;
}
static inline struct input_event *
queue_next_element(struct libevdev *dev)
{
if (dev->queue_next == dev->queue_size)
return NULL;
return &dev->queue[dev->queue_next];
}
static inline int
queue_set_num_elements(struct libevdev *dev, size_t nelem)
{
if (nelem > dev->queue_size)
return 1;
dev->queue_next = nelem;
return 0;
}
#define max_mask(uc, lc) \
case EV_##uc: \
*mask = dev->lc##_bits; \
max = libevdev_event_type_get_max(type); \
break;
static inline int
type_to_mask_const(const struct libevdev *dev, unsigned int type, const unsigned long **mask)
{
int max;
switch(type) {
max_mask(ABS, abs);
max_mask(REL, rel);
max_mask(KEY, key);
max_mask(LED, led);
max_mask(MSC, msc);
max_mask(SW, sw);
max_mask(FF, ff);
max_mask(REP, rep);
max_mask(SND, snd);
default:
max = -1;
break;
}
return max;
}
static inline int
type_to_mask(struct libevdev *dev, unsigned int type, unsigned long **mask)
{
int max;
switch(type) {
max_mask(ABS, abs);
max_mask(REL, rel);
max_mask(KEY, key);
max_mask(LED, led);
max_mask(MSC, msc);
max_mask(SW, sw);
max_mask(FF, ff);
max_mask(REP, rep);
max_mask(SND, snd);
default:
max = -1;
break;
}
return max;
}
#undef max_mask
#endif

80
Linux/libevdev/libevdev-util.h Executable file
View file

@ -0,0 +1,80 @@
/*
* Copyright © 2013 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#ifndef _UTIL_H_
#define _UTIL_H_
#include <stdbool.h>
#include <string.h>
#define LONG_BITS (sizeof(long) * 8)
#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
#define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0])))
#define unlikely(x) (__builtin_expect(!!(x),0))
#undef min
#undef max
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _b : _a; \
})
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; \
})
static inline bool
startswith(const char *str, size_t len, const char *prefix, size_t plen)
{
return len >= plen && !strncmp(str, prefix, plen);
}
static inline int
bit_is_set(const unsigned long *array, int bit)
{
return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS)));
}
static inline void
set_bit(unsigned long *array, int bit)
{
array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS));
}
static inline void
clear_bit(unsigned long *array, int bit)
{
array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS));
}
static inline void
set_bit_state(unsigned long *array, int bit, int state)
{
if (state)
set_bit(array, bit);
else
clear_bit(array, bit);
}
#endif

1722
Linux/libevdev/libevdev.c Executable file

File diff suppressed because it is too large Load diff

2177
Linux/libevdev/libevdev.h Executable file

File diff suppressed because it is too large Load diff

View file

@ -5,14 +5,15 @@
COREOBJ=$(patsubst Core/%.cpp,Core/obj/%.o,$(wildcard Core/*.cpp))
UTILOBJ=$(patsubst Utilities/%.cpp,Utilities/obj/%.o,$(wildcard Utilities/*.cpp)) $(patsubst Utilities/HQX/%.cpp,Utilities/obj/%.o,$(wildcard Utilities/HQX/*.cpp)) $(patsubst Utilities/xBRZ/%.cpp,Utilities/obj/%.o,$(wildcard Utilities/xBRZ/*.cpp)) $(patsubst Utilities/KreedSaiEagle/%.cpp,Utilities/obj/%.o,$(wildcard Utilities/KreedSaiEagle/*.cpp)) $(patsubst Utilities/Scale2x/%.cpp,Utilities/obj/%.o,$(wildcard Utilities/Scale2x/*.cpp))
LINUXOBJ=$(patsubst Linux/%.cpp,Linux/obj/%.o,$(wildcard Linux/*.cpp))
LINUXOBJ=$(patsubst Linux/%.cpp,Linux/obj/%.o,$(wildcard Linux/*.cpp))
LIBEVDEVOBJ=$(patsubst Linux/libevdev/%.c,Linux/obj/%.o,$(wildcard Linux/libevdev/*.c))
SEVENZIPOBJ=$(patsubst SevenZip/%.c,SevenZip/obj/%.o,$(wildcard SevenZip/*.c))
CPPC=clang++
GCCOPTIONS=-fPIC -Wall --std=c++14 -O3
CC=clang
7ZOPTIONS=-fPIC -Wall -O3
CCOPTIONS=-fPIC -Wall -O3
SHAREDLIB=libMesenCore.dll
RELEASEFOLDER=bin/x64/Release
@ -42,7 +43,7 @@ testhelper: $(SHAREDLIB)
cd TestHelper/obj && $(CPPC) $(GCCOPTIONS) -Wl,-z,defs -Wno-parentheses -Wno-switch -o testhelper ../*.cpp ../../InteropDLL/ConsoleWrapper.cpp -L ./ -lCore -lMesenLinux -lUtilities -lSevenZip -pthread -lSDL2 -lstdc++fs
SevenZip/obj/%.o: SevenZip/%.c
mkdir -p SevenZip/obj && cd SevenZip/obj && $(CC) $(7ZOPTIONS) -fpermissive -c $(patsubst SevenZip/%, ../%, $<)
mkdir -p SevenZip/obj && cd SevenZip/obj && $(CC) $(CCOPTIONS) -fpermissive -c $(patsubst SevenZip/%, ../%, $<)
Utilities/obj/%.o: Utilities/%.cpp
mkdir -p Utilities/obj && cd Utilities/obj && $(CPPC) $(GCCOPTIONS) -c $(patsubst Utilities/%, ../%, $<)
Utilities/obj/%.o: Utilities/HQX/%.cpp
@ -57,11 +58,13 @@ Core/obj/%.o: Core/%.cpp
mkdir -p Core/obj && cd Core/obj && $(CPPC) $(GCCOPTIONS) -Wno-parentheses -Wno-switch -c $(patsubst Core/%, ../%, $<)
Linux/obj/%.o: Linux/%.cpp
mkdir -p Linux/obj && cd Linux/obj && $(CPPC) $(GCCOPTIONS) -Wno-parentheses -Wno-switch -c $(patsubst Linux/%, ../%, $<)
Linux/obj/%.o: Linux/libevdev/%.c
mkdir -p Linux/obj && cd Linux/obj && $(CC) $(CCOPTIONS) -Wno-parentheses -Wno-switch -c $(patsubst Linux/%, ../%, $<)
$(SHAREDLIB): $(SEVENZIPOBJ) $(UTILOBJ) $(COREOBJ) $(LINUXOBJ) InteropDLL/ConsoleWrapper.cpp InteropDLL/DebugWrapper.cpp
$(SHAREDLIB): $(SEVENZIPOBJ) $(UTILOBJ) $(COREOBJ) $(LIBEVDEVOBJ) $(LINUXOBJ) InteropDLL/ConsoleWrapper.cpp InteropDLL/DebugWrapper.cpp
mkdir -p InteropDLL/obj
ar -rcs InteropDLL/obj/libSevenZip.a $(SEVENZIPOBJ)
ar -rcs InteropDLL/obj/libMesenLinux.a $(LINUXOBJ)
ar -rcs InteropDLL/obj/libMesenLinux.a $(LINUXOBJ) $(LIBEVDEVOBJ)
ar -rcs InteropDLL/obj/libUtilities.a $(UTILOBJ)
ar -rcs InteropDLL/obj/libCore.a $(COREOBJ)
cd InteropDLL/obj && $(CPPC) $(GCCOPTIONS) -Wl,-z,defs -Wno-parentheses -Wno-switch -shared -o $(SHAREDLIB) ../*.cpp -L . -lCore -lMesenLinux -lUtilities -lSevenZip -pthread -lSDL2 -lstdc++fs