Files
lindbergh-loader_lindbergh-…/src/lindbergh/evdevinput.c
T
Hein-Pieter van Braam 84705ee963 Add per analogue input deadzones
Each analogue input now gets 3 deadzones, start, middle, and end. These
deadzones are a percentage of the total range that should be squashed to
the extreme.
2024-12-29 16:25:01 +01:00

1372 lines
46 KiB
C

#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include "evdevinput.h"
#include "config.h"
#include "jvs.h"
int jvsBits = 10;
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x) - 1) / BITS_PER_LONG) + 1)
#define OFF(x) ((x) % BITS_PER_LONG)
#define LONG(x) ((x) / BITS_PER_LONG)
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
#define NAME_ELEMENT(element) [element] = #element
ArcadeInput arcadeInputs[] = {{"TEST_BUTTON", 0, BUTTON_TEST, 1},
{"PLAYER_1_BUTTON_START", 1, BUTTON_START, 1},
{"PLAYER_1_BUTTON_SERVICE", 1, BUTTON_SERVICE, 1},
{"PLAYER_1_BUTTON_UP", 1, BUTTON_UP, 1},
{"PLAYER_1_BUTTON_DOWN", 1, BUTTON_DOWN, 1},
{"PLAYER_1_BUTTON_LEFT", 1, BUTTON_LEFT, 1},
{"PLAYER_1_BUTTON_RIGHT", 1, BUTTON_RIGHT, 1},
{"PLAYER_1_BUTTON_1", 1, BUTTON_1, 1},
{"PLAYER_1_BUTTON_2", 1, BUTTON_2, 1},
{"PLAYER_1_BUTTON_3", 1, BUTTON_3, 1},
{"PLAYER_1_BUTTON_4", 1, BUTTON_4, 1},
{"PLAYER_1_BUTTON_5", 1, BUTTON_5, 1},
{"PLAYER_1_BUTTON_6", 1, BUTTON_6, 1},
{"PLAYER_1_BUTTON_7", 1, BUTTON_7, 1},
{"PLAYER_1_BUTTON_8", 1, BUTTON_8, 1},
{"PLAYER_1_BUTTON_9", 1, BUTTON_9, 1},
{"PLAYER_1_BUTTON_10", 1, BUTTON_10, 1},
{"PLAYER_2_BUTTON_START", 2, BUTTON_START, 1},
{"PLAYER_2_BUTTON_SERVICE", 2, BUTTON_SERVICE, 1},
{"PLAYER_2_BUTTON_UP", 2, BUTTON_UP, 1},
{"PLAYER_2_BUTTON_DOWN", 2, BUTTON_DOWN, 1},
{"PLAYER_2_BUTTON_LEFT", 2, BUTTON_LEFT, 1},
{"PLAYER_2_BUTTON_RIGHT", 2, BUTTON_RIGHT, 1},
{"PLAYER_2_BUTTON_1", 2, BUTTON_1, 1},
{"PLAYER_2_BUTTON_2", 2, BUTTON_2, 1},
{"PLAYER_2_BUTTON_3", 2, BUTTON_3, 1},
{"PLAYER_2_BUTTON_4", 2, BUTTON_4, 1},
{"PLAYER_2_BUTTON_5", 2, BUTTON_5, 1},
{"PLAYER_2_BUTTON_6", 2, BUTTON_6, 1},
{"PLAYER_2_BUTTON_7", 2, BUTTON_7, 1},
{"PLAYER_2_BUTTON_8", 2, BUTTON_8, 1},
{"PLAYER_2_BUTTON_9", 2, BUTTON_9, 1},
{"PLAYER_2_BUTTON_10", 2, BUTTON_10, 1},
{"ANALOGUE_1", 0, ANALOGUE_1, 1},
{"ANALOGUE_2", 0, ANALOGUE_2, 1},
{"ANALOGUE_3", 0, ANALOGUE_3, 1},
{"ANALOGUE_4", 0, ANALOGUE_4, 1},
{"ANALOGUE_5", 0, ANALOGUE_5, 1},
{"ANALOGUE_6", 0, ANALOGUE_6, 1},
{"ANALOGUE_7", 0, ANALOGUE_7, 1},
{"ANALOGUE_8", 0, ANALOGUE_8, 1},
{"END", 0, 0, 0}};
static const char *const events[EV_MAX + 1] = {
[0 ... EV_MAX] = NULL, NAME_ELEMENT(EV_SYN), NAME_ELEMENT(EV_KEY), NAME_ELEMENT(EV_REL), NAME_ELEMENT(EV_ABS),
NAME_ELEMENT(EV_MSC), NAME_ELEMENT(EV_LED), NAME_ELEMENT(EV_SND), NAME_ELEMENT(EV_REP), NAME_ELEMENT(EV_FF),
NAME_ELEMENT(EV_PWR), NAME_ELEMENT(EV_FF_STATUS), NAME_ELEMENT(EV_SW),
};
static const int maxval[EV_MAX + 1] = {
[0 ... EV_MAX] = -1, [EV_SYN] = SYN_MAX, [EV_KEY] = KEY_MAX, [EV_REL] = REL_MAX,
[EV_ABS] = ABS_MAX, [EV_MSC] = MSC_MAX, [EV_SW] = SW_MAX, [EV_LED] = LED_MAX,
[EV_SND] = SND_MAX, [EV_REP] = REP_MAX, [EV_FF] = FF_MAX, [EV_FF_STATUS] = FF_STATUS_MAX,
};
static const char *const absolutes[ABS_MAX + 1] = {
[0 ... ABS_MAX] = NULL,
NAME_ELEMENT(ABS_X),
NAME_ELEMENT(ABS_Y),
NAME_ELEMENT(ABS_Z),
NAME_ELEMENT(ABS_RX),
NAME_ELEMENT(ABS_RY),
NAME_ELEMENT(ABS_RZ),
NAME_ELEMENT(ABS_THROTTLE),
NAME_ELEMENT(ABS_RUDDER),
NAME_ELEMENT(ABS_WHEEL),
NAME_ELEMENT(ABS_GAS),
NAME_ELEMENT(ABS_BRAKE),
NAME_ELEMENT(ABS_HAT0X),
NAME_ELEMENT(ABS_HAT0Y),
NAME_ELEMENT(ABS_HAT1X),
NAME_ELEMENT(ABS_HAT1Y),
NAME_ELEMENT(ABS_HAT2X),
NAME_ELEMENT(ABS_HAT2Y),
NAME_ELEMENT(ABS_HAT3X),
NAME_ELEMENT(ABS_HAT3Y),
NAME_ELEMENT(ABS_PRESSURE),
NAME_ELEMENT(ABS_DISTANCE),
NAME_ELEMENT(ABS_TILT_X),
NAME_ELEMENT(ABS_TILT_Y),
NAME_ELEMENT(ABS_TOOL_WIDTH),
NAME_ELEMENT(ABS_VOLUME),
NAME_ELEMENT(ABS_MISC),
#ifdef ABS_MT_BLOB_ID
NAME_ELEMENT(ABS_MT_TOUCH_MAJOR),
NAME_ELEMENT(ABS_MT_TOUCH_MINOR),
NAME_ELEMENT(ABS_MT_WIDTH_MAJOR),
NAME_ELEMENT(ABS_MT_WIDTH_MINOR),
NAME_ELEMENT(ABS_MT_ORIENTATION),
NAME_ELEMENT(ABS_MT_POSITION_X),
NAME_ELEMENT(ABS_MT_POSITION_Y),
NAME_ELEMENT(ABS_MT_TOOL_TYPE),
NAME_ELEMENT(ABS_MT_BLOB_ID),
#endif
#ifdef ABS_MT_TRACKING_ID
NAME_ELEMENT(ABS_MT_TRACKING_ID),
#endif
#ifdef ABS_MT_PRESSURE
NAME_ELEMENT(ABS_MT_PRESSURE),
#endif
#ifdef ABS_MT_SLOT
NAME_ELEMENT(ABS_MT_SLOT),
#endif
#ifdef ABS_MT_TOOL_X
NAME_ELEMENT(ABS_MT_TOOL_X),
NAME_ELEMENT(ABS_MT_TOOL_Y),
NAME_ELEMENT(ABS_MT_DISTANCE),
#endif
};
static const char *const keys[KEY_MAX + 1] = {
[0 ... KEY_MAX] = NULL,
NAME_ELEMENT(KEY_RESERVED),
NAME_ELEMENT(KEY_ESC),
NAME_ELEMENT(KEY_1),
NAME_ELEMENT(KEY_2),
NAME_ELEMENT(KEY_3),
NAME_ELEMENT(KEY_4),
NAME_ELEMENT(KEY_5),
NAME_ELEMENT(KEY_6),
NAME_ELEMENT(KEY_7),
NAME_ELEMENT(KEY_8),
NAME_ELEMENT(KEY_9),
NAME_ELEMENT(KEY_0),
NAME_ELEMENT(KEY_MINUS),
NAME_ELEMENT(KEY_EQUAL),
NAME_ELEMENT(KEY_BACKSPACE),
NAME_ELEMENT(KEY_TAB),
NAME_ELEMENT(KEY_Q),
NAME_ELEMENT(KEY_W),
NAME_ELEMENT(KEY_E),
NAME_ELEMENT(KEY_R),
NAME_ELEMENT(KEY_T),
NAME_ELEMENT(KEY_Y),
NAME_ELEMENT(KEY_U),
NAME_ELEMENT(KEY_I),
NAME_ELEMENT(KEY_O),
NAME_ELEMENT(KEY_P),
NAME_ELEMENT(KEY_LEFTBRACE),
NAME_ELEMENT(KEY_RIGHTBRACE),
NAME_ELEMENT(KEY_ENTER),
NAME_ELEMENT(KEY_LEFTCTRL),
NAME_ELEMENT(KEY_A),
NAME_ELEMENT(KEY_S),
NAME_ELEMENT(KEY_D),
NAME_ELEMENT(KEY_F),
NAME_ELEMENT(KEY_G),
NAME_ELEMENT(KEY_H),
NAME_ELEMENT(KEY_J),
NAME_ELEMENT(KEY_K),
NAME_ELEMENT(KEY_L),
NAME_ELEMENT(KEY_SEMICOLON),
NAME_ELEMENT(KEY_APOSTROPHE),
NAME_ELEMENT(KEY_GRAVE),
NAME_ELEMENT(KEY_LEFTSHIFT),
NAME_ELEMENT(KEY_BACKSLASH),
NAME_ELEMENT(KEY_Z),
NAME_ELEMENT(KEY_X),
NAME_ELEMENT(KEY_C),
NAME_ELEMENT(KEY_V),
NAME_ELEMENT(KEY_B),
NAME_ELEMENT(KEY_N),
NAME_ELEMENT(KEY_M),
NAME_ELEMENT(KEY_COMMA),
NAME_ELEMENT(KEY_DOT),
NAME_ELEMENT(KEY_SLASH),
NAME_ELEMENT(KEY_RIGHTSHIFT),
NAME_ELEMENT(KEY_KPASTERISK),
NAME_ELEMENT(KEY_LEFTALT),
NAME_ELEMENT(KEY_SPACE),
NAME_ELEMENT(KEY_CAPSLOCK),
NAME_ELEMENT(KEY_F1),
NAME_ELEMENT(KEY_F2),
NAME_ELEMENT(KEY_F3),
NAME_ELEMENT(KEY_F4),
NAME_ELEMENT(KEY_F5),
NAME_ELEMENT(KEY_F6),
NAME_ELEMENT(KEY_F7),
NAME_ELEMENT(KEY_F8),
NAME_ELEMENT(KEY_F9),
NAME_ELEMENT(KEY_F10),
NAME_ELEMENT(KEY_NUMLOCK),
NAME_ELEMENT(KEY_SCROLLLOCK),
NAME_ELEMENT(KEY_KP7),
NAME_ELEMENT(KEY_KP8),
NAME_ELEMENT(KEY_KP9),
NAME_ELEMENT(KEY_KPMINUS),
NAME_ELEMENT(KEY_KP4),
NAME_ELEMENT(KEY_KP5),
NAME_ELEMENT(KEY_KP6),
NAME_ELEMENT(KEY_KPPLUS),
NAME_ELEMENT(KEY_KP1),
NAME_ELEMENT(KEY_KP2),
NAME_ELEMENT(KEY_KP3),
NAME_ELEMENT(KEY_KP0),
NAME_ELEMENT(KEY_KPDOT),
NAME_ELEMENT(KEY_ZENKAKUHANKAKU),
NAME_ELEMENT(KEY_102ND),
NAME_ELEMENT(KEY_F11),
NAME_ELEMENT(KEY_F12),
NAME_ELEMENT(KEY_RO),
NAME_ELEMENT(KEY_KATAKANA),
NAME_ELEMENT(KEY_HIRAGANA),
NAME_ELEMENT(KEY_HENKAN),
NAME_ELEMENT(KEY_KATAKANAHIRAGANA),
NAME_ELEMENT(KEY_MUHENKAN),
NAME_ELEMENT(KEY_KPJPCOMMA),
NAME_ELEMENT(KEY_KPENTER),
NAME_ELEMENT(KEY_RIGHTCTRL),
NAME_ELEMENT(KEY_KPSLASH),
NAME_ELEMENT(KEY_SYSRQ),
NAME_ELEMENT(KEY_RIGHTALT),
NAME_ELEMENT(KEY_LINEFEED),
NAME_ELEMENT(KEY_HOME),
NAME_ELEMENT(KEY_UP),
NAME_ELEMENT(KEY_PAGEUP),
NAME_ELEMENT(KEY_LEFT),
NAME_ELEMENT(KEY_RIGHT),
NAME_ELEMENT(KEY_END),
NAME_ELEMENT(KEY_DOWN),
NAME_ELEMENT(KEY_PAGEDOWN),
NAME_ELEMENT(KEY_INSERT),
NAME_ELEMENT(KEY_DELETE),
NAME_ELEMENT(KEY_MACRO),
NAME_ELEMENT(KEY_MUTE),
NAME_ELEMENT(KEY_VOLUMEDOWN),
NAME_ELEMENT(KEY_VOLUMEUP),
NAME_ELEMENT(KEY_POWER),
NAME_ELEMENT(KEY_KPEQUAL),
NAME_ELEMENT(KEY_KPPLUSMINUS),
NAME_ELEMENT(KEY_PAUSE),
NAME_ELEMENT(KEY_KPCOMMA),
NAME_ELEMENT(KEY_HANGUEL),
NAME_ELEMENT(KEY_HANJA),
NAME_ELEMENT(KEY_YEN),
NAME_ELEMENT(KEY_LEFTMETA),
NAME_ELEMENT(KEY_RIGHTMETA),
NAME_ELEMENT(KEY_COMPOSE),
NAME_ELEMENT(KEY_STOP),
NAME_ELEMENT(KEY_AGAIN),
NAME_ELEMENT(KEY_PROPS),
NAME_ELEMENT(KEY_UNDO),
NAME_ELEMENT(KEY_FRONT),
NAME_ELEMENT(KEY_COPY),
NAME_ELEMENT(KEY_OPEN),
NAME_ELEMENT(KEY_PASTE),
NAME_ELEMENT(KEY_FIND),
NAME_ELEMENT(KEY_CUT),
NAME_ELEMENT(KEY_HELP),
NAME_ELEMENT(KEY_MENU),
NAME_ELEMENT(KEY_CALC),
NAME_ELEMENT(KEY_SETUP),
NAME_ELEMENT(KEY_SLEEP),
NAME_ELEMENT(KEY_WAKEUP),
NAME_ELEMENT(KEY_FILE),
NAME_ELEMENT(KEY_SENDFILE),
NAME_ELEMENT(KEY_DELETEFILE),
NAME_ELEMENT(KEY_XFER),
NAME_ELEMENT(KEY_PROG1),
NAME_ELEMENT(KEY_PROG2),
NAME_ELEMENT(KEY_WWW),
NAME_ELEMENT(KEY_MSDOS),
NAME_ELEMENT(KEY_COFFEE),
NAME_ELEMENT(KEY_DIRECTION),
NAME_ELEMENT(KEY_CYCLEWINDOWS),
NAME_ELEMENT(KEY_MAIL),
NAME_ELEMENT(KEY_BOOKMARKS),
NAME_ELEMENT(KEY_COMPUTER),
NAME_ELEMENT(KEY_BACK),
NAME_ELEMENT(KEY_FORWARD),
NAME_ELEMENT(KEY_CLOSECD),
NAME_ELEMENT(KEY_EJECTCD),
NAME_ELEMENT(KEY_EJECTCLOSECD),
NAME_ELEMENT(KEY_NEXTSONG),
NAME_ELEMENT(KEY_PLAYPAUSE),
NAME_ELEMENT(KEY_PREVIOUSSONG),
NAME_ELEMENT(KEY_STOPCD),
NAME_ELEMENT(KEY_RECORD),
NAME_ELEMENT(KEY_REWIND),
NAME_ELEMENT(KEY_PHONE),
NAME_ELEMENT(KEY_ISO),
NAME_ELEMENT(KEY_CONFIG),
NAME_ELEMENT(KEY_HOMEPAGE),
NAME_ELEMENT(KEY_REFRESH),
NAME_ELEMENT(KEY_EXIT),
NAME_ELEMENT(KEY_MOVE),
NAME_ELEMENT(KEY_EDIT),
NAME_ELEMENT(KEY_SCROLLUP),
NAME_ELEMENT(KEY_SCROLLDOWN),
NAME_ELEMENT(KEY_KPLEFTPAREN),
NAME_ELEMENT(KEY_KPRIGHTPAREN),
NAME_ELEMENT(KEY_F13),
NAME_ELEMENT(KEY_F14),
NAME_ELEMENT(KEY_F15),
NAME_ELEMENT(KEY_F16),
NAME_ELEMENT(KEY_F17),
NAME_ELEMENT(KEY_F18),
NAME_ELEMENT(KEY_F19),
NAME_ELEMENT(KEY_F20),
NAME_ELEMENT(KEY_F21),
NAME_ELEMENT(KEY_F22),
NAME_ELEMENT(KEY_F23),
NAME_ELEMENT(KEY_F24),
NAME_ELEMENT(KEY_PLAYCD),
NAME_ELEMENT(KEY_PAUSECD),
NAME_ELEMENT(KEY_PROG3),
NAME_ELEMENT(KEY_PROG4),
NAME_ELEMENT(KEY_SUSPEND),
NAME_ELEMENT(KEY_CLOSE),
NAME_ELEMENT(KEY_PLAY),
NAME_ELEMENT(KEY_FASTFORWARD),
NAME_ELEMENT(KEY_BASSBOOST),
NAME_ELEMENT(KEY_PRINT),
NAME_ELEMENT(KEY_HP),
NAME_ELEMENT(KEY_CAMERA),
NAME_ELEMENT(KEY_SOUND),
NAME_ELEMENT(KEY_QUESTION),
NAME_ELEMENT(KEY_EMAIL),
NAME_ELEMENT(KEY_CHAT),
NAME_ELEMENT(KEY_SEARCH),
NAME_ELEMENT(KEY_CONNECT),
NAME_ELEMENT(KEY_FINANCE),
NAME_ELEMENT(KEY_SPORT),
NAME_ELEMENT(KEY_SHOP),
NAME_ELEMENT(KEY_ALTERASE),
NAME_ELEMENT(KEY_CANCEL),
NAME_ELEMENT(KEY_BRIGHTNESSDOWN),
NAME_ELEMENT(KEY_BRIGHTNESSUP),
NAME_ELEMENT(KEY_MEDIA),
NAME_ELEMENT(KEY_UNKNOWN),
NAME_ELEMENT(KEY_OK),
NAME_ELEMENT(KEY_SELECT),
NAME_ELEMENT(KEY_GOTO),
NAME_ELEMENT(KEY_CLEAR),
NAME_ELEMENT(KEY_POWER2),
NAME_ELEMENT(KEY_OPTION),
NAME_ELEMENT(KEY_INFO),
NAME_ELEMENT(KEY_TIME),
NAME_ELEMENT(KEY_VENDOR),
NAME_ELEMENT(KEY_ARCHIVE),
NAME_ELEMENT(KEY_PROGRAM),
NAME_ELEMENT(KEY_CHANNEL),
NAME_ELEMENT(KEY_FAVORITES),
NAME_ELEMENT(KEY_EPG),
NAME_ELEMENT(KEY_PVR),
NAME_ELEMENT(KEY_MHP),
NAME_ELEMENT(KEY_LANGUAGE),
NAME_ELEMENT(KEY_TITLE),
NAME_ELEMENT(KEY_SUBTITLE),
NAME_ELEMENT(KEY_ANGLE),
NAME_ELEMENT(KEY_ZOOM),
NAME_ELEMENT(KEY_MODE),
NAME_ELEMENT(KEY_KEYBOARD),
NAME_ELEMENT(KEY_SCREEN),
NAME_ELEMENT(KEY_PC),
NAME_ELEMENT(KEY_TV),
NAME_ELEMENT(KEY_TV2),
NAME_ELEMENT(KEY_VCR),
NAME_ELEMENT(KEY_VCR2),
NAME_ELEMENT(KEY_SAT),
NAME_ELEMENT(KEY_SAT2),
NAME_ELEMENT(KEY_CD),
NAME_ELEMENT(KEY_TAPE),
NAME_ELEMENT(KEY_RADIO),
NAME_ELEMENT(KEY_TUNER),
NAME_ELEMENT(KEY_PLAYER),
NAME_ELEMENT(KEY_TEXT),
NAME_ELEMENT(KEY_DVD),
NAME_ELEMENT(KEY_AUX),
NAME_ELEMENT(KEY_MP3),
NAME_ELEMENT(KEY_AUDIO),
NAME_ELEMENT(KEY_VIDEO),
NAME_ELEMENT(KEY_DIRECTORY),
NAME_ELEMENT(KEY_LIST),
NAME_ELEMENT(KEY_MEMO),
NAME_ELEMENT(KEY_CALENDAR),
NAME_ELEMENT(KEY_RED),
NAME_ELEMENT(KEY_GREEN),
NAME_ELEMENT(KEY_YELLOW),
NAME_ELEMENT(KEY_BLUE),
NAME_ELEMENT(KEY_CHANNELUP),
NAME_ELEMENT(KEY_CHANNELDOWN),
NAME_ELEMENT(KEY_FIRST),
NAME_ELEMENT(KEY_LAST),
NAME_ELEMENT(KEY_AB),
NAME_ELEMENT(KEY_NEXT),
NAME_ELEMENT(KEY_RESTART),
NAME_ELEMENT(KEY_SLOW),
NAME_ELEMENT(KEY_SHUFFLE),
NAME_ELEMENT(KEY_BREAK),
NAME_ELEMENT(KEY_PREVIOUS),
NAME_ELEMENT(KEY_DIGITS),
NAME_ELEMENT(KEY_TEEN),
NAME_ELEMENT(KEY_TWEN),
NAME_ELEMENT(KEY_DEL_EOL),
NAME_ELEMENT(KEY_DEL_EOS),
NAME_ELEMENT(KEY_INS_LINE),
NAME_ELEMENT(KEY_DEL_LINE),
NAME_ELEMENT(KEY_VIDEOPHONE),
NAME_ELEMENT(KEY_GAMES),
NAME_ELEMENT(KEY_ZOOMIN),
NAME_ELEMENT(KEY_ZOOMOUT),
NAME_ELEMENT(KEY_ZOOMRESET),
NAME_ELEMENT(KEY_WORDPROCESSOR),
NAME_ELEMENT(KEY_EDITOR),
NAME_ELEMENT(KEY_SPREADSHEET),
NAME_ELEMENT(KEY_GRAPHICSEDITOR),
NAME_ELEMENT(KEY_PRESENTATION),
NAME_ELEMENT(KEY_DATABASE),
NAME_ELEMENT(KEY_NEWS),
NAME_ELEMENT(KEY_VOICEMAIL),
NAME_ELEMENT(KEY_ADDRESSBOOK),
NAME_ELEMENT(KEY_MESSENGER),
NAME_ELEMENT(KEY_DISPLAYTOGGLE),
#ifdef KEY_SPELLCHECK
NAME_ELEMENT(KEY_SPELLCHECK),
#endif
#ifdef KEY_LOGOFF
NAME_ELEMENT(KEY_LOGOFF),
#endif
#ifdef KEY_DOLLAR
NAME_ELEMENT(KEY_DOLLAR),
#endif
#ifdef KEY_EURO
NAME_ELEMENT(KEY_EURO),
#endif
#ifdef KEY_FRAMEBACK
NAME_ELEMENT(KEY_FRAMEBACK),
#endif
#ifdef KEY_FRAMEFORWARD
NAME_ELEMENT(KEY_FRAMEFORWARD),
#endif
#ifdef KEY_CONTEXT_MENU
NAME_ELEMENT(KEY_CONTEXT_MENU),
#endif
#ifdef KEY_MEDIA_REPEAT
NAME_ELEMENT(KEY_MEDIA_REPEAT),
#endif
#ifdef KEY_10CHANNELSUP
NAME_ELEMENT(KEY_10CHANNELSUP),
#endif
#ifdef KEY_10CHANNELSDOWN
NAME_ELEMENT(KEY_10CHANNELSDOWN),
#endif
#ifdef KEY_IMAGES
NAME_ELEMENT(KEY_IMAGES),
#endif
NAME_ELEMENT(KEY_DEL_EOL),
NAME_ELEMENT(KEY_DEL_EOS),
NAME_ELEMENT(KEY_INS_LINE),
NAME_ELEMENT(KEY_DEL_LINE),
NAME_ELEMENT(KEY_FN),
NAME_ELEMENT(KEY_FN_ESC),
NAME_ELEMENT(KEY_FN_F1),
NAME_ELEMENT(KEY_FN_F2),
NAME_ELEMENT(KEY_FN_F3),
NAME_ELEMENT(KEY_FN_F4),
NAME_ELEMENT(KEY_FN_F5),
NAME_ELEMENT(KEY_FN_F6),
NAME_ELEMENT(KEY_FN_F7),
NAME_ELEMENT(KEY_FN_F8),
NAME_ELEMENT(KEY_FN_F9),
NAME_ELEMENT(KEY_FN_F10),
NAME_ELEMENT(KEY_FN_F11),
NAME_ELEMENT(KEY_FN_F12),
NAME_ELEMENT(KEY_FN_1),
NAME_ELEMENT(KEY_FN_2),
NAME_ELEMENT(KEY_FN_D),
NAME_ELEMENT(KEY_FN_E),
NAME_ELEMENT(KEY_FN_F),
NAME_ELEMENT(KEY_FN_S),
NAME_ELEMENT(KEY_FN_B),
NAME_ELEMENT(KEY_BRL_DOT1),
NAME_ELEMENT(KEY_BRL_DOT2),
NAME_ELEMENT(KEY_BRL_DOT3),
NAME_ELEMENT(KEY_BRL_DOT4),
NAME_ELEMENT(KEY_BRL_DOT5),
NAME_ELEMENT(KEY_BRL_DOT6),
NAME_ELEMENT(KEY_BRL_DOT7),
NAME_ELEMENT(KEY_BRL_DOT8),
NAME_ELEMENT(KEY_BRL_DOT9),
NAME_ELEMENT(KEY_BRL_DOT10),
#ifdef KEY_NUMERIC_0
NAME_ELEMENT(KEY_NUMERIC_0),
NAME_ELEMENT(KEY_NUMERIC_1),
NAME_ELEMENT(KEY_NUMERIC_2),
NAME_ELEMENT(KEY_NUMERIC_3),
NAME_ELEMENT(KEY_NUMERIC_4),
NAME_ELEMENT(KEY_NUMERIC_5),
NAME_ELEMENT(KEY_NUMERIC_6),
NAME_ELEMENT(KEY_NUMERIC_7),
NAME_ELEMENT(KEY_NUMERIC_8),
NAME_ELEMENT(KEY_NUMERIC_9),
NAME_ELEMENT(KEY_NUMERIC_STAR),
NAME_ELEMENT(KEY_NUMERIC_POUND),
#endif
NAME_ELEMENT(KEY_BATTERY),
NAME_ELEMENT(KEY_BLUETOOTH),
NAME_ELEMENT(KEY_BRIGHTNESS_CYCLE),
NAME_ELEMENT(KEY_BRIGHTNESS_ZERO),
#ifdef KEY_DASHBOARD
NAME_ELEMENT(KEY_DASHBOARD),
#endif
NAME_ELEMENT(KEY_DISPLAY_OFF),
NAME_ELEMENT(KEY_DOCUMENTS),
NAME_ELEMENT(KEY_FORWARDMAIL),
NAME_ELEMENT(KEY_NEW),
NAME_ELEMENT(KEY_KBDILLUMDOWN),
NAME_ELEMENT(KEY_KBDILLUMUP),
NAME_ELEMENT(KEY_KBDILLUMTOGGLE),
NAME_ELEMENT(KEY_REDO),
NAME_ELEMENT(KEY_REPLY),
NAME_ELEMENT(KEY_SAVE),
#ifdef KEY_SCALE
NAME_ELEMENT(KEY_SCALE),
#endif
NAME_ELEMENT(KEY_SEND),
NAME_ELEMENT(KEY_SCREENLOCK),
NAME_ELEMENT(KEY_SWITCHVIDEOMODE),
#ifdef KEY_UWB
NAME_ELEMENT(KEY_UWB),
#endif
#ifdef KEY_VIDEO_NEXT
NAME_ELEMENT(KEY_VIDEO_NEXT),
#endif
#ifdef KEY_VIDEO_PREV
NAME_ELEMENT(KEY_VIDEO_PREV),
#endif
#ifdef KEY_WIMAX
NAME_ELEMENT(KEY_WIMAX),
#endif
#ifdef KEY_WLAN
NAME_ELEMENT(KEY_WLAN),
#endif
#ifdef KEY_RFKILL
NAME_ELEMENT(KEY_RFKILL),
#endif
#ifdef KEY_MICMUTE
NAME_ELEMENT(KEY_MICMUTE),
#endif
#ifdef KEY_CAMERA_FOCUS
NAME_ELEMENT(KEY_CAMERA_FOCUS),
#endif
#ifdef KEY_WPS_BUTTON
NAME_ELEMENT(KEY_WPS_BUTTON),
#endif
#ifdef KEY_TOUCHPAD_TOGGLE
NAME_ELEMENT(KEY_TOUCHPAD_TOGGLE),
NAME_ELEMENT(KEY_TOUCHPAD_ON),
NAME_ELEMENT(KEY_TOUCHPAD_OFF),
#endif
#ifdef KEY_CAMERA_ZOOMIN
NAME_ELEMENT(KEY_CAMERA_ZOOMIN),
NAME_ELEMENT(KEY_CAMERA_ZOOMOUT),
NAME_ELEMENT(KEY_CAMERA_UP),
NAME_ELEMENT(KEY_CAMERA_DOWN),
NAME_ELEMENT(KEY_CAMERA_LEFT),
NAME_ELEMENT(KEY_CAMERA_RIGHT),
#endif
#ifdef KEY_ATTENDANT_ON
NAME_ELEMENT(KEY_ATTENDANT_ON),
NAME_ELEMENT(KEY_ATTENDANT_OFF),
NAME_ELEMENT(KEY_ATTENDANT_TOGGLE),
NAME_ELEMENT(KEY_LIGHTS_TOGGLE),
#endif
NAME_ELEMENT(BTN_0),
NAME_ELEMENT(BTN_1),
NAME_ELEMENT(BTN_2),
NAME_ELEMENT(BTN_3),
NAME_ELEMENT(BTN_4),
NAME_ELEMENT(BTN_5),
NAME_ELEMENT(BTN_6),
NAME_ELEMENT(BTN_7),
NAME_ELEMENT(BTN_8),
NAME_ELEMENT(BTN_9),
NAME_ELEMENT(BTN_LEFT),
NAME_ELEMENT(BTN_RIGHT),
NAME_ELEMENT(BTN_MIDDLE),
NAME_ELEMENT(BTN_SIDE),
NAME_ELEMENT(BTN_EXTRA),
NAME_ELEMENT(BTN_FORWARD),
NAME_ELEMENT(BTN_BACK),
NAME_ELEMENT(BTN_TASK),
NAME_ELEMENT(BTN_TRIGGER),
NAME_ELEMENT(BTN_THUMB),
NAME_ELEMENT(BTN_THUMB2),
NAME_ELEMENT(BTN_TOP),
NAME_ELEMENT(BTN_TOP2),
NAME_ELEMENT(BTN_PINKIE),
NAME_ELEMENT(BTN_BASE),
NAME_ELEMENT(BTN_BASE2),
NAME_ELEMENT(BTN_BASE3),
NAME_ELEMENT(BTN_BASE4),
NAME_ELEMENT(BTN_BASE5),
NAME_ELEMENT(BTN_BASE6),
NAME_ELEMENT(BTN_DEAD),
NAME_ELEMENT(BTN_C),
#ifdef BTN_SOUTH
NAME_ELEMENT(BTN_SOUTH),
NAME_ELEMENT(BTN_EAST),
NAME_ELEMENT(BTN_NORTH),
NAME_ELEMENT(BTN_WEST),
#else
NAME_ELEMENT(BTN_A),
NAME_ELEMENT(BTN_B),
NAME_ELEMENT(BTN_X),
NAME_ELEMENT(BTN_Y),
#endif
NAME_ELEMENT(BTN_Z),
NAME_ELEMENT(BTN_TL),
NAME_ELEMENT(BTN_TR),
NAME_ELEMENT(BTN_TL2),
NAME_ELEMENT(BTN_TR2),
NAME_ELEMENT(BTN_SELECT),
NAME_ELEMENT(BTN_START),
NAME_ELEMENT(BTN_MODE),
NAME_ELEMENT(BTN_THUMBL),
NAME_ELEMENT(BTN_THUMBR),
NAME_ELEMENT(BTN_TOOL_PEN),
NAME_ELEMENT(BTN_TOOL_RUBBER),
NAME_ELEMENT(BTN_TOOL_BRUSH),
NAME_ELEMENT(BTN_TOOL_PENCIL),
NAME_ELEMENT(BTN_TOOL_AIRBRUSH),
NAME_ELEMENT(BTN_TOOL_FINGER),
NAME_ELEMENT(BTN_TOOL_MOUSE),
NAME_ELEMENT(BTN_TOOL_LENS),
NAME_ELEMENT(BTN_TOUCH),
NAME_ELEMENT(BTN_STYLUS),
NAME_ELEMENT(BTN_STYLUS2),
NAME_ELEMENT(BTN_TOOL_DOUBLETAP),
NAME_ELEMENT(BTN_TOOL_TRIPLETAP),
#ifdef BTN_TOOL_QUADTAP
NAME_ELEMENT(BTN_TOOL_QUADTAP),
#endif
NAME_ELEMENT(BTN_GEAR_DOWN),
NAME_ELEMENT(BTN_GEAR_UP),
#ifdef BTN_DPAD_UP
NAME_ELEMENT(BTN_DPAD_UP),
NAME_ELEMENT(BTN_DPAD_DOWN),
NAME_ELEMENT(BTN_DPAD_LEFT),
NAME_ELEMENT(BTN_DPAD_RIGHT),
#endif
#ifdef KEY_ALS_TOGGLE
NAME_ELEMENT(KEY_ALS_TOGGLE),
#endif
#ifdef KEY_BUTTONCONFIG
NAME_ELEMENT(KEY_BUTTONCONFIG),
#endif
#ifdef KEY_TASKMANAGER
NAME_ELEMENT(KEY_TASKMANAGER),
#endif
#ifdef KEY_JOURNAL
NAME_ELEMENT(KEY_JOURNAL),
#endif
#ifdef KEY_CONTROLPANEL
NAME_ELEMENT(KEY_CONTROLPANEL),
#endif
#ifdef KEY_APPSELECT
NAME_ELEMENT(KEY_APPSELECT),
#endif
#ifdef KEY_SCREENSAVER
NAME_ELEMENT(KEY_SCREENSAVER),
#endif
#ifdef KEY_VOICECOMMAND
NAME_ELEMENT(KEY_VOICECOMMAND),
#endif
#ifdef KEY_BRIGHTNESS_MIN
NAME_ELEMENT(KEY_BRIGHTNESS_MIN),
#endif
#ifdef KEY_BRIGHTNESS_MAX
NAME_ELEMENT(KEY_BRIGHTNESS_MAX),
#endif
#ifdef KEY_KBDINPUTASSIST_PREV
NAME_ELEMENT(KEY_KBDINPUTASSIST_PREV),
#endif
#ifdef KEY_KBDINPUTASSIST_NEXT
NAME_ELEMENT(KEY_KBDINPUTASSIST_NEXT),
#endif
#ifdef KEY_KBDINPUTASSIST_PREVGROUP
NAME_ELEMENT(KEY_KBDINPUTASSIST_PREVGROUP),
#endif
#ifdef KEY_KBDINPUTASSIST_NEXTGROUP
NAME_ELEMENT(KEY_KBDINPUTASSIST_NEXTGROUP),
#endif
#ifdef KEY_KBDINPUTASSIST_ACCEPT
NAME_ELEMENT(KEY_KBDINPUTASSIST_ACCEPT),
#endif
#ifdef KEY_KBDINPUTASSIST_CANCEL
NAME_ELEMENT(KEY_KBDINPUTASSIST_CANCEL),
#endif
#ifdef BTN_TRIGGER_HAPPY
NAME_ELEMENT(BTN_TRIGGER_HAPPY1),
NAME_ELEMENT(BTN_TRIGGER_HAPPY11),
NAME_ELEMENT(BTN_TRIGGER_HAPPY2),
NAME_ELEMENT(BTN_TRIGGER_HAPPY12),
NAME_ELEMENT(BTN_TRIGGER_HAPPY3),
NAME_ELEMENT(BTN_TRIGGER_HAPPY13),
NAME_ELEMENT(BTN_TRIGGER_HAPPY4),
NAME_ELEMENT(BTN_TRIGGER_HAPPY14),
NAME_ELEMENT(BTN_TRIGGER_HAPPY5),
NAME_ELEMENT(BTN_TRIGGER_HAPPY15),
NAME_ELEMENT(BTN_TRIGGER_HAPPY6),
NAME_ELEMENT(BTN_TRIGGER_HAPPY16),
NAME_ELEMENT(BTN_TRIGGER_HAPPY7),
NAME_ELEMENT(BTN_TRIGGER_HAPPY17),
NAME_ELEMENT(BTN_TRIGGER_HAPPY8),
NAME_ELEMENT(BTN_TRIGGER_HAPPY18),
NAME_ELEMENT(BTN_TRIGGER_HAPPY9),
NAME_ELEMENT(BTN_TRIGGER_HAPPY19),
NAME_ELEMENT(BTN_TRIGGER_HAPPY10),
NAME_ELEMENT(BTN_TRIGGER_HAPPY20),
NAME_ELEMENT(BTN_TRIGGER_HAPPY21),
NAME_ELEMENT(BTN_TRIGGER_HAPPY31),
NAME_ELEMENT(BTN_TRIGGER_HAPPY22),
NAME_ELEMENT(BTN_TRIGGER_HAPPY32),
NAME_ELEMENT(BTN_TRIGGER_HAPPY23),
NAME_ELEMENT(BTN_TRIGGER_HAPPY33),
NAME_ELEMENT(BTN_TRIGGER_HAPPY24),
NAME_ELEMENT(BTN_TRIGGER_HAPPY34),
NAME_ELEMENT(BTN_TRIGGER_HAPPY25),
NAME_ELEMENT(BTN_TRIGGER_HAPPY35),
NAME_ELEMENT(BTN_TRIGGER_HAPPY26),
NAME_ELEMENT(BTN_TRIGGER_HAPPY36),
NAME_ELEMENT(BTN_TRIGGER_HAPPY27),
NAME_ELEMENT(BTN_TRIGGER_HAPPY37),
NAME_ELEMENT(BTN_TRIGGER_HAPPY28),
NAME_ELEMENT(BTN_TRIGGER_HAPPY38),
NAME_ELEMENT(BTN_TRIGGER_HAPPY29),
NAME_ELEMENT(BTN_TRIGGER_HAPPY39),
NAME_ELEMENT(BTN_TRIGGER_HAPPY30),
NAME_ELEMENT(BTN_TRIGGER_HAPPY40),
#endif
#ifdef BTN_TOOL_QUINTTAP
NAME_ELEMENT(BTN_TOOL_QUINTTAP),
#endif
};
static const char *const *const names[EV_MAX + 1] = {
[0 ... EV_MAX] = NULL,
[EV_KEY] = keys,
[EV_ABS] = absolutes,
};
typedef struct
{
Controller *controller;
Controllers *controllers;
int analogue_deadzone_start[8];
int analogue_deadzone_middle[8];
int analogue_deadzone_end[8];
} ThreadArguments;
typedef struct
{
double start_max;
double middle_min;
double middle_max;
double end_min;
} AnalogueDeadzones;
static inline const char *typename(unsigned int type)
{
return (type <= EV_MAX && events[type]) ? events[type] : "?";
}
static inline const char *codename(unsigned int type, unsigned int code)
{
return (type <= EV_MAX && code <= maxval[type] && names[type] && names[type][code]) ? names[type][code] : "?";
}
static int isEventDevice(const struct dirent *dir)
{
return strncmp("event", dir->d_name, 5) == 0;
}
ControllerStatus stopControllers(Controllers *controllers)
{
controllers->threadsRunning = 0;
for (int i = 0; i < controllers->threadIndex; i++)
{
pthread_join(controllers->thread[i], NULL);
}
free(controllers->controller);
return CONTROLLER_STATUS_SUCCESS;
}
void normaliseName(char *string)
{
for (size_t j = 0; j < strlen(string); j++)
{
string[j] = toupper(string[j]);
if (string[j] == ' ' || string[j] == '/' || string[j] == '(' || string[j] == ',' || string[j] == '=' ||
string[j] == '-' || string[j] == ')')
{
string[j] = '_';
}
}
}
static ControllerStatus listControllers(Controllers *controllers)
{
struct dirent **namelist;
controllers->count = scandir("/dev/input", &namelist, isEventDevice, alphasort);
controllers->controller = malloc(sizeof(Controller) * controllers->count);
if (controllers == NULL)
{
for (int i = 0; i < controllers->count; i++)
{
free(namelist[i]);
}
free(namelist);
return CONTROLLER_STATUS_ERROR;
}
for (int i = 0; i < controllers->count; i++)
{
snprintf(controllers->controller[i].path, sizeof(controllers->controller[i].path), "%s/%s", "/dev/input",
namelist[i]->d_name);
free(namelist[i]);
controllers->controller[i].enabled = 0;
controllers->controller[i].inputCount = 0;
controllers->controller[i].inUse = 0;
int controller = open(controllers->controller[i].path, O_RDONLY);
if (controller < 0)
{
strcpy(controllers->controller[i].name, "Unknown");
continue;
}
// Get the controllers name
ioctl(controller, EVIOCGNAME(sizeof(controllers->controller[i].name)), controllers->controller[i].name);
normaliseName(controllers->controller[i].name);
// Count how many duplicates there are
int duplicates = 0;
for (int j = 0; j < i; j++)
{
if (strcmp(controllers->controller[i].name, controllers->controller[j].name) == 0)
{
duplicates += 1;
}
}
// Append the controller number
if (duplicates > 0)
{
char number[SIZE];
sprintf(number, "%d", duplicates + 1);
strcat(controllers->controller[i].name, "-");
strcat(controllers->controller[i].name, number);
}
// Get the controllers physical location
ioctl(controller, EVIOCGPHYS(sizeof(controllers->controller[i].physicalLocation)),
controllers->controller[i].physicalLocation);
for (size_t j = 0; j < strlen(controllers->controller[i].physicalLocation); j++)
{
if (controllers->controller[i].physicalLocation[j] == '/')
{
controllers->controller[i].physicalLocation[j] = 0;
break;
}
}
// Get the capabilities of the controllers
unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
memset(bit, 0, sizeof(bit));
ioctl(controller, EVIOCGBIT(0, EV_MAX), bit[0]);
if (test_bit(EV_KEY, bit[0]))
{
ioctl(controller, EVIOCGBIT(EV_KEY, KEY_MAX), bit[EV_KEY]);
for (int code = 0; code < KEY_MAX; code++)
{
if (test_bit(code, bit[EV_KEY]))
{
controllers->controller[i].enabled = 1;
ControllerInput *controllerInput =
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
controllerInput->evType = EV_KEY;
controllerInput->evCode = code;
controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
strcpy(controllerInput->inputName, controllers->controller[i].name);
strcat(controllerInput->inputName, "_");
strcat(controllerInput->inputName, codename(EV_KEY, code));
normaliseName(controllerInput->inputName);
}
}
}
if (test_bit(EV_ABS, bit[0]))
{
ioctl(controller, EVIOCGBIT(EV_ABS, KEY_MAX), bit[EV_ABS]);
for (int code = 0; code < KEY_MAX; code++)
{
if (test_bit(code, bit[EV_ABS]))
{
controllers->controller[i].enabled = 1;
ControllerInput *controllerInput =
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
controllerInput->evType = EV_ABS;
controllerInput->evCode = code;
controllerInput->specialFunction = NO_SPECIAL_FUNCTION;
strcpy(controllerInput->inputName, controllers->controller[i].name);
strcat(controllerInput->inputName, "_");
strcat(controllerInput->inputName, codename(EV_ABS, code));
normaliseName(controllerInput->inputName);
ControllerInput *minControllerInput =
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
minControllerInput->evType = EV_ABS;
minControllerInput->evCode = code;
minControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MIN;
strcpy(minControllerInput->inputName, controllerInput->inputName);
strcat(minControllerInput->inputName, "_MIN");
ControllerInput *maxControllerInput =
&controllers->controller[i].inputs[controllers->controller[i].inputCount++];
maxControllerInput->evType = EV_ABS;
maxControllerInput->evCode = code;
maxControllerInput->specialFunction = ANALOGUE_TO_DIGITAL_MAX;
strcpy(maxControllerInput->inputName, controllerInput->inputName);
strcat(maxControllerInput->inputName, "_MAX");
struct input_absinfo absoluteFeatures;
ioctl(controller, EVIOCGABS(code), &absoluteFeatures);
controllers->controller[i].absMin[code] = absoluteFeatures.minimum;
controllers->controller[i].absMax[code] = absoluteFeatures.maximum;
}
}
}
close(controller);
}
free(namelist);
// Sort the list of controllers
for (int i = 0; i < controllers->count - 1; i++)
{
for (int j = 0; j < controllers->count - 1 - i; j++)
{
Controller tmp;
if (strcmp(controllers->controller[j].physicalLocation, controllers->controller[j + 1].physicalLocation) >
0)
{
tmp = controllers->controller[j];
controllers->controller[j] = controllers->controller[j + 1];
controllers->controller[j + 1] = tmp;
}
}
}
return CONTROLLER_STATUS_SUCCESS;
}
void *controllerThread(void *_args)
{
ThreadArguments *args = (ThreadArguments *)_args;
int fd = open(args->controller->path, O_RDONLY);
if (fd < 0)
{
printf("Error: Failed to open device file descriptor %d \n", fd);
free(args);
return 0;
}
struct input_event event;
AnalogueDeadzones analogue_deadzones[8];
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
fd_set file_descriptor;
struct timeval tv;
args->controllers->threadsRunning = 1;
for (int i = 0; i < 8; ++i) {
analogue_deadzones[i].start_max = args->analogue_deadzone_start[i] / 100.0;
analogue_deadzones[i].middle_min = 0.5 - (args->analogue_deadzone_middle[i] / 100.0);
analogue_deadzones[i].middle_max = 0.5 + (args->analogue_deadzone_middle[i] / 100.0);
analogue_deadzones[i].end_min = 1.0 - (args->analogue_deadzone_end[i] / 100.0);
}
while (args->controllers->threadsRunning)
{
FD_ZERO(&file_descriptor);
FD_SET(fd, &file_descriptor);
tv.tv_sec = 0;
tv.tv_usec = 2 * 1000;
if (select(fd + 1, &file_descriptor, NULL, NULL, &tv) < 1)
continue;
if (read(fd, &event, sizeof(event)) == sizeof(event))
{
switch (event.type)
{
case EV_KEY:
{
if (!args->controller->keyTriggers[event.code].enabled)
continue;
if (args->controller->keyTriggers[event.code].isAnalogue)
{
setAnalogue(args->controller->keyTriggers[event.code].channel,
event.value == 0 ? 0 : 1 * (pow(2, jvsBits) - 1));
continue;
}
setSwitch(args->controller->keyTriggers[event.code].player,
args->controller->keyTriggers[event.code].channel, event.value == 0 ? 0 : 1);
}
break;
case EV_ABS:
{
double scaled =
((double)event.value - (double)args->controller->absMin[event.code]) /
((double)args->controller->absMax[event.code] - (double)args->controller->absMin[event.code]);
if (args->controller->absTriggers[event.code].enabled)
{
int channel = args->controller->absTriggers[event.code].channel;
// Deadzone handling
if (scaled < analogue_deadzones[channel].start_max)
scaled = 0.0;
if (scaled > analogue_deadzones[channel].middle_min && scaled < analogue_deadzones[channel].middle_max)
scaled = 0.5;
if (scaled > analogue_deadzones[channel].end_min)
scaled = 1.0;
setAnalogue(channel, scaled * (pow(2, jvsBits) - 1));
}
if (args->controller->absTriggers[event.code].minEnabled)
{
setSwitch(args->controller->absTriggers[event.code].minPlayer,
args->controller->absTriggers[event.code].minChannel, scaled < 0.2);
}
if (args->controller->absTriggers[event.code].maxEnabled)
{
setSwitch(args->controller->absTriggers[event.code].maxPlayer,
args->controller->absTriggers[event.code].maxChannel, scaled > 0.8);
}
}
break;
default:
break;
}
}
}
close(fd);
free(args);
return NULL;
}
char *getMapping(char *mapping)
{
EmulatorConfig *config = getConfig();
// Test button
if (strcmp(mapping, config->arcadeInputs.test) == 0)
return "TEST_BUTTON";
// Player 1 controls
if (strcmp(mapping, config->arcadeInputs.player1_button_start) == 0)
return "PLAYER_1_BUTTON_START";
if (strcmp(mapping, config->arcadeInputs.player1_button_service) == 0)
return "PLAYER_1_BUTTON_SERVICE";
if (strcmp(mapping, config->arcadeInputs.player1_button_up) == 0)
return "PLAYER_1_BUTTON_UP";
if (strcmp(mapping, config->arcadeInputs.player1_button_down) == 0)
return "PLAYER_1_BUTTON_DOWN";
if (strcmp(mapping, config->arcadeInputs.player1_button_left) == 0)
return "PLAYER_1_BUTTON_LEFT";
if (strcmp(mapping, config->arcadeInputs.player1_button_right) == 0)
return "PLAYER_1_BUTTON_RIGHT";
if (strcmp(mapping, config->arcadeInputs.player1_button_1) == 0)
return "PLAYER_1_BUTTON_1";
if (strcmp(mapping, config->arcadeInputs.player1_button_2) == 0)
return "PLAYER_1_BUTTON_2";
if (strcmp(mapping, config->arcadeInputs.player1_button_3) == 0)
return "PLAYER_1_BUTTON_3";
if (strcmp(mapping, config->arcadeInputs.player1_button_4) == 0)
return "PLAYER_1_BUTTON_4";
if (strcmp(mapping, config->arcadeInputs.player1_button_5) == 0)
return "PLAYER_1_BUTTON_5";
if (strcmp(mapping, config->arcadeInputs.player1_button_6) == 0)
return "PLAYER_1_BUTTON_6";
if (strcmp(mapping, config->arcadeInputs.player1_button_7) == 0)
return "PLAYER_1_BUTTON_7";
if (strcmp(mapping, config->arcadeInputs.player1_button_8) == 0)
return "PLAYER_1_BUTTON_8";
if (strcmp(mapping, config->arcadeInputs.player1_button_9) == 0)
return "PLAYER_1_BUTTON_9";
if (strcmp(mapping, config->arcadeInputs.player1_button_10) == 0)
return "PLAYER_1_BUTTON_10";
// Player 2 controls
if (strcmp(mapping, config->arcadeInputs.player2_button_start) == 0)
return "PLAYER_2_BUTTON_START";
if (strcmp(mapping, config->arcadeInputs.player2_button_service) == 0)
return "PLAYER_2_BUTTON_SERVICE";
if (strcmp(mapping, config->arcadeInputs.player2_button_up) == 0)
return "PLAYER_2_BUTTON_UP";
if (strcmp(mapping, config->arcadeInputs.player2_button_down) == 0)
return "PLAYER_2_BUTTON_DOWN";
if (strcmp(mapping, config->arcadeInputs.player2_button_left) == 0)
return "PLAYER_2_BUTTON_LEFT";
if (strcmp(mapping, config->arcadeInputs.player2_button_right) == 0)
return "PLAYER_2_BUTTON_RIGHT";
if (strcmp(mapping, config->arcadeInputs.player2_button_1) == 0)
return "PLAYER_2_BUTTON_1";
if (strcmp(mapping, config->arcadeInputs.player2_button_2) == 0)
return "PLAYER_2_BUTTON_2";
if (strcmp(mapping, config->arcadeInputs.player2_button_3) == 0)
return "PLAYER_2_BUTTON_3";
if (strcmp(mapping, config->arcadeInputs.player2_button_4) == 0)
return "PLAYER_2_BUTTON_4";
if (strcmp(mapping, config->arcadeInputs.player2_button_5) == 0)
return "PLAYER_2_BUTTON_5";
if (strcmp(mapping, config->arcadeInputs.player2_button_6) == 0)
return "PLAYER_2_BUTTON_6";
if (strcmp(mapping, config->arcadeInputs.player2_button_7) == 0)
return "PLAYER_2_BUTTON_7";
if (strcmp(mapping, config->arcadeInputs.player2_button_8) == 0)
return "PLAYER_2_BUTTON_8";
if (strcmp(mapping, config->arcadeInputs.player2_button_9) == 0)
return "PLAYER_2_BUTTON_9";
if (strcmp(mapping, config->arcadeInputs.player2_button_10) == 0)
return "PLAYER_2_BUTTON_10";
// Analogue inputs
if (strcmp(mapping, config->arcadeInputs.analogue_1) == 0)
return "ANALOGUE_1";
if (strcmp(mapping, config->arcadeInputs.analogue_2) == 0)
return "ANALOGUE_2";
if (strcmp(mapping, config->arcadeInputs.analogue_3) == 0)
return "ANALOGUE_3";
if (strcmp(mapping, config->arcadeInputs.analogue_4) == 0)
return "ANALOGUE_4";
if (strcmp(mapping, config->arcadeInputs.analogue_5) == 0)
return "ANALOGUE_5";
if (strcmp(mapping, config->arcadeInputs.analogue_6) == 0)
return "ANALOGUE_6";
if (strcmp(mapping, config->arcadeInputs.analogue_7) == 0)
return "ANALOGUE_7";
if (strcmp(mapping, config->arcadeInputs.analogue_8) == 0)
return "ANALOGUE_8";
return NULL;
}
ControllerStatus getArcadeInputByName(char *name, ArcadeInput *input)
{
int index = 0;
char *tempName = arcadeInputs[index].name;
while (strcmp(tempName, "END") != 0)
{
if (strcmp(tempName, name) == 0)
{
strcpy(input->name, arcadeInputs[index].name);
input->channel = arcadeInputs[index].channel;
input->player = arcadeInputs[index].player;
return CONTROLLER_STATUS_SUCCESS;
}
index = index + 1;
tempName = arcadeInputs[index].name;
}
return CONTROLLER_STATUS_ERROR;
}
ControllerStatus startControllerThreads(Controllers *controllers)
{
// Don't start EVDEV threads if SDL/X11-only mode is enabled
if (getConfig()->inputMode == 1) {
return CONTROLLER_STATUS_SUCCESS;
}
for (int i = 0; i < controllers->count; i++)
{
if (!controllers->controller[i].enabled)
continue;
for (int j = 0; j < ABS_MAX; j++)
{
controllers->controller[i].absTriggers[j].enabled = 0;
controllers->controller[i].absTriggers[j].minEnabled = 0;
controllers->controller[i].absTriggers[j].maxEnabled = 0;
}
for (int j = 0; j < KEY_MAX; j++)
{
controllers->controller[i].keyTriggers[j].enabled = 0;
controllers->controller[i].keyTriggers[j].isAnalogue = 0;
}
for (int j = 0; j < controllers->controller[i].inputCount; j++)
{
char *mapping = getMapping(controllers->controller[i].inputs[j].inputName);
if (mapping == NULL)
continue;
ArcadeInput input = {0};
ControllerStatus status = getArcadeInputByName(mapping, &input);
if (status != CONTROLLER_STATUS_SUCCESS)
{
printf("Warning: Couldn't find arcade mapping with title %s\n", mapping);
continue;
}
switch (controllers->controller[i].inputs[j].evType)
{
case EV_ABS:
{
switch (controllers->controller[i].inputs[j].specialFunction)
{
case NO_SPECIAL_FUNCTION:
{
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].channel =
input.channel;
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].name,
input.name);
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].player =
input.player;
}
break;
case ANALOGUE_TO_DIGITAL_MAX:
{
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxEnabled = 1;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxChannel =
input.channel;
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxName,
input.name);
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].maxPlayer =
input.player;
}
break;
case ANALOGUE_TO_DIGITAL_MIN:
{
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minEnabled = 1;
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minChannel =
input.channel;
strcpy(controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minName,
input.name);
controllers->controller[i].absTriggers[controllers->controller[i].inputs[j].evCode].minPlayer =
input.player;
}
break;
default:
break;
}
}
break;
case EV_KEY:
{
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].enabled = 1;
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].channel =
input.channel;
strcpy(controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].name,
input.name);
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].player =
input.player;
if (strstr(input.name, "ANALOGUE") != NULL)
controllers->controller[i].keyTriggers[controllers->controller[i].inputs[j].evCode].isAnalogue = 1;
}
break;
default:
{
printf("Warining: Unknown mapping type\n");
}
}
}
int controllerHasInputsEnabled = 0;
for (int j = 0; j < KEY_MAX; j++)
{
if (controllers->controller[i].keyTriggers[j].enabled)
controllerHasInputsEnabled = 1;
}
for (int j = 0; j < ABS_MAX; j++)
if (controllers->controller[i].absTriggers[j].enabled)
controllerHasInputsEnabled = 1;
if (!controllerHasInputsEnabled)
continue;
EmulatorConfig *config = getConfig();
ThreadArguments *args = malloc(sizeof(ThreadArguments));
args->controller = &controllers->controller[i];
args->controllers = controllers;
memcpy(args->analogue_deadzone_start, config->arcadeInputs.analogue_deadzone_start, sizeof(config->arcadeInputs.analogue_deadzone_start));
memcpy(args->analogue_deadzone_middle, config->arcadeInputs.analogue_deadzone_middle, sizeof(config->arcadeInputs.analogue_deadzone_middle));
memcpy(args->analogue_deadzone_end, config->arcadeInputs.analogue_deadzone_end, sizeof(config->arcadeInputs.analogue_deadzone_end));
pthread_create(&controllers->thread[controllers->threadIndex++], NULL, controllerThread, args);
controllers->controller[i].inUse = 1;
}
return CONTROLLER_STATUS_SUCCESS;
}
ControllerStatus initControllers(Controllers *controllers)
{
jvsBits = getJVSIO()->capabilities.analogueInBits;
controllers->threadIndex = 0;
if (listControllers(controllers) != CONTROLLER_STATUS_SUCCESS)
{
return CONTROLLER_STATUS_ERROR;
}
if (startControllerThreads(controllers) != CONTROLLER_STATUS_SUCCESS)
{
return CONTROLLER_STATUS_ERROR;
}
return CONTROLLER_STATUS_SUCCESS;
}