mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2025-07-03 16:25:31 +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 "keyboard.h"
|
||||||
|
#include "mapping.h"
|
||||||
|
|
||||||
#include "libevdev/libevdev.h"
|
#include "libevdev/libevdev.h"
|
||||||
#include "limelight-common/Limelight.h"
|
#include "limelight-common/Limelight.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <poll.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 input_device {
|
||||||
struct libevdev *dev;
|
struct libevdev *dev;
|
||||||
|
struct mapping map;
|
||||||
int fd;
|
int fd;
|
||||||
__s32 mouseDeltaX, mouseDeltaY, mouseScroll;
|
__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 pollfd* fds = NULL;
|
||||||
static struct input_device* devices = NULL;
|
static struct input_device* devices = NULL;
|
||||||
static int numDevices = 0;
|
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;
|
int dev = numDevices;
|
||||||
numDevices++;
|
numDevices++;
|
||||||
|
|
||||||
@ -68,9 +95,56 @@ void input_create(char* device) {
|
|||||||
libevdev_set_fd(devices[dev].dev, devices[dev].fd);
|
libevdev_set_fd(devices[dev].dev, devices[dev].fd);
|
||||||
fds[dev].fd = devices[dev].fd;
|
fds[dev].fd = devices[dev].fd;
|
||||||
fds[dev].events = POLLIN;
|
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) {
|
switch (ev->type) {
|
||||||
case EV_SYN:
|
case EV_SYN:
|
||||||
if (dev->mouseDeltaX != 0 || dev->mouseDeltaY != 0) {
|
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);
|
LiSendScrollEvent(dev->mouseScroll);
|
||||||
dev->mouseScroll = 0;
|
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;
|
break;
|
||||||
case EV_KEY:
|
case EV_KEY:
|
||||||
if (ev->code < sizeof(keyCodes)/sizeof(keyCodes[0])) {
|
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);
|
LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, 0);
|
||||||
} else {
|
} else {
|
||||||
int mouseCode = 0;
|
int mouseCode = 0;
|
||||||
|
short gamepadCode = 0;
|
||||||
switch (ev->code) {
|
switch (ev->code) {
|
||||||
case BTN_LEFT:
|
case BTN_LEFT:
|
||||||
mouseCode = BUTTON_LEFT;
|
mouseCode = BUTTON_LEFT;
|
||||||
@ -99,10 +178,55 @@ static void handle_event(struct input_event *ev, struct input_device *dev) {
|
|||||||
case BTN_RIGHT:
|
case BTN_RIGHT:
|
||||||
mouseCode = BUTTON_RIGHT;
|
mouseCode = BUTTON_RIGHT;
|
||||||
break;
|
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) {
|
if (mouseCode > 0) {
|
||||||
LiSendMouseButtonEvent(ev->value?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, mouseCode);
|
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;
|
break;
|
||||||
@ -119,7 +243,51 @@ static void handle_event(struct input_event *ev, struct input_device *dev) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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() {
|
void input_loop() {
|
||||||
@ -132,7 +300,7 @@ void input_loop() {
|
|||||||
if (rc == LIBEVDEV_READ_STATUS_SYNC)
|
if (rc == LIBEVDEV_READ_STATUS_SYNC)
|
||||||
fprintf(stderr, "Error: cannot keep up\n");
|
fprintf(stderr, "Error: cannot keep up\n");
|
||||||
else if (rc == LIBEVDEV_READ_STATUS_SUCCESS);
|
else if (rc == LIBEVDEV_READ_STATUS_SUCCESS);
|
||||||
handle_event(&ev, &devices[i]);
|
input_handle_event(&ev, &devices[i]);
|
||||||
}
|
}
|
||||||
if (rc != -EAGAIN && rc < 0) {
|
if (rc != -EAGAIN && rc < 0) {
|
||||||
fprintf(stderr, "Error: %s\n", strerror(-rc));
|
fprintf(stderr, "Error: %s\n", strerror(-rc));
|
||||||
|
@ -17,5 +17,5 @@
|
|||||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
* 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();
|
void input_loop();
|
||||||
|
@ -82,6 +82,7 @@ static void help() {
|
|||||||
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
|
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
|
||||||
printf("\t-app <app>\t\tName of app to stream\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-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);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,12 +105,14 @@ int main(int argc, char* argv[]) {
|
|||||||
{"packetsize", required_argument, 0, 'h'},
|
{"packetsize", required_argument, 0, 'h'},
|
||||||
{"app", required_argument, 0, 'i'},
|
{"app", required_argument, 0, 'i'},
|
||||||
{"input", required_argument, 0, 'j'},
|
{"input", required_argument, 0, 'j'},
|
||||||
|
{"mapping", required_argument, 0, 'k'},
|
||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
char* app = "Steam";
|
char* app = "Steam";
|
||||||
char* action = NULL;
|
char* action = NULL;
|
||||||
char* address = NULL;
|
char* address = NULL;
|
||||||
|
char* mapping = NULL;
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long_only (argc, argv, "-abc:d:efg:h:i:j:", long_options, &option_index)) != -1) {
|
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;
|
app = optarg;
|
||||||
break;
|
break;
|
||||||
case 'j':
|
case 'j':
|
||||||
input_create(optarg);
|
input_create(optarg, mapping);
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
mapping = optarg;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (action == NULL)
|
if (action == NULL)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user