Support gamepad with mapping

This commit is contained in:
Iwan Timmer 2015-05-06 23:41:40 +02:00
parent 4a745b43f1
commit 59d710c95f
3 changed files with 179 additions and 5 deletions

View File

@ -18,30 +18,57 @@
*/
#include "keyboard.h"
#include "mapping.h"
#include "libevdev/libevdev.h"
#include "limelight-common/Limelight.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <limits.h>
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->value<parms->avg?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)<parms->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));

View File

@ -17,5 +17,5 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
void input_create(char* device);
void input_create(char* device, char* mapFile);
void input_loop();

View File

@ -82,6 +82,7 @@ static void help() {
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
printf("\t-app <app>\t\tName of app to stream\n");
printf("\t-input <device>\t\tUse <device> as input. Can be used multiple times\n");
printf("\t-mapping <file>\t\tUse <file> 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)