From 59d710c95f170fe1217fbae28acbee8260ee7266 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 6 May 2015 23:41:40 +0200 Subject: [PATCH] Support gamepad with mapping --- src/input.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/input.h | 2 +- src/main.c | 8 ++- 3 files changed, 179 insertions(+), 5 deletions(-) diff --git a/src/input.c b/src/input.c index 6ea5963..530f9a0 100644 --- a/src/input.c +++ b/src/input.c @@ -18,30 +18,57 @@ */ #include "keyboard.h" +#include "mapping.h" #include "libevdev/libevdev.h" #include "limelight-common/Limelight.h" #include #include +#include #include #include #include #include #include #include +#include + +struct input_abs_parms { + int min, max; + int flat; + int avg; + int range, diff; +}; struct input_device { struct libevdev *dev; + struct mapping map; int fd; __s32 mouseDeltaX, mouseDeltaY, mouseScroll; + int buttonFlags; + short leftTrigger, rightTrigger; + short leftStickX, leftStickY; + short rightStickX, rightStickY; + bool gamepadModified; + struct input_abs_parms xParms, yParms, rxParms, ryParms, zParms, rzParms; + struct input_abs_parms dpadxParms, dpadyParms; }; static struct pollfd* fds = NULL; static struct input_device* devices = NULL; static int numDevices = 0; -void input_create(char* device) { +static void input_init_parms(struct input_device *dev, struct input_abs_parms *parms, int code) { + parms->flat = libevdev_get_abs_flat(dev->dev, code); + parms->min = libevdev_get_abs_minimum(dev->dev, code); + parms->max = libevdev_get_abs_maximum(dev->dev, code); + parms->avg = (parms->min+parms->max)/2; + parms->range = parms->max - parms->avg; + parms->diff = parms->max - parms->min; +} + +void input_create(char* device, char* mapFile) { int dev = numDevices; numDevices++; @@ -68,9 +95,56 @@ void input_create(char* device) { libevdev_set_fd(devices[dev].dev, devices[dev].fd); fds[dev].fd = devices[dev].fd; fds[dev].events = POLLIN; + + if (mapFile != NULL) + mapping_load(mapFile, &(devices[dev].map)); + + input_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_x); + input_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_y); + input_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_z); + input_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rx); + input_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_ry); + input_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_rz); + input_init_parms(&devices[dev], &(devices[dev].dpadxParms), devices[dev].map.abs_dpad_x); + input_init_parms(&devices[dev], &(devices[dev].dpadyParms), devices[dev].map.abs_dpad_y); } -static void handle_event(struct input_event *ev, struct input_device *dev) { +static short input_convert_value(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) { + if (abs(ev->value) < parms->flat) + return 0; + else if (ev->value > parms->max) + return SHRT_MAX; + else if (ev->value < parms->min) + return SHRT_MIN; + else { + int value = ev->value + (ev->valueavg?parms->flat:-parms->flat); + return (value-parms->avg) * SHRT_MAX / (parms->range - parms->flat); + } +} + +static char input_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) { + if (abs(ev->value-parms->min)flat) + return 0; + else if (ev->value>parms->max) + return UCHAR_MAX; + else { + int value = ev->value + parms->flat; + return (value-parms->min) * UCHAR_MAX / (parms->diff-parms->flat); + } +} + +static int input_convert_value_direction(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) { + if (ev->value > (parms->avg+parms->range/4)) + return 1; + else if (ev->value < (parms->avg-parms->range/4)) + return -1; + else + return 0; +} + +static void input_handle_event(struct input_event *ev, struct input_device *dev) { + bool gamepadModified = false; + switch (ev->type) { case EV_SYN: if (dev->mouseDeltaX != 0 || dev->mouseDeltaY != 0) { @@ -82,6 +156,10 @@ static void handle_event(struct input_event *ev, struct input_device *dev) { LiSendScrollEvent(dev->mouseScroll); dev->mouseScroll = 0; } + if (dev->gamepadModified) { + LiSendControllerEvent(dev->buttonFlags, dev->leftTrigger, dev->rightTrigger, dev->leftStickX, dev->leftStickY, dev->rightStickX, dev->rightStickY); + dev->gamepadModified = false; + } break; case EV_KEY: if (ev->code < sizeof(keyCodes)/sizeof(keyCodes[0])) { @@ -89,6 +167,7 @@ static void handle_event(struct input_event *ev, struct input_device *dev) { LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, 0); } else { int mouseCode = 0; + short gamepadCode = 0; switch (ev->code) { case BTN_LEFT: mouseCode = BUTTON_LEFT; @@ -99,10 +178,55 @@ static void handle_event(struct input_event *ev, struct input_device *dev) { case BTN_RIGHT: mouseCode = BUTTON_RIGHT; break; + default: + if (ev->code == dev->map.btn_south) + gamepadCode = A_FLAG; + else if (ev->code == dev->map.btn_west) + gamepadCode = X_FLAG; + else if (ev->code == dev->map.btn_north) + gamepadCode = Y_FLAG; + else if (ev->code == dev->map.btn_east) + gamepadCode = B_FLAG; + else if (ev->code == dev->map.btn_dpad_up) + gamepadCode = UP_FLAG; + else if (ev->code == dev->map.btn_dpad_down) + gamepadCode = DOWN_FLAG; + else if (ev->code == dev->map.btn_dpad_right) + gamepadCode = RIGHT_FLAG; + else if (ev->code == dev->map.btn_dpad_left) + gamepadCode = LEFT_FLAG; + else if (ev->code == dev->map.btn_thumbl) + gamepadCode = LS_CLK_FLAG; + else if (ev->code == dev->map.btn_thumbr) + gamepadCode = RS_CLK_FLAG; + else if (ev->code == dev->map.btn_tl) + gamepadCode = LB_FLAG; + else if (ev->code == dev->map.btn_tr) + gamepadCode = RB_FLAG; + else if (ev->code == dev->map.btn_start) + gamepadCode = PLAY_FLAG; + else if (ev->code == dev->map.btn_select) + gamepadCode = BACK_FLAG; + else if (ev->code == dev->map.btn_mode) + gamepadCode = SPECIAL_FLAG; } if (mouseCode > 0) { LiSendMouseButtonEvent(ev->value?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, mouseCode); + } else { + gamepadModified = true; + + if (gamepadCode > 0) { + if (ev->value) + dev->buttonFlags |= gamepadCode; + else + dev->buttonFlags &= ~gamepadCode; + } else if (ev->code == dev->map.btn_tl2) + dev->leftTrigger = ev->value?USHRT_MAX:0; + else if (ev->code == dev->map.btn_tr2) + dev->rightTrigger = ev->value?USHRT_MAX:0; + else + gamepadModified = false; } } break; @@ -119,7 +243,51 @@ static void handle_event(struct input_event *ev, struct input_device *dev) { break; } break; + case EV_ABS: + gamepadModified = true; + if (ev->code == dev->map.abs_x) + dev->leftStickX = input_convert_value(ev, dev, &dev->xParms); + else if (ev->code == dev->map.abs_y) + dev->leftStickY = input_convert_value(ev, dev, &dev->yParms); + else if (ev->code == dev->map.abs_rx) + dev->rightStickX = input_convert_value(ev, dev, &dev->rxParms); + else if (ev->code == dev->map.abs_ry) + dev->rightStickY = input_convert_value(ev, dev, &dev->ryParms); + else if (ev->code == dev->map.abs_z) + dev->leftTrigger = input_convert_value_byte(ev, dev, &dev->zParms); + else if (ev->code == dev->map.abs_rz) + dev->rightTrigger = input_convert_value_byte(ev, dev, &dev->rzParms); + else if (ev->code == dev->map.abs_dpad_x) { + int dir = input_convert_value_direction(ev, dev, &dev->dpadxParms); + if (dir == 1) { + dev->buttonFlags |= RIGHT_FLAG; + dev->buttonFlags &= ~LEFT_FLAG; + } else if (dir == -1) { + dev->buttonFlags &= ~RIGHT_FLAG; + dev->buttonFlags &= ~LEFT_FLAG; + } else { + dev->buttonFlags &= ~RIGHT_FLAG; + dev->buttonFlags |= LEFT_FLAG; + } + } else if (ev->code == dev->map.abs_dpad_y) { + int dir = input_convert_value_direction(ev, dev, &dev->dpadyParms); + if (dir == 1) { + dev->buttonFlags |= UP_FLAG; + dev->buttonFlags &= ~DOWN_FLAG; + } else if (dir == -1) { + dev->buttonFlags &= ~UP_FLAG; + dev->buttonFlags &= ~DOWN_FLAG; + } else { + dev->buttonFlags &= ~DOWN_FLAG; + dev->buttonFlags |= UP_FLAG; + } + } else + gamepadModified = false; + + break; } + + dev->gamepadModified |= gamepadModified; } void input_loop() { @@ -132,7 +300,7 @@ void input_loop() { if (rc == LIBEVDEV_READ_STATUS_SYNC) fprintf(stderr, "Error: cannot keep up\n"); else if (rc == LIBEVDEV_READ_STATUS_SUCCESS); - handle_event(&ev, &devices[i]); + input_handle_event(&ev, &devices[i]); } if (rc != -EAGAIN && rc < 0) { fprintf(stderr, "Error: %s\n", strerror(-rc)); diff --git a/src/input.h b/src/input.h index 6209803..eab3459 100644 --- a/src/input.h +++ b/src/input.h @@ -17,5 +17,5 @@ * along with Moonlight; if not, see . */ -void input_create(char* device); +void input_create(char* device, char* mapFile); void input_loop(); diff --git a/src/main.c b/src/main.c index 56723da..41d050e 100644 --- a/src/main.c +++ b/src/main.c @@ -82,6 +82,7 @@ static void help() { printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); printf("\t-app \t\tName of app to stream\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); + printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); exit(0); } @@ -104,12 +105,14 @@ int main(int argc, char* argv[]) { {"packetsize", required_argument, 0, 'h'}, {"app", required_argument, 0, 'i'}, {"input", required_argument, 0, 'j'}, + {"mapping", required_argument, 0, 'k'}, {0, 0, 0, 0}, }; char* app = "Steam"; char* action = NULL; char* address = NULL; + char* mapping = NULL; int option_index = 0; int c; while ((c = getopt_long_only (argc, argv, "-abc:d:efg:h:i:j:", long_options, &option_index)) != -1) { @@ -144,7 +147,10 @@ int main(int argc, char* argv[]) { app = optarg; break; case 'j': - input_create(optarg); + input_create(optarg, mapping); + break; + case 'k': + mapping = optarg; break; case 1: if (action == NULL)