From 0a5b4ec5cc652b90dd964fc5475fbf9f920cdb84 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Thu, 6 Apr 2017 22:37:44 +0200 Subject: [PATCH 01/72] Remove map creation support --- docs/README.pod | 4 --- src/input/evdev.c | 81 +------------------------------------------ src/input/evdev.h | 3 +- src/input/mapping.c | 83 ++++++++++----------------------------------- src/input/mapping.h | 3 +- src/main.c | 16 +-------- 6 files changed, 21 insertions(+), 169 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 5da4f0a..d278943 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -11,10 +11,6 @@ Usage: I EactionE [options] [host] =over 4 -=item B - -Create a mapping file for a gamepad. - =item B Pair this computer with the host. diff --git a/src/input/evdev.c b/src/input/evdev.c index b3c1136..b241d21 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -429,85 +429,6 @@ void evdev_create(const char* device, char* mapFile) { loop_add_fd(devices[dev].fd, &evdev_handle, POLLIN); } -static void evdev_map_key(char* keyName, short* key) { - printf("Press %s\n", keyName); - currentKey = key; - currentAbs = NULL; - *key = -1; - loop_main(); - - usleep(250000); - evdev_drain(); -} - -static void evdev_map_abs(char* keyName, short* abs, bool* reverse) { - printf("Move %s\n", keyName); - currentKey = NULL; - currentAbs = abs; - currentReverse = reverse; - *abs = -1; - loop_main(); - - usleep(250000); - evdev_drain(); -} - -static void evdev_map_abskey(char* keyName, short* key, short* abs, bool* reverse) { - printf("Press %s\n", keyName); - currentKey = key; - currentAbs = abs; - currentReverse = reverse; - *key = -1; - *abs = -1; - *currentReverse = false; - loop_main(); - - usleep(250000); - evdev_drain(); -} - -void evdev_map(char* fileName) { - struct mapping map; - - handler = evdev_handle_mapping_event; - - evdev_map_abs("Left Stick Right", &(map.abs_x), &(map.reverse_x)); - evdev_map_abs("Left Stick Up", &(map.abs_y), &(map.reverse_y)); - evdev_map_key("Left Stick Button", &(map.btn_thumbl)); - - evdev_map_abs("Right Stick Right", &(map.abs_rx), &(map.reverse_rx)); - evdev_map_abs("Right Stick Up", &(map.abs_ry), &(map.reverse_ry)); - evdev_map_key("Right Stick Button", &(map.btn_thumbr)); - - evdev_map_abskey("D-Pad Right", &(map.btn_dpad_right), &(map.abs_dpad_x), &(map.reverse_dpad_x)); - if (map.btn_dpad_right >= 0) - evdev_map_key("D-Pad Left", &(map.btn_dpad_left)); - else - map.btn_dpad_left = -1; - - evdev_map_abskey("D-Pad Down", &(map.btn_dpad_down), &(map.abs_dpad_y), &(map.reverse_dpad_y)); - if (map.btn_dpad_down >= 0) - evdev_map_key("D-Pad Up", &(map.btn_dpad_up)); - else - map.btn_dpad_up = -1; - - evdev_map_key("Button X (1)", &(map.btn_west)); - evdev_map_key("Button A (2)", &(map.btn_south)); - evdev_map_key("Button B (3)", &(map.btn_east)); - evdev_map_key("Button Y (4)", &(map.btn_north)); - evdev_map_key("Back Button", &(map.btn_select)); - evdev_map_key("Start Button", &(map.btn_start)); - evdev_map_key("Special Button", &(map.btn_mode)); - - bool ignored; - evdev_map_abskey("Left Trigger", &(map.btn_tl2), &(map.abs_z), &ignored); - evdev_map_abskey("Right Trigger", &(map.btn_tr2), &(map.abs_rz), &ignored); - - evdev_map_key("Left Bumper", &(map.btn_tl)); - evdev_map_key("Right Bumper", &(map.btn_tr)); - mapping_save(fileName, &map); -} - void evdev_start() { // After grabbing, the only way to quit via the keyboard // is via the special key combo that the input handling diff --git a/src/input/evdev.h b/src/input/evdev.h index 37af027..aca8699 100644 --- a/src/input/evdev.h +++ b/src/input/evdev.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,7 +19,6 @@ void evdev_create(const char* device, char* mapFile); void evdev_loop(); -void evdev_map(char* fileName); void evdev_init(); void evdev_start(); diff --git a/src/input/mapping.c b/src/input/mapping.c index 4cccf06..d88a616 100644 --- a/src/input/mapping.c +++ b/src/input/mapping.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +23,6 @@ #include #include -#define write_config(fd, key, value) fprintf(fd, "%s = %hd\n", key, value) -#define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false"); - void mapping_load(char* fileName, struct mapping* map) { FILE* fd = fopen(fileName, "r"); if (fd == NULL) { @@ -53,10 +50,22 @@ void mapping_load(char* fileName, struct mapping* map) { map->abs_rz = int_value; else if (strcmp("abs_deadzone", key) == 0) map->abs_deadzone = int_value; - else if (strcmp("abs_dpad_x", key) == 0) - map->abs_dpad_x = int_value; - else if (strcmp("abs_dpad_y", key) == 0) - map->abs_dpad_y = int_value; + else if (strcmp("hat_dpad_right", key) == 0) + map->hat_dpad_right = int_value; + else if (strcmp("hat_dpad_left", key) == 0) + map->hat_dpad_left = int_value; + else if (strcmp("hat_dpad_up", key) == 0) + map->hat_dpad_up = int_value; + else if (strcmp("hat_dpad_down", key) == 0) + map->hat_dpad_down = int_value; + else if (strcmp("hat_dpad_dir_right", key) == 0) + map->hat_dpad_dir_right = int_value; + else if (strcmp("hat_dpad_dir_left", key) == 0) + map->hat_dpad_dir_left = int_value; + else if (strcmp("hat_dpad_dir_up", key) == 0) + map->hat_dpad_dir_up = int_value; + else if (strcmp("hat_dpad_dir_down", key) == 0) + map->hat_dpad_dir_down = int_value; else if (strcmp("btn_south", key) == 0) map->btn_south = int_value; else if (strcmp("btn_north", key) == 0) @@ -99,10 +108,6 @@ void mapping_load(char* fileName, struct mapping* map) { map->reverse_rx = strcmp("true", value) == 0; else if (strcmp("reverse_ry", key) == 0) map->reverse_ry = strcmp("true", value) == 0; - else if (strcmp("reverse_dpad_x", key) == 0) - map->reverse_dpad_x = strcmp("true", value) == 0; - else if (strcmp("reverse_dpad_y", key) == 0) - map->reverse_dpad_y = strcmp("true", value) == 0; else fprintf(stderr, "Can't map (%s)\n", key); } @@ -114,57 +119,3 @@ void mapping_load(char* fileName, struct mapping* map) { } free(line); } - -void mapping_save(char* fileName, struct mapping* map) { - FILE* fd = fopen(fileName, "w"); - if (fd == NULL) { - fprintf(stderr, "Can't open mapping file: %s\n", fileName); - exit(EXIT_FAILURE); - } - - write_config(fd, "abs_x", map->abs_x); - write_config(fd, "abs_y", map->abs_y); - write_config(fd, "abs_z", map->abs_z); - - write_config_bool(fd, "reverse_x", map->reverse_x); - write_config_bool(fd, "reverse_y", map->reverse_y); - - write_config(fd, "abs_rx", map->abs_rx); - write_config(fd, "abs_ry", map->abs_ry); - write_config(fd, "abs_rz", map->abs_rz); - - write_config_bool(fd, "reverse_rx", map->reverse_rx); - write_config_bool(fd, "reverse_ry", map->reverse_ry); - - write_config(fd, "abs_deadzone", map->abs_deadzone); - - write_config(fd, "abs_dpad_x", map->abs_dpad_x); - write_config(fd, "abs_dpad_y", map->abs_dpad_y); - - write_config_bool(fd, "reverse_dpad_x", map->reverse_dpad_x); - write_config_bool(fd, "reverse_dpad_y", map->reverse_dpad_y); - - write_config(fd, "btn_north", map->btn_north); - write_config(fd, "btn_east", map->btn_east); - write_config(fd, "btn_south", map->btn_south); - write_config(fd, "btn_west", map->btn_west); - - write_config(fd, "btn_select", map->btn_select); - write_config(fd, "btn_start", map->btn_start); - write_config(fd, "btn_mode", map->btn_mode); - - write_config(fd, "btn_thumbl", map->btn_thumbl); - write_config(fd, "btn_thumbr", map->btn_thumbr); - - write_config(fd, "btn_tl", map->btn_tl); - write_config(fd, "btn_tr", map->btn_tr); - write_config(fd, "btn_tl2", map->btn_tl2); - write_config(fd, "btn_tr2", map->btn_tr2); - - write_config(fd, "btn_dpad_up", map->btn_dpad_up); - write_config(fd, "btn_dpad_down", map->btn_dpad_down); - write_config(fd, "btn_dpad_left", map->btn_dpad_left); - write_config(fd, "btn_dpad_right", map->btn_dpad_right); - - fclose(fd); -} diff --git a/src/input/mapping.h b/src/input/mapping.h index f62d1b1..2701b9f 100644 --- a/src/input/mapping.h +++ b/src/input/mapping.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,4 +40,3 @@ struct mapping { }; void mapping_load(char* fileName, struct mapping* map); -void mapping_save(char* fileName, struct mapping* map); diff --git a/src/main.c b/src/main.c index 619cd3e..5b45639 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -119,7 +119,6 @@ static void help() { printf("Usage: moonlight [action] (options) [host]\n"); printf(" moonlight [configfile]\n"); printf("\n Actions\n\n"); - printf("\tmap\t\t\tCreate mapping file for gamepad\n"); printf("\tpair\t\t\tPair device with computer\n"); printf("\tunpair\t\t\tUnpair device with computer\n"); printf("\tstream\t\t\tStream computer to device\n"); @@ -183,19 +182,6 @@ int main(int argc, char* argv[]) { exit(-1); } config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system)); - - if (strcmp("map", config.action) == 0) { - if (config.address == NULL) { - perror("No filename for mapping"); - exit(-1); - } - udev_init(!inputAdded, config.mapping); - for (int i=0;i Date: Sat, 8 Apr 2017 14:25:20 +0200 Subject: [PATCH 02/72] Use SDL-style mapping for input --- src/input/evdev.c | 174 +++++++++++++++++++++++------------------- src/input/mapping.c | 179 ++++++++++++++++++++++++-------------------- src/input/mapping.h | 34 +++++---- 3 files changed, 213 insertions(+), 174 deletions(-) diff --git a/src/input/evdev.c b/src/input/evdev.c index b241d21..441c770 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -49,6 +49,9 @@ struct input_abs_parms { struct input_device { struct libevdev *dev; struct mapping map; + int key_map[KEY_MAX]; + int abs_map[ABS_MAX]; + int hats_state[3][2]; int fd; char modifiers; __s32 mouseDeltaX, mouseDeltaY, mouseScroll; @@ -59,9 +62,16 @@ struct input_device { short rightStickX, rightStickY; bool gamepadModified; struct input_abs_parms xParms, yParms, rxParms, ryParms, zParms, rzParms; - struct input_abs_parms dpadxParms, dpadyParms; }; +#define HAT_UP 1 +#define HAT_RIGHT 2 +#define HAT_DOWN 4 +#define HAT_LEFT 8 +static const int hat_constants[3][3] = {{HAT_UP | HAT_LEFT, HAT_UP, HAT_UP | HAT_RIGHT}, {HAT_LEFT, 0, HAT_RIGHT}, {HAT_LEFT | HAT_DOWN, HAT_DOWN, HAT_RIGHT}}; + +#define set_hat(flags, flag, hat, hat_flag) flags = (-((hat & hat_flag) > 0) ^ flags) & flag + static struct input_device* devices = NULL; static int numDevices = 0; static int assignedControllerIds = 0; @@ -78,9 +88,9 @@ static bool grabbingDevices; static bool (*handler) (struct input_event*, struct input_device*); static void evdev_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->flat = libevdev_get_abs_flat(dev->dev, dev->abs_map[code]); + parms->min = libevdev_get_abs_minimum(dev->dev, dev->abs_map[code]); + parms->max = libevdev_get_abs_maximum(dev->dev, dev->abs_map[code]); parms->avg = (parms->min+parms->max)/2; parms->range = parms->max - parms->avg; parms->diff = parms->max - parms->min; @@ -120,15 +130,6 @@ static char evdev_convert_value_byte(struct input_event *ev, struct input_device return (ev->value - parms->flat - parms->min) * UCHAR_MAX / (parms->diff - parms->flat); } -static int evdev_convert_value_direction(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) { - if (ev->value > (parms->avg+parms->range/4)) - return reverse?-1:1; - else if (ev->value < (parms->avg-parms->range/4)) - return reverse?1:-1; - else - return 0; -} - static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) { bool gamepadModified = false; @@ -195,6 +196,8 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) } else { int mouseCode = 0; short gamepadCode = 0; + int index = dev->key_map[ev->code - BTN_MISC]; + switch (ev->code) { case BTN_LEFT: mouseCode = BUTTON_LEFT; @@ -206,35 +209,35 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) mouseCode = BUTTON_RIGHT; break; default: - if (ev->code == dev->map.btn_south) + if (index == dev->map.btn_a) gamepadCode = A_FLAG; - else if (ev->code == dev->map.btn_west) + else if (index == dev->map.btn_x) gamepadCode = X_FLAG; - else if (ev->code == dev->map.btn_north) + else if (index == dev->map.btn_y) gamepadCode = Y_FLAG; - else if (ev->code == dev->map.btn_east) + else if (index == dev->map.btn_b) gamepadCode = B_FLAG; - else if (ev->code == dev->map.btn_dpad_up) + else if (index == dev->map.btn_dpup) gamepadCode = UP_FLAG; - else if (ev->code == dev->map.btn_dpad_down) + else if (index == dev->map.btn_dpdown) gamepadCode = DOWN_FLAG; - else if (ev->code == dev->map.btn_dpad_right) + else if (index == dev->map.btn_dpright) gamepadCode = RIGHT_FLAG; - else if (ev->code == dev->map.btn_dpad_left) + else if (index == dev->map.btn_dpleft) gamepadCode = LEFT_FLAG; - else if (ev->code == dev->map.btn_thumbl) + else if (index == dev->map.btn_leftstick) gamepadCode = LS_CLK_FLAG; - else if (ev->code == dev->map.btn_thumbr) + else if (index == dev->map.btn_rightstick) gamepadCode = RS_CLK_FLAG; - else if (ev->code == dev->map.btn_tl) + else if (index == dev->map.btn_leftshoulder) gamepadCode = LB_FLAG; - else if (ev->code == dev->map.btn_tr) + else if (index == dev->map.btn_rightshoulder) gamepadCode = RB_FLAG; - else if (ev->code == dev->map.btn_start) + else if (index == dev->map.btn_start) gamepadCode = PLAY_FLAG; - else if (ev->code == dev->map.btn_select) + else if (index == dev->map.btn_back) gamepadCode = BACK_FLAG; - else if (ev->code == dev->map.btn_mode) + else if (index == dev->map.btn_guide) gamepadCode = SPECIAL_FLAG; } @@ -248,9 +251,9 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) dev->buttonFlags |= gamepadCode; else dev->buttonFlags &= ~gamepadCode; - } else if (ev->code == dev->map.btn_tl2) + } else if (index == dev->map.btn_lefttrigger) dev->leftTrigger = ev->value?UCHAR_MAX:0; - else if (ev->code == dev->map.btn_tr2) + else if (index == dev->map.btn_righttrigger) dev->rightTrigger = ev->value?UCHAR_MAX:0; else { fprintf(stderr, "Unmapped button: %d\n", ev->code); @@ -274,46 +277,46 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) break; case EV_ABS: gamepadModified = true; - if (ev->code == dev->map.abs_x) - dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map.reverse_x); - else if (ev->code == dev->map.abs_y) - dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, dev->map.reverse_y); - else if (ev->code == dev->map.abs_rx) - dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map.reverse_rx); - else if (ev->code == dev->map.abs_ry) - dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, dev->map.reverse_ry); - else if (ev->code == dev->map.abs_z) - dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms); - else if (ev->code == dev->map.abs_rz) - dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms); - else if (ev->code == dev->map.abs_dpad_x) { - int dir = evdev_convert_value_direction(ev, dev, &dev->dpadxParms, dev->map.reverse_dpad_x); - if (dir == 1) { - dev->buttonFlags |= RIGHT_FLAG; - dev->buttonFlags &= ~LEFT_FLAG; - } else if (dir == 0) { - 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 = evdev_convert_value_direction(ev, dev, &dev->dpadyParms, dev->map.reverse_dpad_y); - if (dir == 1) { - dev->buttonFlags |= DOWN_FLAG; - dev->buttonFlags &= ~UP_FLAG; - } else if (dir == 0) { - dev->buttonFlags &= ~DOWN_FLAG; - dev->buttonFlags &= ~UP_FLAG; - } else { - dev->buttonFlags &= ~DOWN_FLAG; - dev->buttonFlags |= UP_FLAG; - } - } else - gamepadModified = false; + int index = dev->abs_map[ev->code]; + int hat_index = (ev->code - ABS_HAT0X) / 2; + int har_dir_index = (ev->code - ABS_HAT0X) % 2; - break; + switch (ev->code) { + case ABS_HAT0X: + case ABS_HAT0Y: + case ABS_HAT1X: + case ABS_HAT1Y: + case ABS_HAT2X: + case ABS_HAT2Y: + case ABS_HAT3X: + case ABS_HAT3Y: + dev->hats_state[hat_index][har_dir_index] = ev->value < 0 ? 0 : (ev->value == 0 ? 1 : 2); + int hat_state = hat_constants[dev->hats_state[hat_index][0]][dev->hats_state[hat_index][1]]; + if (hat_index == dev->map.hat_dpup) + set_hat(dev->buttonFlags, UP_FLAG, hat_state, dev->map.hat_dir_dpup); + if (hat_index == dev->map.hat_dpdown) + set_hat(dev->buttonFlags, DOWN_FLAG, hat_state, dev->map.hat_dir_dpdown); + if (hat_index == dev->map.hat_dpright) + set_hat(dev->buttonFlags, HAT_RIGHT, hat_state, dev->map.hat_dir_dpright); + if (hat_index == dev->map.hat_dpleft) + set_hat(dev->buttonFlags, HAT_LEFT, hat_state, dev->map.hat_dir_dpleft); + break; + } + default: + if (index == dev->map.abs_leftx) + dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map.reverse_leftx); + else if (index == dev->map.abs_lefty) + dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, !dev->map.reverse_lefty); + else if (index == dev->map.abs_rightx) + dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map.reverse_rightx); + else if (index == dev->map.abs_righty) + dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map.reverse_righty); + else if (index == dev->map.abs_lefttrigger) + dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms); + else if (index == dev->map.abs_righttrigger) + dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms); + else + gamepadModified = false; } dev->gamepadModified |= gamepadModified; @@ -410,15 +413,32 @@ void evdev_create(const char* device, char* mapFile) { if (mapFile != NULL) mapping_load(mapFile, &(devices[dev].map)); + int nbuttons = 0; + for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) { + if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i)) + devices[dev].key_map[i - BTN_MISC] = nbuttons++; + } + for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) { + if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i)) + devices[dev].key_map[i - BTN_MISC] = nbuttons++; + } + + int naxes = 0; + for (int i = 0; i < ABS_MAX; ++i) { + /* Skip hats */ + if (i == ABS_HAT0X) + i = ABS_HAT3Y; + else if (libevdev_has_event_code(devices[dev].dev, EV_ABS, i)) + devices[dev].abs_map[i] = naxes++; + } + devices[dev].controllerId = -1; - evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_x); - evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_y); - evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_z); - evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rx); - evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_ry); - evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_rz); - evdev_init_parms(&devices[dev], &(devices[dev].dpadxParms), devices[dev].map.abs_dpad_x); - evdev_init_parms(&devices[dev], &(devices[dev].dpadyParms), devices[dev].map.abs_dpad_y); + evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_leftx); + evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_lefty); + evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_lefttrigger); + evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rightx); + evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_righty); + evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_righttrigger); if (grabbingDevices) { if (ioctl(fd, EVIOCGRAB, 1) < 0) { diff --git a/src/input/mapping.c b/src/input/mapping.c index d88a616..2260c12 100644 --- a/src/input/mapping.c +++ b/src/input/mapping.c @@ -33,89 +33,104 @@ void mapping_load(char* fileName, struct mapping* map) { char *line = NULL; size_t len = 0; while (getline(&line, &len, fd) != -1) { - char *key = NULL, *value = NULL; - if (sscanf(line, "%ms = %ms", &key, &value) == 2) { - long int_value = strtol(value, NULL, 10); - if (strcmp("abs_x", key) == 0) - map->abs_x = int_value; - else if (strcmp("abs_y", key) == 0) - map->abs_y = int_value; - else if (strcmp("abs_z", key) == 0) - map->abs_z = int_value; - else if (strcmp("abs_rx", key) == 0) - map->abs_rx = int_value; - else if (strcmp("abs_ry", key) == 0) - map->abs_ry = int_value; - else if (strcmp("abs_rz", key) == 0) - map->abs_rz = int_value; - else if (strcmp("abs_deadzone", key) == 0) - map->abs_deadzone = int_value; - else if (strcmp("hat_dpad_right", key) == 0) - map->hat_dpad_right = int_value; - else if (strcmp("hat_dpad_left", key) == 0) - map->hat_dpad_left = int_value; - else if (strcmp("hat_dpad_up", key) == 0) - map->hat_dpad_up = int_value; - else if (strcmp("hat_dpad_down", key) == 0) - map->hat_dpad_down = int_value; - else if (strcmp("hat_dpad_dir_right", key) == 0) - map->hat_dpad_dir_right = int_value; - else if (strcmp("hat_dpad_dir_left", key) == 0) - map->hat_dpad_dir_left = int_value; - else if (strcmp("hat_dpad_dir_up", key) == 0) - map->hat_dpad_dir_up = int_value; - else if (strcmp("hat_dpad_dir_down", key) == 0) - map->hat_dpad_dir_down = int_value; - else if (strcmp("btn_south", key) == 0) - map->btn_south = int_value; - else if (strcmp("btn_north", key) == 0) - map->btn_north = int_value; - else if (strcmp("btn_east", key) == 0) - map->btn_east = int_value; - else if (strcmp("btn_west", key) == 0) - map->btn_west = int_value; - else if (strcmp("btn_select", key) == 0) - map->btn_select = int_value; - else if (strcmp("btn_start", key) == 0) - map->btn_start = int_value; - else if (strcmp("btn_mode", key) == 0) - map->btn_mode = int_value; - else if (strcmp("btn_thumbl", key) == 0) - map->btn_thumbl = int_value; - else if (strcmp("btn_thumbr", key) == 0) - map->btn_thumbr = int_value; - else if (strcmp("btn_tl", key) == 0) - map->btn_tl = int_value; - else if (strcmp("btn_tr", key) == 0) - map->btn_tr = int_value; - else if (strcmp("btn_tl2", key) == 0) - map->btn_tl2 = int_value; - else if (strcmp("btn_tr2", key) == 0) - map->btn_tr2 = int_value; - else if (strcmp("btn_dpad_up", key) == 0) - map->btn_dpad_up = int_value; - else if (strcmp("btn_dpad_down", key) == 0) - map->btn_dpad_down = int_value; - else if (strcmp("btn_dpad_left", key) == 0) - map->btn_dpad_left = int_value; - else if (strcmp("btn_dpad_right", key) == 0) - map->btn_dpad_right = int_value; - else if (strcmp("reverse_x", key) == 0) - map->reverse_x = strcmp("true", value) == 0; - else if (strcmp("reverse_y", key) == 0) - map->reverse_y = strcmp("true", value) == 0; - else if (strcmp("reverse_rx", key) == 0) - map->reverse_rx = strcmp("true", value) == 0; - else if (strcmp("reverse_ry", key) == 0) - map->reverse_ry = strcmp("true", value) == 0; - else - fprintf(stderr, "Can't map (%s)\n", key); - } - if (key != NULL) - free(key); + char* strpoint; + char* guid = strtok_r(line, ",", &strpoint); + char* name = strtok_r(NULL, ",", &strpoint); + if (guid == NULL || name == NULL) + continue; - if (value != NULL) - free(value); + strncpy(map->guid, guid, sizeof(map->guid)); + strncpy(map->name, name, sizeof(map->name)); + + char* option; + while ((option = strtok_r(NULL, ",", &strpoint)) != NULL) { + char *key = NULL, *value = NULL; + int ret; + if ((ret = sscanf(option, "%m[^:]:%ms", &key, &value)) == 2) { + int int_value, direction_value; + char flag = NULL; + if (strcmp("platform", key) == 0) + strncpy(map->platform, value, sizeof(map->platform)); + else if (sscanf(value, "b%d", &int_value) == 1) { + if (strcmp("a", key) == 0) + map->btn_a = int_value; + else if (strcmp("y", key) == 0) + map->btn_y = int_value; + else if (strcmp("x", key) == 0) + map->btn_x = int_value; + else if (strcmp("b", key) == 0) + map->btn_b = int_value; + else if (strcmp("back", key) == 0) + map->btn_back = int_value; + else if (strcmp("start", key) == 0) + map->btn_start = int_value; + else if (strcmp("guide", key) == 0) + map->btn_guide = int_value; + else if (strcmp("dpup", key) == 0) + map->btn_dpup = int_value; + else if (strcmp("dpdown", key) == 0) + map->btn_dpdown = int_value; + else if (strcmp("dpleft", key) == 0) + map->btn_dpleft = int_value; + else if (strcmp("dpright", key) == 0) + map->btn_dpright = int_value; + else if (strcmp("leftstick", key) == 0) + map->btn_leftstick = int_value; + else if (strcmp("rightstick", key) == 0) + map->btn_rightstick = int_value; + else if (strcmp("leftshoulder", key) == 0) + map->btn_leftshoulder = int_value; + else if (strcmp("rightshoulder", key) == 0) + map->btn_rightshoulder = int_value; + else if (strcmp("lefttrigger", key) == 0) + map->btn_lefttrigger = int_value; + else if (strcmp("righttrigger", key) == 0) + map->btn_righttrigger = int_value; + } else if (sscanf(value, "a%d%c", &int_value, &flag) >= 1) { + if (strcmp("leftx", key) == 0) { + map->abs_leftx = int_value; + map->reverse_leftx = flag == '~'; + } else if (strcmp("lefty", key) == 0) { + map->abs_lefty = int_value; + map->reverse_lefty = flag == '~'; + } else if (strcmp("rightx", key) == 0) { + map->abs_rightx = int_value; + map->reverse_rightx = flag == '~'; + } else if (strcmp("righty", key) == 0) { + map->abs_righty = int_value; + map->reverse_righty = flag == '~'; + } else if (strcmp("lefttrigger", key) == 0) + map->abs_lefttrigger = int_value; + else if (strcmp("righttrigger", key) == 0) + map->abs_righttrigger = int_value; + } else if (sscanf(value, "h%d.%d", &int_value, &direction_value) == 2) { + if (strcmp("dpright", key) == 0) { + map->hat_dpright = int_value; + map->hat_dir_dpright = direction_value; + } else if (strcmp("dpleft", key) == 0) { + map->hat_dpleft = int_value; + map->hat_dir_dpleft = direction_value; + } else if (strcmp("dpup", key) == 0) { + map->hat_dpup = int_value; + map->hat_dir_dpup = direction_value; + } else if (strcmp("dpdown", key) == 0) { + map->hat_dpdown = int_value; + map->hat_dir_dpdown = direction_value; + } + } else + fprintf(stderr, "Can't map (%s)\n", option); + } else if (ret == 0 && option[0] != '\n') + fprintf(stderr, "Can't map (%s)\n", option); + + if (key != NULL) + free(key); + + if (value != NULL) + free(value); + } + map->guid[32] = '\0'; + map->name[256] = '\0'; + map->platform[32] = '\0'; } free(line); } diff --git a/src/input/mapping.h b/src/input/mapping.h index 2701b9f..44e2c10 100644 --- a/src/input/mapping.h +++ b/src/input/mapping.h @@ -20,23 +20,27 @@ #include struct mapping { - short abs_x, abs_y, abs_z; - short abs_rx, abs_ry, abs_rz; + char guid[33]; + char platform[33]; + char name[257]; - bool reverse_x, reverse_y; - bool reverse_rx, reverse_ry; + bool reverse_leftx, reverse_lefty; + bool reverse_rightx, reverse_righty; + + short abs_leftx, abs_lefty; + short abs_rightx, abs_righty; - short abs_deadzone; - - short abs_dpad_x, abs_dpad_y; - bool reverse_dpad_x, reverse_dpad_y; - - short btn_south, btn_east, btn_north, btn_west; - short btn_select, btn_start, btn_mode; - short btn_thumbl, btn_thumbr; - short btn_tl, btn_tr, btn_tl2, btn_tr2; - - short btn_dpad_up, btn_dpad_down, btn_dpad_left, btn_dpad_right; + short hat_dpright, hat_dpleft, hat_dpup, hat_dpdown; + short hat_dir_dpright, hat_dir_dpleft, hat_dir_dpup, hat_dir_dpdown; + short btn_dpup, btn_dpdown, btn_dpleft, btn_dpright; + + short btn_a, btn_x, btn_y, btn_b; + short btn_back, btn_start, btn_guide; + short btn_leftstick, btn_rightstick; + short btn_leftshoulder, btn_rightshoulder; + + short abs_lefttrigger, abs_righttrigger; + short btn_lefttrigger, btn_righttrigger; }; void mapping_load(char* fileName, struct mapping* map); From 1d7c2be7e2a967f40b4a8047bf16d24c5e45dd8d Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 8 Apr 2017 15:50:24 +0200 Subject: [PATCH 03/72] Single mapping file for all inputs --- docs/README.pod | 4 +- src/config.c | 13 ++--- src/config.h | 9 +--- src/input/evdev.c | 125 ++++++++++++++++++++++++++----------------- src/input/evdev.h | 2 +- src/input/mapping.c | 17 +++++- src/input/mapping.h | 6 ++- src/input/sdlinput.c | 6 +-- src/input/sdlinput.h | 4 +- src/input/udev.c | 13 ++--- src/input/udev.h | 4 +- src/main.c | 15 ++++-- src/sdl.c | 2 - 13 files changed, 131 insertions(+), 89 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index d278943..fd48502 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -128,8 +128,8 @@ By default the encryption keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cach =item B<-mapping> [I] -Use I as the mapping file for all inputs specified after this B<-mapping>. -If no B<-input> is specified after the B<-mapping> this mapping is used for autoloaded inputs. +Use I as the mapping file for all inputs. +This mapping file should have the same format as the gamecontrollerdb.txt for SDL. =item B<-input> [I] diff --git a/src/config.c b/src/config.c index 1381905..47ddd71 100644 --- a/src/config.c +++ b/src/config.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,6 @@ #define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false"); bool inputAdded = false; -static bool mapped = true; const char* audio_device = NULL; static struct option long_options[] = { @@ -152,11 +151,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { perror("Too many inputs specified"); exit(-1); } - config->inputs[config->inputsCount].path = value; - config->inputs[config->inputsCount].mapping = config->mapping; + config->inputs[config->inputsCount] = value; config->inputsCount++; inputAdded = true; - mapped = true; break; case 'k': config->mapping = get_path(value, getenv("XDG_DATA_DIRS")); @@ -164,7 +161,6 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { fprintf(stderr, "Unable to open custom mapping file: %s\n", value); exit(-1); } - mapped = false; break; case 'l': config->sops = false; @@ -352,10 +348,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { } if (inputAdded) { - if (!mapped) { - fprintf(stderr, "Mapping option should be followed by the input to be mapped.\n"); - exit(-1); - } else if (config->mapping == NULL) { + if (config->mapping == NULL) { fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n"); exit(-1); } diff --git a/src/config.h b/src/config.h index de7d3a8..3478f71 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,11 +25,6 @@ enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC }; -struct input_config { - char* path; - char* mapping; -}; - typedef struct _CONFIGURATION { STREAM_CONFIGURATION stream; char* app; @@ -44,7 +39,7 @@ typedef struct _CONFIGURATION { bool fullscreen; bool forcehw; bool unsupported_version; - struct input_config inputs[MAX_INPUTS]; + char* inputs[MAX_INPUTS]; int inputsCount; enum codecs codec; } CONFIGURATION, *PCONFIGURATION; diff --git a/src/input/evdev.c b/src/input/evdev.c index 441c770..1879e82 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -38,6 +38,13 @@ #include #include #include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define int16_to_le(val) val +#else +#define int16_to_le(val) ((((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00)) +#endif struct input_abs_parms { int min, max; @@ -48,7 +55,7 @@ struct input_abs_parms { struct input_device { struct libevdev *dev; - struct mapping map; + struct mapping* map; int key_map[KEY_MAX]; int abs_map[ABS_MAX]; int hats_state[3][2]; @@ -209,35 +216,35 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) mouseCode = BUTTON_RIGHT; break; default: - if (index == dev->map.btn_a) + if (index == dev->map->btn_a) gamepadCode = A_FLAG; - else if (index == dev->map.btn_x) + else if (index == dev->map->btn_x) gamepadCode = X_FLAG; - else if (index == dev->map.btn_y) + else if (index == dev->map->btn_y) gamepadCode = Y_FLAG; - else if (index == dev->map.btn_b) + else if (index == dev->map->btn_b) gamepadCode = B_FLAG; - else if (index == dev->map.btn_dpup) + else if (index == dev->map->btn_dpup) gamepadCode = UP_FLAG; - else if (index == dev->map.btn_dpdown) + else if (index == dev->map->btn_dpdown) gamepadCode = DOWN_FLAG; - else if (index == dev->map.btn_dpright) + else if (index == dev->map->btn_dpright) gamepadCode = RIGHT_FLAG; - else if (index == dev->map.btn_dpleft) + else if (index == dev->map->btn_dpleft) gamepadCode = LEFT_FLAG; - else if (index == dev->map.btn_leftstick) + else if (index == dev->map->btn_leftstick) gamepadCode = LS_CLK_FLAG; - else if (index == dev->map.btn_rightstick) + else if (index == dev->map->btn_rightstick) gamepadCode = RS_CLK_FLAG; - else if (index == dev->map.btn_leftshoulder) + else if (index == dev->map->btn_leftshoulder) gamepadCode = LB_FLAG; - else if (index == dev->map.btn_rightshoulder) + else if (index == dev->map->btn_rightshoulder) gamepadCode = RB_FLAG; - else if (index == dev->map.btn_start) + else if (index == dev->map->btn_start) gamepadCode = PLAY_FLAG; - else if (index == dev->map.btn_back) + else if (index == dev->map->btn_back) gamepadCode = BACK_FLAG; - else if (index == dev->map.btn_guide) + else if (index == dev->map->btn_guide) gamepadCode = SPECIAL_FLAG; } @@ -251,9 +258,9 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) dev->buttonFlags |= gamepadCode; else dev->buttonFlags &= ~gamepadCode; - } else if (index == dev->map.btn_lefttrigger) + } else if (index == dev->map->btn_lefttrigger) dev->leftTrigger = ev->value?UCHAR_MAX:0; - else if (index == dev->map.btn_righttrigger) + else if (index == dev->map->btn_righttrigger) dev->rightTrigger = ev->value?UCHAR_MAX:0; else { fprintf(stderr, "Unmapped button: %d\n", ev->code); @@ -292,28 +299,28 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) case ABS_HAT3Y: dev->hats_state[hat_index][har_dir_index] = ev->value < 0 ? 0 : (ev->value == 0 ? 1 : 2); int hat_state = hat_constants[dev->hats_state[hat_index][0]][dev->hats_state[hat_index][1]]; - if (hat_index == dev->map.hat_dpup) - set_hat(dev->buttonFlags, UP_FLAG, hat_state, dev->map.hat_dir_dpup); - if (hat_index == dev->map.hat_dpdown) - set_hat(dev->buttonFlags, DOWN_FLAG, hat_state, dev->map.hat_dir_dpdown); - if (hat_index == dev->map.hat_dpright) - set_hat(dev->buttonFlags, HAT_RIGHT, hat_state, dev->map.hat_dir_dpright); - if (hat_index == dev->map.hat_dpleft) - set_hat(dev->buttonFlags, HAT_LEFT, hat_state, dev->map.hat_dir_dpleft); + if (hat_index == dev->map->hat_dpup) + set_hat(dev->buttonFlags, UP_FLAG, hat_state, dev->map->hat_dir_dpup); + if (hat_index == dev->map->hat_dpdown) + set_hat(dev->buttonFlags, DOWN_FLAG, hat_state, dev->map->hat_dir_dpdown); + if (hat_index == dev->map->hat_dpright) + set_hat(dev->buttonFlags, HAT_RIGHT, hat_state, dev->map->hat_dir_dpright); + if (hat_index == dev->map->hat_dpleft) + set_hat(dev->buttonFlags, HAT_LEFT, hat_state, dev->map->hat_dir_dpleft); break; } default: - if (index == dev->map.abs_leftx) - dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map.reverse_leftx); - else if (index == dev->map.abs_lefty) - dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, !dev->map.reverse_lefty); - else if (index == dev->map.abs_rightx) - dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map.reverse_rightx); - else if (index == dev->map.abs_righty) - dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map.reverse_righty); - else if (index == dev->map.abs_lefttrigger) + if (index == dev->map->abs_leftx) + dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map->reverse_leftx); + else if (index == dev->map->abs_lefty) + dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, !dev->map->reverse_lefty); + else if (index == dev->map->abs_rightx) + dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map->reverse_rightx); + else if (index == dev->map->abs_righty) + dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map->reverse_righty); + else if (index == dev->map->abs_lefttrigger) dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms); - else if (index == dev->map.abs_righttrigger) + else if (index == dev->map->abs_righttrigger) dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms); else gamepadModified = false; @@ -383,7 +390,7 @@ static int evdev_handle(int fd) { return LOOP_OK; } -void evdev_create(const char* device, char* mapFile) { +void evdev_create(const char* device, struct mapping* mappings) { int fd = open(device, O_RDONLY|O_NONBLOCK); if (fd <= 0) { fprintf(stderr, "Failed to open device %s\n", device); @@ -391,6 +398,31 @@ void evdev_create(const char* device, char* mapFile) { return; } + struct libevdev *evdev = libevdev_new(); + libevdev_set_fd(evdev, fd); + + int16_t guid[8] = {0}; + guid[0] = int16_to_le(libevdev_get_id_bustype(evdev)); + guid[2] = int16_to_le(libevdev_get_id_vendor(evdev)); + guid[4] = int16_to_le(libevdev_get_id_product(evdev)); + guid[6] = int16_to_le(libevdev_get_id_version(evdev)); + + char str_guid[33]; + char* buf = str_guid; + for (int i = 0; i < 16; i++) + buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]); + + while (mappings != NULL && strncmp(str_guid, mappings->guid, 32) != 0) + mappings = mappings->next; + + if (mappings == NULL) { + fprintf(stderr, "No mapping available for %s\n", device); + fflush(stderr); + close(fd); + libevdev_free(evdev); + return; + } + int dev = numDevices; numDevices++; @@ -407,11 +439,8 @@ void evdev_create(const char* device, char* mapFile) { memset(&devices[dev], 0, sizeof(devices[0])); devices[dev].fd = fd; - devices[dev].dev = libevdev_new(); - libevdev_set_fd(devices[dev].dev, devices[dev].fd); - - if (mapFile != NULL) - mapping_load(mapFile, &(devices[dev].map)); + devices[dev].dev = evdev; + devices[dev].map = mappings; int nbuttons = 0; for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) { @@ -433,12 +462,12 @@ void evdev_create(const char* device, char* mapFile) { } devices[dev].controllerId = -1; - evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_leftx); - evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_lefty); - evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_lefttrigger); - evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rightx); - evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_righty); - evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_righttrigger); + evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map->abs_leftx); + evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map->abs_lefty); + evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map->abs_lefttrigger); + evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map->abs_rightx); + evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map->abs_righty); + evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map->abs_righttrigger); if (grabbingDevices) { if (ioctl(fd, EVIOCGRAB, 1) < 0) { diff --git a/src/input/evdev.h b/src/input/evdev.h index aca8699..e52319a 100644 --- a/src/input/evdev.h +++ b/src/input/evdev.h @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -void evdev_create(const char* device, char* mapFile); +void evdev_create(const char* device, struct mapping* mappings); void evdev_loop(); void evdev_init(); diff --git a/src/input/mapping.c b/src/input/mapping.c index 2260c12..dd357ba 100644 --- a/src/input/mapping.c +++ b/src/input/mapping.c @@ -23,7 +23,9 @@ #include #include -void mapping_load(char* fileName, struct mapping* map) { +struct mapping* mapping_load(char* fileName) { + struct mapping* mappings = NULL; + struct mapping* map = NULL; FILE* fd = fopen(fileName, "r"); if (fd == NULL) { fprintf(stderr, "Can't open mapping file: %s\n", fileName); @@ -39,6 +41,17 @@ void mapping_load(char* fileName, struct mapping* map) { if (guid == NULL || name == NULL) continue; + struct mapping* newmap = malloc(sizeof(struct mapping)); + if (newmap == NULL) { + fprintf(stderr, "Not enough memory"); + exit(EXIT_FAILURE); + } else if (mappings == NULL) + mappings = newmap; + else + map->next = newmap; + + map = newmap; + strncpy(map->guid, guid, sizeof(map->guid)); strncpy(map->name, name, sizeof(map->name)); @@ -133,4 +146,6 @@ void mapping_load(char* fileName, struct mapping* map) { map->platform[32] = '\0'; } free(line); + + return mappings; } diff --git a/src/input/mapping.h b/src/input/mapping.h index 44e2c10..c80b0a5 100644 --- a/src/input/mapping.h +++ b/src/input/mapping.h @@ -17,6 +17,8 @@ * along with Moonlight; if not, see . */ +#pragma once + #include struct mapping { @@ -41,6 +43,8 @@ struct mapping { short abs_lefttrigger, abs_righttrigger; short btn_lefttrigger, btn_righttrigger; + + struct mapping* next; }; -void mapping_load(char* fileName, struct mapping* map); +struct mapping* mapping_load(char* fileName); diff --git a/src/input/sdlinput.c b/src/input/sdlinput.c index 67e0670..cf91d79 100644 --- a/src/input/sdlinput.c +++ b/src/input/sdlinput.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,11 +43,11 @@ static GAMEPAD_STATE gamepads[4]; static int keyboard_modifiers; static int activeGamepadMask = 0; -void sdlinput_init() { +void sdlinput_init(char* mappings) { memset(gamepads, 0, sizeof(gamepads)); SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); - SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); + SDL_GameControllerAddMappingsFromFile(mappings); for (int i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i)) { diff --git a/src/input/sdlinput.h b/src/input/sdlinput.h index e7cd2f3..4e6e371 100644 --- a/src/input/sdlinput.h +++ b/src/input/sdlinput.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,7 +51,7 @@ static const short keyCodes[] = { 0x26, //SDLK_UP }; -void sdlinput_init(); +void sdlinput_init(char* mappings); int sdlinput_handle_event(SDL_Event* event); #endif /* HAVE_SDL */ diff --git a/src/input/udev.c b/src/input/udev.c index b587cd9..6766b91 100644 --- a/src/input/udev.c +++ b/src/input/udev.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ #include "../loop.h" +#include "udev.h" #include "evdev.h" #include @@ -31,7 +32,7 @@ #include static bool autoadd; -static char* defaultMapfile; +static struct mapping* defaultMappings; static struct udev *udev; static struct udev_monitor *udev_mon; @@ -45,7 +46,7 @@ static int udev_handle(int fd) { const char *devnode = udev_device_get_devnode(dev); int id; if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) { - evdev_create(devnode, defaultMapfile); + evdev_create(devnode, defaultMappings); } } udev_device_unref(dev); @@ -53,7 +54,7 @@ static int udev_handle(int fd) { return LOOP_OK; } -void udev_init(bool autoload, char* mapfile) { +void udev_init(bool autoload, struct mapping* mappings) { udev = udev_new(); if (!udev) { fprintf(stderr, "Can't create udev\n"); @@ -74,7 +75,7 @@ void udev_init(bool autoload, char* mapfile) { const char *devnode = udev_device_get_devnode(dev); int id; if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) { - evdev_create(devnode, mapfile); + evdev_create(devnode, mappings); } udev_device_unref(dev); } @@ -86,7 +87,7 @@ void udev_init(bool autoload, char* mapfile) { udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "input", NULL); udev_monitor_enable_receiving(udev_mon); - defaultMapfile = mapfile; + defaultMappings = mappings; int udev_fd = udev_monitor_get_fd(udev_mon); loop_add_fd(udev_fd, &udev_handle, POLLIN); diff --git a/src/input/udev.h b/src/input/udev.h index b834adc..9414b6a 100644 --- a/src/input/udev.h +++ b/src/input/udev.h @@ -17,5 +17,7 @@ * along with Moonlight; if not, see . */ -void udev_init(bool autoload, char* mapfile); +#include "mapping.h" + +void udev_init(bool autoload, struct mapping* mappings); void evdev_destroy(); diff --git a/src/main.c b/src/main.c index 5b45639..18c9675 100644 --- a/src/main.c +++ b/src/main.c @@ -28,6 +28,7 @@ #include "platform.h" #include "sdl.h" +#include "input/mapping.h" #include "input/evdev.h" #include "input/udev.h" #include "input/cec.h" @@ -145,13 +146,13 @@ static void help() { printf("\t-localaudio\t\tPlay audio locally\n"); printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n"); printf("\t-keydir \tLoad encryption keys from directory\n"); + printf("\t-mapping \t\tUse as gamepad mappings configuration file\n"); #ifdef HAVE_SDL printf("\n Video options (SDL Only)\n\n"); printf("\t-windowed\t\tDisplay screen in a window\n"); #endif #ifdef HAVE_EMBEDDED printf("\n I/O options\n\n"); - printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); printf("\t-audio \t\tUse as audio output device\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); @@ -231,20 +232,24 @@ int main(int argc, char* argv[]) { } else if (strcmp("stream", config.action) == 0) { pair_check(&server); if (IS_EMBEDDED(system)) { + struct mapping* mappings = mapping_load(config.mapping); + for (int i=0;i Date: Sat, 8 Apr 2017 16:00:50 +0200 Subject: [PATCH 04/72] Replace mappings with SDL compataible mappings --- CMakeLists.txt | 2 +- gamecontrollerdb.txt | 158 +++++++++++++++++++++++++++++++ mappings/cordlessrumblepad2.conf | 30 ------ mappings/default.conf | 32 ------- mappings/dualshock3.conf | 32 ------- mappings/dualshock4.conf | 32 ------- mappings/rumblepad2.conf | 31 ------ mappings/wiiupro.conf | 32 ------- mappings/xbox360.conf | 32 ------- src/config.c | 2 +- 10 files changed, 160 insertions(+), 223 deletions(-) create mode 100644 gamecontrollerdb.txt delete mode 100644 mappings/cordlessrumblepad2.conf delete mode 100644 mappings/default.conf delete mode 100644 mappings/dualshock3.conf delete mode 100644 mappings/dualshock4.conf delete mode 100644 mappings/rumblepad2.conf delete mode 100644 mappings/wiiupro.conf delete mode 100644 mappings/xbox360.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index c668c3a..170a2d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,5 +143,5 @@ target_link_libraries(moonlight ${EVDEV_LIBRARIES} ${ALSA_LIBRARY} ${OPUS_LIBRAR add_subdirectory(docs) install(TARGETS moonlight DESTINATION ${CMAKE_INSTALL_BINDIR}) -install(DIRECTORY mappings DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight) +install(FILES gamecontrollerdb.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight) install(FILES moonlight.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}) diff --git a/gamecontrollerdb.txt b/gamecontrollerdb.txt new file mode 100644 index 0000000..0ff8bb1 --- /dev/null +++ b/gamecontrollerdb.txt @@ -0,0 +1,158 @@ +03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3, +05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3, +05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3, +03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3, +03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0, +03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3, +03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0, +0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3, +030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0, +030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +03000000380700008433000011010000,Mad Catz FightStick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +03000000380700008483000011010000,Mad Catz FightStick TE S+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3, +03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3, +03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3, +050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3, +050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2, +030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3, +05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2, +030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12, +050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12, +030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12, +030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3, +030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2, +03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3, +050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3, +0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1, +03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3, +050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4, +05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4, +03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3, +0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, +03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux, +030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d04000016c2000011010000,Logitech F310 Gamepad (DInput),x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux, +030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, +030000004c050000c405000011010000,Sony DualShock 4,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +050000004c050000c405000000010000,Sony DualShock 4 BT,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +030000004c050000cc09000011010000,Sony DualShock 4 V2,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +050000004c050000cc09000000010000,Sony DualShock 4 V2 BT,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +030000004c050000a00b000011010000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b13,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, +030000006f0e00003001000001010000,EA Sports PS3 Controller,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +03000000de280000ff11000001000000,Valve Streaming Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,y:b0,x:b3,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux, +03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a4,lefttrigger:b6,righttrigger:b7,platform:Linux, +030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,y:b3,x:b1,start:b9,guide:,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux, +030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a5, +030000008f0e00000300000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, +030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick ,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2, +030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, +030000006d04000016c2000010010000,Logitech Logitech Dual Action,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +03000000260900008888000000010000,GameCube {WiseGroup USB box},a:b0,b:b2,y:b3,x:b1,start:b7,leftshoulder:,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,rightstick:,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux, +030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,y:b4,x:b3,start:b8,guide:b5,back:b2,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b9,righttrigger:b10,platform:Linux, +030000006d04000018c2000010010000,Logitech Logitech RumblePad 2 USB,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +05000000d6200000ad0d000001000000,Moga Pro,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4, +030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,guide:b12,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,platform:Linux,a:b1,b:b2,x:b0,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:b6,righttrigger:b7, +0300000000f000000300000000010000,RetroUSB.com RetroPad,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux, +0300000000f00000f100000000010000,RetroUSB.com Super RetroPort,a:b1,b:b5,x:b0,y:b4,back:b2,start:b3,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,platform:Linux, +030000006f0e00001f01000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000280400000140000000010000,Gravis GamePad Pro USB ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftx:a0,lefty:a1, +030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000005e0400008502000000010000,Microsoft X-Box pad (Japan),platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b6,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:a2,rightshoulder:b2,righttrigger:a5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000006f0e00001e01000011010000,Rock Candy Gamepad for PS3,platform:Linux,a:b1,b:b2,x:b0,y:b3,back:b8,start:b9,guide:b12,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2, +03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,platform:Linux,a:b2,b:b1,y:b0,x:b3,start:b8,back:b9,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5, +030000008916000000fd000024010000,Razer Onza Tournament,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b11,dpdown:b14,dpright:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, +030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,y:b3,x:b1,start:b10,guide:b8,back:b9,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Linux, +03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux, +060000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux, +050000004c0500006802000000010000,PS3 Controller (Bluetooth),a:b14,b:b13,y:b12,x:b15,start:b3,guide:b16,back:b0,leftstick:b1,rightstick:b2,leftshoulder:b10,rightshoulder:b11,dpup:b4,dpleft:b7,dpdown:b6,dpright:b5,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b8,righttrigger:b9,platform:Linux, +03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick ,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a3,rightx:a1,righty:a4, +03000000666600000488000000010000,Super Joy Box 5 Pro,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b9,start:b8,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b4,righttrigger:b5,dpup:b12,dpleft:b15,dpdown:b14,dpright:b13, +05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2, +05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,platform:Linux,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2, +030000008916000001fd000024010000,Razer Onza Classic Edition,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:b11,dpdown:b14,dpright:b12,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000005e040000d102000001010000,Microsoft X-Box One pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000005e040000dd02000003020000,Microsoft X-Box One pad v2,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux, +03000000790000001100000010010000,RetroLink Saturn Classic Controller,platform:Linux,x:b3,a:b0,b:b1,y:b4,back:b5,guide:b2,start:b8,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1, +050000007e0500003003000001000000,Nintendo Wii U Pro Controller,platform:Linux,a:b0,b:b1,x:b3,y:b2,back:b8,start:b9,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16, +030000005e0400008e02000004010000,Microsoft X-Box 360 pad,platform:Linux,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2, +030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1, +030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7 +03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b7,back:b6,guide:b8,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,lefttrigger:a5,righttrigger:a4,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a2,righty:a3, +03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, +030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000006f0e00001304000000010000,Generic X-Box pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:a0,rightstick:a3,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,start:b9,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000830500006020000010010000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,x:b3,y:b2,back:b6,start:b7,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux, +03000000bd12000015d0000010010000,Tomee SNES USB Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux, +03000000790000001100000010010000,Retrolink Classic Controller,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,platform:Linux, +03000000c9110000f055000011010000,HJC Game GAMEPAD,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b11,rightshoulder:b5,rightx:a2,start:b9,righty:a3,dpleft:h0.8,lefttrigger:b6,x:b2,dpup:h0.1,back:b8,leftstick:b10,leftshoulder:b4,y:b3,a:b0,dpright:h0.2,righttrigger:b7,b:b1,platform:Linux, +03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,platform:Linux, +03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,y:b3,x:b0,start:b9,guide:,back:,leftstick:,rightstick:,leftshoulder:,dpleft:b15,dpdown:b14,dpright:b13,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,rightshoulder:b7,dpup:b12,platform:Linux, +030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,platform:Linux,x:b0,a:b2,b:b3,y:b1,back:b10,guide:b12,start:b11,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3, +030000006f0e00004601000001010000,Rock Candy Wired Controller for Xbox One,platform:Linux,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,lefttrigger:a2,righttrigger:a5,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +030000006f0e00003901000020060000,Afterglow Wired Controller for Xbox One,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,platform:Linux, +030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7, +05000000102800000900000000010000,8Bitdo SFC30 GamePad,platform:Linux,x:b4,a:b1,b:b0,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1, +03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,platform:Linux,a:b1,b:b2,y:b3,x:b0,start:b12,guide:b9,back:b8,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,leftx:a0,lefty:a1, +030000000d0f00000d00000000010000,hori,platform:Linux,a:b0,b:b6,y:b2,x:b1,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,start:b9,guide:b10,back:b8,leftshoulder:b3,rightshoulder:b7,leftx:b4,lefty:b5, +03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5, +03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,platform:Linux,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftshoulder:b4,rightshoulder:b5,leftx:a0,lefty:a1,lefttrigger:b6,righttrigger:b7, +03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),platform:Linux,a:b3,b:b4,y:b1,x:b0,start:b7,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5, +05000000010000000100000003000000,Nintendo Wiimote,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7, +030000005e0400008e02000062230000,Microsoft X-Box 360 pad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,y:b1,x:b0,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b6,righttrigger:b7,platform:Linux, +030000006f0e00000103000000020000,Logic3 Controller,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.0,dpdown:h0.4,dpright:h0.0,dpright:h0.2,dpup:h0.0,dpup:h0.1,leftshoulder:h0.0,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +05000000380700006652000025010000,Mad Catz C.T.R.L.R ,platform:Linux,x:b0,a:b1,b:b2,y:b3,back:b8,guide:b12,start:b9,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3, +030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,platform:Linux,x:b2,a:b0,b:b1,y:b3,back:b6,guide:b8,start:b7,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4, +03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,platform:Linux,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:a2,righttrigger:a5, +05000000a00500003232000001000000,8Bitdo Zero GamePad,platform:Linux,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1, +030000001008000001e5000010010000,NEXT Classic USB Game Controller,a:b0,b:b1,back:b8,start:b9,rightx:a2,righty:a3,leftx:a0,lefty:a1,platform:Linux, +03000000100800000300000010010000,USB Gamepad,platform:Linux,a:b2,b:b1,x:b3,y:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5, +05000000ac0500003232000001000000,VR-BOX,platform:Linux,a:b0,b:b1,x:b2,y:b3,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5, +03000000780000000600000010010000,Microntek USB Joystick,platform:Linux,x:b3,a:b2,b:b1,y:b0,back:b8,start:b9,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,leftx:a0,lefty:a1, diff --git a/mappings/cordlessrumblepad2.conf b/mappings/cordlessrumblepad2.conf deleted file mode 100644 index 3cd667b..0000000 --- a/mappings/cordlessrumblepad2.conf +++ /dev/null @@ -1,30 +0,0 @@ -abs_x = 0 -abs_y = 1 -abs_z = -1 -reverse_x = false -reverse_y = true -abs_rx = 2 -abs_ry = 5 -abs_rz = -1 -reverse_rx = false -reverse_ry = true -abs_dpad_x = 16 -abs_dpad_y = 17 -reverse_dpad_x = false -reverse_dpad_y = false -btn_east = 306 -btn_south = 305 -btn_north = 307 -btn_west = 304 -btn_select = 312 -btn_start = 313 -btn_thumbl = 314 -btn_thumbr = 315 -btn_tl = 308 -btn_tr = 309 -btn_tl2 = 310 -btn_tr2 = 311 -btn_dpad_up = -1 -btn_dpad_down = -1 -btn_dpad_left = -1 -btn_dpad_right = -1 diff --git a/mappings/default.conf b/mappings/default.conf deleted file mode 100644 index 33946e4..0000000 --- a/mappings/default.conf +++ /dev/null @@ -1,32 +0,0 @@ -abs_x = 0 -abs_y = 1 -abs_z = 2 -abs_rx = 3 -abs_ry = 4 -abs_rz = 5 -abs_deadzone = 0 -abs_dpad_y = 16 -abs_dpad_x = 17 -reverse_x = false -reverse_y = true -reverse_rx = false -reverse_ry = true -reverse_dpad_y = false -reverse_dpad_x = false -btn_south = 304 -btn_east = 305 -btn_north = 307 -btn_west = 308 -btn_select = 314 -btn_start = 315 -btn_mode = 316 -btn_thumbl = 317 -btn_thumbr = 318 -btn_tl = 310 -btn_tr = 311 -btn_tl2 = 312 -btn_tr2 = 313 -btn_dpad_up = 544 -btn_dpad_down = 545 -btn_dpad_left = 546 -btn_dpad_right = 547 diff --git a/mappings/dualshock3.conf b/mappings/dualshock3.conf deleted file mode 100644 index 01e9b19..0000000 --- a/mappings/dualshock3.conf +++ /dev/null @@ -1,32 +0,0 @@ -abs_x = 0 -abs_y = 1 -abs_z = 50 -abs_rx = 2 -abs_ry = 5 -abs_rz = 51 -abs_deadzone = 0 -abs_dpad_x = -1 -abs_dpad_y = -1 -reverse_x = false -reverse_y = true -reverse_rx = false -reverse_ry = true -reverse_dpad_x = false -reverse_dpad_y = false -btn_west = 303 -btn_south = 302 -btn_north = 300 -btn_east = 301 -btn_select = 288 -btn_start = 291 -btn_mode = 704 -btn_thumbl = 289 -btn_thumbr = 290 -btn_tl = 298 -btn_tr = 299 -btn_tl2 = 296 -btn_tr2 = 297 -btn_dpad_up = 292 -btn_dpad_down = 294 -btn_dpad_left = 295 -btn_dpad_right = 293 diff --git a/mappings/dualshock4.conf b/mappings/dualshock4.conf deleted file mode 100644 index 195e7f6..0000000 --- a/mappings/dualshock4.conf +++ /dev/null @@ -1,32 +0,0 @@ -abs_x = 0 -abs_y = 1 -abs_z = 3 -abs_rx = 2 -abs_ry = 5 -abs_rz = 4 -abs_deadzone = 0 -abs_dpad_x = 16 -abs_dpad_y = 17 -reverse_x = false -reverse_y = true -reverse_rx = false -reverse_ry = true -reverse_dpad_x = false -reverse_dpad_y = false -btn_west = 304 -btn_south = 305 -btn_north = 307 -btn_east = 306 -btn_select = 312 -btn_start = 313 -btn_mode = 316 -btn_thumbl = 314 -btn_thumbr = 315 -btn_tl = 308 -btn_tr = 309 -btn_tl2 = 310 -btn_tr2 = 311 -btn_dpad_up = -1 -btn_dpad_down = -1 -btn_dpad_left = -1 -btn_dpad_right = -1 diff --git a/mappings/rumblepad2.conf b/mappings/rumblepad2.conf deleted file mode 100644 index 3c93388..0000000 --- a/mappings/rumblepad2.conf +++ /dev/null @@ -1,31 +0,0 @@ -abs_x = 0 -abs_y = 1 -abs_z = -1 -reverse_x = false -reverse_y = true -abs_rx = 2 -abs_ry = 5 -abs_rz = -1 -reverse_rx = false -reverse_ry = true -abs_dpad_x = 16 -abs_dpad_y = 17 -reverse_dpad_x = false -reverse_dpad_y = false -btn_north = 291 -btn_east = 290 -btn_south = 289 -btn_west = 288 -btn_select = 297 -btn_start = 296 -btn_mode = 0 -btn_thumbl = 298 -btn_thumbr = 299 -btn_tl = 292 -btn_tr = 293 -btn_tl2 = 294 -btn_tr2 = 295 -btn_dpad_up = -1 -btn_dpad_down = -1 -btn_dpad_left = -1 -btn_dpad_right = -1 diff --git a/mappings/wiiupro.conf b/mappings/wiiupro.conf deleted file mode 100644 index 18a9bb9..0000000 --- a/mappings/wiiupro.conf +++ /dev/null @@ -1,32 +0,0 @@ -abs_x = 0 -abs_y = 1 -abs_z = -1 -reverse_x = false -reverse_y = true -abs_rx = 3 -abs_ry = 4 -abs_rz = -1 -reverse_rx = false -reverse_ry = true -abs_deadzone = 24088 -abs_dpad_x = -1 -abs_dpad_y = -1 -reverse_dpad_x = false -reverse_dpad_y = false -btn_north = 308 -btn_east = 304 -btn_south = 305 -btn_west = 307 -btn_select = 314 -btn_start = 315 -btn_mode = 316 -btn_thumbl = 317 -btn_thumbr = 318 -btn_tl = 310 -btn_tr = 311 -btn_tl2 = 312 -btn_tr2 = 313 -btn_dpad_up = 544 -btn_dpad_down = 545 -btn_dpad_left = 546 -btn_dpad_right = 547 \ No newline at end of file diff --git a/mappings/xbox360.conf b/mappings/xbox360.conf deleted file mode 100644 index d62423a..0000000 --- a/mappings/xbox360.conf +++ /dev/null @@ -1,32 +0,0 @@ -abs_x = 0 -abs_y = 1 -abs_z = 10 -abs_rx = 3 -abs_ry = 4 -abs_rz = 9 -abs_deadzone = 0 -abs_dpad_x = 17 -abs_dpad_y = 16 -reverse_x = false -reverse_y = true -reverse_rx = false -reverse_ry = true -reverse_dpad_x = false -reverse_dpad_y = false -btn_east = 305 -btn_south = 304 -btn_north = 308 -btn_west = 307 -btn_select = 314 -btn_start = 315 -btn_mode = 316 -btn_thumbl = 317 -btn_thumbr = 318 -btn_tl = 310 -btn_tr = 311 -btn_tl2 = 312 -btn_tr2 = 313 -btn_dpad_up = -1 -btn_dpad_down = -1 -btn_dpad_left = -1 -btn_dpad_right = -1 diff --git a/src/config.c b/src/config.c index 47ddd71..da04502 100644 --- a/src/config.c +++ b/src/config.c @@ -305,7 +305,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->codec = CODEC_UNSPECIFIED; config->inputsCount = 0; - config->mapping = get_path("mappings/default.conf", getenv("XDG_DATA_DIRS")); + config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS")); config->key_dir[0] = 0; char* config_file = get_path("moonlight.conf", "/etc"); From d46fe49c5748da8d6d2bd5753453a4fe6e3b13cb Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 26 Apr 2017 20:00:09 +0200 Subject: [PATCH 05/72] Compatability fixes for OpenSSL 1.1 --- libgamestream/CMakeLists.txt | 2 +- libgamestream/client.c | 5 ++++- libgamestream/mkcert.c | 4 ++-- third_party/moonlight-common-c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libgamestream/CMakeLists.txt b/libgamestream/CMakeLists.txt index f9f9299..7f1a813 100644 --- a/libgamestream/CMakeLists.txt +++ b/libgamestream/CMakeLists.txt @@ -1,7 +1,7 @@ find_package(LibUUID REQUIRED) find_package(Threads REQUIRED) find_package(CURL REQUIRED) -find_package(OpenSSL REQUIRED) +find_package(OpenSSL 1.1 REQUIRED) find_package(EXPAT REQUIRED) pkg_check_modules(AVAHI REQUIRED avahi-client) diff --git a/libgamestream/client.c b/libgamestream/client.c index 2d547ce..bc6c020 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -494,12 +494,15 @@ int gs_pair(PSERVER_DATA server, char* pin) { char client_secret_data[16]; RAND_bytes(client_secret_data, 16); + const ASN1_BIT_STRING *asnSignature; + X509_get0_signature(&asnSignature, NULL, cert); + char challenge_response[16 + 256 + 16]; char challenge_response_hash[32]; char challenge_response_hash_enc[32]; char challenge_response_hex[65]; memcpy(challenge_response, challenge_response_data + hash_length, 16); - memcpy(challenge_response + 16, cert->signature->data, 256); + memcpy(challenge_response + 16, asnSignature->data, 256); memcpy(challenge_response + 16 + 256, client_secret_data, 16); if (server->serverMajorVersion >= 7) SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash); diff --git a/libgamestream/mkcert.c b/libgamestream/mkcert.c index b0d5e5a..e3f6b9e 100644 --- a/libgamestream/mkcert.c +++ b/libgamestream/mkcert.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,7 @@ CERT_KEY_PAIR mkcert_generate() { CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); - SSLeay_add_all_algorithms(); + OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS); @@ -54,7 +55,6 @@ CERT_KEY_PAIR mkcert_generate() { #endif CRYPTO_cleanup_all_ex_data(); - CRYPTO_mem_leaks(bio_err); BIO_free(bio_err); return (CERT_KEY_PAIR) {x509, pkey, p12}; diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index dcdfcd5..27213a1 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit dcdfcd55e575e7f73e813e338fe9ab11ed1211a7 +Subproject commit 27213a167808e85824cc4e2163c7dcaaadf64e50 From 45f9b0554ebee79da45b7a68809087525bd3ed3a Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 8 May 2017 18:11:37 +0200 Subject: [PATCH 06/72] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 27213a1..cd9f473 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 27213a167808e85824cc4e2163c7dcaaadf64e50 +Subproject commit cd9f47371a3611c2029bc8428f86bad7650e46a3 From 56f88f4fe8c4e566f59ad7ab70cb51c84599759f Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 10 May 2017 15:18:51 +0200 Subject: [PATCH 07/72] Update keycodes for SDL --- src/input/sdlinput.c | 16 +++++++++++--- src/input/sdlinput.h | 50 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/input/sdlinput.c b/src/input/sdlinput.c index cf91d79..26fab78 100644 --- a/src/input/sdlinput.c +++ b/src/input/sdlinput.c @@ -103,10 +103,20 @@ int sdlinput_handle_event(SDL_Event* event) { case SDL_KEYDOWN: case SDL_KEYUP: button = event->key.keysym.sym; - if (button >= (0x40000000 + 0x39) && button < (0x40000000 + 0x39 + sizeof(keyCodes))) - button = keyCodes[button - 0x40000039]; - else if (button >= 0x61) + if (button >= 0x21 && button <= 0x2f) + button = keyCodes1[button - 0x21]; + else if (button >= 0x3a && button <= 0x40) + button = keyCodes2[button - 0x3a]; + else if (button >= 0x5b && button <= 0x60) + button = keyCodes3[button - 0x5b]; + else if (button >= 0x40000039 && button < 0x40000039 + sizeof(keyCodes4)) + button = keyCodes4[button - 0x40000039]; + else if (button >= 0x400000E0 && button <= 0x400000E7) + button = keyCodes5[button - 0x400000E0]; + else if (button >= 0x61 && button <= 0x7a) button -= 0x20; + else if (button == 0x7f) + button = 0x2e; int modifier = 0; switch (event->key.keysym.sym) { diff --git a/src/input/sdlinput.h b/src/input/sdlinput.h index 4e6e371..8b2b23b 100644 --- a/src/input/sdlinput.h +++ b/src/input/sdlinput.h @@ -22,7 +22,44 @@ #include #include -static const short keyCodes[] = { +static const short keyCodes1[] = { + 0, //SDLK_EXCLAIM + 0, //SDLK_QUOTEDBL + 0, //SDLK_HASH + 0, //SDLK_DOLLAR + 0, //SDLK_PERCENT + 0, //SDLK_AMPERSAND + 0xDE, //SDLK_QUOTE + 0, //SDLK_LEFTPAREN + 0, //SDLK_RIGHTPAREN + 0, //SDLK_ASTERISK + 0, //SDLK_PLUS + 0xBC, //SDLK_COMMA + 0xBD, //SDLK_MINUS + 0xBE, //SDLK_PERIOD + 0xBF, //SDLK_SLASH +}; + +static const short keyCodes2[] = { + 0, //SDLK_COLON + 0xBA, //SDLK_SEMICOLON + 0, //SDLK_LESS + 0xBB, //SDLK_EQUALS + 0, //SDLK_GREATER + 0, //SDLK_QUESTION + 0, //SDLK_AT +}; + +static const short keyCodes3[] = { + 0xDB, //SDLK_LEFTBRACKET + 0xDC, //SDLK_BACKSLASH + 0xDD, //SDLK_RIGHTBRACKET + 0, //SDLK_CARET + 0, //SDLK_UNDERSCORE + 0xC0, //SDLK_BACKQUOTE +}; + +static const short keyCodes4[] = { 0x14, //SDLK_CAPSLOCK 0x70, //SDLK_F1 0x71, //SDLK_F2 @@ -51,6 +88,17 @@ static const short keyCodes[] = { 0x26, //SDLK_UP }; +static const short keyCodes5[] = { + 0x11, //SDLK_LCTRL + 0x10, //SDLK_LSHIFT + 0x12, //SDLK_LALT + 0x5B, //SDLK_LGUI + 0x11, //SDLK_LRCTRL + 0x10, //SDLK_RSHIFT + 0x12, //SDLK_RALT + 0x5C, //SDLK_RGUI +}; + void sdlinput_init(char* mappings); int sdlinput_handle_event(SDL_Event* event); From 7cbea75ab2a4004f6de052b07fc124e974e657d5 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 10 May 2017 15:53:04 +0200 Subject: [PATCH 08/72] Check for unsuported parameters for SDL --- src/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.c b/src/main.c index 18c9675..71c2696 100644 --- a/src/main.c +++ b/src/main.c @@ -181,6 +181,9 @@ int main(int argc, char* argv[]) { if (system == 0) { fprintf(stderr, "Platform '%s' not found\n", config.platform); exit(-1); + } else if (system == SDL && audio_device != NULL) { + fprintf(stderr, "You can't select a audio device for SDL\n"); + exit(-1); } config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system)); @@ -247,6 +250,11 @@ int main(int argc, char* argv[]) { } #ifdef HAVE_SDL else if (system == SDL) { + if (config.inputsCount > 0) { + fprintf(stderr, "You can't select input devices as SDL will automatically use all available controllers\n"); + exit(-1); + } + sdl_init(config.stream.width, config.stream.height, config.fullscreen); sdlinput_init(config.mapping); } From 4bf3137bfc02523a511fc128c14219069dfe5caf Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 10 May 2017 15:58:32 +0200 Subject: [PATCH 09/72] Cleanup Pulseaudio code to allow server select using audio argument --- src/audio/pulse.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/audio/pulse.c b/src/audio/pulse.c index bcb92aa..be47dc5 100644 --- a/src/audio/pulse.c +++ b/src/audio/pulse.c @@ -35,19 +35,19 @@ static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static int channelCount; bool audio_pulse_init() { - pa_sample_spec spec = { - .format = PA_SAMPLE_S16LE, - .rate = 44000, - .channels = 2 - }; + pa_sample_spec spec = { + .format = PA_SAMPLE_S16LE, + .rate = 44000, + .channels = 2 + }; - int error; - dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error); - if (dev) { - pa_simple_free(dev); - return true; - } else - return false; + int error; + dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error); + + if (dev) + pa_simple_free(dev); + + return (bool) dev; } static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { @@ -69,12 +69,7 @@ static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIG alsaMapping[5] = opusConfig->mapping[3]; } - decoder = opus_multistream_decoder_create(opusConfig->sampleRate, - opusConfig->channelCount, - opusConfig->streams, - opusConfig->coupledStreams, - alsaMapping, - &rc); + decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, alsaMapping, &rc); pa_sample_spec spec = { .format = PA_SAMPLE_S16LE, @@ -82,7 +77,7 @@ static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIG .channels = opusConfig->channelCount }; - dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error); + dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error); if (!dev) { printf("Pulseaudio error: %s\n", pa_strerror(error)); From 045e608681b2e8a258c87fd135709ae640377cf9 Mon Sep 17 00:00:00 2001 From: OtherCrashOverride Date: Mon, 9 Jan 2017 23:59:49 -0500 Subject: [PATCH 10/72] Fix Odroid-C2 build. --- cmake/FindAmlogic.cmake | 2 +- src/video/aml.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 cmake/FindAmlogic.cmake mode change 100644 => 100755 src/video/aml.c diff --git a/cmake/FindAmlogic.cmake b/cmake/FindAmlogic.cmake old mode 100644 new mode 100755 index eb06031..82a8fe9 --- a/cmake/FindAmlogic.cmake +++ b/cmake/FindAmlogic.cmake @@ -1,7 +1,7 @@ find_path(AMLOGIC_INCLUDE_DIR NAMES codec.h DOC "Amlogic include directory" - PATHS /usr/local/include/amcodec /usr/include/amcodec) + PATHS /usr/local/include/amcodec /usr/include/amcodec /usr/include/) mark_as_advanced(AMLOGIC_INCLUDE_DIR) find_library(AMAVUTILS_LIBRARY diff --git a/src/video/aml.c b/src/video/aml.c old mode 100644 new mode 100755 index 7925c9c..291af62 --- a/src/video/aml.c +++ b/src/video/aml.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #define SYNC_OUTSIDE 0x02 #define UCODE_IP_ONLY_PARAM 0x08 From 441bf6c7e05e8fbb6c4dff53a354016b595722b1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 10 May 2017 19:44:48 +0200 Subject: [PATCH 11/72] Use Freerun mode for Amlogic decoder --- src/video/aml.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/video/aml.c b/src/video/aml.c index 291af62..6662b53 100755 --- a/src/video/aml.c +++ b/src/video/aml.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * Copyright (C) 2016 OtherCrashOverride, Daniel Mehrwald * * Moonlight is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include #include #include +#include #include #define SYNC_OUTSIDE 0x02 @@ -94,9 +95,14 @@ void aml_setup(int videoFormat, int width, int height, int redrawRate, void* con codecParam.am_sysinfo.rate = 96000 / redrawRate; codecParam.am_sysinfo.param = (void*) ((size_t) codecParam.am_sysinfo.param | SYNC_OUTSIDE); - int api = codec_init(&codecParam); - if (api != 0) { - fprintf(stderr, "codec_init error: %x\n", api); + int ret; + if ((ret = codec_init(&codecParam)) != 0) { + fprintf(stderr, "codec_init error: %x\n", ret); + exit(1); + } + + if ((ret = codec_set_freerun_mode(&codecParam, 1)) != 0) { + fprintf(stderr, "Can't set Freerun mode: %x\n", ret); exit(1); } } From 8f9e5102ccec429224674b36e7e9471935266426 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 10 May 2017 20:10:09 +0200 Subject: [PATCH 12/72] Autodetect number of cores for software rendering --- src/video/sdl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/video/sdl.c b/src/video/sdl.c index 14016a1..e9ac2b2 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -26,6 +26,7 @@ #include #include +#include #include #define DECODER_BUFFER_SIZE 92*1024 @@ -37,7 +38,7 @@ static void sdl_setup(int videoFormat, int width, int height, int redrawRate, vo if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; - if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, 2) < 0) { + if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); exit(1); } @@ -92,5 +93,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl = { .setup = sdl_setup, .cleanup = sdl_cleanup, .submitDecodeUnit = sdl_submit_decode_unit, - .capabilities = CAPABILITY_SLICES_PER_FRAME(2) | CAPABILITY_REFERENCE_FRAME_INVALIDATION | CAPABILITY_DIRECT_SUBMIT, + .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION | CAPABILITY_DIRECT_SUBMIT, }; From fca16aa801fd6f616b60e5e9ce2e3fa9b03a5f50 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Thu, 25 May 2017 16:48:58 +0200 Subject: [PATCH 13/72] Cleanup audio decoding code --- src/audio.h | 6 +++++- src/audio/alsa.c | 22 +++++++--------------- src/audio/omx.c | 44 ++++++++++++++++++-------------------------- src/audio/pulse.c | 7 ++----- src/audio/sdl.c | 12 ++---------- 5 files changed, 34 insertions(+), 57 deletions(-) diff --git a/src/audio.h b/src/audio.h index 3981212..ad485fa 100644 --- a/src/audio.h +++ b/src/audio.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,10 @@ #include +#define MAX_CHANNEL_COUNT 6 +#define FRAME_SIZE 240 +#define FRAME_BUFFER 12 + extern const char* audio_device; extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa; diff --git a/src/audio/alsa.c b/src/audio/alsa.c index 273ce82..dfbe98e 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,16 +25,13 @@ #define CHECK_RETURN(f) if ((rc = f) < 0) { printf("Alsa error code %d\n", rc); exit(-1); } -#define MAX_CHANNEL_COUNT 6 -#define FRAME_SIZE 240 - static snd_pcm_t *handle; static OpusMSDecoder* decoder; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc; - unsigned char alsaMapping[6]; + unsigned char alsaMapping[MAX_CHANNEL_COUNT]; /* The supplied mapping array has order: FL-FR-C-LFE-RL-RR * ALSA expects the order: FL-FR-RL-RR-C-LFE @@ -49,17 +46,12 @@ static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU alsaMapping[5] = opusConfig->mapping[3]; } - decoder = opus_multistream_decoder_create(opusConfig->sampleRate, - opusConfig->channelCount, - opusConfig->streams, - opusConfig->coupledStreams, - alsaMapping, - &rc); + decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, alsaMapping, &rc); snd_pcm_hw_params_t *hw_params; snd_pcm_sw_params_t *sw_params; - snd_pcm_uframes_t period_size = FRAME_SIZE * opusConfig->channelCount * 2; - snd_pcm_uframes_t buffer_size = 12 * period_size; + snd_pcm_uframes_t period_size = FRAME_SIZE * FRAME_BUFFER; + snd_pcm_uframes_t buffer_size = 2 * period_size; unsigned int sampleRate = opusConfig->sampleRate; if (audio_device == NULL) @@ -75,16 +67,16 @@ static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU CHECK_RETURN(snd_pcm_hw_params_set_format(handle, hw_params, SND_PCM_FORMAT_S16_LE)); CHECK_RETURN(snd_pcm_hw_params_set_rate_near(handle, hw_params, &sampleRate, NULL)); CHECK_RETURN(snd_pcm_hw_params_set_channels(handle, hw_params, opusConfig->channelCount)); - CHECK_RETURN(snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffer_size)); CHECK_RETURN(snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, NULL)); + CHECK_RETURN(snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffer_size)); CHECK_RETURN(snd_pcm_hw_params(handle, hw_params)); snd_pcm_hw_params_free(hw_params); /* Set software parameters */ CHECK_RETURN(snd_pcm_sw_params_malloc(&sw_params)); CHECK_RETURN(snd_pcm_sw_params_current(handle, sw_params)); - CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, buffer_size - period_size)); CHECK_RETURN(snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size)); + CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1)); CHECK_RETURN(snd_pcm_sw_params(handle, sw_params)); snd_pcm_sw_params_free(sw_params); diff --git a/src/audio/omx.c b/src/audio/omx.c index bf8f7d3..1c21427 100644 --- a/src/audio/omx.c +++ b/src/audio/omx.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,9 +25,6 @@ #include "bcm_host.h" #include "ilclient.h" -#define MAX_CHANNEL_COUNT 6 -#define FRAME_SIZE 240 - static OpusMSDecoder* decoder; ILCLIENT_T* handle; COMPONENT_T* component; @@ -38,7 +35,7 @@ static int channelCount; static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc, error; OMX_ERRORTYPE err; - unsigned char omxMapping[6]; + unsigned char omxMapping[MAX_CHANNEL_COUNT]; char* componentName = "audio_render"; channelCount = opusConfig->channelCount; @@ -52,17 +49,12 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR omxMapping[3] = opusConfig->mapping[2]; } - decoder = opus_multistream_decoder_create(opusConfig->sampleRate, - opusConfig->channelCount, - opusConfig->streams, - opusConfig->coupledStreams, - omxMapping, - &rc); + decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, omxMapping, &rc); handle = ilclient_init(); if (handle == NULL) { - fprintf(stderr, "IL client init failed\n"); - exit(1); + fprintf(stderr, "IL client init failed\n"); + exit(1); } if (ilclient_create_component(handle, &component, componentName, ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) { @@ -104,30 +96,30 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR switch(channelCount) { case 1: - sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelCF; - break; + sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelCF; + break; case 8: - sPCMMode.eChannelMapping[7] = OMX_AUDIO_ChannelRS; + sPCMMode.eChannelMapping[7] = OMX_AUDIO_ChannelRS; case 7: - sPCMMode.eChannelMapping[6] = OMX_AUDIO_ChannelLS; + sPCMMode.eChannelMapping[6] = OMX_AUDIO_ChannelLS; case 6: - sPCMMode.eChannelMapping[5] = OMX_AUDIO_ChannelRR; + sPCMMode.eChannelMapping[5] = OMX_AUDIO_ChannelRR; case 5: - sPCMMode.eChannelMapping[4] = OMX_AUDIO_ChannelLR; + sPCMMode.eChannelMapping[4] = OMX_AUDIO_ChannelLR; case 4: - sPCMMode.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + sPCMMode.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; case 3: - sPCMMode.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + sPCMMode.eChannelMapping[2] = OMX_AUDIO_ChannelCF; case 2: - sPCMMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF; - sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF; - break; + sPCMMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + break; } err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPcm, &sPCMMode); if(err != OMX_ErrorNone){ - fprintf(stderr, "PCM mode unsupported\n"); - return; + fprintf(stderr, "PCM mode unsupported\n"); + return; } OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest; diff --git a/src/audio/pulse.c b/src/audio/pulse.c index be47dc5..9322502 100644 --- a/src/audio/pulse.c +++ b/src/audio/pulse.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,9 +26,6 @@ #include #include -#define MAX_CHANNEL_COUNT 6 -#define FRAME_SIZE 240 - static OpusMSDecoder* decoder; static pa_simple *dev = NULL; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; @@ -52,7 +49,7 @@ bool audio_pulse_init() { static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc, error; - unsigned char alsaMapping[6]; + unsigned char alsaMapping[MAX_CHANNEL_COUNT]; channelCount = opusConfig->channelCount; diff --git a/src/audio/sdl.c b/src/audio/sdl.c index 8da514a..35c65ba 100644 --- a/src/audio/sdl.c +++ b/src/audio/sdl.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,9 +25,6 @@ #include #include -#define MAX_CHANNEL_COUNT 6 -#define FRAME_SIZE 240 - static OpusMSDecoder* decoder; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static SDL_AudioDeviceID dev; @@ -35,12 +32,7 @@ static int channelCount; static void sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc; - decoder = opus_multistream_decoder_create(opusConfig->sampleRate, - opusConfig->channelCount, - opusConfig->streams, - opusConfig->coupledStreams, - opusConfig->mapping, - &rc); + decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc); channelCount = opusConfig->channelCount; From a426ae6e2d61b3f4cae3acc2591ffa6bc5b4787d Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Thu, 25 May 2017 16:18:50 +0200 Subject: [PATCH 14/72] Support for X11 with software decoder using GLES 2.0 and EGL to perform YUV to RGB color conversion --- CMakeLists.txt | 47 +++++++++--- src/platform.c | 8 ++ src/platform.h | 5 +- src/video/egl.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++ src/video/egl.h | 24 ++++++ src/video/x11.c | 97 +++++++++++++++++++++++ 6 files changed, 368 insertions(+), 12 deletions(-) create mode 100644 src/video/egl.c create mode 100644 src/video/egl.h create mode 100644 src/video/x11.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c668c3a..b7d6ca2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,13 +29,20 @@ pkg_check_modules(XLIB x11-xcb) pkg_check_modules(LIBVA vdpau) pkg_check_modules(PULSE libpulse-simple) pkg_check_modules(CEC libcec>=3.0.0) +pkg_check_modules(EGL egl) +pkg_check_modules(GLES glesv2) -if(AVCODEC_FOUND AND AVUTIL_FOUND AND SDL_FOUND) - set(SOFTWARE_FOUND TRUE) - if(XLIB_FOUND AND LIBVA_FOUND) - set(VDPAU_FOUND TRUE) - else() - set(VDPAU_FOUND FALSE) +if(AVCODEC_FOUND AND AVUTIL_FOUND) + if(EGL_FOUND AND GLES_FOUND AND XLIB_FOUND) + set(X11_FOUND TRUE) + endif() + if(SDL_FOUND OR X11_FOUND) + set(SOFTWARE_FOUND TRUE) + if(XLIB_FOUND AND LIBVA_FOUND) + set(VDPAU_FOUND TRUE) + else() + set(VDPAU_FOUND FALSE) + endif() endif() else() set(SOFTWARE_FOUND FALSE) @@ -52,9 +59,17 @@ elseif(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT endif() if (SOFTWARE_FOUND) - list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c) - list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL) - list(APPEND MOONLIGHT_OPTIONS SDL) + list(APPEND SRC_LIST ./src/video/ffmpeg.c) + if (SDL_FOUND) + list(APPEND SRC_LIST ./src/video/sdl.c ./src/audio/sdl.c) + list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL) + list(APPEND MOONLIGHT_OPTIONS SDL) + endif() + if (X11_FOUND) + list(APPEND SRC_LIST ./src/video/x11.c ./src/video/egl.c) + list(APPEND MOONLIGHT_DEFINITIONS HAVE_X11) + list(APPEND MOONLIGHT_OPTIONS X11) + endif() if(VDPAU_FOUND) list(APPEND SRC_LIST ./src/video/ffmpeg_vdpau.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_VDPAU) @@ -120,9 +135,19 @@ if(FREESCALE_FOUND) install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() +if(SDL_FOUND) + target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS}) + target_link_libraries(moonlight ${SDL_LIBRARIES}) +endif() + +if(X11_FOUND) + target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${EGL_INCLUDE_DIRS} ${GLES_INCLUDE_DIRS}) + target_link_libraries(moonlight ${XLIB_LIBRARIES} ${EGL_LIBRARIES} ${GLES_LIBRARIES}) +endif() + if (SOFTWARE_FOUND) - target_include_directories(moonlight PRIVATE ${SDL_INCLUDE_DIRS} ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS}) - target_link_libraries(moonlight ${SDL_LIBRARIES} ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES}) + target_include_directories(moonlight PRIVATE ${AVCODEC_INCLUDE_DIRS} ${AVUTIL_INCLUDE_DIRS}) + target_link_libraries(moonlight ${AVCODEC_LIBRARIES} ${AVUTIL_LIBRARIES}) if(VDPAU_FOUND) target_include_directories(moonlight PRIVATE ${XLIB_INCLUDE_DIRS} ${LIBVA_INCLUDE_DIRS}) target_link_libraries(moonlight ${XLIB_LIBRARIES} ${LIBVA_LIBRARIES}) diff --git a/src/platform.c b/src/platform.c index 951d853..e4ea788 100644 --- a/src/platform.c +++ b/src/platform.c @@ -55,6 +55,10 @@ enum platform platform_check(char* name) { return AML; } #endif + #ifdef HAVE_X11 + if (std || strcmp(name, "x11") == 0) + return X11; + #endif #ifdef HAVE_SDL if (std || strcmp(name, "sdl") == 0) return SDL; @@ -67,6 +71,10 @@ enum platform platform_check(char* name) { DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) { switch (system) { + #ifdef HAVE_X11 + case X11: + return &decoder_callbacks_x11; + #endif #ifdef HAVE_SDL case SDL: return &decoder_callbacks_sdl; diff --git a/src/platform.h b/src/platform.h index 8e9ee8c..d9aa03e 100644 --- a/src/platform.h +++ b/src/platform.h @@ -26,7 +26,7 @@ #define IS_EMBEDDED(SYSTEM) SYSTEM != SDL -enum platform { NONE, SDL, PI, IMX, AML, FAKE }; +enum platform { NONE, SDL, X11, PI, IMX, AML, FAKE }; enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); @@ -35,6 +35,9 @@ bool platform_supports_hevc(enum platform system); extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake; extern AUDIO_RENDERER_CALLBACKS audio_callbacks_fake; +#ifdef HAVE_X11 +extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; +#endif #ifdef HAVE_SDL extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl; void sdl_loop(); diff --git a/src/video/egl.c b/src/video/egl.c new file mode 100644 index 0000000..0486e85 --- /dev/null +++ b/src/video/egl.c @@ -0,0 +1,199 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include "egl.h" + +#include + +#include + +#include +#include +#include +#include +#include + +static const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; +static const char* texture_mappings[] = { "ymap", "umap", "vmap" }; +static const char* vertex_source = "\ +attribute vec2 position;\ +varying mediump vec2 tex_position;\ +\ +void main() {\ + gl_Position = vec4(position, 0, 1);\ + tex_position = vec2((position.x + 1.) / 2., (1. - position.y) / 2.);\ +}\ +"; + +static const char* fragment_source = "\ +uniform lowp sampler2D ymap;\ +uniform lowp sampler2D umap;\ +uniform lowp sampler2D vmap;\ +varying mediump vec2 tex_position;\ +\ +void main() {\ + mediump float y = texture2D(ymap, tex_position).r;\ + mediump float u = texture2D(umap, tex_position).r - .5;\n\ + mediump float v = texture2D(vmap, tex_position).r - .5;\n\ + lowp float r = y + 1.28033 * v;\ + lowp float g = y - .21482 * u - .38059 * v;\ + lowp float b = y + 2.12798 * u;\ + gl_FragColor = vec4(r, g, b, 1.0);\ +}\ +"; + +static const float vertices[] = { + -1.f, 1.f, + -1.f, -1.f, + 1.f, -1.f, + 1.f, 1.f +}; + +static const GLuint elements[] = { + 0, 1, 2, + 2, 3, 0 +}; + +static EGLDisplay display; +static EGLSurface surface; +static EGLContext context; + +static int width, height; +static bool current; + +static GLuint texture_id[3], texture_uniform[3]; +static GLuint shader_program; + +void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_window, int display_width, int display_height) { + width = display_width; + height = display_height; + + // get an EGL display connection + display = eglGetDisplay(native_display); + if (display == EGL_NO_DISPLAY) { + fprintf( stderr, "EGL: error get display\n" ); + exit(EXIT_FAILURE); + } + + // initialize the EGL display connection + int major, minor; + EGLBoolean result = eglInitialize(display, &major, &minor); + if (result == EGL_FALSE) { + fprintf( stderr, "EGL: error initialising display\n"); + exit(EXIT_FAILURE); + } + + // get our config from the config class + EGLConfig config = NULL; + static const EGLint attribute_list[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; + + EGLint totalConfigsFound = 0; + result = eglChooseConfig(display, attribute_list, &config, 1, &totalConfigsFound); + if (result != EGL_TRUE || totalConfigsFound == 0) { + fprintf(stderr, "EGL: Unable to query for available configs, found %d.\n", totalConfigsFound); + exit(EXIT_FAILURE); + } + + // bind the OpenGL API to the EGL + result = eglBindAPI(EGL_OPENGL_ES_API); + if (result == EGL_FALSE) { + fprintf(stderr, "EGL: error binding API\n"); + exit(EXIT_FAILURE); + } + + // create an EGL rendering context + context = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attributes); + if (context == EGL_NO_CONTEXT) { + fprintf(stderr, "EGL: couldn't get a valid context\n"); + exit(EXIT_FAILURE); + } + + // finally we can create a new surface using this config and window + surface = eglCreateWindowSurface(display, config, (NativeWindowType) native_window, NULL); + eglMakeCurrent(display, surface, surface, context); + + glEnable(GL_TEXTURE_2D); + + GLuint vbo; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + GLuint ebo; + glGenBuffers(1, &ebo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW); + + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_source, NULL); + glCompileShader(vertex_shader); + GLint maxLength = 0; + + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_source, NULL); + glCompileShader(fragment_shader); + + shader_program = glCreateProgram(); + glAttachShader(shader_program, vertex_shader); + glAttachShader(shader_program, fragment_shader); + + glLinkProgram(shader_program); + glBindAttribLocation(shader_program, 0, "position"); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0); + + glGenTextures(3, texture_id); + for (int i = 0; i < 3; i++) { + glBindTexture(GL_TEXTURE_2D, texture_id[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, i > 0 ? width / 2 : width, i > 0 ? height / 2 : height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); + + texture_uniform[i] = glGetUniformLocation(shader_program, texture_mappings[i]); + } + + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +} + +void egl_draw(const uint8_t* image[3]) { + if (!current) { + eglMakeCurrent(display, surface, surface, context); + current = True; + } + + glUseProgram(shader_program); + glEnableVertexAttribArray(0); + + for (int i = 0; i < 3; i++) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, texture_id[i]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, i > 0 ? width / 2 : width, i > 0 ? height / 2 : height, GL_LUMINANCE, GL_UNSIGNED_BYTE, image[i]); + glUniform1i(texture_uniform[i], i); + } + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + + eglSwapBuffers(display, surface); +} + +void egl_destroy() { + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(display, surface); + eglDestroyContext(display, context); + eglTerminate(display); +} diff --git a/src/video/egl.h b/src/video/egl.h new file mode 100644 index 0000000..fe6b800 --- /dev/null +++ b/src/video/egl.h @@ -0,0 +1,24 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include + +void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_window, int display_width, int display_height); +void egl_draw(const uint8_t* image[3]); +void egl_destroy(); diff --git a/src/video/x11.c b/src/video/x11.c new file mode 100644 index 0000000..d855f8c --- /dev/null +++ b/src/video/x11.c @@ -0,0 +1,97 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include "../video.h" +#include "egl.h" +#include "ffmpeg.h" + +#include + +#include +#include +#include + +#include + +#define DECODER_BUFFER_SIZE 92*1024 + +static char* ffmpeg_buffer = NULL; + +static Display *display; + +void x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { + int avc_flags = SLICE_THREADING; + if (drFlags & FORCE_HARDWARE_ACCELERATION) + avc_flags |= HARDWARE_ACCELERATION; + + if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) { + fprintf(stderr, "Couldn't initialize video decoding\n"); + exit(1); + } + + ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); + if (ffmpeg_buffer == NULL) { + fprintf(stderr, "Not enough memory\n"); + exit(1); + } + + display = XOpenDisplay(NULL); + if (!display) { + fprintf(stderr, "Error: failed to open X display.\n"); + return; + } + + Window root = DefaultRootWindow(display); + XSetWindowAttributes winattr = {0}; + Window window = XCreateWindow(display, root, 0, 0, width, height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr); + XMapWindow(display, window); + XStoreName(display, window, "Moonlight"); + + egl_init(display, window, width, height); +} + +void x11_cleanup() { + ffmpeg_destroy(); + egl_destroy(); +} + +int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { + if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) { + PLENTRY entry = decodeUnit->bufferList; + int length = 0; + while (entry != NULL) { + memcpy(ffmpeg_buffer+length, entry->data, entry->length); + length += entry->length; + entry = entry->next; + } + ffmpeg_decode(ffmpeg_buffer, length); + AVFrame* frame = ffmpeg_get_frame(); + if (frame != NULL) + egl_draw(frame->data); + } + + return DR_OK; +} + +DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = { + .setup = x11_setup, + .cleanup = x11_cleanup, + .submitDecodeUnit = x11_submit_decode_unit, + .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION | CAPABILITY_DIRECT_SUBMIT, +}; From 89cca8bdc4564c19bb01d9d21d175531beef1980 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Thu, 25 May 2017 16:55:15 +0200 Subject: [PATCH 15/72] Allow X11 window to be fullscreen --- src/video/x11.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/video/x11.c b/src/video/x11.c index d855f8c..7a5dc6e 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -63,6 +63,22 @@ void x11_setup(int videoFormat, int width, int height, int redrawRate, void* con XMapWindow(display, window); XStoreName(display, window, "Moonlight"); + if (drFlags & DISPLAY_FULLSCREEN) { + Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); + + XEvent xev = {0}; + xev.type = ClientMessage; + xev.xclient.window = window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 1; + xev.xclient.data.l[1] = fullscreen; + xev.xclient.data.l[2] = 0; + + XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + } + egl_init(display, window, width, height); } From e4946b3717e04543df57643acabfc8360499f7f9 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Thu, 25 May 2017 17:08:15 +0200 Subject: [PATCH 16/72] Quit when close button is pressed --- src/video/x11.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/video/x11.c b/src/video/x11.c index 7a5dc6e..907bb31 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -18,6 +18,8 @@ */ #include "../video.h" +#include "../global.h" +#include "../loop.h" #include "egl.h" #include "ffmpeg.h" @@ -28,6 +30,7 @@ #include #include +#include #define DECODER_BUFFER_SIZE 92*1024 @@ -35,6 +38,20 @@ static char* ffmpeg_buffer = NULL; static Display *display; +static Atom wm_deletemessage; + +static int x11_handler(int fd) { + XEvent event; + XNextEvent(display, &event); + switch (event.type) { + case ClientMessage: + if (event.xclient.data.l[0] == wm_deletemessage) + quit(); + + break; + } +} + void x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; if (drFlags & FORCE_HARDWARE_ACCELERATION) @@ -79,7 +96,11 @@ void x11_setup(int videoFormat, int width, int height, int redrawRate, void* con XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } + wm_deletemessage = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, window, &wm_deletemessage, 1); + egl_init(display, window, width, height); + loop_add_fd(ConnectionNumber(display), x11_handler, POLLIN | POLLERR | POLLHUP); } void x11_cleanup() { From 8d4670339f2e36c94688cb4db9d9f5c1333ae518 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Thu, 25 May 2017 18:11:08 +0200 Subject: [PATCH 17/72] Add mouse and keyboard input for X11 --- CMakeLists.txt | 2 +- src/input/x11.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ src/input/x11.h | 22 ++++++++++ src/video/x11.c | 26 ++--------- 4 files changed, 140 insertions(+), 24 deletions(-) create mode 100644 src/input/x11.c create mode 100644 src/input/x11.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b7d6ca2..d061f0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,7 @@ if (SOFTWARE_FOUND) list(APPEND MOONLIGHT_OPTIONS SDL) endif() if (X11_FOUND) - list(APPEND SRC_LIST ./src/video/x11.c ./src/video/egl.c) + list(APPEND SRC_LIST ./src/video/x11.c ./src/video/egl.c ./src/input/x11.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_X11) list(APPEND MOONLIGHT_OPTIONS X11) endif() diff --git a/src/input/x11.c b/src/input/x11.c new file mode 100644 index 0000000..5f7055d --- /dev/null +++ b/src/input/x11.c @@ -0,0 +1,114 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include "x11.h" +#include "keyboard.h" +#include "../global.h" +#include "../loop.h" + +#include + +#include +#include + +#include + +static Display *display; + +static Atom wm_deletemessage; + +static int last_x = -1, last_y = -1; +static int keyboard_modifiers; + +static int x11_handler(int fd) { + XEvent event; + int button = 0; + + XNextEvent(display, &event); + switch (event.type) { + case KeyPress: + case KeyRelease: + if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) { + int modifier = 0; + switch (event.xkey.keycode) { + case XK_Shift_R: + case XK_Shift_L: + modifier = MODIFIER_SHIFT; + break; + case XK_Alt_R: + case XK_Alt_L: + modifier = MODIFIER_ALT; + break; + case XK_Control_R: + case XK_Control_L: + modifier = MODIFIER_CTRL; + break; + } + + if (modifier != 0) { + if (event.type == KeyPress) + keyboard_modifiers |= modifier; + else + keyboard_modifiers &= ~modifier; + } + + short code = 0x80 << 8 | keyCodes[event.xkey.keycode - 8]; + LiSendKeyboardEvent(code, event.type == KeyPress ? KEY_ACTION_DOWN : KEY_ACTION_UP, keyboard_modifiers); + } + break; + case ButtonPress: + case ButtonRelease: + switch (event.xbutton.button) { + case Button1: + button = BUTTON_LEFT; + break; + case Button2: + button = BUTTON_MIDDLE; + break; + case Button3: + button = BUTTON_RIGHT; + break; + } + + if (button != 0) + LiSendMouseButtonEvent(event.type==ButtonPress ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, button); + break; + case MotionNotify: + if (last_x >= 0 && last_y >= 0) { + LiSendMouseMoveEvent(event.xmotion.x - last_x, event.xmotion.y - last_y); + } + last_x = event.xmotion.x; + last_y = event.xmotion.y; + break; + case ClientMessage: + if (event.xclient.data.l[0] == wm_deletemessage) + quit(); + + break; + } +} + +void x11_input_init(Display* x11_display, Window window) { + display = x11_display; + + wm_deletemessage = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, window, &wm_deletemessage, 1); + + loop_add_fd(ConnectionNumber(display), x11_handler, POLLIN | POLLERR | POLLHUP); +} diff --git a/src/input/x11.h b/src/input/x11.h new file mode 100644 index 0000000..86f6f21 --- /dev/null +++ b/src/input/x11.h @@ -0,0 +1,22 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include + +void x11_input_init(Display* display, Window window); diff --git a/src/video/x11.c b/src/video/x11.c index 907bb31..6efc40c 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -18,19 +18,16 @@ */ #include "../video.h" -#include "../global.h" -#include "../loop.h" +#include "../input/x11.h" #include "egl.h" #include "ffmpeg.h" #include -#include #include #include #include -#include #define DECODER_BUFFER_SIZE 92*1024 @@ -38,20 +35,6 @@ static char* ffmpeg_buffer = NULL; static Display *display; -static Atom wm_deletemessage; - -static int x11_handler(int fd) { - XEvent event; - XNextEvent(display, &event); - switch (event.type) { - case ClientMessage: - if (event.xclient.data.l[0] == wm_deletemessage) - quit(); - - break; - } -} - void x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; if (drFlags & FORCE_HARDWARE_ACCELERATION) @@ -75,7 +58,7 @@ void x11_setup(int videoFormat, int width, int height, int redrawRate, void* con } Window root = DefaultRootWindow(display); - XSetWindowAttributes winattr = {0}; + XSetWindowAttributes winattr = { .event_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask }; Window window = XCreateWindow(display, root, 0, 0, width, height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr); XMapWindow(display, window); XStoreName(display, window, "Moonlight"); @@ -96,11 +79,8 @@ void x11_setup(int videoFormat, int width, int height, int redrawRate, void* con XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } - wm_deletemessage = XInternAtom(display, "WM_DELETE_WINDOW", False); - XSetWMProtocols(display, window, &wm_deletemessage, 1); - egl_init(display, window, width, height); - loop_add_fd(ConnectionNumber(display), x11_handler, POLLIN | POLLERR | POLLHUP); + x11_input_init(display, window); } void x11_cleanup() { From a30f0eec0553cc74aa81171d5183b49b791e2379 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Thu, 25 May 2017 20:36:06 +0200 Subject: [PATCH 18/72] Grab mouse on X11 --- src/input/x11.c | 54 +++++++++++++++++++++++++++++++++++++++---------- src/video/x11.c | 1 + 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/input/x11.c b/src/input/x11.c index 5f7055d..e661efe 100644 --- a/src/input/x11.c +++ b/src/input/x11.c @@ -27,36 +27,51 @@ #include #include +#include +#include #include +#define MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL) + static Display *display; +static Window window; static Atom wm_deletemessage; static int last_x = -1, last_y = -1; static int keyboard_modifiers; +static const char data[1] = {0}; +static Cursor cursor; +static bool grabbed = True; + static int x11_handler(int fd) { XEvent event; int button = 0; + int motion_x, motion_y; XNextEvent(display, &event); switch (event.type) { case KeyPress: case KeyRelease: if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) { + if ((keyboard_modifiers & MODIFIERS) == MODIFIERS && event.type == KeyRelease) { + grabbed = !grabbed; + XDefineCursor(display, window, grabbed ? cursor : NULL); + } + int modifier = 0; switch (event.xkey.keycode) { - case XK_Shift_R: - case XK_Shift_L: + case 0x32: + case 0x3e: modifier = MODIFIER_SHIFT; break; - case XK_Alt_R: - case XK_Alt_L: + case 0x40: + case 0x6c: modifier = MODIFIER_ALT; break; - case XK_Control_R: - case XK_Control_L: + case 0x25: + case 0x69: modifier = MODIFIER_CTRL; break; } @@ -90,11 +105,18 @@ static int x11_handler(int fd) { LiSendMouseButtonEvent(event.type==ButtonPress ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, button); break; case MotionNotify: - if (last_x >= 0 && last_y >= 0) { - LiSendMouseMoveEvent(event.xmotion.x - last_x, event.xmotion.y - last_y); + motion_x = event.xmotion.x - last_x; + motion_y = event.xmotion.y - last_y; + if (abs(motion_x) > 0 || abs(motion_y) > 0) { + if (last_x >= 0 && last_y >= 0) + LiSendMouseMoveEvent(motion_x, motion_y); + + if (grabbed) + XWarpPointer(display, None, window, 0, 0, 0, 0, 640, 360); } - last_x = event.xmotion.x; - last_y = event.xmotion.y; + + last_x = grabbed ? 640 : event.xmotion.x; + last_y = grabbed ? 360 : event.xmotion.y; break; case ClientMessage: if (event.xclient.data.l[0] == wm_deletemessage) @@ -102,13 +124,23 @@ static int x11_handler(int fd) { break; } + + return LOOP_OK; } -void x11_input_init(Display* x11_display, Window window) { +void x11_input_init(Display* x11_display, Window x11_window) { display = x11_display; + window = x11_window; wm_deletemessage = XInternAtom(display, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, window, &wm_deletemessage, 1); + /* make a blank cursor */ + XColor dummy; + Pixmap blank = XCreateBitmapFromData(display, window, data, 1, 1); + cursor = XCreatePixmapCursor(display, blank, blank, &dummy, &dummy, 0, 0); + XFreePixmap(display, blank); + XDefineCursor(display, window, cursor); + loop_add_fd(ConnectionNumber(display), x11_handler, POLLIN | POLLERR | POLLHUP); } diff --git a/src/video/x11.c b/src/video/x11.c index 6efc40c..d1c591e 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -51,6 +51,7 @@ void x11_setup(int videoFormat, int width, int height, int redrawRate, void* con exit(1); } + XInitThreads(); display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "Error: failed to open X display.\n"); From 7e73de80b3fcce814698202ccbbee6b955fbaa7b Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 16:10:59 +0200 Subject: [PATCH 19/72] Get additional server information --- libgamestream/client.c | 5 ++++- libgamestream/client.h | 3 ++- src/main.c | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index bc6c020..24ce1d4 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015-2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -226,6 +226,9 @@ static int load_server_status(PSERVER_DATA server) { if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK) goto cleanup; + if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK) + goto cleanup; + if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK) goto cleanup; diff --git a/libgamestream/client.h b/libgamestream/client.h index f8b821d..d1b8f69 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ typedef struct _SERVER_DATA { bool supports4K; int currentGame; int serverMajorVersion; + char* gsVersion; SERVER_INFORMATION serverInfo; } SERVER_DATA, *PSERVER_DATA; diff --git a/src/main.c b/src/main.c index 71c2696..9c45aee 100644 --- a/src/main.c +++ b/src/main.c @@ -227,7 +227,7 @@ int main(int argc, char* argv[]) { exit(-1); } - printf("NVIDIA %s, GFE %s (protocol version %d)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.serverMajorVersion); + printf("NVIDIA %s, GFE %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion); if (strcmp("list", config.action) == 0) { pair_check(&server); From 0e1cabbd82ba43304150037131b2613299a1a647 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 16:41:52 +0200 Subject: [PATCH 20/72] Check display mode before streaming --- libgamestream/client.c | 15 ++++++++++++ libgamestream/client.h | 1 + libgamestream/errors.h | 1 + libgamestream/xml.c | 52 ++++++++++++++++++++++++++++++++++++++++++ libgamestream/xml.h | 8 +++++++ src/main.c | 2 ++ 6 files changed, 79 insertions(+) diff --git a/libgamestream/client.c b/libgamestream/client.c index 24ce1d4..79c12f3 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -232,6 +232,9 @@ static int load_server_status(PSERVER_DATA server) { if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK) goto cleanup; + if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK) + goto cleanup; + // These fields are present on all version of GFE that this client supports if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText)) goto cleanup; @@ -641,6 +644,18 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b char* result = NULL; char uuid_str[37]; + PDISPLAY_MODE mode = server->modes; + bool correct_mode = false; + while (mode != NULL) { + if (mode->width == config->width && mode->height == config->height && mode->refresh == config->fps) + correct_mode = true; + + mode = mode->next; + } + + if (!correct_mode) + return GS_NOT_SUPPORTED_MODE; + if (config->height >= 2160 && !server->supports4K) return GS_NOT_SUPPORTED_4K; diff --git a/libgamestream/client.h b/libgamestream/client.h index d1b8f69..5591d70 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -36,6 +36,7 @@ typedef struct _SERVER_DATA { int currentGame; int serverMajorVersion; char* gsVersion; + PDISPLAY_MODE modes; SERVER_INFORMATION serverInfo; } SERVER_DATA, *PSERVER_DATA; diff --git a/libgamestream/errors.h b/libgamestream/errors.h index 99e3187..f657ae7 100644 --- a/libgamestream/errors.h +++ b/libgamestream/errors.h @@ -27,5 +27,6 @@ #define GS_IO_ERROR -5 #define GS_NOT_SUPPORTED_4K -6 #define GS_UNSUPPORTED_VERSION -7 +#define GS_NOT_SUPPORTED_MODE -8 const char* gs_error; diff --git a/libgamestream/xml.c b/libgamestream/xml.c index 9efa963..4e5e86e 100644 --- a/libgamestream/xml.c +++ b/libgamestream/xml.c @@ -79,6 +79,37 @@ static void XMLCALL _xml_end_applist_element(void *userData, const char *name) { } } +static void XMLCALL _xml_start_mode_element(void *userData, const char *name, const char **atts) { + struct xml_query *search = (struct xml_query*) userData; + if (strcmp("DisplayMode", name) == 0) { + PDISPLAY_MODE mode = calloc(1, sizeof(DISPLAY_MODE)); + if (mode != NULL) { + mode->next = (PDISPLAY_MODE) search->data; + search->data = mode; + } + } else if (search->data != NULL && (strcmp("Height", name) == 0 || strcmp("Width", name) == 0 || strcmp("RefreshRate", name) == 0)) { + search->memory = malloc(1); + search->size = 0; + search->start = 1; + } +} + +static void XMLCALL _xml_end_mode_element(void *userData, const char *name) { + struct xml_query *search = (struct xml_query*) userData; + if (search->data != NULL && search->start) { + PDISPLAY_MODE mode = (PDISPLAY_MODE) search->data; + if (strcmp("Width", name) == 0) + mode->width = atoi(search->memory); + else if (strcmp("Height", name) == 0) + mode->height = atoi(search->memory); + else if (strcmp("RefreshRate", name) == 0) + mode->refresh = atoi(search->memory); + + free(search->memory); + search->start = 0; + } +} + static void XMLCALL _xml_write_data(void *userData, const XML_Char *s, int len) { struct xml_query *search = (struct xml_query*) userData; if (search->start > 0) { @@ -141,3 +172,24 @@ int xml_applist(char* data, size_t len, PAPP_LIST *app_list) { return GS_OK; } + +int xml_modelist(char* data, size_t len, PDISPLAY_MODE *mode_list) { + struct xml_query query = {0}; + query.memory = calloc(1, 1); + XML_Parser parser = XML_ParserCreate("UTF-8"); + XML_SetUserData(parser, &query); + XML_SetElementHandler(parser, _xml_start_mode_element, _xml_end_mode_element); + XML_SetCharacterDataHandler(parser, _xml_write_data); + if (! XML_Parse(parser, data, len, 1)) { + int code = XML_GetErrorCode(parser); + gs_error = XML_ErrorString(code); + XML_ParserFree(parser); + return GS_INVALID; + } + + XML_ParserFree(parser); + *mode_list = (PDISPLAY_MODE) query.data; + + return GS_OK; + +} diff --git a/libgamestream/xml.h b/libgamestream/xml.h index 99f488c..aee4214 100644 --- a/libgamestream/xml.h +++ b/libgamestream/xml.h @@ -26,5 +26,13 @@ typedef struct _APP_LIST { struct _APP_LIST *next; } APP_LIST, *PAPP_LIST; +typedef struct _DISPLAY_MODE { + unsigned int height; + unsigned int width; + unsigned int refresh; + struct _DISPLAY_MODE *next; +} DISPLAY_MODE, *PDISPLAY_MODE; + int xml_search(char* data, size_t len, char* node, char** result); int xml_applist(char* data, size_t len, PAPP_LIST *app_list); +int xml_modelist(char* data, size_t len, PDISPLAY_MODE *mode_list); diff --git a/src/main.c b/src/main.c index 9c45aee..d07d678 100644 --- a/src/main.c +++ b/src/main.c @@ -88,6 +88,8 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys if (ret < 0) { if (ret == GS_NOT_SUPPORTED_4K) fprintf(stderr, "Server doesn't support 4K\n"); + else if (ret == GS_NOT_SUPPORTED_MODE) + fprintf(stderr, "Server doesn't support %dx%d (%d fps)\n", config->stream.width, config->stream.height, config->stream.fps); else fprintf(stderr, "Errorcode starting app: %d\n", ret); exit(-1); From fe41e046073288b531b0d8047477e15495262941 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 16:50:29 +0200 Subject: [PATCH 21/72] Add 4k command line option --- docs/README.pod | 4 ++++ src/config.c | 5 +++++ src/main.c | 1 + 3 files changed, 10 insertions(+) diff --git a/docs/README.pod b/docs/README.pod index fd48502..5bfb6e2 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -60,6 +60,10 @@ This is the default option. Use the resolution 1920x1080 for streaming. +=item B<-4k> + +Use the resolution 3840x2160 for streaming. + =item B<-width> [I] Change the horizontal resolution to I diff --git a/src/config.c b/src/config.c index da04502..d517069 100644 --- a/src/config.c +++ b/src/config.c @@ -42,6 +42,7 @@ const char* audio_device = NULL; static struct option long_options[] = { {"720", no_argument, NULL, 'a'}, {"1080", no_argument, NULL, 'b'}, + {"4k", no_argument, NULL, '0'}, {"width", required_argument, NULL, 'c'}, {"height", required_argument, NULL, 'd'}, {"30fps", no_argument, NULL, 'e'}, @@ -125,6 +126,10 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { config->stream.width = 1920; config->stream.height = 1080; break; + case '0': + config->stream.width = 3840; + config->stream.height = 2160; + break; case 'c': config->stream.width = atoi(value); break; diff --git a/src/main.c b/src/main.c index d07d678..c49e586 100644 --- a/src/main.c +++ b/src/main.c @@ -134,6 +134,7 @@ static void help() { printf("\n Streaming options\n\n"); printf("\t-720\t\t\tUse 1280x720 resolution [default]\n"); printf("\t-1080\t\t\tUse 1920x1080 resolution\n"); + printf("\t-4k\t\t\t\tUse 3840x2160 resolution\n"); printf("\t-width \t\tHorizontal resolution (default 1280)\n"); printf("\t-height \tVertical resolution (default 720)\n"); printf("\t-30fps\t\t\tUse 30fps\n"); From 1d47b9205e8daae45d98df35e7d4b475ccf98ec7 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 16:53:40 +0200 Subject: [PATCH 22/72] Default to 30 fps for 1080p and higher --- docs/README.pod | 3 ++- src/config.c | 5 ++++- src/main.c | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 5bfb6e2..31f36e3 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -75,11 +75,12 @@ Change the vertical resolution to I =item B<-30fps> Use 30 fps for streaming. +This is the default configuration for 1080p and higher. =item B<-60fps> Use 60 fps for streaming. -This is the default configuration. +This is the default configuration for 720p. =item B<-fps> [I] diff --git a/src/config.c b/src/config.c index d517069..e843da8 100644 --- a/src/config.c +++ b/src/config.c @@ -290,7 +290,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->stream.width = 1280; config->stream.height = 720; - config->stream.fps = 60; + config->stream.fps = -1; config->stream.bitrate = -1; config->stream.packetSize = 1024; config->stream.streamingRemotely = 0; @@ -343,6 +343,9 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { } } + if (config->stream.fps == -1) + config->stream.fps = config->stream.height >= 1080 ? 30 : 60; + if (config->stream.bitrate == -1) { if (config->stream.height >= 1080 && config->stream.fps >= 60) config->stream.bitrate = 20000; diff --git a/src/main.c b/src/main.c index c49e586..5a47485 100644 --- a/src/main.c +++ b/src/main.c @@ -138,7 +138,7 @@ static void help() { printf("\t-width \t\tHorizontal resolution (default 1280)\n"); printf("\t-height \tVertical resolution (default 720)\n"); printf("\t-30fps\t\t\tUse 30fps\n"); - printf("\t-60fps\t\t\tUse 60fps [default]\n"); + printf("\t-60fps\t\t\tUse 60fps\n"); printf("\t-bitrate \tSpecify the bitrate in Kbps\n"); printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); printf("\t-hevc\t\t\tUse the high efficiency video coding (HEVC)\n"); From 764ece15e2a62fed67f17b8275c1c0834494f49f Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 16:56:20 +0200 Subject: [PATCH 23/72] Correct 4k server capability check --- libgamestream/client.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 79c12f3..4e4179d 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -179,7 +179,6 @@ static int load_server_status(PSERVER_DATA server) { char *pairedText = NULL; char *currentGameText = NULL; char *stateText = NULL; - char *heightText = NULL; char *serverCodecModeSupportText = NULL; ret = GS_INVALID; @@ -217,9 +216,6 @@ static int load_server_status(PSERVER_DATA server) { if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK) goto cleanup; - if (xml_search(data->memory, data->size, "Height", &heightText) != GS_OK) - goto cleanup; - if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK) goto cleanup; @@ -241,7 +237,7 @@ static int load_server_status(PSERVER_DATA server) { server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0; server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText); - server->supports4K = heightText != NULL && serverCodecModeSupportText != NULL && atoi(heightText) >= 2160; + server->supports4K = serverCodecModeSupportText != NULL; server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion); if (strstr(stateText, "_SERVER_AVAILABLE")) { @@ -262,9 +258,6 @@ static int load_server_status(PSERVER_DATA server) { if (currentGameText != NULL) free(currentGameText); - if (heightText != NULL) - free(heightText); - if (serverCodecModeSupportText != NULL) free(serverCodecModeSupportText); From 3461d1711971e83136d2b3e5cc9e537589ec6e63 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 17:06:03 +0200 Subject: [PATCH 24/72] Remove saving to file for fake platform --- CMakeLists.txt | 2 +- src/audio/fake.c | 44 ------------------------------------------- src/platform.c | 4 ---- src/platform.h | 2 -- src/video/fake.c | 49 ------------------------------------------------ 5 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 src/audio/fake.c delete mode 100644 src/video/fake.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 05a4d57..e59ed19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,7 @@ endif() include_directories("${PROJECT_BINARY_DIR}") -list(APPEND SRC_LIST ./src/audio/alsa.c ./src/audio/fake.c ./src/video/fake.c) +list(APPEND SRC_LIST ./src/audio/alsa.c) add_subdirectory(libgamestream) diff --git a/src/audio/fake.c b/src/audio/fake.c deleted file mode 100644 index d0c674e..0000000 --- a/src/audio/fake.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of Moonlight Embedded. - * - * Copyright (C) 2016 Iwan Timmer - * - * Moonlight is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Moonlight is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Moonlight; if not, see . - */ - -#include "../audio.h" - -#include - -static FILE* fd; -static const char* fileName = "fake.opus"; - -static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { - fd = fopen(fileName, "w"); -} - -static void alsa_renderer_cleanup() { - fclose(fd); -} - -static void alsa_renderer_decode_and_play_sample(char* data, int length) { - fwrite(data, length, 1, fd); -} - -AUDIO_RENDERER_CALLBACKS audio_callbacks_fake = { - .init = alsa_renderer_init, - .cleanup = alsa_renderer_cleanup, - .decodeAndPlaySample = alsa_renderer_decode_and_play_sample, - .capabilities = CAPABILITY_DIRECT_SUBMIT, -}; diff --git a/src/platform.c b/src/platform.c index e4ea788..1320d0c 100644 --- a/src/platform.c +++ b/src/platform.c @@ -91,8 +91,6 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) { case AML: return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml"); #endif - case FAKE: - return &decoder_callbacks_fake; } return NULL; } @@ -114,8 +112,6 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { return &audio_callbacks_pulse; #endif return &audio_callbacks_alsa; - case FAKE: - return &audio_callbacks_fake; } return NULL; } diff --git a/src/platform.h b/src/platform.h index d9aa03e..60ffd30 100644 --- a/src/platform.h +++ b/src/platform.h @@ -33,8 +33,6 @@ PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system); bool platform_supports_hevc(enum platform system); -extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake; -extern AUDIO_RENDERER_CALLBACKS audio_callbacks_fake; #ifdef HAVE_X11 extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; #endif diff --git a/src/video/fake.c b/src/video/fake.c deleted file mode 100644 index a9e3e3d..0000000 --- a/src/video/fake.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of Moonlight Embedded. - * - * Copyright (C) 2015 Iwan Timmer - * - * Moonlight is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Moonlight is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Moonlight; if not, see . - */ - -#include - -#include - -static FILE* fd; -static const char* fileName = "fake.h264"; - -void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - fd = fopen(fileName, "w"); -} - -void decoder_renderer_cleanup() { - fclose(fd); -} - -int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) { - PLENTRY entry = decodeUnit->bufferList; - while (entry != NULL) { - fwrite(entry->data, entry->length, 1, fd); - entry = entry->next; - } - return DR_OK; -} - -DECODER_RENDERER_CALLBACKS decoder_callbacks_fake = { - .setup = decoder_renderer_setup, - .cleanup = decoder_renderer_cleanup, - .submitDecodeUnit = decoder_renderer_submit_decode_unit, - .capabilities = CAPABILITY_DIRECT_SUBMIT, -}; From bbeffe2782aafdcb35db317273bd688094546da4 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 17:27:58 +0200 Subject: [PATCH 25/72] Make Alsa audio backend optional --- CMakeLists.txt | 17 +++++++++++++---- src/audio.h | 2 ++ src/platform.c | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e59ed19..eff1116 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ aux_source_directory(./src/input SRC_LIST) set(MOONLIGHT_DEFINITIONS) -find_package(ALSA REQUIRED) +find_package(ALSA) find_package(Opus REQUIRED) find_package(Broadcom) find_package(Freescale) @@ -82,6 +82,12 @@ if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR CMAKE_BUILD_TYPE MATCH list(APPEND MOONLIGHT_OPTIONS EMBEDDED) endif() +if (ALSA_FOUND) + list(APPEND SRC_LIST ./src/audio/alsa.c) + list(APPEND MOONLIGHT_DEFINITIONS HAVE_ALSA) + list(APPEND MOONLIGHT_OPTIONS ALSA) +endif() + if (PULSE_FOUND) list(APPEND SRC_LIST ./src/audio/pulse.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_PULSE) @@ -90,8 +96,6 @@ endif() include_directories("${PROJECT_BINARY_DIR}") -list(APPEND SRC_LIST ./src/audio/alsa.c) - add_subdirectory(libgamestream) add_executable(moonlight ${SRC_LIST}) @@ -154,6 +158,11 @@ if (SOFTWARE_FOUND) endif() endif() +if (ALSA_FOUND) + target_include_directories(moonlight PRIVATE ${ALSA_INCLUDE_DIR}) + target_link_libraries(moonlight ${ALSA_LIBRARY}) +endif() + if (PULSE_FOUND) target_include_directories(moonlight PRIVATE ${PULSE_INCLUDE_DIRS}) target_link_libraries(moonlight ${PULSE_LIBRARIES}) @@ -163,7 +172,7 @@ configure_file("./src/configuration.h.in" "${PROJECT_BINARY_DIR}/configuration.h set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${MOONLIGHT_DEFINITIONS}) target_include_directories(moonlight PRIVATE ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS} ${EVDEV_INCLUDE_DIRS} ${UDEV_INCLUDE_DIRS}) -target_link_libraries(moonlight ${EVDEV_LIBRARIES} ${ALSA_LIBRARY} ${OPUS_LIBRARY} ${UDEV_LIBRARIES} ${CMAKE_DL_LIBS}) +target_link_libraries(moonlight ${EVDEV_LIBRARIES} ${OPUS_LIBRARY} ${UDEV_LIBRARIES} ${CMAKE_DL_LIBS}) add_subdirectory(docs) diff --git a/src/audio.h b/src/audio.h index ad485fa..e4f1359 100644 --- a/src/audio.h +++ b/src/audio.h @@ -27,7 +27,9 @@ extern const char* audio_device; +#ifdef HAVE_ALSA extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa; +#endif #ifdef HAVE_SDL extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl; #endif diff --git a/src/platform.c b/src/platform.c index 1320d0c..58e1332 100644 --- a/src/platform.c +++ b/src/platform.c @@ -111,7 +111,9 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { if (audio_pulse_init()) return &audio_callbacks_pulse; #endif + #ifdef HAVE_ALSA return &audio_callbacks_alsa; + #endif } return NULL; } From 6139d3bc9c63c1922fb5020b72daff89f74a6ee1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 17:28:47 +0200 Subject: [PATCH 26/72] Correct header for sps fixes --- libgamestream/sps.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgamestream/sps.h b/libgamestream/sps.h index 9123b6f..ed55c3f 100644 --- a/libgamestream/sps.h +++ b/libgamestream/sps.h @@ -22,5 +22,5 @@ #define GS_SPS_BITSTREAM_FIXUP 0x01 #define GS_SPS_BASELINE_HACK 0x02 -void gs_sps_init(); +void gs_sps_init(int width, int height); PLENTRY gs_sps_fix(PLENTRY *entry, int flags); From 3134046bae0d6f6e1382424fdbe3832e4bbae7ce Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 17:45:50 +0200 Subject: [PATCH 27/72] Cleanup header files --- src/audio.h | 3 --- src/platform.c | 3 ++- src/platform.h | 10 +--------- src/video.h | 11 ++++++++++- src/video/sdl.c | 2 -- src/video/x11.c | 2 -- 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/audio.h b/src/audio.h index e4f1359..f99d047 100644 --- a/src/audio.h +++ b/src/audio.h @@ -37,6 +37,3 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl; extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse; bool audio_pulse_init(); #endif -#ifdef HAVE_PI -extern AUDIO_RENDERER_CALLBACKS audio_callbacks_omx; -#endif diff --git a/src/platform.c b/src/platform.c index 58e1332..9441078 100644 --- a/src/platform.c +++ b/src/platform.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #include "platform.h" #include "audio.h" +#include "video.h" #include #include diff --git a/src/platform.h b/src/platform.h index 60ffd30..c271834 100644 --- a/src/platform.h +++ b/src/platform.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,11 +32,3 @@ enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system); bool platform_supports_hevc(enum platform system); - -#ifdef HAVE_X11 -extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; -#endif -#ifdef HAVE_SDL -extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl; -void sdl_loop(); -#endif diff --git a/src/video.h b/src/video.h index f258896..17360d6 100644 --- a/src/video.h +++ b/src/video.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,5 +17,14 @@ * along with Moonlight; if not, see . */ +#include + #define DISPLAY_FULLSCREEN 1 #define FORCE_HARDWARE_ACCELERATION 2 + +#ifdef HAVE_X11 +extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; +#endif +#ifdef HAVE_SDL +extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl; +#endif diff --git a/src/video/sdl.c b/src/video/sdl.c index e9ac2b2..96f1811 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -21,8 +21,6 @@ #include "../sdl.h" #include "ffmpeg.h" -#include - #include #include diff --git a/src/video/x11.c b/src/video/x11.c index d1c591e..3b1c3b0 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -22,8 +22,6 @@ #include "egl.h" #include "ffmpeg.h" -#include - #include #include From 85cc4da449f2d122f908ed9f685014170192a690 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 27 May 2017 21:43:20 +0200 Subject: [PATCH 28/72] Input methods seperatly added in CMakeLists --- CMakeLists.txt | 10 +++++++--- src/input/cec.c | 3 --- src/input/{sdlinput.c => sdl.c} | 6 +----- src/input/{sdlinput.h => sdl.h} | 4 ---- src/main.c | 6 +++++- src/sdl.c | 2 +- 6 files changed, 14 insertions(+), 17 deletions(-) rename src/input/{sdlinput.c => sdl.c} (99%) rename src/input/{sdlinput.h => sdl.h} (98%) diff --git a/CMakeLists.txt b/CMakeLists.txt index eff1116..ff325dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ set(MOONLIGHT_PATCH_VERSION 0) set(MOONLIGHT_VERSION ${MOONLIGHT_MAJOR_VERSION}.${MOONLIGHT_MINOR_VERSION}.${MOONLIGHT_PATCH_VERSION}) aux_source_directory(./src SRC_LIST) -aux_source_directory(./src/input SRC_LIST) +list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c) set(MOONLIGHT_DEFINITIONS) @@ -25,7 +25,7 @@ pkg_check_modules(UDEV REQUIRED libudev) pkg_check_modules(SDL sdl2>=2.0.4) pkg_check_modules(AVCODEC libavcodec) pkg_check_modules(AVUTIL libavutil) -pkg_check_modules(XLIB x11-xcb) +pkg_check_modules(XLIB x11) pkg_check_modules(LIBVA vdpau) pkg_check_modules(PULSE libpulse-simple) pkg_check_modules(CEC libcec>=3.0.0) @@ -61,7 +61,7 @@ endif() if (SOFTWARE_FOUND) list(APPEND SRC_LIST ./src/video/ffmpeg.c) if (SDL_FOUND) - list(APPEND SRC_LIST ./src/video/sdl.c ./src/audio/sdl.c) + list(APPEND SRC_LIST ./src/video/sdl.c ./src/audio/sdl.c ./src/input/sdl.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL) list(APPEND MOONLIGHT_OPTIONS SDL) endif() @@ -94,6 +94,10 @@ if (PULSE_FOUND) list(APPEND MOONLIGHT_OPTIONS PULSE) endif() +if (CEC_FOUND) + list(APPEND SRC_LIST ./src/input/cec.c) +endif() + include_directories("${PROJECT_BINARY_DIR}") add_subdirectory(libgamestream) diff --git a/src/input/cec.c b/src/input/cec.c index 58ec12b..342ef27 100644 --- a/src/input/cec.c +++ b/src/input/cec.c @@ -17,8 +17,6 @@ * along with Moonlight; if not, see . */ -#ifdef HAVE_LIBCEC - #include #include @@ -111,4 +109,3 @@ void cec_init() { g_iface.set_active_source(g_iface.connection, g_config.deviceTypes.types[0]); } -#endif /* HAVE_LIBCEC */ diff --git a/src/input/sdlinput.c b/src/input/sdl.c similarity index 99% rename from src/input/sdlinput.c rename to src/input/sdl.c index 26fab78..950af43 100644 --- a/src/input/sdlinput.c +++ b/src/input/sdl.c @@ -17,9 +17,7 @@ * along with Moonlight; if not, see . */ -#ifdef HAVE_SDL - -#include "sdlinput.h" +#include "sdl.h" #include "../sdl.h" #include @@ -239,5 +237,3 @@ int sdlinput_handle_event(SDL_Event* event) { } return SDL_NOTHING; } - -#endif /* HAVE_SDL */ diff --git a/src/input/sdlinput.h b/src/input/sdl.h similarity index 98% rename from src/input/sdlinput.h rename to src/input/sdl.h index 8b2b23b..9157680 100644 --- a/src/input/sdlinput.h +++ b/src/input/sdl.h @@ -17,8 +17,6 @@ * along with Moonlight; if not, see . */ -#ifdef HAVE_SDL - #include #include @@ -101,5 +99,3 @@ static const short keyCodes5[] = { void sdlinput_init(char* mappings); int sdlinput_handle_event(SDL_Event* event); - -#endif /* HAVE_SDL */ diff --git a/src/main.c b/src/main.c index 5a47485..55a3578 100644 --- a/src/main.c +++ b/src/main.c @@ -31,8 +31,12 @@ #include "input/mapping.h" #include "input/evdev.h" #include "input/udev.h" +#ifdef HAVE_CEC #include "input/cec.h" -#include "input/sdlinput.h" +#endif +#ifdef HAVE_SDL +#include "input/sdl.h" +#endif #include diff --git a/src/sdl.c b/src/sdl.c index 0b1342c..a49d02e 100644 --- a/src/sdl.c +++ b/src/sdl.c @@ -20,7 +20,7 @@ #ifdef HAVE_SDL #include "sdl.h" -#include "input/sdlinput.h" +#include "input/sdl.h" #include From b8d561a62d0c83f2a70aaf761f92fbf69d3fc617 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 12:50:09 +0200 Subject: [PATCH 29/72] Correctly handle sticks when using evdev input stack --- src/input/evdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/evdev.c b/src/input/evdev.c index 1879e82..0501d7a 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -308,7 +308,6 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) if (hat_index == dev->map->hat_dpleft) set_hat(dev->buttonFlags, HAT_LEFT, hat_state, dev->map->hat_dir_dpleft); break; - } default: if (index == dev->map->abs_leftx) dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map->reverse_leftx); @@ -324,6 +323,7 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms); else gamepadModified = false; + } } dev->gamepadModified |= gamepadModified; From a7e3aa354e3c36af11d5288147468584801b7339 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 12:51:12 +0200 Subject: [PATCH 30/72] Don't block on X11 if there are no events --- src/input/x11.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/input/x11.c b/src/input/x11.c index e661efe..b9a2c57 100644 --- a/src/input/x11.c +++ b/src/input/x11.c @@ -19,7 +19,6 @@ #include "x11.h" #include "keyboard.h" -#include "../global.h" #include "../loop.h" #include @@ -46,6 +45,9 @@ static Cursor cursor; static bool grabbed = True; static int x11_handler(int fd) { + if (!XPending(display)) + return LOOP_OK; + XEvent event; int button = 0; int motion_x, motion_y; @@ -120,7 +122,7 @@ static int x11_handler(int fd) { break; case ClientMessage: if (event.xclient.data.l[0] == wm_deletemessage) - quit(); + return LOOP_RETURN; break; } From 49a447c3d44af7c3a7f6fab892d99cae5a1614b5 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 13:17:40 +0200 Subject: [PATCH 31/72] Combine 'global' with connection code --- src/connection.c | 11 +++++++---- src/connection.h | 5 ++++- src/global.c | 27 --------------------------- src/global.h | 24 ------------------------ src/input/evdev.c | 1 - src/loop.c | 2 +- 6 files changed, 12 insertions(+), 58 deletions(-) delete mode 100644 src/global.c delete mode 100644 src/global.h diff --git a/src/connection.c b/src/connection.c index f25a508..9838350 100644 --- a/src/connection.c +++ b/src/connection.c @@ -18,12 +18,15 @@ */ #include "connection.h" -#include "global.h" #include +#include -static void connection_connection_terminated() { - quit(); +pthread_t main_thread_id = 0; + +static void connection_terminated() { + if (main_thread_id != 0) + pthread_kill(main_thread_id, SIGTERM); } static void connection_display_message(const char *msg) { @@ -39,7 +42,7 @@ CONNECTION_LISTENER_CALLBACKS connection_callbacks = { .stageComplete = NULL, .stageFailed = NULL, .connectionStarted = NULL, - .connectionTerminated = connection_connection_terminated, + .connectionTerminated = connection_terminated, .displayMessage = connection_display_message, .displayTransientMessage = connection_display_transient_message, }; diff --git a/src/connection.h b/src/connection.h index 8b985b6..009e787 100644 --- a/src/connection.h +++ b/src/connection.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,4 +19,7 @@ #include +#include + extern CONNECTION_LISTENER_CALLBACKS connection_callbacks; +extern pthread_t main_thread_id; diff --git a/src/global.c b/src/global.c deleted file mode 100644 index c5992ba..0000000 --- a/src/global.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file is part of Moonlight Embedded. - * - * Copyright (C) 2015 Iwan Timmer - * - * Moonlight is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Moonlight is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Moonlight; if not, see . - */ - -#include -#include - -pthread_t main_thread_id; - -void quit() { - pthread_kill(main_thread_id, SIGTERM); -} diff --git a/src/global.h b/src/global.h deleted file mode 100644 index f558716..0000000 --- a/src/global.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This file is part of Moonlight Embedded. - * - * Copyright (C) 2015 Iwan Timmer - * - * Moonlight is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * Moonlight is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Moonlight; if not, see . - */ - -#include - -extern pthread_t main_thread_id; - -void quit(); diff --git a/src/input/evdev.c b/src/input/evdev.c index 0501d7a..2adf713 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -18,7 +18,6 @@ */ #include "../loop.h" -#include "../global.h" #include "keyboard.h" #include "mapping.h" diff --git a/src/loop.c b/src/loop.c index 10f80ba..c7b68f4 100644 --- a/src/loop.c +++ b/src/loop.c @@ -19,7 +19,7 @@ #include "loop.h" -#include "global.h" +#include "connection.h" #include #include From be2b44f124415d283e8d4ee41b8e3bf3cae3b4e3 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 13:18:57 +0200 Subject: [PATCH 32/72] Cleanup import of headers --- src/audio/alsa.c | 2 +- src/{ => audio}/audio.h | 0 src/audio/omx.c | 2 +- src/audio/pulse.c | 2 +- src/audio/sdl.c | 2 +- src/config.c | 5 +++-- src/input/x11.c | 1 + src/main.c | 10 ++++++---- src/platform.c | 5 +++-- src/video/imx.c | 3 ++- src/video/pi.c | 4 ++-- src/video/sdl.c | 5 +++-- src/{ => video}/video.h | 0 src/video/x11.c | 5 +++-- 14 files changed, 27 insertions(+), 19 deletions(-) rename src/{ => audio}/audio.h (100%) rename src/{ => video}/video.h (100%) diff --git a/src/audio/alsa.c b/src/audio/alsa.c index dfbe98e..a1fbc85 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "../audio.h" +#include "audio.h" #include #include diff --git a/src/audio.h b/src/audio/audio.h similarity index 100% rename from src/audio.h rename to src/audio/audio.h diff --git a/src/audio/omx.c b/src/audio/omx.c index 1c21427..dde2e66 100644 --- a/src/audio/omx.c +++ b/src/audio/omx.c @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "../audio.h" +#include "audio.h" #include diff --git a/src/audio/pulse.c b/src/audio/pulse.c index 9322502..17429cb 100644 --- a/src/audio/pulse.c +++ b/src/audio/pulse.c @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "../audio.h" +#include "audio.h" #include #include diff --git a/src/audio/sdl.c b/src/audio/sdl.c index 35c65ba..f5021af 100644 --- a/src/audio/sdl.c +++ b/src/audio/sdl.c @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "../audio.h" +#include "audio.h" #include #include diff --git a/src/config.c b/src/config.c index e843da8..64ee138 100644 --- a/src/config.c +++ b/src/config.c @@ -17,9 +17,10 @@ * along with Moonlight; if not, see . */ -#include "input/evdev.h" #include "config.h" -#include "audio.h" + +#include "input/evdev.h" +#include "audio/audio.h" #include #include diff --git a/src/input/x11.c b/src/input/x11.c index b9a2c57..1e51579 100644 --- a/src/input/x11.c +++ b/src/input/x11.c @@ -19,6 +19,7 @@ #include "x11.h" #include "keyboard.h" + #include "../loop.h" #include diff --git a/src/main.c b/src/main.c index 55a3578..addbcea 100644 --- a/src/main.c +++ b/src/main.c @@ -18,16 +18,15 @@ */ #include "loop.h" -#include "client.h" #include "connection.h" #include "configuration.h" -#include "audio.h" -#include "video.h" -#include "discover.h" #include "config.h" #include "platform.h" #include "sdl.h" +#include "audio/audio.h" +#include "video/video.h" + #include "input/mapping.h" #include "input/evdev.h" #include "input/udev.h" @@ -40,6 +39,9 @@ #include +#include +#include + #include #include #include diff --git a/src/platform.c b/src/platform.c index 9441078..404eb7d 100644 --- a/src/platform.c +++ b/src/platform.c @@ -20,8 +20,9 @@ #define _GNU_SOURCE #include "platform.h" -#include "audio.h" -#include "video.h" + +#include "audio/audio.h" +#include "video/video.h" #include #include diff --git a/src/video/imx.c b/src/video/imx.c index 83979b0..18587c9 100644 --- a/src/video/imx.c +++ b/src/video/imx.c @@ -17,9 +17,10 @@ * along with Moonlight; if not, see . */ -#include "../loop.h" #include "imx_vpu.h" +#include "../loop.h" + #include #include #include diff --git a/src/video/pi.c b/src/video/pi.c index c433e08..8818c34 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -28,10 +28,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Video decode on Raspberry Pi using OpenMAX IL though the ilcient helper library // Based upon video decode example from the Raspberry Pi firmware -#include "sps.h" - #include +#include + #include #include #include diff --git a/src/video/sdl.c b/src/video/sdl.c index 96f1811..c539613 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -17,10 +17,11 @@ * along with Moonlight; if not, see . */ -#include "../video.h" -#include "../sdl.h" +#include "video.h" #include "ffmpeg.h" +#include "../sdl.h" + #include #include diff --git a/src/video.h b/src/video/video.h similarity index 100% rename from src/video.h rename to src/video/video.h diff --git a/src/video/x11.c b/src/video/x11.c index 3b1c3b0..03de6aa 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -17,11 +17,12 @@ * along with Moonlight; if not, see . */ -#include "../video.h" -#include "../input/x11.h" +#include "video.h" #include "egl.h" #include "ffmpeg.h" +#include "../input/x11.h" + #include #include From 6a8350caa57c8ac9045b4ccc79270ea16a6676bf Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 14:39:49 +0200 Subject: [PATCH 33/72] No longer use deprecated RSA generation function --- libgamestream/mkcert.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libgamestream/mkcert.c b/libgamestream/mkcert.c index e3f6b9e..4ced6f6 100644 --- a/libgamestream/mkcert.c +++ b/libgamestream/mkcert.c @@ -104,7 +104,18 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) { x = *x509p; } - rsa = RSA_generate_key(bits, RSA_F4, NULL, NULL); + BIGNUM* bne = BN_new(); + if (bne == NULL) { + abort(); + goto err; + } + + BN_set_word(bne, RSA_F4); + if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) { + abort(); + goto err; + } + if (!EVP_PKEY_assign_RSA(pk, rsa)) { abort(); goto err; From 91d9aef6ff4e372d0addcfed4c4fcbc5dd12d8ea Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 14:42:24 +0200 Subject: [PATCH 34/72] Correct libcec include --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index addbcea..bfc3538 100644 --- a/src/main.c +++ b/src/main.c @@ -30,7 +30,7 @@ #include "input/mapping.h" #include "input/evdev.h" #include "input/udev.h" -#ifdef HAVE_CEC +#ifdef HAVE_LIBCEC #include "input/cec.h" #endif #ifdef HAVE_SDL From 343e3992c182f402e3ffafaf759f9c2c54314900 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 14:46:21 +0200 Subject: [PATCH 35/72] Solve compile warnings --- src/input/evdev.c | 5 +++-- src/input/evdev.h | 2 ++ src/input/mapping.c | 2 +- src/input/x11.c | 2 +- src/video/x11.c | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/input/evdev.c b/src/input/evdev.c index 2adf713..e26fbbd 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -17,10 +17,11 @@ * along with Moonlight; if not, see . */ -#include "../loop.h" +#include "evdev.h" #include "keyboard.h" -#include "mapping.h" + +#include "../loop.h" #include "libevdev/libevdev.h" #include diff --git a/src/input/evdev.h b/src/input/evdev.h index e52319a..c3330e4 100644 --- a/src/input/evdev.h +++ b/src/input/evdev.h @@ -17,6 +17,8 @@ * along with Moonlight; if not, see . */ +#include "mapping.h" + void evdev_create(const char* device, struct mapping* mappings); void evdev_loop(); diff --git a/src/input/mapping.c b/src/input/mapping.c index dd357ba..029f1a7 100644 --- a/src/input/mapping.c +++ b/src/input/mapping.c @@ -61,7 +61,7 @@ struct mapping* mapping_load(char* fileName) { int ret; if ((ret = sscanf(option, "%m[^:]:%ms", &key, &value)) == 2) { int int_value, direction_value; - char flag = NULL; + char flag = 0; if (strcmp("platform", key) == 0) strncpy(map->platform, value, sizeof(map->platform)); else if (sscanf(value, "b%d", &int_value) == 1) { diff --git a/src/input/x11.c b/src/input/x11.c index 1e51579..5f76c53 100644 --- a/src/input/x11.c +++ b/src/input/x11.c @@ -60,7 +60,7 @@ static int x11_handler(int fd) { if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) { if ((keyboard_modifiers & MODIFIERS) == MODIFIERS && event.type == KeyRelease) { grabbed = !grabbed; - XDefineCursor(display, window, grabbed ? cursor : NULL); + XDefineCursor(display, window, grabbed ? cursor : 0); } int modifier = 0; diff --git a/src/video/x11.c b/src/video/x11.c index 03de6aa..38f2834 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -100,7 +100,7 @@ int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { ffmpeg_decode(ffmpeg_buffer, length); AVFrame* frame = ffmpeg_get_frame(); if (frame != NULL) - egl_draw(frame->data); + egl_draw((const unsigned char**) frame->data); } return DR_OK; From a0462e49b827b42148da9bcc86736917dbea37d4 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 14:56:36 +0200 Subject: [PATCH 36/72] Update Moonlight-common-c --- src/audio/alsa.c | 6 ++++-- src/audio/omx.c | 18 ++++++++++-------- src/audio/pulse.c | 6 ++++-- src/audio/sdl.c | 5 ++++- src/video/aml.c | 8 ++++---- src/video/imx.c | 26 +++++++++++++------------- src/video/pi.c | 20 +++++++++++--------- src/video/sdl.c | 11 +++++++---- src/video/x11.c | 13 ++++++++----- third_party/moonlight-common-c | 2 +- 10 files changed, 66 insertions(+), 49 deletions(-) diff --git a/src/audio/alsa.c b/src/audio/alsa.c index a1fbc85..c30ab3b 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -23,13 +23,13 @@ #include #include -#define CHECK_RETURN(f) if ((rc = f) < 0) { printf("Alsa error code %d\n", rc); exit(-1); } +#define CHECK_RETURN(f) if ((rc = f) < 0) { printf("Alsa error code %d\n", rc); return -1; } static snd_pcm_t *handle; static OpusMSDecoder* decoder; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; -static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc; unsigned char alsaMapping[MAX_CHANNEL_COUNT]; @@ -81,6 +81,8 @@ static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU snd_pcm_sw_params_free(sw_params); CHECK_RETURN(snd_pcm_prepare(handle)); + + return 0; } static void alsa_renderer_cleanup() { diff --git a/src/audio/omx.c b/src/audio/omx.c index dde2e66..a6c76f6 100644 --- a/src/audio/omx.c +++ b/src/audio/omx.c @@ -32,7 +32,7 @@ static OMX_BUFFERHEADERTYPE *buf; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static int channelCount; -static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc, error; OMX_ERRORTYPE err; unsigned char omxMapping[MAX_CHANNEL_COUNT]; @@ -54,17 +54,17 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR handle = ilclient_init(); if (handle == NULL) { fprintf(stderr, "IL client init failed\n"); - exit(1); + return -1; } if (ilclient_create_component(handle, &component, componentName, ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) { fprintf(stderr, "Component create failed\n"); - exit(1); + return -1; } if (ilclient_change_component_state(component, OMX_StateIdle)!= 0) { fprintf(stderr, "Couldn't change state to Idle\n"); - exit(1); + return -1; } // must be before we enable buffers @@ -119,7 +119,7 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPcm, &sPCMMode); if(err != OMX_ErrorNone){ fprintf(stderr, "PCM mode unsupported\n"); - return; + return -1; } OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest; @@ -136,7 +136,7 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexConfigBrcmAudioDestination, &arDest); if (err != OMX_ErrorNone) { fprintf(stderr, "Error on setting audio destination\nomx option must be set to hdmi or local\n"); - exit(1); + return -1; } } @@ -146,9 +146,11 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR err = ilclient_change_component_state(component, OMX_StateExecuting); if (err < 0) { - fprintf(stderr, "Couldn't change state to Executing\n"); - exit(1); + fprintf(stderr, "Couldn't change state to Executing\n"); + return -1; } + + return 0; } static void omx_renderer_cleanup() { diff --git a/src/audio/pulse.c b/src/audio/pulse.c index 17429cb..0cc0a4f 100644 --- a/src/audio/pulse.c +++ b/src/audio/pulse.c @@ -47,7 +47,7 @@ bool audio_pulse_init() { return (bool) dev; } -static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc, error; unsigned char alsaMapping[MAX_CHANNEL_COUNT]; @@ -78,8 +78,10 @@ static void pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIG if (!dev) { printf("Pulseaudio error: %s\n", pa_strerror(error)); - exit(-1); + return -1; } + + return 0; } static void pulse_renderer_decode_and_play_sample(char* data, int length) { diff --git a/src/audio/sdl.c b/src/audio/sdl.c index f5021af..ce55f48 100644 --- a/src/audio/sdl.c +++ b/src/audio/sdl.c @@ -30,7 +30,7 @@ static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static SDL_AudioDeviceID dev; static int channelCount; -static void sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { int rc; decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc); @@ -48,11 +48,14 @@ static void sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE); if (dev == 0) { printf("Failed to open audio: %s\n", SDL_GetError()); + return -1; } else { if (have.format != want.format) // we let this one thing change. printf("We didn't get requested audio format.\n"); SDL_PauseAudioDevice(dev, 0); // start audio playing. } + + return 0; } static void sdl_renderer_cleanup() { diff --git a/src/video/aml.c b/src/video/aml.c index 6662b53..6015977 100755 --- a/src/video/aml.c +++ b/src/video/aml.c @@ -54,7 +54,7 @@ static int osd_blank(char *path,int cmd) { return -1; } -void aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { +int aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { osd_blank("/sys/class/graphics/fb0/blank",1); osd_blank("/sys/class/graphics/fb1/blank",0); @@ -87,7 +87,7 @@ void aml_setup(int videoFormat, int width, int height, int redrawRate, void* con break; default: printf("Video format not supported\n"); - exit(1); + return -1; } codecParam.am_sysinfo.width = width; @@ -98,12 +98,12 @@ void aml_setup(int videoFormat, int width, int height, int redrawRate, void* con int ret; if ((ret = codec_init(&codecParam)) != 0) { fprintf(stderr, "codec_init error: %x\n", ret); - exit(1); + return -2; } if ((ret = codec_set_freerun_mode(&codecParam, 1)) != 0) { fprintf(stderr, "Can't set Freerun mode: %x\n", ret); - exit(1); + return -2; } } diff --git a/src/video/imx.c b/src/video/imx.c index 18587c9..3d7476f 100644 --- a/src/video/imx.c +++ b/src/video/imx.c @@ -115,10 +115,10 @@ static int frame_handle(int pipefd) { return LOOP_OK; } -static void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { +int void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { if (videoFormat != VIDEO_FORMAT_H264) { fprintf(stderr, "Video format not supported\n"); - exit(1); + return -1; } struct mxcfb_gbl_alpha alpha; @@ -127,14 +127,14 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r if (fd_fb < 0){ fprintf(stderr, "Can't access framebuffer\n"); - exit(EXIT_FAILURE); + return -2; } alpha.alpha = 0; alpha.enable = 1; if (ioctl(fd_fb, MXCFB_SET_GBL_ALPHA, &alpha) < 0){ fprintf(stderr, "Can't set framebuffer output\n"); - exit(EXIT_FAILURE); + return -2; } close(fd_fb); @@ -148,7 +148,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r fd = open(v4l_device, O_RDWR, 0); if (fd < 0){ fprintf(stderr, "Can't access video output\n"); - exit(EXIT_FAILURE); + return -2; } struct v4l2_rect icrop = {0}; @@ -167,7 +167,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { fprintf(stderr, "Can't set source video format\n"); - exit(EXIT_FAILURE); + return -2; } if (ioctl(fd, VIDIOC_G_FMT, &fmt) < 0) { @@ -184,12 +184,12 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) { fprintf(stderr, "Can't get video buffers\n"); - exit(EXIT_FAILURE); + return -2; } if (reqbuf.count < regfbcount) { fprintf(stderr, "Not enough video buffers\n"); - exit(EXIT_FAILURE); + return -2; } for (int i = 0; i < regfbcount; i++) { @@ -199,7 +199,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r buf = calloc(1, sizeof(struct vpu_buf)); if (buf == NULL) { fprintf(stderr, "Not enough memory\n"); - exit(EXIT_FAILURE); + return -2; } buffers[i] = buf; @@ -210,7 +210,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0) { fprintf(stderr, "Can't get video buffer\n"); - exit(EXIT_FAILURE); + return -2; } buf->start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer.m.offset); @@ -221,7 +221,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r */ if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0) { fprintf(stderr, "Can't set source video format\n"); - exit(EXIT_FAILURE); + return -2; } buf->offset = buffer.m.offset; @@ -229,7 +229,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r if (buf->start == MAP_FAILED) { fprintf(stderr, "Failed to map video buffer\n"); - exit(EXIT_FAILURE); + return -2; } } @@ -237,7 +237,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r if (pipe(pipefd) == -1 || pipe(clearpipefd) == -1) { fprintf(stderr, "Can't create communication channel between threads\n"); - exit(EXIT_FAILURE); + return -2; } loop_add_fd(pipefd[0], &frame_handle, POLLIN); diff --git a/src/video/pi.c b/src/video/pi.c index 8818c34..d3c9997 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -53,10 +53,10 @@ static unsigned char *dest; static int port_settings_changed; static int first_packet; -static void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { +int void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { if (videoFormat != VIDEO_FORMAT_H264) { fprintf(stderr, "Video format not supported\n"); - exit(1); + return -1; } bcm_host_init(); @@ -71,18 +71,18 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r if((client = ilclient_init()) == NULL) { fprintf(stderr, "Can't initialize video\n"); - exit(EXIT_FAILURE); + return -2; } if(OMX_Init() != OMX_ErrorNone) { fprintf(stderr, "Can't initialize OMX\n"); - exit(EXIT_FAILURE); + return -2; } // create video_decode if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0){ fprintf(stderr, "Can't create video decode\n"); - exit(EXIT_FAILURE); + return -2; } list[0] = video_decode; @@ -90,7 +90,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r // create video_render if(ilclient_create_component(client, &video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0){ fprintf(stderr, "Can't create video renderer\n"); - exit(EXIT_FAILURE); + return -2; } list[1] = video_render; @@ -117,7 +117,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) != OMX_ErrorNone || OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmDataUnit, &unit) != OMX_ErrorNone) { fprintf(stderr, "Failed to set video parameters\n"); - exit(EXIT_FAILURE); + return -2; } OMX_PARAM_PORTDEFINITIONTYPE port; @@ -128,7 +128,7 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r port.nPortIndex = 130; if(OMX_GetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port) != OMX_ErrorNone) { fprintf(stderr, "Failed to get decoder port definition\n"); - exit(EXIT_FAILURE); + return -2; } // Increase the buffer size to fit the largest possible frame @@ -143,8 +143,10 @@ static void decoder_renderer_setup(int videoFormat, int width, int height, int r ilclient_change_component_state(video_decode, OMX_StateExecuting); } else { fprintf(stderr, "Can't setup video\n"); - exit(EXIT_FAILURE); + return -2; } + + return 0; } static void decoder_renderer_cleanup() { diff --git a/src/video/sdl.c b/src/video/sdl.c index c539613..69f9a23 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -32,21 +32,24 @@ static char* ffmpeg_buffer; -static void sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { +static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); - exit(1); + return -1; } ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); if (ffmpeg_buffer == NULL) { fprintf(stderr, "Not enough memory\n"); - exit(1); + ffmpeg_destroy(); + return -1; } + + return 0; } static void sdl_cleanup() { @@ -92,5 +95,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl = { .setup = sdl_setup, .cleanup = sdl_cleanup, .submitDecodeUnit = sdl_submit_decode_unit, - .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION | CAPABILITY_DIRECT_SUBMIT, + .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT, }; diff --git a/src/video/x11.c b/src/video/x11.c index 38f2834..e8c796e 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -34,27 +34,28 @@ static char* ffmpeg_buffer = NULL; static Display *display; -void x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { +int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); - exit(1); + return -1; } ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); if (ffmpeg_buffer == NULL) { fprintf(stderr, "Not enough memory\n"); - exit(1); + ffmpeg_destroy(); + return -1; } XInitThreads(); display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "Error: failed to open X display.\n"); - return; + return -2; } Window root = DefaultRootWindow(display); @@ -81,6 +82,8 @@ void x11_setup(int videoFormat, int width, int height, int redrawRate, void* con egl_init(display, window, width, height); x11_input_init(display, window); + + return 0; } void x11_cleanup() { @@ -110,5 +113,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = { .setup = x11_setup, .cleanup = x11_cleanup, .submitDecodeUnit = x11_submit_decode_unit, - .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION | CAPABILITY_DIRECT_SUBMIT, + .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT, }; diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index cd9f473..560cd32 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit cd9f47371a3611c2029bc8428f86bad7650e46a3 +Subproject commit 560cd3241fb0273b040e28a269a83d4483a0454b From c2036acca10501b5f9fdcfc50e7e5001989beee1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 15:26:26 +0200 Subject: [PATCH 37/72] Clear framebuffer for both amlogic and pi --- src/main.c | 3 +++ src/platform.c | 34 ++++++++++++++++++++++++++++++++++ src/platform.h | 3 +++ src/util.c | 39 +++++++++++++++++++++++++++++++++++++++ src/util.h | 22 ++++++++++++++++++++++ src/video/aml.c | 26 +------------------------- 6 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 src/util.c create mode 100644 src/util.h diff --git a/src/main.c b/src/main.c index bfc3538..9c03646 100644 --- a/src/main.c +++ b/src/main.c @@ -109,6 +109,8 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys drFlags |= FORCE_HARDWARE_ACCELERATION; printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate); + + platform_start(system); LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system), NULL, drFlags); if (IS_EMBEDDED(system)) { @@ -122,6 +124,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys #endif LiStopConnection(); + platform_stop(system); } static void help() { diff --git a/src/platform.c b/src/platform.c index 404eb7d..01cb4d7 100644 --- a/src/platform.c +++ b/src/platform.c @@ -21,6 +21,8 @@ #include "platform.h" +#include "util.h" + #include "audio/audio.h" #include "video/video.h" @@ -71,6 +73,38 @@ enum platform platform_check(char* name) { return 0; } +void platform_start(enum platform system) { + switch (system) { + #ifdef HAVE_AML + case AML: + blank_fb("/sys/class/graphics/fb0/blank", true); + blank_fb("/sys/class/graphics/fb1/blank", true); + break; + #endif + #ifdef HAVE_PI + case PI: + blank_fb("/sys/class/graphics/fb0/blank", true); + break; + #endif + } +} + +void platform_stop(enum platform system) { + switch (system) { + #ifdef HAVE_AML + case AML: + blank_fb("/sys/class/graphics/fb0/blank", false); + blank_fb("/sys/class/graphics/fb1/blank", false); + break; + #endif + #ifdef HAVE_PI + case PI: + blank_fb("/sys/class/graphics/fb0/blank", false); + break; + #endif + } +} + DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) { switch (system) { #ifdef HAVE_X11 diff --git a/src/platform.h b/src/platform.h index c271834..642f8e8 100644 --- a/src/platform.h +++ b/src/platform.h @@ -32,3 +32,6 @@ enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system); bool platform_supports_hevc(enum platform system); + +void platform_start(enum platform system); +void platform_stop(enum platform system); diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..79b79c8 --- /dev/null +++ b/src/util.c @@ -0,0 +1,39 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include "util.h" + +#include +#include +#include +#include + +int blank_fb(char *path, bool clear) { + int fd = open(path, O_RDWR); + + if(fd >= 0) { + int ret = write(fd, clear ? "1" : "0", 1); + if (ret < 0) + fprintf(stderr, "Failed to clear framebuffer %s: %d\n", path, ret); + + close(fd); + return 0; + } else + return -1; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..7e6541c --- /dev/null +++ b/src/util.h @@ -0,0 +1,22 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2017 Iwan Timmer + * + * Moonlight is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include + +int blank_fb(char *path, bool clear); diff --git a/src/video/aml.c b/src/video/aml.c index 6015977..90139f6 100755 --- a/src/video/aml.c +++ b/src/video/aml.c @@ -35,29 +35,7 @@ static codec_para_t codecParam = { 0 }; -static int osd_blank(char *path,int cmd) { - int fd; - char bcmd[16]; - - fd = open(path, O_CREAT|O_RDWR | O_TRUNC, 0644); - - if(fd>=0) { - sprintf(bcmd,"%d",cmd); - int ret = write(fd,bcmd,strlen(bcmd)); - if (ret < 0) { - printf("osd_blank error during write: %x\n", ret); - } - close(fd); - return 0; - } - - return -1; -} - int aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - osd_blank("/sys/class/graphics/fb0/blank",1); - osd_blank("/sys/class/graphics/fb1/blank",0); - codecParam.stream_type = STREAM_TYPE_ES_VIDEO; codecParam.has_video = 1; codecParam.noblock = 0; @@ -108,9 +86,7 @@ int aml_setup(int videoFormat, int width, int height, int redrawRate, void* cont } void aml_cleanup() { - int api = codec_close(&codecParam); - osd_blank("/sys/class/graphics/fb0/blank",0); - osd_blank("/sys/class/graphics/fb1/blank",0); + codec_close(&codecParam); } int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) { From f5cb2d1880298b76cc8caa9ab7a036b335a87fa0 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 16:06:37 +0200 Subject: [PATCH 38/72] Provide selected audio device as context --- src/audio/alsa.c | 3 ++- src/audio/audio.h | 4 +--- src/audio/omx.c | 2 +- src/audio/pulse.c | 5 +++-- src/audio/sdl.c | 2 +- src/config.c | 4 ++-- src/config.h | 1 + src/main.c | 4 ++-- src/platform.c | 4 ++-- src/platform.h | 2 +- third_party/moonlight-common-c | 2 +- 11 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/audio/alsa.c b/src/audio/alsa.c index c30ab3b..8e08256 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -29,7 +29,7 @@ static snd_pcm_t *handle; static OpusMSDecoder* decoder; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; -static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) { int rc; unsigned char alsaMapping[MAX_CHANNEL_COUNT]; @@ -54,6 +54,7 @@ static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR snd_pcm_uframes_t buffer_size = 2 * period_size; unsigned int sampleRate = opusConfig->sampleRate; + char* audio_device = (char*) context; if (audio_device == NULL) audio_device = "sysdefault"; diff --git a/src/audio/audio.h b/src/audio/audio.h index f99d047..91776ca 100644 --- a/src/audio/audio.h +++ b/src/audio/audio.h @@ -25,8 +25,6 @@ #define FRAME_SIZE 240 #define FRAME_BUFFER 12 -extern const char* audio_device; - #ifdef HAVE_ALSA extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa; #endif @@ -35,5 +33,5 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl; #endif #ifdef HAVE_PULSE extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse; -bool audio_pulse_init(); +bool audio_pulse_init(char* audio_device); #endif diff --git a/src/audio/omx.c b/src/audio/omx.c index a6c76f6..60b120a 100644 --- a/src/audio/omx.c +++ b/src/audio/omx.c @@ -32,7 +32,7 @@ static OMX_BUFFERHEADERTYPE *buf; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static int channelCount; -static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) { int rc, error; OMX_ERRORTYPE err; unsigned char omxMapping[MAX_CHANNEL_COUNT]; diff --git a/src/audio/pulse.c b/src/audio/pulse.c index 0cc0a4f..2da5631 100644 --- a/src/audio/pulse.c +++ b/src/audio/pulse.c @@ -31,7 +31,7 @@ static pa_simple *dev = NULL; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static int channelCount; -bool audio_pulse_init() { +bool audio_pulse_init(char* audio_device) { pa_sample_spec spec = { .format = PA_SAMPLE_S16LE, .rate = 44000, @@ -47,7 +47,7 @@ bool audio_pulse_init() { return (bool) dev; } -static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) { int rc, error; unsigned char alsaMapping[MAX_CHANNEL_COUNT]; @@ -74,6 +74,7 @@ static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU .channels = opusConfig->channelCount }; + char* audio_device = (char*) context; dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error); if (!dev) { diff --git a/src/audio/sdl.c b/src/audio/sdl.c index ce55f48..15a9ee0 100644 --- a/src/audio/sdl.c +++ b/src/audio/sdl.c @@ -30,7 +30,7 @@ static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; static SDL_AudioDeviceID dev; static int channelCount; -static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { +static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) { int rc; decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc); diff --git a/src/config.c b/src/config.c index 64ee138..0a8bd91 100644 --- a/src/config.c +++ b/src/config.c @@ -38,7 +38,6 @@ #define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false"); bool inputAdded = false; -const char* audio_device = NULL; static struct option long_options[] = { {"720", no_argument, NULL, 'a'}, @@ -172,7 +171,7 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { config->sops = false; break; case 'm': - audio_device = value; + config->audio_device = value; break; case 'n': config->localaudio = true; @@ -303,6 +302,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->action = NULL; config->address = NULL; config->config_file = NULL; + config->audio_device = NULL; config->sops = true; config->localaudio = false; config->fullscreen = true; diff --git a/src/config.h b/src/config.h index 3478f71..bd45269 100644 --- a/src/config.h +++ b/src/config.h @@ -32,6 +32,7 @@ typedef struct _CONFIGURATION { char* address; char* mapping; char* platform; + char* audio_device; char* config_file; char key_dir[4096]; bool sops; diff --git a/src/main.c b/src/main.c index 9c03646..8b79251 100644 --- a/src/main.c +++ b/src/main.c @@ -111,7 +111,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate); platform_start(system); - LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system), NULL, drFlags); + LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system, config->audio_device), NULL, drFlags, config->audio_device, 0); if (IS_EMBEDDED(system)) { evdev_start(); @@ -193,7 +193,7 @@ int main(int argc, char* argv[]) { if (system == 0) { fprintf(stderr, "Platform '%s' not found\n", config.platform); exit(-1); - } else if (system == SDL && audio_device != NULL) { + } else if (system == SDL && config.audio_device != NULL) { fprintf(stderr, "You can't select a audio device for SDL\n"); exit(-1); } diff --git a/src/platform.c b/src/platform.c index 01cb4d7..8837b82 100644 --- a/src/platform.c +++ b/src/platform.c @@ -131,7 +131,7 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) { return NULL; } -AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { +AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_device) { switch (system) { #ifdef HAVE_SDL case SDL: @@ -144,7 +144,7 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { #endif default: #ifdef HAVE_PULSE - if (audio_pulse_init()) + if (audio_pulse_init(audio_device)) return &audio_callbacks_pulse; #endif #ifdef HAVE_ALSA diff --git a/src/platform.h b/src/platform.h index 642f8e8..eecd390 100644 --- a/src/platform.h +++ b/src/platform.h @@ -30,7 +30,7 @@ enum platform { NONE, SDL, X11, PI, IMX, AML, FAKE }; enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); -PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system); +PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device); bool platform_supports_hevc(enum platform system); void platform_start(enum platform system); diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 560cd32..66ce27a 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 560cd3241fb0273b040e28a269a83d4483a0454b +Subproject commit 66ce27ab19213f8e4b36dcaf33c5b3e909912b9c From 9441dde68a75cf98be8a59b711349c876af9cf44 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 17 Mar 2017 15:19:54 +0000 Subject: [PATCH 39/72] Configure latency target for video output on Raspberry Pi --- src/video/pi.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/video/pi.c b/src/video/pi.c index d3c9997..8784e75 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -120,6 +120,24 @@ int void decoder_renderer_setup(int videoFormat, int width, int height, int redr return -2; } + OMX_CONFIG_LATENCYTARGETTYPE latencyTarget; + memset(&latencyTarget, 0, sizeof(OMX_CONFIG_LATENCYTARGETTYPE)); + latencyTarget.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + latencyTarget.nVersion.nVersion = OMX_VERSION; + latencyTarget.nPortIndex = 90; + latencyTarget.bEnabled = OMX_TRUE; + latencyTarget.nFilter = 2; + latencyTarget.nTarget = 4000; + latencyTarget.nShift = 3; + latencyTarget.nSpeedFactor = -135; + latencyTarget.nInterFactor = 500; + latencyTarget.nAdjCap = 20; + + if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) { + fprintf(stderr, "Failed to set video render parameters\n"); + exit(EXIT_FAILURE); + } + OMX_PARAM_PORTDEFINITIONTYPE port; memset(&port, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE)); From 51d2fe2a3512b207f1d8ff34ff329c8f49d4832a Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 16:04:33 +0000 Subject: [PATCH 40/72] Request fullscreen for Raspberry Pi --- src/video/pi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/video/pi.c b/src/video/pi.c index 8784e75..bc3dec7 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -133,6 +133,13 @@ int void decoder_renderer_setup(int videoFormat, int width, int height, int redr latencyTarget.nInterFactor = 500; latencyTarget.nAdjCap = 20; + OMX_CONFIG_DISPLAYREGIONTYPE displayRegion; + displayRegion.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE); + displayRegion.nVersion.nVersion = OMX_VERSION; + displayRegion.nPortIndex = 90; + displayRegion.fullscreen = OMX_TRUE; + displayRegion.mode = OMX_DISPLAY_SET_FULLSCREEN; + if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) { fprintf(stderr, "Failed to set video render parameters\n"); exit(EXIT_FAILURE); From 26ddab2bde54cf93275eb29ac69feda1efd4c5b1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 28 May 2017 18:34:48 +0200 Subject: [PATCH 41/72] Update documentation --- README.md | 4 ++-- docs/README.pod | 28 ++++++++++++++++++++++------ src/main.c | 8 ++++---- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 7fb13ea..f762051 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Moonlight Embedded is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for Linux. -Moonlight Embedded allows you to stream your full collection of Steam games from +Moonlight Embedded allows you to stream your full collection of games from your powerful Windows desktop to your (embedded) Linux system, like Raspberry Pi, CuBox-i and ODROID. ## Documentation @@ -11,7 +11,7 @@ More information about installing and runnning Moonlight Embedded is available o ## Requirements -* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900 series GPU (for the PC you're streaming from) +* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900/1000 series GPU (for the PC you're streaming from) * High-end wireless router (802.11n dual-band recommended) or wired network * Geforce Experience 2.1.1 or higher diff --git a/docs/README.pod b/docs/README.pod index 31f36e3..d858601 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -84,11 +84,12 @@ This is the default configuration for 720p. =item B<-fps> [I] -Change the number of frame per second to I +Change the number of frame per second to I. +Only 30 and 60 fps are currently supported by Gamestream. =item B<-bitrate> [I] -Change bitrate to I kbps. +Change bitrate to I Kbps. By default the bitrate depends on the selected resolution and fps. For resolution 1080p and 60 fps and higher 20 Mbps is used. For resolution 1080p or 60 fps and higher 10 Mbps is used @@ -96,8 +97,9 @@ For other configurations 5 Mbps is used by default. =item B<-packetsize> [I] -Change the network packetsize to I. +Change the network packetsize to I bytes. The packetsize should the smaller than the MTU of the network. +This value must be a multiply of 16. By default a safe value of 1024 is used. =item B<-hevc> @@ -124,7 +126,11 @@ Stop GFE from changing the graphical settings of the requested game or applicati =item B<-localaudio> -Play the audio on the host instead of this computer. +Play the audio on the host computer instead of this device. + +=item B<-surround> + +Enable 5.1 surround sound instead of stereo. =item B<-keydir> [I] @@ -134,20 +140,30 @@ By default the encryption keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cach =item B<-mapping> [I] Use I as the mapping file for all inputs. -This mapping file should have the same format as the gamecontrollerdb.txt for SDL. +This mapping file should have the same format as the gamecontrollerdb.txt for SDL2. +By default the gamecontrollerdb.txt provided by Moonlight Embedded is used. =item B<-input> [I] Enable the I device. By default all available input devices are enabled. Only evdev devices /dev/input/event* are supported. -To use a different gamepad mapping then the default the B<-mapping> should be specified before the B<-input>. =item B<-audio> [I] Use as audio output device. The default value is 'sysdefault' for ALSA and 'hdmi' for OMX on the Raspberry Pi. +=item B<-windowed> + +Display the stream in a window instead of fullscreen. +Only available when X11 or SDL platform is used. + +=item B<-forcehw> + +This will enable unsupported hardware acceleration. +Currently only VDPAU when using X11 or SDL is unsupported. + =back =head1 CONFIG FILE diff --git a/src/main.c b/src/main.c index 8b79251..a349ad5 100644 --- a/src/main.c +++ b/src/main.c @@ -159,15 +159,15 @@ static void help() { printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n"); printf("\t-keydir \tLoad encryption keys from directory\n"); printf("\t-mapping \t\tUse as gamepad mappings configuration file\n"); - #ifdef HAVE_SDL - printf("\n Video options (SDL Only)\n\n"); + #if defined(HAVE_SDL) || defined(HAVE_X11) + printf("\n Video options (SDL and X11 only)\n\n"); printf("\t-windowed\t\tDisplay screen in a window\n"); + printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif #ifdef HAVE_EMBEDDED - printf("\n I/O options\n\n"); + printf("\n I/O options (PI, IMX, AML and X11 only)\n\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); printf("\t-audio \t\tUse as audio output device\n"); - printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n"); exit(0); From 32a26d19f995a61fee51b1f80a6d51a4a728d969 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 31 May 2017 18:03:21 +0000 Subject: [PATCH 42/72] Correct typos for embedded platforms --- src/audio/omx.c | 1 + src/video/imx.c | 2 +- src/video/pi.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/audio/omx.c b/src/audio/omx.c index 60b120a..9fa9d8c 100644 --- a/src/audio/omx.c +++ b/src/audio/omx.c @@ -123,6 +123,7 @@ static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA } OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest; + char* audio_device = (char*) context; if (audio_device == NULL) audio_device = "hdmi"; diff --git a/src/video/imx.c b/src/video/imx.c index 3d7476f..bf51f1d 100644 --- a/src/video/imx.c +++ b/src/video/imx.c @@ -115,7 +115,7 @@ static int frame_handle(int pipefd) { return LOOP_OK; } -int void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { +int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { if (videoFormat != VIDEO_FORMAT_H264) { fprintf(stderr, "Video format not supported\n"); return -1; diff --git a/src/video/pi.c b/src/video/pi.c index bc3dec7..bb91503 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -53,7 +53,7 @@ static unsigned char *dest; static int port_settings_changed; static int first_packet; -int void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { +int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { if (videoFormat != VIDEO_FORMAT_H264) { fprintf(stderr, "Video format not supported\n"); return -1; From 2d0c76d0b5040d3fb67acfb75329c252766d52d3 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 31 May 2017 20:13:27 +0200 Subject: [PATCH 43/72] Return 0 for video renderer setup --- src/video/aml.c | 2 ++ src/video/imx.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/video/aml.c b/src/video/aml.c index 90139f6..3858967 100755 --- a/src/video/aml.c +++ b/src/video/aml.c @@ -83,6 +83,8 @@ int aml_setup(int videoFormat, int width, int height, int redrawRate, void* cont fprintf(stderr, "Can't set Freerun mode: %x\n", ret); return -2; } + + return 0; } void aml_cleanup() { diff --git a/src/video/imx.c b/src/video/imx.c index bf51f1d..64e490a 100644 --- a/src/video/imx.c +++ b/src/video/imx.c @@ -244,6 +244,8 @@ int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRat fcntl(clearpipefd[0], F_SETFL, O_NONBLOCK); fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + + return 0; } static int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) { From 5cb4a8cc42b5341ba72d463dd61276c67dd16e8a Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 1 Jun 2017 10:29:43 -0700 Subject: [PATCH 44/72] Lower required OpenSSL version to 1.0.2 --- libgamestream/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libgamestream/CMakeLists.txt b/libgamestream/CMakeLists.txt index 7f1a813..23d7da9 100644 --- a/libgamestream/CMakeLists.txt +++ b/libgamestream/CMakeLists.txt @@ -1,7 +1,7 @@ find_package(LibUUID REQUIRED) find_package(Threads REQUIRED) find_package(CURL REQUIRED) -find_package(OpenSSL 1.1 REQUIRED) +find_package(OpenSSL 1.0.2 REQUIRED) find_package(EXPAT REQUIRED) pkg_check_modules(AVAHI REQUIRED avahi-client) From f79a99bc05ca410032c540ebfc223f5a0f58b31e Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 4 Jun 2017 14:47:11 +0200 Subject: [PATCH 45/72] Render EGL in main thread --- src/video/x11.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/video/x11.c b/src/video/x11.c index e8c796e..96e8f92 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -22,11 +22,15 @@ #include "ffmpeg.h" #include "../input/x11.h" +#include "../loop.h" #include #include #include +#include +#include +#include #define DECODER_BUFFER_SIZE 92*1024 @@ -34,6 +38,17 @@ static char* ffmpeg_buffer = NULL; static Display *display; +static int pipefd[2]; + +static int frame_handle(int pipefd) { + const unsigned char** data = NULL; + while (read(pipefd, &data, sizeof(void*)) > 0); + if (data) + egl_draw(data); + + return LOOP_OK; +} + int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; if (drFlags & FORCE_HARDWARE_ACCELERATION) @@ -83,6 +98,13 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont egl_init(display, window, width, height); x11_input_init(display, window); + if (pipe(pipefd) == -1) { + fprintf(stderr, "Can't create communication channel between threads\n"); + return -2; + } + loop_add_fd(pipefd[0], &frame_handle, POLLIN); + fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + return 0; } @@ -102,8 +124,10 @@ int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { } ffmpeg_decode(ffmpeg_buffer, length); AVFrame* frame = ffmpeg_get_frame(); - if (frame != NULL) - egl_draw((const unsigned char**) frame->data); + if (frame != NULL) { + void* pointer = frame->data; + write(pipefd[1], &pointer, sizeof(void*)); + } } return DR_OK; From 697bee2954d9f67df2bc62870ec6c7fd68de4677 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 4 Jun 2017 14:48:26 +0200 Subject: [PATCH 46/72] Read all pending X11 events during input handling --- src/input/x11.c | 129 ++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/src/input/x11.c b/src/input/x11.c index 5f76c53..09d8d95 100644 --- a/src/input/x11.c +++ b/src/input/x11.c @@ -46,86 +46,85 @@ static Cursor cursor; static bool grabbed = True; static int x11_handler(int fd) { - if (!XPending(display)) - return LOOP_OK; - XEvent event; int button = 0; int motion_x, motion_y; - XNextEvent(display, &event); - switch (event.type) { - case KeyPress: - case KeyRelease: - if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) { - if ((keyboard_modifiers & MODIFIERS) == MODIFIERS && event.type == KeyRelease) { - grabbed = !grabbed; - XDefineCursor(display, window, grabbed ? cursor : 0); - } + while (XPending(display)) { + XNextEvent(display, &event); + switch (event.type) { + case KeyPress: + case KeyRelease: + if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) { + if ((keyboard_modifiers & MODIFIERS) == MODIFIERS && event.type == KeyRelease) { + grabbed = !grabbed; + XDefineCursor(display, window, grabbed ? cursor : 0); + } - int modifier = 0; - switch (event.xkey.keycode) { - case 0x32: - case 0x3e: - modifier = MODIFIER_SHIFT; + int modifier = 0; + switch (event.xkey.keycode) { + case 0x32: + case 0x3e: + modifier = MODIFIER_SHIFT; + break; + case 0x40: + case 0x6c: + modifier = MODIFIER_ALT; + break; + case 0x25: + case 0x69: + modifier = MODIFIER_CTRL; + break; + } + + if (modifier != 0) { + if (event.type == KeyPress) + keyboard_modifiers |= modifier; + else + keyboard_modifiers &= ~modifier; + } + + short code = 0x80 << 8 | keyCodes[event.xkey.keycode - 8]; + LiSendKeyboardEvent(code, event.type == KeyPress ? KEY_ACTION_DOWN : KEY_ACTION_UP, keyboard_modifiers); + } + break; + case ButtonPress: + case ButtonRelease: + switch (event.xbutton.button) { + case Button1: + button = BUTTON_LEFT; break; - case 0x40: - case 0x6c: - modifier = MODIFIER_ALT; + case Button2: + button = BUTTON_MIDDLE; break; - case 0x25: - case 0x69: - modifier = MODIFIER_CTRL; + case Button3: + button = BUTTON_RIGHT; break; } - if (modifier != 0) { - if (event.type == KeyPress) - keyboard_modifiers |= modifier; - else - keyboard_modifiers &= ~modifier; + if (button != 0) + LiSendMouseButtonEvent(event.type==ButtonPress ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, button); + break; + case MotionNotify: + motion_x = event.xmotion.x - last_x; + motion_y = event.xmotion.y - last_y; + if (abs(motion_x) > 0 || abs(motion_y) > 0) { + if (last_x >= 0 && last_y >= 0) + LiSendMouseMoveEvent(motion_x, motion_y); + + if (grabbed) + XWarpPointer(display, None, window, 0, 0, 0, 0, 640, 360); } - short code = 0x80 << 8 | keyCodes[event.xkey.keycode - 8]; - LiSendKeyboardEvent(code, event.type == KeyPress ? KEY_ACTION_DOWN : KEY_ACTION_UP, keyboard_modifiers); - } - break; - case ButtonPress: - case ButtonRelease: - switch (event.xbutton.button) { - case Button1: - button = BUTTON_LEFT; + last_x = grabbed ? 640 : event.xmotion.x; + last_y = grabbed ? 360 : event.xmotion.y; break; - case Button2: - button = BUTTON_MIDDLE; - break; - case Button3: - button = BUTTON_RIGHT; + case ClientMessage: + if (event.xclient.data.l[0] == wm_deletemessage) + return LOOP_RETURN; + break; } - - if (button != 0) - LiSendMouseButtonEvent(event.type==ButtonPress ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, button); - break; - case MotionNotify: - motion_x = event.xmotion.x - last_x; - motion_y = event.xmotion.y - last_y; - if (abs(motion_x) > 0 || abs(motion_y) > 0) { - if (last_x >= 0 && last_y >= 0) - LiSendMouseMoveEvent(motion_x, motion_y); - - if (grabbed) - XWarpPointer(display, None, window, 0, 0, 0, 0, 640, 360); - } - - last_x = grabbed ? 640 : event.xmotion.x; - last_y = grabbed ? 360 : event.xmotion.y; - break; - case ClientMessage: - if (event.xclient.data.l[0] == wm_deletemessage) - return LOOP_RETURN; - - break; } return LOOP_OK; From 10edbfdb204584d7d863ba801986fea9921929ab Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 5 Jun 2017 00:12:06 +0200 Subject: [PATCH 47/72] Use VDPAU video output in X11 --- src/video/ffmpeg.c | 19 ++++++++-------- src/video/ffmpeg.h | 9 ++++++-- src/video/ffmpeg_vdpau.c | 48 +++++++++++++++++++++++++++++++++------- src/video/ffmpeg_vdpau.h | 5 ++++- src/video/sdl.c | 4 ++-- src/video/x11.c | 26 ++++++++++++++-------- 6 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/video/ffmpeg.c b/src/video/ffmpeg.c index 7e0d9b9..653676a 100644 --- a/src/video/ffmpeg.c +++ b/src/video/ffmpeg.c @@ -40,14 +40,13 @@ static AVFrame** dec_frames; static int dec_frames_cnt; static int current_frame, next_frame; -enum decoders {SOFTWARE, VDPAU}; -enum decoders decoder_system; +enum decoders ffmpeg_decoder; #define BYTES_PER_PIXEL 4 // This function must be called before // any other decoding functions -int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count) { +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count, void* context) { // Initialize the avcodec library and register codecs av_log_set_level(AV_LOG_QUIET); avcodec_register_all(); @@ -66,12 +65,12 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer } if (decoder != NULL) - decoder_system = VDPAU; + ffmpeg_decoder = VDPAU; } #endif if (decoder == NULL) { - decoder_system = SOFTWARE; + ffmpeg_decoder = SOFTWARE; switch (videoFormat) { case VIDEO_FORMAT_H264: decoder = avcodec_find_decoder_by_name("h264"); @@ -133,8 +132,8 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer } #ifdef HAVE_VDPAU - if (decoder_system == VDPAU) - vdpau_init(decoder_ctx, width, height); + if (ffmpeg_decoder == VDPAU) + vdpau_init(decoder_ctx, (Display*) context, width, height); #endif return 0; @@ -156,16 +155,16 @@ void ffmpeg_destroy(void) { } } -AVFrame* ffmpeg_get_frame() { +AVFrame* ffmpeg_get_frame(bool native_frame) { int err = avcodec_receive_frame(decoder_ctx, dec_frames[next_frame]); if (err == 0) { current_frame = next_frame; next_frame = (current_frame+1) % dec_frames_cnt; - if (decoder_system == SOFTWARE) + if (ffmpeg_decoder == SOFTWARE || native_frame) return dec_frames[current_frame]; #ifdef HAVE_VDPAU - else if (decoder_system == VDPAU) + else if (ffmpeg_decoder == VDPAU) return vdpau_get_frame(dec_frames[current_frame]); #endif } else if (err != AVERROR(EAGAIN)) { diff --git a/src/video/ffmpeg.h b/src/video/ffmpeg.h index e65f585..d5449b4 100644 --- a/src/video/ffmpeg.h +++ b/src/video/ffmpeg.h @@ -17,6 +17,8 @@ * along with Moonlight; if not, see . */ +#include + #include // Disables the deblocking filter at the cost of image quality @@ -34,9 +36,12 @@ // Uses hardware acceleration #define HARDWARE_ACCELERATION 0x40 -int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count); +enum decoders {SOFTWARE, VDPAU}; +extern enum decoders ffmpeg_decoder; + +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count, void* context); void ffmpeg_destroy(void); int ffmpeg_draw_frame(AVFrame *pict); -AVFrame* ffmpeg_get_frame(); +AVFrame* ffmpeg_get_frame(bool native_frame); int ffmpeg_decode(unsigned char* indata, int inlen); diff --git a/src/video/ffmpeg_vdpau.c b/src/video/ffmpeg_vdpau.c index ae2b119..eddd024 100644 --- a/src/video/ffmpeg_vdpau.c +++ b/src/video/ffmpeg_vdpau.c @@ -34,6 +34,9 @@ static AVFrame* cpu_frame; static VdpDevice vdp_device; static VdpDecoder vdp_decoder; static VdpVideoMixer vdp_mixer; +static VdpPresentationQueue vdp_queue; +static VdpPresentationQueueTarget vdp_queue_target; +static VdpOutputSurface vdp_output; static struct vdpau_render_state* vdp_render_state[MAX_RENDER_STATES]; static int vdp_render_states = 0; @@ -43,8 +46,13 @@ static VdpDecoderRender* vdp_decoder_render; static VdpVideoSurfaceGetBitsYCbCr* vdp_video_surface_get_bits_y_cb_cr; static VdpVideoSurfaceCreate* vdp_video_surface_create; static VdpVideoMixerCreate* vdp_video_mixer_create; +static VdpVideoMixerRender* vdp_video_mixer_render; +static VdpOutputSurfaceCreate* vdp_output_surface_create; +static VdpPresentationQueueCreate* vdp_presentation_queue_create; +static VdpPresentationQueueDisplay* vdp_presentation_queue_display; +static VdpPresentationQueueTargetCreateX11* vdp_presentation_queue_target_create_x11; -struct vdpau_render_state* vdp_get_free_render_state() { +struct vdpau_render_state* vdp_get_free_render_state(int width, int height) { for (unsigned i = 0; i < vdp_render_states; i++) { struct vdpau_render_state* render_state = vdp_render_state[i]; if (!render_state->state) @@ -60,7 +68,7 @@ struct vdpau_render_state* vdp_get_free_render_state() { vdp_render_states++; memset(render_state, 0, sizeof(struct vdpau_render_state)); render_state->surface = VDP_INVALID_HANDLE; - VdpStatus status = vdp_video_surface_create(vdp_device, VDP_CHROMA_TYPE_420, 1280, 720, &render_state->surface); + VdpStatus status = vdp_video_surface_create(vdp_device, VDP_CHROMA_TYPE_420, width, height, &render_state->surface); return render_state; } @@ -70,7 +78,7 @@ static void vdp_release_buffer(void* opaque, uint8_t *data) { } static int vdp_get_buffer(AVCodecContext* context, AVFrame* frame, int flags) { - struct vdpau_render_state* pRenderState = vdp_get_free_render_state(); + struct vdpau_render_state* pRenderState = vdp_get_free_render_state(frame->width, frame->height); frame->data[0] = (uint8_t*) pRenderState; frame->buf[0] = av_buffer_create(frame->data[0], 0, vdp_release_buffer, NULL, 0); @@ -89,23 +97,27 @@ static void vdp_draw_horiz_band(struct AVCodecContext* context, const AVFrame* f status = vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers); } -int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) { +int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height) { if (vdp_device) return vdp_device; - Display* xdisplay = XOpenDisplay(0); - if (!xdisplay) - return -1; + if (!display) + display = XOpenDisplay(NULL); - VdpStatus status = vdp_device_create_x11(xdisplay, DefaultScreen(xdisplay), &vdp_device, &vdp_get_proc_address); + VdpStatus status = vdp_device_create_x11(display, DefaultScreen(display), &vdp_device, &vdp_get_proc_address); if (status != VDP_STATUS_OK) return -1; vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**)&vdp_video_surface_get_bits_y_cb_cr); vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**)&vdp_video_surface_create); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, (void**)&vdp_output_surface_create); vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DECODER_RENDER, (void**)&vdp_decoder_render); vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DECODER_CREATE, (void**)&vdp_decoder_create); vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_MIXER_CREATE, (void**)&vdp_video_mixer_create); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_MIXER_RENDER, (void**)&vdp_video_mixer_render); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE, (void**)&vdp_presentation_queue_create); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11, (void**)&vdp_presentation_queue_target_create_x11); + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY, (void**)&vdp_presentation_queue_display); decoder_ctx->get_buffer2 = vdp_get_buffer; decoder_ctx->draw_horiz_band = vdp_draw_horiz_band; @@ -153,6 +165,8 @@ int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) { return -1; } + vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, width, height, &vdp_output); + return vdp_device; } @@ -172,3 +186,21 @@ AVFrame* vdpau_get_frame(AVFrame* dec_frame) { VdpStatus status = vdp_video_surface_get_bits_y_cb_cr(render_state->surface, VDP_YCBCR_FORMAT_YV12, dest, pitches); return cpu_frame; } + +int vdpau_init_queue(Drawable win) { + if(vdp_presentation_queue_target_create_x11(vdp_device, win, &vdp_queue_target) != VDP_STATUS_OK) + return -1; + + if(vdp_presentation_queue_create(vdp_device, vdp_queue_target, &vdp_queue) != VDP_STATUS_OK) + return -1; + + return 0; +} + +void vdpau_queue(AVFrame* dec_frame) { + struct vdpau_render_state *render_state = (struct vdpau_render_state *)dec_frame->data[0]; + int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; + vdp_video_mixer_render(vdp_mixer, VDP_INVALID_HANDLE, 0, field, 0, (VdpVideoSurface*)VDP_INVALID_HANDLE, render_state->surface, 0,(VdpVideoSurface*)VDP_INVALID_HANDLE, NULL, vdp_output, NULL, NULL, 0, NULL); + + vdp_presentation_queue_display(vdp_queue, vdp_output, 0, 0, 0); +} diff --git a/src/video/ffmpeg_vdpau.h b/src/video/ffmpeg_vdpau.h index 13f2b69..27d0554 100644 --- a/src/video/ffmpeg_vdpau.h +++ b/src/video/ffmpeg_vdpau.h @@ -17,7 +17,10 @@ * along with Moonlight; if not, see . */ +#include #include -int vdpau_init(AVCodecContext* decoder_ctx, int width, int height); +int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height); AVFrame* vdpau_get_frame(AVFrame* dec_frame); +int vdpau_init_queue(Drawable win); +void vdpau_queue(AVFrame* dec_frame); diff --git a/src/video/sdl.c b/src/video/sdl.c index 69f9a23..459f3a1 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -37,7 +37,7 @@ static int sdl_setup(int videoFormat, int width, int height, int redrawRate, voi if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; - if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) { + if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN), NULL) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); return -1; } @@ -68,7 +68,7 @@ static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) { ffmpeg_decode(ffmpeg_buffer, length); if (SDL_LockMutex(mutex) == 0) { - AVFrame* frame = ffmpeg_get_frame(); + AVFrame* frame = ffmpeg_get_frame(false); if (frame != NULL) { sdlNextFrame++; diff --git a/src/video/x11.c b/src/video/x11.c index 96e8f92..1aa5be3 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -20,6 +20,7 @@ #include "video.h" #include "egl.h" #include "ffmpeg.h" +#include "ffmpeg_vdpau.h" #include "../input/x11.h" #include "../loop.h" @@ -54,11 +55,6 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; - if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) { - fprintf(stderr, "Couldn't initialize video decoding\n"); - return -1; - } - ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); if (ffmpeg_buffer == NULL) { fprintf(stderr, "Not enough memory\n"); @@ -95,7 +91,16 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } - egl_init(display, window, width, height); + if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2, display) < 0) { + fprintf(stderr, "Couldn't initialize video decoding\n"); + return -1; + } + + if (ffmpeg_decoder == SOFTWARE) + egl_init(display, window, width, height); + else + vdpau_init_queue(window); + x11_input_init(display, window); if (pipe(pipefd) == -1) { @@ -123,10 +128,13 @@ int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { entry = entry->next; } ffmpeg_decode(ffmpeg_buffer, length); - AVFrame* frame = ffmpeg_get_frame(); + AVFrame* frame = ffmpeg_get_frame(true); if (frame != NULL) { - void* pointer = frame->data; - write(pipefd[1], &pointer, sizeof(void*)); + if (ffmpeg_decoder == SOFTWARE) { + void* pointer = frame->data; + write(pipefd[1], &pointer, sizeof(void*)); + } else + vdpau_queue(frame); } } From faf19943fdb467647247152a42116bab221f5bd1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 9 Jun 2017 21:34:13 +0200 Subject: [PATCH 48/72] Add missing header --- src/util.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util.c b/src/util.c index 79b79c8..2376221 100644 --- a/src/util.c +++ b/src/util.c @@ -23,6 +23,7 @@ #include #include #include +#include int blank_fb(char *path, bool clear) { int fd = open(path, O_RDWR); From 8960cc458f3aa9548c905e064e809904d52aa415 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 9 Jun 2017 22:19:22 +0200 Subject: [PATCH 49/72] Remove 30 and 60 fps options --- docs/README.pod | 11 +---------- src/config.c | 8 -------- src/main.c | 2 -- 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index d858601..a62c0d4 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -72,19 +72,10 @@ Change the horizontal resolution to I Change the vertical resolution to I -=item B<-30fps> - -Use 30 fps for streaming. -This is the default configuration for 1080p and higher. - -=item B<-60fps> - -Use 60 fps for streaming. -This is the default configuration for 720p. - =item B<-fps> [I] Change the number of frame per second to I. +Defaults to 60fps for 720p and 30fps for 1080p and higher. Only 30 and 60 fps are currently supported by Gamestream. =item B<-bitrate> [I] diff --git a/src/config.c b/src/config.c index 0a8bd91..3b2bb12 100644 --- a/src/config.c +++ b/src/config.c @@ -45,8 +45,6 @@ static struct option long_options[] = { {"4k", no_argument, NULL, '0'}, {"width", required_argument, NULL, 'c'}, {"height", required_argument, NULL, 'd'}, - {"30fps", no_argument, NULL, 'e'}, - {"60fps", no_argument, NULL, 'f'}, {"bitrate", required_argument, NULL, 'g'}, {"packetsize", required_argument, NULL, 'h'}, {"app", required_argument, NULL, 'i'}, @@ -136,12 +134,6 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { case 'd': config->stream.height = atoi(value); break; - case 'e': - config->stream.fps = 30; - break; - case 'f': - config->stream.fps = 60; - break; case 'g': config->stream.bitrate = atoi(value); break; diff --git a/src/main.c b/src/main.c index a349ad5..275e7b7 100644 --- a/src/main.c +++ b/src/main.c @@ -146,8 +146,6 @@ static void help() { printf("\t-4k\t\t\t\tUse 3840x2160 resolution\n"); printf("\t-width \t\tHorizontal resolution (default 1280)\n"); printf("\t-height \tVertical resolution (default 720)\n"); - printf("\t-30fps\t\t\tUse 30fps\n"); - printf("\t-60fps\t\t\tUse 60fps\n"); printf("\t-bitrate \tSpecify the bitrate in Kbps\n"); printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); printf("\t-hevc\t\t\tUse the high efficiency video coding (HEVC)\n"); From 63f05b2baf9704dbe81a06de4c6c702647fec229 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 9 Jun 2017 22:35:07 +0200 Subject: [PATCH 50/72] Update sample configuration file --- moonlight.conf | 19 +++++++++++++------ src/config.c | 7 +++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/moonlight.conf b/moonlight.conf index a800d6c..ebdc59e 100644 --- a/moonlight.conf +++ b/moonlight.conf @@ -18,7 +18,7 @@ #packetsize = 1024 ## Use of h265/HEVC video codec -#h265 = false +#hevc = false ## Default started application on host #app = Steam @@ -26,7 +26,7 @@ ## Default used mapping for streaming ## Searched for in $XDG_DATA_DIRS/moonlight or /usr/share/moonlight and /usr/local/share/moonlight ## Mapping can also be user overrided in $XDG_CONFIG_DIR/moonlight or ~/.config/moonlight or current directory -#mapping = mappings/default.conf +#mapping = gamecontrollerdb.txt ## Enable selected input devices ## By default all available input devices should be used @@ -34,8 +34,8 @@ ## To use a different mapping then default another mapping should be declared above the input #input = /dev/input/event1 -## Let GFE change graphical game settings for optimal performance and quality -#sops = true +## Stop GFE from changing graphical game settings for optimal performance and quality +#nosops = false ## Play audio on host instead of streaming to client #localaudio = false @@ -48,13 +48,20 @@ ## aml - hardware video decoder for ODROID-C1/C2 ## omx - hardware video decoder for Raspberry Pi ## imx - hardware video decoder for i.MX6 devices -## sdl - software decoder -## fake - save to file (only available in debug builds) +## x11 - software decoder +## sdl - software decoder with SDL input and audio +## fake - no audio and video #platform = default ## Directory to store encryption keys ## By default keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cache/moonlight #keydir = /dir/to/keys +## Enable QOS settings to optimize for internet instead of local network +#remote = false + +## Enable 5.1 surround sound +#surround = false + ## Load additional configuration files #config = /path/to/config diff --git a/src/config.c b/src/config.c index 3b2bb12..2d57250 100644 --- a/src/config.c +++ b/src/config.c @@ -239,8 +239,11 @@ bool config_file_parse(char* filename, PCONFIGURATION config) { config->localaudio = strcmp("true", value) == 0; } else { for (int i=0;long_options[i].name != NULL;i++) { - if (long_options[i].has_arg == required_argument && strcmp(long_options[i].name, key) == 0) { - parse_argument(long_options[i].val, value, config); + if (strcmp(long_options[i].name, key) == 0) { + if (long_options[i].has_arg == required_argument) + parse_argument(long_options[i].val, value, config); + else if (strcmp("true", value) == 0) + parse_argument(long_options[i].val, NULL, config); } } } From 0b7c2f62a7e2a078d21b14611f6016cac08d9cad Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 9 Jun 2017 22:50:48 +0200 Subject: [PATCH 51/72] Replace seperate codec options with single option --- docs/README.pod | 12 +++++------- moonlight.conf | 8 ++++---- src/config.c | 13 +++++++------ src/main.c | 3 +-- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index a62c0d4..7387cd4 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -93,14 +93,12 @@ The packetsize should the smaller than the MTU of the network. This value must be a multiply of 16. By default a safe value of 1024 is used. -=item B<-hevc> +=item B<-codec> [I] -Request a h265/HEVC from the server. -Will still use h264 if server doesn't support HEVC. - -=item B<-h264> - -Request a h264 from the server even if server and video decoder supports HEVC. +Select codec to use. +Can be 'auto', 'h264', 'h265' or 'hevc'. +Not all video decoders do support H.265/HEVC. +Will still use H.264 if server doesn't support HEVC. =item B<-remote> diff --git a/moonlight.conf b/moonlight.conf index ebdc59e..f3311c5 100644 --- a/moonlight.conf +++ b/moonlight.conf @@ -17,8 +17,8 @@ ## Size of network packets should be lower than MTU #packetsize = 1024 -## Use of h265/HEVC video codec -#hevc = false +## Select video codec (auto/h264/h265) +#codec = auto ## Default started application on host #app = Steam @@ -34,8 +34,8 @@ ## To use a different mapping then default another mapping should be declared above the input #input = /dev/input/event1 -## Stop GFE from changing graphical game settings for optimal performance and quality -#nosops = false +## Enable GFE for changing graphical game settings for optimal performance and quality +#sops = true ## Play audio on host instead of streaming to client #localaudio = false diff --git a/src/config.c b/src/config.c index 2d57250..9843375 100644 --- a/src/config.c +++ b/src/config.c @@ -62,8 +62,7 @@ static struct option long_options[] = { {"surround", no_argument, NULL, 'u'}, {"fps", required_argument, NULL, 'v'}, {"forcehw", no_argument, NULL, 'w'}, - {"hevc", no_argument, NULL, 'x'}, - {"h264", no_argument, NULL, 'z'}, + {"codec", required_argument, NULL, 'x'}, {"unsupported", no_argument, NULL, 'y'}, {0, 0, 0, 0}, }; @@ -198,10 +197,12 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { config->forcehw = true; break; case 'x': - config->codec = CODEC_HEVC; - break; - case 'z': - config->codec = CODEC_H264; + if (strcasecmp(value, "auto") == 0) + config->codec = CODEC_UNSPECIFIED; + else if (strcasecmp(value, "h264") == 0) + config->codec = CODEC_H264; + if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0) + config->codec = CODEC_HEVC; break; case 'y': config->unsupported_version = true; diff --git a/src/main.c b/src/main.c index 275e7b7..1881718 100644 --- a/src/main.c +++ b/src/main.c @@ -148,8 +148,7 @@ static void help() { printf("\t-height \tVertical resolution (default 720)\n"); printf("\t-bitrate \tSpecify the bitrate in Kbps\n"); printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); - printf("\t-hevc\t\t\tUse the high efficiency video coding (HEVC)\n"); - printf("\t-h264\t\t\tUse the advanced video coding (H264)\n"); + printf("\t-codec \t\tSelect used codec auto/h264/h265 (default auto)\n"); printf("\t-remote\t\t\tEnable remote optimizations\n"); printf("\t-app \t\tName of app to stream\n"); printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n"); From a8f432cbed2ce7ff94429b71ef4c20de12fcf90d Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 9 Jun 2017 22:58:50 +0200 Subject: [PATCH 52/72] Add missing parameters to help command --- src/config.c | 2 +- src/main.c | 7 +++++-- src/platform.c | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/config.c b/src/config.c index 9843375..5a08024 100644 --- a/src/config.c +++ b/src/config.c @@ -293,7 +293,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO; config->stream.supportsHevc = false; - config->platform = "default"; + config->platform = "auto"; config->app = "Steam"; config->action = NULL; config->address = NULL; diff --git a/src/main.c b/src/main.c index 1881718..02fb0e7 100644 --- a/src/main.c +++ b/src/main.c @@ -143,12 +143,13 @@ static void help() { printf("\n Streaming options\n\n"); printf("\t-720\t\t\tUse 1280x720 resolution [default]\n"); printf("\t-1080\t\t\tUse 1920x1080 resolution\n"); - printf("\t-4k\t\t\t\tUse 3840x2160 resolution\n"); + printf("\t-4k\t\t\tUse 3840x2160 resolution\n"); printf("\t-width \t\tHorizontal resolution (default 1280)\n"); printf("\t-height \tVertical resolution (default 720)\n"); + printf("\t-fps \t\tSpecify the fps to use (default -1)\n"); printf("\t-bitrate \tSpecify the bitrate in Kbps\n"); printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); - printf("\t-codec \t\tSelect used codec auto/h264/h265 (default auto)\n"); + printf("\t-codec \t\tSelect used codec: auto/h264/h265 (default auto)\n"); printf("\t-remote\t\t\tEnable remote optimizations\n"); printf("\t-app \t\tName of app to stream\n"); printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n"); @@ -156,6 +157,8 @@ static void help() { printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n"); printf("\t-keydir \tLoad encryption keys from directory\n"); printf("\t-mapping \t\tUse as gamepad mappings configuration file\n"); + printf("\t-platform \tSpecify system used for audio, video and input: pi/imx/aml/x11/sdl (default auto)\n"); + printf("\t-unsupported\t\tTry streaming if GFE version is unsupported\n"); #if defined(HAVE_SDL) || defined(HAVE_X11) printf("\n Video options (SDL and X11 only)\n\n"); printf("\t-windowed\t\tDisplay screen in a window\n"); diff --git a/src/platform.c b/src/platform.c index 8837b82..449312d 100644 --- a/src/platform.c +++ b/src/platform.c @@ -34,7 +34,7 @@ typedef bool(*ImxInit)(); enum platform platform_check(char* name) { - bool std = strcmp(name, "default") == 0; + bool std = strcmp(name, "auto") == 0; #ifdef HAVE_IMX if (std || strcmp(name, "imx") == 0) { void *handle = dlopen("libmoonlight-imx.so", RTLD_NOW | RTLD_GLOBAL); From 6c8b0aea6609714331db2c30914f6cf0c8d0b8b2 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 9 Jun 2017 23:03:45 +0200 Subject: [PATCH 53/72] Remove debug build type --- CMakeLists.txt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff325dd..f00292a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,10 +51,7 @@ endif() SET(MOONLIGHT_COMMON_INCLUDE_DIR ./third_party/moonlight-common-c/src) SET(GAMESTREAM_INCLUDE_DIR ./libgamestream) -if(CMAKE_BUILD_TYPE MATCHES Debug) - list(APPEND MOONLIGHT_DEFINITIONS LC_DEBUG) - list(APPEND MOONLIGHT_OPTIONS DEBUG) -elseif(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT SOFTWARE_FOUND) +if(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT SOFTWARE_FOUND) message(FATAL_ERROR "No video output available") endif() @@ -77,7 +74,7 @@ if (SOFTWARE_FOUND) endif() endif() -if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR CMAKE_BUILD_TYPE MATCHES Debug) +if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR X11_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED) list(APPEND MOONLIGHT_OPTIONS EMBEDDED) endif() From b21dce7f2a8c8e74c4793059c8512b48a9af7ad7 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 15:28:14 +0200 Subject: [PATCH 54/72] Cleanup VDPAU code --- src/video/ffmpeg_vdpau.c | 48 ++++++++++++++-------------------------- src/video/ffmpeg_vdpau.h | 2 +- src/video/x11.c | 2 +- 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/video/ffmpeg_vdpau.c b/src/video/ffmpeg_vdpau.c index eddd024..56bd7af 100644 --- a/src/video/ffmpeg_vdpau.c +++ b/src/video/ffmpeg_vdpau.c @@ -93,17 +93,13 @@ static enum AVPixelFormat vdp_get_format(AVCodecContext* context, const enum AVP static void vdp_draw_horiz_band(struct AVCodecContext* context, const AVFrame* frame, int offset[4], int y, int type, int height) { struct vdpau_render_state* render_state = (struct vdpau_render_state*)frame->data[0]; - VdpStatus status = vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers); - status = vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers); + vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers); } int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height) { if (vdp_device) return vdp_device; - if (!display) - display = XOpenDisplay(NULL); - VdpStatus status = vdp_device_create_x11(display, DefaultScreen(display), &vdp_device, &vdp_get_proc_address); if (status != VDP_STATUS_OK) return -1; @@ -122,7 +118,6 @@ int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int hei decoder_ctx->get_buffer2 = vdp_get_buffer; decoder_ctx->draw_horiz_band = vdp_draw_horiz_band; decoder_ctx->get_format = vdp_get_format; - decoder_ctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; cpu_frame = av_frame_alloc(); if (cpu_frame == NULL) { @@ -145,28 +140,6 @@ int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int hei return -1; } - VdpVideoMixerFeature features[] = { - VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL, - VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL, - }; - VdpVideoMixerParameter params[] = { - VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH, - VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT, - VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE, - VDP_VIDEO_MIXER_PARAMETER_LAYERS - }; - VdpChromaType chroma = VDP_CHROMA_TYPE_420; - int numLayers = 0; - void const* paramValues[] = { &width, &height, &chroma, &numLayers }; - - status = vdp_video_mixer_create(vdp_device, 0, features, 4, params, paramValues, &vdp_mixer); - if (status != VDP_STATUS_OK) { - printf("Can't create VDPAU mixer\n"); - return -1; - } - - vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, width, height, &vdp_output); - return vdp_device; } @@ -183,11 +156,23 @@ AVFrame* vdpau_get_frame(AVFrame* dec_frame) { cpu_frame->linesize[1] }; - VdpStatus status = vdp_video_surface_get_bits_y_cb_cr(render_state->surface, VDP_YCBCR_FORMAT_YV12, dest, pitches); + vdp_video_surface_get_bits_y_cb_cr(render_state->surface, VDP_YCBCR_FORMAT_YV12, dest, pitches); return cpu_frame; } -int vdpau_init_queue(Drawable win) { +int vdpau_init_presentation(Drawable win, int width, int height) { + VdpVideoMixerParameter params[] = { + VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH, + VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT + }; + void const* paramValues[] = { &width, &height }; + + if (vdp_video_mixer_create(vdp_device, 0, NULL, 2, params, paramValues, &vdp_mixer) != VDP_STATUS_OK) + return -1; + + if (vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, width, height, &vdp_output) != VDP_STATUS_OK) + return -1; + if(vdp_presentation_queue_target_create_x11(vdp_device, win, &vdp_queue_target) != VDP_STATUS_OK) return -1; @@ -199,8 +184,7 @@ int vdpau_init_queue(Drawable win) { void vdpau_queue(AVFrame* dec_frame) { struct vdpau_render_state *render_state = (struct vdpau_render_state *)dec_frame->data[0]; - int field = VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME; - vdp_video_mixer_render(vdp_mixer, VDP_INVALID_HANDLE, 0, field, 0, (VdpVideoSurface*)VDP_INVALID_HANDLE, render_state->surface, 0,(VdpVideoSurface*)VDP_INVALID_HANDLE, NULL, vdp_output, NULL, NULL, 0, NULL); + vdp_video_mixer_render(vdp_mixer, VDP_INVALID_HANDLE, 0, VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME, 0, (VdpVideoSurface*)VDP_INVALID_HANDLE, render_state->surface, 0,(VdpVideoSurface*)VDP_INVALID_HANDLE, NULL, vdp_output, NULL, NULL, 0, NULL); vdp_presentation_queue_display(vdp_queue, vdp_output, 0, 0, 0); } diff --git a/src/video/ffmpeg_vdpau.h b/src/video/ffmpeg_vdpau.h index 27d0554..b47861d 100644 --- a/src/video/ffmpeg_vdpau.h +++ b/src/video/ffmpeg_vdpau.h @@ -22,5 +22,5 @@ int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height); AVFrame* vdpau_get_frame(AVFrame* dec_frame); -int vdpau_init_queue(Drawable win); +int vdpau_init_presentation(Drawable win, int width, int height); void vdpau_queue(AVFrame* dec_frame); diff --git a/src/video/x11.c b/src/video/x11.c index 1aa5be3..e83033b 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -99,7 +99,7 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont if (ffmpeg_decoder == SOFTWARE) egl_init(display, window, width, height); else - vdpau_init_queue(window); + vdpau_init_presentation(window, width, height); x11_input_init(display, window); From 819503dd4d55942d3683d006a91a644df9f6b306 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 15:30:05 +0200 Subject: [PATCH 55/72] Remove VDPAU support on SDL --- src/video/sdl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/video/sdl.c b/src/video/sdl.c index 459f3a1..f1a20ee 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -34,8 +34,6 @@ static char* ffmpeg_buffer; static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; - if (drFlags & FORCE_HARDWARE_ACCELERATION) - avc_flags |= HARDWARE_ACCELERATION; if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN), NULL) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); From c1b06dca758f7a5ce4f1d4ab54472386648bb15d Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 15:42:53 +0200 Subject: [PATCH 56/72] Provide seperate platform option to enable VDPAU --- CMakeLists.txt | 13 ++++++------- docs/README.pod | 5 ----- src/config.c | 5 ----- src/config.h | 1 - src/main.c | 10 +++------- src/platform.c | 8 ++++++++ src/platform.h | 2 +- src/video/video.h | 5 ++++- src/video/x11.c | 13 ++++++++++++- 9 files changed, 34 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f00292a..be4b79b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,20 +32,19 @@ pkg_check_modules(CEC libcec>=3.0.0) pkg_check_modules(EGL egl) pkg_check_modules(GLES glesv2) +set(VDPAU_FOUND FALSE) +set(SOFTWARE_FOUND FALSE) + if(AVCODEC_FOUND AND AVUTIL_FOUND) if(EGL_FOUND AND GLES_FOUND AND XLIB_FOUND) set(X11_FOUND TRUE) + if(XLIB_FOUND AND LIBVA_FOUND) + set(VDPAU_FOUND TRUE) + endif() endif() if(SDL_FOUND OR X11_FOUND) set(SOFTWARE_FOUND TRUE) - if(XLIB_FOUND AND LIBVA_FOUND) - set(VDPAU_FOUND TRUE) - else() - set(VDPAU_FOUND FALSE) - endif() endif() -else() - set(SOFTWARE_FOUND FALSE) endif() SET(MOONLIGHT_COMMON_INCLUDE_DIR ./third_party/moonlight-common-c/src) diff --git a/docs/README.pod b/docs/README.pod index 7387cd4..757a0ca 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -148,11 +148,6 @@ The default value is 'sysdefault' for ALSA and 'hdmi' for OMX on the Raspberry P Display the stream in a window instead of fullscreen. Only available when X11 or SDL platform is used. -=item B<-forcehw> - -This will enable unsupported hardware acceleration. -Currently only VDPAU when using X11 or SDL is unsupported. - =back =head1 CONFIG FILE diff --git a/src/config.c b/src/config.c index 5a08024..5e580f9 100644 --- a/src/config.c +++ b/src/config.c @@ -61,7 +61,6 @@ static struct option long_options[] = { {"windowed", no_argument, NULL, 't'}, {"surround", no_argument, NULL, 'u'}, {"fps", required_argument, NULL, 'v'}, - {"forcehw", no_argument, NULL, 'w'}, {"codec", required_argument, NULL, 'x'}, {"unsupported", no_argument, NULL, 'y'}, {0, 0, 0, 0}, @@ -193,9 +192,6 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { case 'v': config->stream.fps = atoi(value); break; - case 'w': - config->forcehw = true; - break; case 'x': if (strcasecmp(value, "auto") == 0) config->codec = CODEC_UNSPECIFIED; @@ -303,7 +299,6 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->localaudio = false; config->fullscreen = true; config->unsupported_version = false; - config->forcehw = false; config->codec = CODEC_UNSPECIFIED; config->inputsCount = 0; diff --git a/src/config.h b/src/config.h index bd45269..0976e7d 100644 --- a/src/config.h +++ b/src/config.h @@ -38,7 +38,6 @@ typedef struct _CONFIGURATION { bool sops; bool localaudio; bool fullscreen; - bool forcehw; bool unsupported_version; char* inputs[MAX_INPUTS]; int inputsCount; diff --git a/src/main.c b/src/main.c index 02fb0e7..e4e1ac7 100644 --- a/src/main.c +++ b/src/main.c @@ -105,9 +105,6 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys if (config->fullscreen) drFlags |= DISPLAY_FULLSCREEN; - if (config->forcehw) - drFlags |= FORCE_HARDWARE_ACCELERATION; - printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate); platform_start(system); @@ -157,15 +154,14 @@ static void help() { printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n"); printf("\t-keydir \tLoad encryption keys from directory\n"); printf("\t-mapping \t\tUse as gamepad mappings configuration file\n"); - printf("\t-platform \tSpecify system used for audio, video and input: pi/imx/aml/x11/sdl (default auto)\n"); + printf("\t-platform \tSpecify system used for audio, video and input: pi/imx/aml/x11/x11_vdpau/sdl (default auto)\n"); printf("\t-unsupported\t\tTry streaming if GFE version is unsupported\n"); #if defined(HAVE_SDL) || defined(HAVE_X11) - printf("\n Video options (SDL and X11 only)\n\n"); + printf("\n WM options (SDL and X11 only)\n\n"); printf("\t-windowed\t\tDisplay screen in a window\n"); - printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif #ifdef HAVE_EMBEDDED - printf("\n I/O options (PI, IMX, AML and X11 only)\n\n"); + printf("\n I/O options (Not for SDL)\n\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); printf("\t-audio \t\tUse as audio output device\n"); #endif diff --git a/src/platform.c b/src/platform.c index 449312d..3644bdf 100644 --- a/src/platform.c +++ b/src/platform.c @@ -62,6 +62,10 @@ enum platform platform_check(char* name) { #ifdef HAVE_X11 if (std || strcmp(name, "x11") == 0) return X11; + #ifdef HAVE_VDPAU + if (std || strcmp(name, "x11_vdpau") == 0) + return X11_VDPAU; + #endif #endif #ifdef HAVE_SDL if (std || strcmp(name, "sdl") == 0) @@ -110,6 +114,10 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) { #ifdef HAVE_X11 case X11: return &decoder_callbacks_x11; + #ifdef HAVE_VDPAU + case X11_VDPAU: + return &decoder_callbacks_x11_vdpau; + #endif #endif #ifdef HAVE_SDL case SDL: diff --git a/src/platform.h b/src/platform.h index eecd390..61a7541 100644 --- a/src/platform.h +++ b/src/platform.h @@ -26,7 +26,7 @@ #define IS_EMBEDDED(SYSTEM) SYSTEM != SDL -enum platform { NONE, SDL, X11, PI, IMX, AML, FAKE }; +enum platform { NONE, SDL, X11, X11_VDPAU, PI, IMX, AML, FAKE }; enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); diff --git a/src/video/video.h b/src/video/video.h index 17360d6..6127414 100644 --- a/src/video/video.h +++ b/src/video/video.h @@ -20,10 +20,13 @@ #include #define DISPLAY_FULLSCREEN 1 -#define FORCE_HARDWARE_ACCELERATION 2 +#define ENABLE_HARDWARE_ACCELERATION 2 #ifdef HAVE_X11 extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; +#ifdef HAVE_VDPAU +extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau; +#endif #endif #ifdef HAVE_SDL extern DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl; diff --git a/src/video/x11.c b/src/video/x11.c index e83033b..519ffdd 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -52,7 +52,7 @@ static int frame_handle(int pipefd) { int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; - if (drFlags & FORCE_HARDWARE_ACCELERATION) + if (drFlags & ENABLE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); @@ -113,6 +113,10 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont return 0; } +int x11_setup_vdpau(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { + x11_setup(videoFormat, width, height, redrawRate, context, drFlags | ENABLE_HARDWARE_ACCELERATION); +} + void x11_cleanup() { ffmpeg_destroy(); egl_destroy(); @@ -147,3 +151,10 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = { .submitDecodeUnit = x11_submit_decode_unit, .capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT, }; + +DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = { + .setup = x11_setup_vdpau, + .cleanup = x11_cleanup, + .submitDecodeUnit = x11_submit_decode_unit, + .capabilities = CAPABILITY_DIRECT_SUBMIT, +}; From 85f90459fefeeb1815632af5242f76a4bf5591bd Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 16:44:27 +0200 Subject: [PATCH 57/72] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 66ce27a..a38af3e 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 66ce27ab19213f8e4b36dcaf33c5b3e909912b9c +Subproject commit a38af3e80ad6da52422a9deb2cfb657719d8a982 From 54b652c35038c6b223f56cf23673cb2725906a45 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 17:58:43 +0200 Subject: [PATCH 58/72] Autodetect VDPAU support --- src/platform.c | 12 +++++++----- src/video/ffmpeg.c | 4 ++-- src/video/ffmpeg.h | 2 +- src/video/ffmpeg_vdpau.c | 17 +++++++++++----- src/video/ffmpeg_vdpau.h | 4 +++- src/video/sdl.c | 2 +- src/video/video.h | 3 +++ src/video/x11.c | 42 ++++++++++++++++++++++++++++------------ 8 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/platform.c b/src/platform.c index 3644bdf..4955cf0 100644 --- a/src/platform.c +++ b/src/platform.c @@ -60,12 +60,14 @@ enum platform platform_check(char* name) { } #endif #ifdef HAVE_X11 - if (std || strcmp(name, "x11") == 0) + if (std || strcmp(name, "x11") == 0 || strcmp(name, "x11_vdpau") == 0) { + int x11 = x11_init(strcmp(name, "x11") != 0); + #ifdef HAVE_VDPAU + if (strcmp(name, "x11") != 0 && x11 == 0) + return X11_VDPAU; + #endif return X11; - #ifdef HAVE_VDPAU - if (std || strcmp(name, "x11_vdpau") == 0) - return X11_VDPAU; - #endif + } #endif #ifdef HAVE_SDL if (std || strcmp(name, "sdl") == 0) diff --git a/src/video/ffmpeg.c b/src/video/ffmpeg.c index 653676a..00e6dda 100644 --- a/src/video/ffmpeg.c +++ b/src/video/ffmpeg.c @@ -46,7 +46,7 @@ enum decoders ffmpeg_decoder; // This function must be called before // any other decoding functions -int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count, void* context) { +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count) { // Initialize the avcodec library and register codecs av_log_set_level(AV_LOG_QUIET); avcodec_register_all(); @@ -133,7 +133,7 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer #ifdef HAVE_VDPAU if (ffmpeg_decoder == VDPAU) - vdpau_init(decoder_ctx, (Display*) context, width, height); + vdpau_init(decoder_ctx, width, height); #endif return 0; diff --git a/src/video/ffmpeg.h b/src/video/ffmpeg.h index d5449b4..9060e5e 100644 --- a/src/video/ffmpeg.h +++ b/src/video/ffmpeg.h @@ -39,7 +39,7 @@ enum decoders {SOFTWARE, VDPAU}; extern enum decoders ffmpeg_decoder; -int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count, void* context); +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer_count, int thread_count); void ffmpeg_destroy(void); int ffmpeg_draw_frame(AVFrame *pict); diff --git a/src/video/ffmpeg_vdpau.c b/src/video/ffmpeg_vdpau.c index 56bd7af..8307700 100644 --- a/src/video/ffmpeg_vdpau.c +++ b/src/video/ffmpeg_vdpau.c @@ -41,6 +41,7 @@ static struct vdpau_render_state* vdp_render_state[MAX_RENDER_STATES]; static int vdp_render_states = 0; static VdpGetProcAddress* vdp_get_proc_address; +static VdpDeviceDestroy* vdp_device_destroy; static VdpDecoderCreate* vdp_decoder_create; static VdpDecoderRender* vdp_decoder_render; static VdpVideoSurfaceGetBitsYCbCr* vdp_video_surface_get_bits_y_cb_cr; @@ -96,14 +97,16 @@ static void vdp_draw_horiz_band(struct AVCodecContext* context, const AVFrame* f vdp_decoder_render(vdp_decoder, render_state->surface, (VdpPictureInfo const*)&(render_state->info), render_state->bitstream_buffers_used, render_state->bitstream_buffers); } -int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height) { - if (vdp_device) - return vdp_device; - +int vdpau_init_lib(Display* display) { VdpStatus status = vdp_device_create_x11(display, DefaultScreen(display), &vdp_device, &vdp_get_proc_address); if (status != VDP_STATUS_OK) return -1; + vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DEVICE_DESTROY, (void**)&vdp_device_destroy); + return 0; +} + +int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) { vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR, (void**)&vdp_video_surface_get_bits_y_cb_cr); vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_SURFACE_CREATE, (void**)&vdp_video_surface_create); vdp_get_proc_address(vdp_device, VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, (void**)&vdp_output_surface_create); @@ -134,7 +137,7 @@ int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int hei return -1; } - status = vdp_decoder_create(vdp_device, VDP_DECODER_PROFILE_H264_HIGH, width, height, 16, &vdp_decoder); + VdpStatus status = vdp_decoder_create(vdp_device, VDP_DECODER_PROFILE_H264_HIGH, width, height, 16, &vdp_decoder); if (status != VDP_STATUS_OK) { printf("Can't create VDPAU decoder\n"); return -1; @@ -143,6 +146,10 @@ int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int hei return vdp_device; } +void vdpau_destroy() { + vdp_device_destroy(vdp_device); +} + AVFrame* vdpau_get_frame(AVFrame* dec_frame) { struct vdpau_render_state *render_state = (struct vdpau_render_state *)dec_frame->data[0]; void *dest[3] = { diff --git a/src/video/ffmpeg_vdpau.h b/src/video/ffmpeg_vdpau.h index b47861d..4507e9a 100644 --- a/src/video/ffmpeg_vdpau.h +++ b/src/video/ffmpeg_vdpau.h @@ -20,7 +20,9 @@ #include #include -int vdpau_init(AVCodecContext* decoder_ctx, Display* display, int width, int height); +int vdpau_init_lib(Display* display); +int vdpau_init(AVCodecContext* decoder_ctx, int width, int height); +void vdpau_destroy(); AVFrame* vdpau_get_frame(AVFrame* dec_frame); int vdpau_init_presentation(Drawable win, int width, int height); void vdpau_queue(AVFrame* dec_frame); diff --git a/src/video/sdl.c b/src/video/sdl.c index f1a20ee..e20a3a1 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -35,7 +35,7 @@ static char* ffmpeg_buffer; static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; - if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN), NULL) < 0) { + if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); return -1; } diff --git a/src/video/video.h b/src/video/video.h index 6127414..e56aeec 100644 --- a/src/video/video.h +++ b/src/video/video.h @@ -19,10 +19,13 @@ #include +#include + #define DISPLAY_FULLSCREEN 1 #define ENABLE_HARDWARE_ACCELERATION 2 #ifdef HAVE_X11 +int x11_init(bool vdpau); extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; #ifdef HAVE_VDPAU extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau; diff --git a/src/video/x11.c b/src/video/x11.c index 519ffdd..a6ab3e0 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -20,7 +20,9 @@ #include "video.h" #include "egl.h" #include "ffmpeg.h" +#ifdef HAVE_VDPAU #include "ffmpeg_vdpau.h" +#endif #include "../input/x11.h" #include "../loop.h" @@ -37,7 +39,7 @@ static char* ffmpeg_buffer = NULL; -static Display *display; +static Display *display = NULL; static int pipefd[2]; @@ -50,23 +52,30 @@ static int frame_handle(int pipefd) { return LOOP_OK; } -int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - int avc_flags = SLICE_THREADING; - if (drFlags & ENABLE_HARDWARE_ACCELERATION) - avc_flags |= HARDWARE_ACCELERATION; +int x11_init(bool vdpau) { + XInitThreads(); + display = XOpenDisplay(NULL); + if (!display) + return -1; + #ifdef HAVE_VDPAU + if (vdpau && vdpau_init_lib(display) != 0) + return -2; + #endif + + return 0; +} + +int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); if (ffmpeg_buffer == NULL) { fprintf(stderr, "Not enough memory\n"); - ffmpeg_destroy(); return -1; } - XInitThreads(); - display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "Error: failed to open X display.\n"); - return -2; + return -1; } Window root = DefaultRootWindow(display); @@ -90,16 +99,25 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } + XFlush(display); - if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2, display) < 0) { + int avc_flags = SLICE_THREADING; + #ifdef HAVE_VDPAU + if (drFlags & ENABLE_HARDWARE_ACCELERATION) + avc_flags |= HARDWARE_ACCELERATION; + #endif + + if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); return -1; } if (ffmpeg_decoder == SOFTWARE) egl_init(display, window, width, height); - else + #ifdef HAVE_VDPAU + else if (ffmpeg_decoder == VDPAU) vdpau_init_presentation(window, width, height); + #endif x11_input_init(display, window); @@ -114,7 +132,7 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont } int x11_setup_vdpau(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - x11_setup(videoFormat, width, height, redrawRate, context, drFlags | ENABLE_HARDWARE_ACCELERATION); + return x11_setup(videoFormat, width, height, redrawRate, context, drFlags | ENABLE_HARDWARE_ACCELERATION); } void x11_cleanup() { From 966dce5f695672fbec5988a0d5ba15f7acce78b9 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 18:05:26 +0200 Subject: [PATCH 59/72] Add ctl-alt-shift-q combo to X11 --- src/input/x11.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/input/x11.c b/src/input/x11.c index 09d8d95..99f4b77 100644 --- a/src/input/x11.c +++ b/src/input/x11.c @@ -57,8 +57,12 @@ static int x11_handler(int fd) { case KeyRelease: if (event.xkey.keycode >= 8 && event.xkey.keycode < (sizeof(keyCodes)/sizeof(keyCodes[0]) + 8)) { if ((keyboard_modifiers & MODIFIERS) == MODIFIERS && event.type == KeyRelease) { - grabbed = !grabbed; - XDefineCursor(display, window, grabbed ? cursor : 0); + if (event.xkey.keycode == 0x18) + return LOOP_RETURN; + else { + grabbed = !grabbed; + XDefineCursor(display, window, grabbed ? cursor : 0); + } } int modifier = 0; From 0ea71fc6357d6fb2a13305647710c6c680d1f4c1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 20:36:42 +0200 Subject: [PATCH 60/72] Fix fullscreen for VDPAU --- src/video/ffmpeg_vdpau.c | 4 ++-- src/video/ffmpeg_vdpau.h | 2 +- src/video/x11.c | 15 +++++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/video/ffmpeg_vdpau.c b/src/video/ffmpeg_vdpau.c index 8307700..3660e6b 100644 --- a/src/video/ffmpeg_vdpau.c +++ b/src/video/ffmpeg_vdpau.c @@ -167,7 +167,7 @@ AVFrame* vdpau_get_frame(AVFrame* dec_frame) { return cpu_frame; } -int vdpau_init_presentation(Drawable win, int width, int height) { +int vdpau_init_presentation(Drawable win, int width, int height, int display_width, int display_height) { VdpVideoMixerParameter params[] = { VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH, VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT @@ -177,7 +177,7 @@ int vdpau_init_presentation(Drawable win, int width, int height) { if (vdp_video_mixer_create(vdp_device, 0, NULL, 2, params, paramValues, &vdp_mixer) != VDP_STATUS_OK) return -1; - if (vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, width, height, &vdp_output) != VDP_STATUS_OK) + if (vdp_output_surface_create(vdp_device, VDP_RGBA_FORMAT_B8G8R8A8, display_width, display_height, &vdp_output) != VDP_STATUS_OK) return -1; if(vdp_presentation_queue_target_create_x11(vdp_device, win, &vdp_queue_target) != VDP_STATUS_OK) diff --git a/src/video/ffmpeg_vdpau.h b/src/video/ffmpeg_vdpau.h index 4507e9a..f28a61b 100644 --- a/src/video/ffmpeg_vdpau.h +++ b/src/video/ffmpeg_vdpau.h @@ -24,5 +24,5 @@ int vdpau_init_lib(Display* display); int vdpau_init(AVCodecContext* decoder_ctx, int width, int height); void vdpau_destroy(); AVFrame* vdpau_get_frame(AVFrame* dec_frame); -int vdpau_init_presentation(Drawable win, int width, int height); +int vdpau_init_presentation(Drawable win, int width, int height, int display_width, int display_height); void vdpau_queue(AVFrame* dec_frame); diff --git a/src/video/x11.c b/src/video/x11.c index a6ab3e0..c46e097 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -78,9 +78,20 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont return -1; } + int display_width; + int display_height; + if (drFlags & DISPLAY_FULLSCREEN) { + Screen* screen = DefaultScreenOfDisplay(display); + display_width = WidthOfScreen(screen); + display_height = HeightOfScreen(screen); + } else { + display_width = width; + display_height = height; + } + Window root = DefaultRootWindow(display); XSetWindowAttributes winattr = { .event_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask }; - Window window = XCreateWindow(display, root, 0, 0, width, height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr); + Window window = XCreateWindow(display, root, 0, 0, display_width, display_height, 0, CopyFromParent, InputOutput, CopyFromParent, CWEventMask, &winattr); XMapWindow(display, window); XStoreName(display, window, "Moonlight"); @@ -116,7 +127,7 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont egl_init(display, window, width, height); #ifdef HAVE_VDPAU else if (ffmpeg_decoder == VDPAU) - vdpau_init_presentation(window, width, height); + vdpau_init_presentation(window, width, height, display_width, display_height); #endif x11_input_init(display, window); From ae54a30ea06bc1f042dafb036365f20fbfa190ba Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 20:48:17 +0200 Subject: [PATCH 61/72] Allocate pipe only for EGL --- src/video/egl.c | 2 +- src/video/egl.h | 2 +- src/video/x11.c | 31 +++++++++++++++---------------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/video/egl.c b/src/video/egl.c index 0486e85..bed03e7 100644 --- a/src/video/egl.c +++ b/src/video/egl.c @@ -170,7 +170,7 @@ void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_windo eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } -void egl_draw(const uint8_t* image[3]) { +void egl_draw(uint8_t* image[3]) { if (!current) { eglMakeCurrent(display, surface, surface, context); current = True; diff --git a/src/video/egl.h b/src/video/egl.h index fe6b800..b38260c 100644 --- a/src/video/egl.h +++ b/src/video/egl.h @@ -20,5 +20,5 @@ #include void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_window, int display_width, int display_height); -void egl_draw(const uint8_t* image[3]); +void egl_draw(uint8_t* image[3]); void egl_destroy(); diff --git a/src/video/x11.c b/src/video/x11.c index c46e097..95de9ee 100644 --- a/src/video/x11.c +++ b/src/video/x11.c @@ -44,10 +44,10 @@ static Display *display = NULL; static int pipefd[2]; static int frame_handle(int pipefd) { - const unsigned char** data = NULL; - while (read(pipefd, &data, sizeof(void*)) > 0); - if (data) - egl_draw(data); + AVFrame* frame = NULL; + while (read(pipefd, &frame, sizeof(void*)) > 0); + if (frame) + egl_draw(frame->data); return LOOP_OK; } @@ -123,8 +123,15 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont return -1; } - if (ffmpeg_decoder == SOFTWARE) + if (ffmpeg_decoder == SOFTWARE) { egl_init(display, window, width, height); + if (pipe(pipefd) == -1) { + fprintf(stderr, "Can't create communication channel between threads\n"); + return -2; + } + loop_add_fd(pipefd[0], &frame_handle, POLLIN); + fcntl(pipefd[0], F_SETFL, O_NONBLOCK); + } #ifdef HAVE_VDPAU else if (ffmpeg_decoder == VDPAU) vdpau_init_presentation(window, width, height, display_width, display_height); @@ -132,13 +139,6 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont x11_input_init(display, window); - if (pipe(pipefd) == -1) { - fprintf(stderr, "Can't create communication channel between threads\n"); - return -2; - } - loop_add_fd(pipefd[0], &frame_handle, POLLIN); - fcntl(pipefd[0], F_SETFL, O_NONBLOCK); - return 0; } @@ -163,10 +163,9 @@ int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { ffmpeg_decode(ffmpeg_buffer, length); AVFrame* frame = ffmpeg_get_frame(true); if (frame != NULL) { - if (ffmpeg_decoder == SOFTWARE) { - void* pointer = frame->data; - write(pipefd[1], &pointer, sizeof(void*)); - } else + if (ffmpeg_decoder == SOFTWARE) + write(pipefd[1], &frame, sizeof(void*)); + else if (ffmpeg_decoder == VDPAU) vdpau_queue(frame); } } From c34282c3133d8dc3664d04fcb09c8361bc7dd0d0 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 20:54:59 +0200 Subject: [PATCH 62/72] Clearer message about missing mapping --- src/config.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/config.c b/src/config.c index 5e580f9..6e27fb3 100644 --- a/src/config.c +++ b/src/config.c @@ -347,10 +347,8 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->stream.bitrate = 5000; } - if (inputAdded) { - if (config->mapping == NULL) { - fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n"); - exit(-1); - } + if (config->mapping == NULL) { + fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n"); + exit(-1); } } From 637c4f69238620c17ce6d1df27d6b7269ecb8187 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 10 Jun 2017 21:07:42 +0200 Subject: [PATCH 63/72] Update version --- CMakeLists.txt | 7 +------ libgamestream/CMakeLists.txt | 8 +++++--- src/configuration.h.in | 8 ++++---- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be4b79b..ed0ad30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,8 @@ -project(moonlight-embedded C) cmake_minimum_required(VERSION 3.1) +project(moonlight-embedded VERSION 2.4.0 LANGUAGES C) SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake) -set(MOONLIGHT_MAJOR_VERSION 2) -set(MOONLIGHT_MINOR_VERSION 3) -set(MOONLIGHT_PATCH_VERSION 0) -set(MOONLIGHT_VERSION ${MOONLIGHT_MAJOR_VERSION}.${MOONLIGHT_MINOR_VERSION}.${MOONLIGHT_PATCH_VERSION}) - aux_source_directory(./src SRC_LIST) list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c) diff --git a/libgamestream/CMakeLists.txt b/libgamestream/CMakeLists.txt index 23d7da9..c6ddde5 100644 --- a/libgamestream/CMakeLists.txt +++ b/libgamestream/CMakeLists.txt @@ -1,7 +1,9 @@ +set(SO_VERSION 1) + find_package(LibUUID REQUIRED) find_package(Threads REQUIRED) find_package(CURL REQUIRED) -find_package(OpenSSL 1.0.2 REQUIRED) +find_package(OpenSSL REQUIRED) find_package(EXPAT REQUIRED) pkg_check_modules(AVAHI REQUIRED avahi-client) @@ -19,8 +21,8 @@ add_library(gamestream SHARED ${GAMESTREAM_SRC_LIST}) set_property(TARGET gamestream PROPERTY C_STANDARD 99) target_link_libraries(gamestream moonlight-common) -set_target_properties(gamestream PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) -set_target_properties(moonlight-common PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) +set_target_properties(gamestream PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION}) +set_target_properties(moonlight-common PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION}) target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS}) target_include_directories(moonlight-common PRIVATE ../third_party/moonlight-common-c/reedsolomon ${ENET_INCLUDE_DIRS}) diff --git a/src/configuration.h.in b/src/configuration.h.in index 1da5c11..e329365 100644 --- a/src/configuration.h.in +++ b/src/configuration.h.in @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,8 +17,8 @@ * along with Moonlight; if not, see . */ -#define VERSION_MAJOR @MOONLIGHT_MAJOR_VERSION@ -#define VERSION_MINOR @MOONLIGHT_MINOR_VERSION@ -#define VERSION_PATCH @MOONLIGHT_PATCH_VERSION@ +#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@ +#define VERSION_MINOR @PROJECT_VERSION_MINOR@ +#define VERSION_PATCH @PROJECT_VERSION_PATCH@ #define COMPILE_OPTIONS "@MOONLIGHT_OPTIONS@" From 1ed34ff95d26452e029f1cfbea31af473151d7ae Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 12:45:12 +0200 Subject: [PATCH 64/72] Allocate memory for RSA key --- libgamestream/mkcert.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libgamestream/mkcert.c b/libgamestream/mkcert.c index 4ced6f6..6dfc1c2 100644 --- a/libgamestream/mkcert.c +++ b/libgamestream/mkcert.c @@ -103,6 +103,9 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) { } else { x = *x509p; } + + if ((rsa = RSA_new()) == NULL) + goto err; BIGNUM* bne = BN_new(); if (bne == NULL) { From c9a3370db9a43821cb6716fd85449b8077d3a73b Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 12:45:55 +0200 Subject: [PATCH 65/72] Determine platform only when stream is started --- src/main.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main.c b/src/main.c index e4e1ac7..863a33f 100644 --- a/src/main.c +++ b/src/main.c @@ -185,16 +185,6 @@ int main(int argc, char* argv[]) { if (config.action == NULL || strcmp("help", config.action) == 0) help(); - enum platform system = platform_check(config.platform); - if (system == 0) { - fprintf(stderr, "Platform '%s' not found\n", config.platform); - exit(-1); - } else if (system == SDL && config.audio_device != NULL) { - fprintf(stderr, "You can't select a audio device for SDL\n"); - exit(-1); - } - config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system)); - if (config.address == NULL) { config.address = malloc(MAX_ADDRESS_SIZE); if (config.address == NULL) { @@ -242,6 +232,17 @@ int main(int argc, char* argv[]) { applist(&server); } else if (strcmp("stream", config.action) == 0) { pair_check(&server); + enum platform system = platform_check(config.platform); + + if (system == 0) { + fprintf(stderr, "Platform '%s' not found\n", config.platform); + exit(-1); + } else if (system == SDL && config.audio_device != NULL) { + fprintf(stderr, "You can't select a audio device for SDL\n"); + exit(-1); + } + config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system)); + if (IS_EMBEDDED(system)) { struct mapping* mappings = mapping_load(config.mapping); From e462c03d7b1737ae1ab09b2e1fb74b972c5c2b77 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 14:03:31 +0200 Subject: [PATCH 66/72] Move github specific files to .github --- CONTRIBUTING.md => .github/CONTRIBUTING.md | 0 ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md | 0 PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename CONTRIBUTING.md => .github/CONTRIBUTING.md (100%) rename ISSUE_TEMPLATE.md => .github/ISSUE_TEMPLATE.md (100%) rename PULL_REQUEST_TEMPLATE.md => .github/PULL_REQUEST_TEMPLATE.md (100%) diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md similarity index 100% rename from ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md From 021fbaf8109356b8e653b8928c93afb087a94f12 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 14:09:20 +0200 Subject: [PATCH 67/72] Add platform option to manpage --- docs/README.pod | 5 +++++ src/main.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/README.pod b/docs/README.pod index 757a0ca..2a9f5aa 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -132,6 +132,11 @@ Use I as the mapping file for all inputs. This mapping file should have the same format as the gamecontrollerdb.txt for SDL2. By default the gamecontrollerdb.txt provided by Moonlight Embedded is used. +=item B<-platform> [I] + +Select platform for audio and video output and input. + can be pi, imx, aml, x11, x11_vdpau, sdl or fake. + =item B<-input> [I] Enable the I device. diff --git a/src/main.c b/src/main.c index 863a33f..77671e2 100644 --- a/src/main.c +++ b/src/main.c @@ -154,7 +154,7 @@ static void help() { printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n"); printf("\t-keydir \tLoad encryption keys from directory\n"); printf("\t-mapping \t\tUse as gamepad mappings configuration file\n"); - printf("\t-platform \tSpecify system used for audio, video and input: pi/imx/aml/x11/x11_vdpau/sdl (default auto)\n"); + printf("\t-platform \tSpecify system used for audio, video and input: pi/imx/aml/x11/x11_vdpau/sdl/fake (default auto)\n"); printf("\t-unsupported\t\tTry streaming if GFE version is unsupported\n"); #if defined(HAVE_SDL) || defined(HAVE_X11) printf("\n WM options (SDL and X11 only)\n\n"); From d759cfdf370e425c1a9ddb7fae67f11845ad9130 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 14:30:51 +0200 Subject: [PATCH 68/72] Complain only about mapping when streaming --- src/config.c | 5 ----- src/main.c | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/config.c b/src/config.c index 6e27fb3..5908a18 100644 --- a/src/config.c +++ b/src/config.c @@ -346,9 +346,4 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { else config->stream.bitrate = 5000; } - - if (config->mapping == NULL) { - fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n"); - exit(-1); - } } diff --git a/src/main.c b/src/main.c index 77671e2..6a9f4fb 100644 --- a/src/main.c +++ b/src/main.c @@ -244,6 +244,10 @@ int main(int argc, char* argv[]) { config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system)); if (IS_EMBEDDED(system)) { + if (config.mapping == NULL) { + fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n"); + exit(-1); + } struct mapping* mappings = mapping_load(config.mapping); for (int i=0;i Date: Sun, 11 Jun 2017 18:06:50 +0200 Subject: [PATCH 69/72] Add verbose option and hide most logging --- docs/README.pod | 8 ++++++++ src/config.c | 5 +++++ src/config.h | 1 + src/connection.c | 10 ++++++++++ src/connection.h | 2 ++ src/main.c | 19 ++++++++++++++----- third_party/moonlight-common-c | 2 +- 7 files changed, 41 insertions(+), 6 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 2a9f5aa..8491a35 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -137,6 +137,14 @@ By default the gamecontrollerdb.txt provided by Moonlight Embedded is used. Select platform for audio and video output and input. can be pi, imx, aml, x11, x11_vdpau, sdl or fake. +=item B<-unsupported> + +Try streaming if GFE version is unsupported + +=item B<-verbose> + +Enable verbose output + =item B<-input> [I] Enable the I device. diff --git a/src/config.c b/src/config.c index 5908a18..545fcf9 100644 --- a/src/config.c +++ b/src/config.c @@ -63,6 +63,7 @@ static struct option long_options[] = { {"fps", required_argument, NULL, 'v'}, {"codec", required_argument, NULL, 'x'}, {"unsupported", no_argument, NULL, 'y'}, + {"verbose", no_argument, NULL, 'z'}, {0, 0, 0, 0}, }; @@ -203,6 +204,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { case 'y': config->unsupported_version = true; break; + case 'z': + config->debug_level = 1; + break; case 1: if (config->action == NULL) config->action = value; @@ -289,6 +293,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO; config->stream.supportsHevc = false; + config->debug_level = 0; config->platform = "auto"; config->app = "Steam"; config->action = NULL; diff --git a/src/config.h b/src/config.h index 0976e7d..f6271bf 100644 --- a/src/config.h +++ b/src/config.h @@ -27,6 +27,7 @@ enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC }; typedef struct _CONFIGURATION { STREAM_CONFIGURATION stream; + int debug_level; char* app; char* action; char* address; diff --git a/src/connection.c b/src/connection.c index 9838350..abf42c4 100644 --- a/src/connection.c +++ b/src/connection.c @@ -20,9 +20,11 @@ #include "connection.h" #include +#include #include pthread_t main_thread_id = 0; +bool connection_debug; static void connection_terminated() { if (main_thread_id != 0) @@ -37,6 +39,13 @@ static void connection_display_transient_message(const char *msg) { printf("%s\n", msg); } +static void connection_log_message(const char* format, ...) { + va_list arglist; + va_start(arglist, format); + vprintf(format, arglist); + va_end(arglist); +} + CONNECTION_LISTENER_CALLBACKS connection_callbacks = { .stageStarting = NULL, .stageComplete = NULL, @@ -45,4 +54,5 @@ CONNECTION_LISTENER_CALLBACKS connection_callbacks = { .connectionTerminated = connection_terminated, .displayMessage = connection_display_message, .displayTransientMessage = connection_display_transient_message, + .logMessage = connection_log_message, }; diff --git a/src/connection.h b/src/connection.h index 009e787..a8a9ea6 100644 --- a/src/connection.h +++ b/src/connection.h @@ -20,6 +20,8 @@ #include #include +#include extern CONNECTION_LISTENER_CALLBACKS connection_callbacks; extern pthread_t main_thread_id; +extern bool connection_debug; diff --git a/src/main.c b/src/main.c index 6a9f4fb..4c4f443 100644 --- a/src/main.c +++ b/src/main.c @@ -105,7 +105,10 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys if (config->fullscreen) drFlags |= DISPLAY_FULLSCREEN; - printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate); + if (config->debug_level > 0) { + printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate); + connection_debug = true; + } platform_start(system); LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system, config->audio_device), NULL, drFlags, config->audio_device, 0); @@ -125,6 +128,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys } static void help() { + printf("Moonlight Embedded %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); printf("Usage: moonlight [action] (options) [host]\n"); printf(" moonlight [configfile]\n"); printf("\n Actions\n\n"); @@ -137,6 +141,7 @@ static void help() { printf("\n Global Options\n\n"); printf("\t-config \tLoad configuration file\n"); printf("\t-save \t\tSave configuration file\n"); + printf("\t-verbose\t\tEnable verbose output\n"); printf("\n Streaming options\n\n"); printf("\t-720\t\t\tUse 1280x720 resolution [default]\n"); printf("\t-1080\t\t\tUse 1920x1080 resolution\n"); @@ -177,14 +182,15 @@ static void pair_check(PSERVER_DATA server) { } int main(int argc, char* argv[]) { - printf("Moonlight Embedded %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, COMPILE_OPTIONS); - CONFIGURATION config; config_parse(argc, argv, &config); if (config.action == NULL || strcmp("help", config.action) == 0) help(); + if (config.debug_level > 0) + printf("Moonlight Embedded %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, COMPILE_OPTIONS); + if (config.address == NULL) { config.address = malloc(MAX_ADDRESS_SIZE); if (config.address == NULL) { @@ -225,7 +231,8 @@ int main(int argc, char* argv[]) { exit(-1); } - printf("NVIDIA %s, GFE %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion); + if (config.debug_level > 0) + printf("NVIDIA %s, GFE %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion); if (strcmp("list", config.action) == 0) { pair_check(&server); @@ -251,7 +258,9 @@ int main(int argc, char* argv[]) { struct mapping* mappings = mapping_load(config.mapping); for (int i=0;i 0) + printf("Add input %s...\n", config.inputs[i]); + evdev_create(config.inputs[i], mappings); } diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index a38af3e..a1bdb36 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit a38af3e80ad6da52422a9deb2cfb657719d8a982 +Subproject commit a1bdb36766f8db5dc9cc0694c9a376f0dca3ab59 From b0660e9dd35b8c2f61f96a39fc5fe75f7c995255 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 18:07:24 +0200 Subject: [PATCH 70/72] Show platform name when verbose option is on --- src/main.c | 2 ++ src/platform.c | 21 +++++++++++++++++++++ src/platform.h | 1 + 3 files changed, 24 insertions(+) diff --git a/src/main.c b/src/main.c index 4c4f443..b1b7745 100644 --- a/src/main.c +++ b/src/main.c @@ -240,6 +240,8 @@ int main(int argc, char* argv[]) { } else if (strcmp("stream", config.action) == 0) { pair_check(&server); enum platform system = platform_check(config.platform); + if (config.debug_level > 0) + printf("Platform %s\n", platform_name(system)); if (system == 0) { fprintf(stderr, "Platform '%s' not found\n", config.platform); diff --git a/src/platform.c b/src/platform.c index 4955cf0..680c1ea 100644 --- a/src/platform.c +++ b/src/platform.c @@ -171,3 +171,24 @@ bool platform_supports_hevc(enum platform system) { } return false; } + +char* platform_name(enum platform system) { + switch(system) { + case PI: + return "Raspberry Pi (Broadcom)"; + case IMX: + return "i.MX6 (MXC Vivante)"; + case AML: + return "AMLogic VPU"; + case X11: + return "X Window System (software decoding)"; + case X11_VDPAU: + return "X Window System (VDPAU)"; + case SDL: + return "SDL2 (software decoding)"; + case FAKE: + return "Fake (no a/v output)"; + default: + return "Unknown"; + } +} diff --git a/src/platform.h b/src/platform.h index 61a7541..54db256 100644 --- a/src/platform.h +++ b/src/platform.h @@ -32,6 +32,7 @@ enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device); bool platform_supports_hevc(enum platform system); +char* platform_name(enum platform system); void platform_start(enum platform system); void platform_stop(enum platform system); From cbf31be73b9936ed2e266961978169616b704d9f Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 18:14:32 +0200 Subject: [PATCH 71/72] Add debug option to show http traffic --- docs/README.pod | 4 ++++ libgamestream/client.c | 4 ++-- libgamestream/client.h | 2 +- libgamestream/http.c | 12 +++++++++++- libgamestream/http.h | 2 +- src/config.c | 4 ++++ src/main.c | 3 ++- 7 files changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 8491a35..3fbd621 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -145,6 +145,10 @@ Try streaming if GFE version is unsupported Enable verbose output +=item B<-debug> + +Enable verbose and debug output + =item B<-input> [I] Enable the I device. diff --git a/libgamestream/client.c b/libgamestream/client.c index 4e4179d..eff1d64 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -727,7 +727,7 @@ int gs_quit_app(PSERVER_DATA server) { return ret; } -int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory) { +int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level) { mkdirtree(keyDirectory); if (load_unique_id(keyDirectory) != GS_OK) return GS_FAILED; @@ -735,7 +735,7 @@ int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory) { if (load_cert(keyDirectory)) return GS_FAILED; - http_init(keyDirectory); + http_init(keyDirectory, log_level); LiInitializeServerInformation(&server->serverInfo); server->serverInfo.address = address; diff --git a/libgamestream/client.h b/libgamestream/client.h index 5591d70..ae0971d 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -40,7 +40,7 @@ typedef struct _SERVER_DATA { SERVER_INFORMATION serverInfo; } SERVER_DATA, *PSERVER_DATA; -int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory); +int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel); int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio); int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list); int gs_unpair(PSERVER_DATA server); diff --git a/libgamestream/http.c b/libgamestream/http.c index 757deb5..c8f9bf1 100644 --- a/libgamestream/http.c +++ b/libgamestream/http.c @@ -20,6 +20,7 @@ #include "http.h" #include "errors.h" +#include #include #include @@ -28,6 +29,8 @@ static CURL *curl; static const char *pCertFile = "./client.pem"; static const char *pKeyFile = "./key.pem"; +static bool debug; + static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; @@ -44,8 +47,9 @@ static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp return realsize; } -int http_init(const char* keyDirectory) { +int http_init(const char* keyDirectory, int logLevel) { curl = curl_easy_init(); + debug = logLevel >= 2; if (!curl) return GS_FAILED; @@ -73,6 +77,9 @@ int http_request(char* url, PHTTP_DATA data) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, data); curl_easy_setopt(curl, CURLOPT_URL, url); + if (debug) + printf("Request %s\n", url); + if (data->size > 0) { free(data->memory); data->memory = malloc(1); @@ -89,6 +96,9 @@ int http_request(char* url, PHTTP_DATA data) { } else if (data->memory == NULL) { return GS_OUT_OF_MEMORY; } + + if (debug) + printf("Response:\n%s\n\n", data->memory); return GS_OK; } diff --git a/libgamestream/http.h b/libgamestream/http.h index be28344..f06ace1 100644 --- a/libgamestream/http.h +++ b/libgamestream/http.h @@ -29,7 +29,7 @@ typedef struct _HTTP_DATA { size_t size; } HTTP_DATA, *PHTTP_DATA; -int http_init(const char* keyDirectory); +int http_init(const char* keyDirectory, int logLevel); PHTTP_DATA http_create_data(); int http_request(char* url, PHTTP_DATA data); void http_free_data(PHTTP_DATA data); diff --git a/src/config.c b/src/config.c index 545fcf9..db7aaa9 100644 --- a/src/config.c +++ b/src/config.c @@ -64,6 +64,7 @@ static struct option long_options[] = { {"codec", required_argument, NULL, 'x'}, {"unsupported", no_argument, NULL, 'y'}, {"verbose", no_argument, NULL, 'z'}, + {"debug", no_argument, NULL, 'Z'}, {0, 0, 0, 0}, }; @@ -207,6 +208,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { case 'z': config->debug_level = 1; break; + case 'Z': + config->debug_level = 2; + break; case 1: if (config->action == NULL) config->action = value; diff --git a/src/main.c b/src/main.c index b1b7745..e565280 100644 --- a/src/main.c +++ b/src/main.c @@ -142,6 +142,7 @@ static void help() { printf("\t-config \tLoad configuration file\n"); printf("\t-save \t\tSave configuration file\n"); printf("\t-verbose\t\tEnable verbose output\n"); + printf("\t-debug\t\t\tEnable verbose and debug output\n"); printf("\n Streaming options\n\n"); printf("\t-720\t\t\tUse 1280x720 resolution [default]\n"); printf("\t-1080\t\t\tUse 1920x1080 resolution\n"); @@ -215,7 +216,7 @@ int main(int argc, char* argv[]) { printf("Connect to %s...\n", config.address); int ret; - if ((ret = gs_init(&server, config.address, config.key_dir)) == GS_OUT_OF_MEMORY) { + if ((ret = gs_init(&server, config.address, config.key_dir, config.debug_level)) == GS_OUT_OF_MEMORY) { fprintf(stderr, "Not enough memory\n"); exit(-1); } else if (ret == GS_INVALID) { From cdca4c1b3a3453509af32d20edac1a6eefb4be9a Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 11 Jun 2017 18:32:46 +0200 Subject: [PATCH 72/72] Add button combination to quit streaming for gamepads --- docs/README.pod | 2 +- src/input/evdev.c | 4 ++++ src/input/sdl.c | 4 ++++ src/main.c | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 3fbd621..7a6ccc3 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -181,7 +181,7 @@ A documented example configuration file can be found at /etc/moonlight/moonlight =head1 COMMENTS -Use Ctrl+Alt+Shift+Q to quit the streaming session. +Use Ctrl+Alt+Shift+Q or Play+Back+LeftShoulder+RightShoulder to quit the streaming session. =head1 AUTHOR diff --git a/src/input/evdev.c b/src/input/evdev.c index e26fbbd..5f9706c 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -91,6 +91,7 @@ static bool grabbingDevices; #define QUIT_MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL) #define QUIT_KEY KEY_Q +#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG) static bool (*handler) (struct input_event*, struct input_device*); @@ -326,6 +327,9 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) } } + if (gamepadModified && (dev->buttonFlags & QUIT_BUTTONS) == QUIT_BUTTONS) + return false; + dev->gamepadModified |= gamepadModified; return true; } diff --git a/src/input/sdl.c b/src/input/sdl.c index 950af43..b0ae29c 100644 --- a/src/input/sdl.c +++ b/src/input/sdl.c @@ -24,6 +24,7 @@ #define ACTION_MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL) #define QUIT_KEY SDLK_q +#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG) #define FULLSCREEN_KEY SDLK_f typedef struct _GAMEPAD_STATE { @@ -232,6 +233,9 @@ int sdlinput_handle_event(SDL_Event* event) { else gamepad->buttons &= ~button; + if ((gamepad->buttons & QUIT_BUTTONS) == QUIT_BUTTONS) + return SDL_QUIT_APPLICATION; + LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY); break; } diff --git a/src/main.c b/src/main.c index e565280..ebfdfe9 100644 --- a/src/main.c +++ b/src/main.c @@ -171,7 +171,7 @@ static void help() { printf("\t-input \t\tUse as input. Can be used multiple times\n"); printf("\t-audio \t\tUse as audio output device\n"); #endif - printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n"); + printf("\nUse Ctrl+Alt+Shift+Q or Play+Back+LeftShoulder+RightShoulder to exit streaming session\n\n"); exit(0); }