mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2025-07-01 23:35:47 +00:00
Support gamepad with mapping
This commit is contained in:
parent
4a745b43f1
commit
59d710c95f
174
src/input.c
174
src/input.c
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user