Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support game controllers #2130

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ scrcpy_print_usage(const char *arg0) {
" Do not display device (only when screen recording is\n"
" enabled).\n"
"\n"
" --no-game-controller\n"
" Disable game controller support.\n"
"\n"
" --no-key-repeat\n"
" Do not forward repeated key events when a key is held down.\n"
"\n"
Expand Down Expand Up @@ -719,6 +722,7 @@ guess_record_format(const char *filename) {
#define OPT_V4L2_SINK 1027
#define OPT_DISPLAY_BUFFER 1028
#define OPT_V4L2_BUFFER 1029
#define OPT_NO_GAME_CONTROLLER 1030

bool
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
Expand All @@ -745,6 +749,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
{"max-size", required_argument, NULL, 'm'},
{"no-control", no_argument, NULL, 'n'},
{"no-display", no_argument, NULL, 'N'},
{"no-game-controller", no_argument, NULL, OPT_NO_GAME_CONTROLLER},
{"no-key-repeat", no_argument, NULL, OPT_NO_KEY_REPEAT},
{"no-mipmaps", no_argument, NULL, OPT_NO_MIPMAPS},
{"port", required_argument, NULL, 'p'},
Expand Down Expand Up @@ -920,6 +925,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
case OPT_NO_MIPMAPS:
opts->mipmaps = false;
break;
case OPT_NO_GAME_CONTROLLER:
opts->forward_game_controllers = false;
break;
case OPT_NO_KEY_REPEAT:
opts->forward_key_repeat = false;
break;
Expand Down
14 changes: 14 additions & 0 deletions app/src/control_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,20 @@ control_msg_serialize(const struct control_msg *msg, unsigned char *buf) {
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
// no additional data
return 1;
case CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_AXIS:
buffer_write16be(&buf[1], msg->inject_game_controller_axis.id);
buf[3] = msg->inject_game_controller_axis.axis;
buffer_write16be(&buf[4], msg->inject_game_controller_axis.value);
return 6;
case CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_BUTTON:
buffer_write16be(&buf[1], msg->inject_game_controller_button.id);
buf[3] = msg->inject_game_controller_button.button;
buf[4] = msg->inject_game_controller_button.state;
return 5;
case CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_DEVICE:
buffer_write16be(&buf[1], msg->inject_game_controller_device.id);
buf[3] = msg->inject_game_controller_device.event;
return 4;
default:
LOGW("Unknown message type: %u", (unsigned) msg->type);
return 0;
Expand Down
17 changes: 17 additions & 0 deletions app/src/control_msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ enum control_msg_type {
CONTROL_MSG_TYPE_SET_CLIPBOARD,
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
CONTROL_MSG_TYPE_ROTATE_DEVICE,
CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_AXIS,
CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_BUTTON,
CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_DEVICE,
};

enum screen_power_mode {
Expand Down Expand Up @@ -76,6 +79,20 @@ struct control_msg {
struct {
enum screen_power_mode mode;
} set_screen_power_mode;
struct {
int16_t id;
uint8_t axis;
int16_t value;
} inject_game_controller_axis;
struct {
int16_t id;
uint8_t button;
uint8_t state;
} inject_game_controller_button;
struct {
int16_t id;
uint8_t event;
} inject_game_controller_device;
};
};

Expand Down
128 changes: 128 additions & 0 deletions app/src/input_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ input_manager_init(struct input_manager *im, struct controller *controller,
const struct scrcpy_options *options) {
im->controller = controller;
im->screen = screen;
memset(im->game_controllers, 0, sizeof(im->game_controllers));
im->repeat = 0;

im->control = options->control;
im->forward_game_controllers = options->forward_game_controllers;
im->forward_key_repeat = options->forward_key_repeat;
im->prefer_text = options->prefer_text;
im->forward_all_clicks = options->forward_all_clicks;
Expand Down Expand Up @@ -808,6 +810,111 @@ input_manager_process_mouse_wheel(struct input_manager *im,
}
}

void
input_manager_process_controller_axis(struct input_manager *im,
const SDL_ControllerAxisEvent *event) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_AXIS;
msg.inject_game_controller_axis.id = event->which;
msg.inject_game_controller_axis.axis = event->axis;
msg.inject_game_controller_axis.value = event->value;
controller_push_msg(im->controller, &msg);
}

void
input_manager_process_controller_button(struct input_manager *im,
const SDL_ControllerButtonEvent *event) {
struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_BUTTON;
msg.inject_game_controller_button.id = event->which;
msg.inject_game_controller_button.button = event->button;
msg.inject_game_controller_button.state = event->state;
controller_push_msg(im->controller, &msg);
}

static SDL_GameController **
find_free_game_controller_slot(struct input_manager *im) {
for (unsigned i = 0; i < MAX_GAME_CONTROLLERS; ++i) {
if (!im->game_controllers[i]) {
return &im->game_controllers[i];
}
}

return NULL;
}

static bool
free_game_controller_slot(struct input_manager *im,
SDL_GameController *game_controller) {
for (unsigned i = 0; i < MAX_GAME_CONTROLLERS; ++i) {
if (im->game_controllers[i] == game_controller) {
im->game_controllers[i] = NULL;
return true;
}
}

return false;
}

void
input_manager_process_controller_device(struct input_manager *im,
const SDL_ControllerDeviceEvent *event) {
SDL_JoystickID id;

switch (event->type) {
case SDL_CONTROLLERDEVICEADDED: {
SDL_GameController **freeGc = find_free_game_controller_slot(im);

if (!freeGc) {
LOGW("Controller limit reached.");
return;
}

SDL_GameController *game_controller;
game_controller = SDL_GameControllerOpen(event->which);

if (game_controller) {
*freeGc = game_controller;

SDL_Joystick *joystick;
joystick = SDL_GameControllerGetJoystick(game_controller);

id = SDL_JoystickInstanceID(joystick);
} else {
LOGW("Could not open game controller #%d", event->which);
return;
}
break;
}

case SDL_CONTROLLERDEVICEREMOVED: {
id = event->which;

SDL_GameController *game_controller;
game_controller = SDL_GameControllerFromInstanceID(id);

SDL_GameControllerClose(game_controller);

if (!free_game_controller_slot(im, game_controller)) {
LOGW("Could not find removed game controller.");
return;
}

break;
}

default:
return;
}

struct control_msg msg;
msg.type = CONTROL_MSG_TYPE_INJECT_GAME_CONTROLLER_DEVICE;
msg.inject_game_controller_device.id = id;
msg.inject_game_controller_device.event = event->type;
msg.inject_game_controller_device.event -= SDL_CONTROLLERDEVICEADDED;
controller_push_msg(im->controller, &msg);
}

bool
input_manager_handle_event(struct input_manager *im, SDL_Event *event) {
switch (event->type) {
Expand Down Expand Up @@ -846,6 +953,27 @@ input_manager_handle_event(struct input_manager *im, SDL_Event *event) {
case SDL_FINGERUP:
input_manager_process_touch(im, &event->tfinger);
return true;
case SDL_CONTROLLERAXISMOTION:
if (!im->control || !im->forward_game_controllers) {
break;
}
input_manager_process_controller_axis(im, &event->caxis);
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
if (!im->control || !im->forward_game_controllers) {
break;
}
input_manager_process_controller_button(im, &event->cbutton);
break;
case SDL_CONTROLLERDEVICEADDED:
// case SDL_CONTROLLERDEVICEREMAPPED:
case SDL_CONTROLLERDEVICEREMOVED:
if (!im->control || !im->forward_game_controllers) {
break;
}
input_manager_process_controller_device(im, &event->cdevice);
break;
}

return false;
Expand Down
4 changes: 4 additions & 0 deletions app/src/input_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@
#include "scrcpy.h"
#include "screen.h"

#define MAX_GAME_CONTROLLERS 16

struct input_manager {
struct controller *controller;
struct screen *screen;
SDL_GameController *game_controllers[MAX_GAME_CONTROLLERS];

// SDL reports repeated events as a boolean, but Android expects the actual
// number of repetitions. This variable keeps track of the count.
unsigned repeat;

bool control;
bool forward_game_controllers;
bool forward_key_repeat;
bool prefer_text;
bool forward_all_clicks;
Expand Down
2 changes: 1 addition & 1 deletion app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
static bool
sdl_init_and_configure(bool display, const char *render_driver,
bool disable_screensaver) {
uint32_t flags = display ? SDL_INIT_VIDEO : SDL_INIT_EVENTS;
uint32_t flags = (display ? SDL_INIT_VIDEO : 0) | SDL_INIT_GAMECONTROLLER;
if (SDL_Init(flags)) {
LOGC("Could not initialize SDL: %s", SDL_GetError());
return false;
Expand Down
2 changes: 2 additions & 0 deletions app/src/scrcpy.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ struct scrcpy_options {
bool stay_awake;
bool force_adb_forward;
bool disable_screensaver;
bool forward_game_controllers;
bool forward_key_repeat;
bool forward_all_clicks;
bool legacy_paste;
Expand Down Expand Up @@ -144,6 +145,7 @@ struct scrcpy_options {
.stay_awake = false, \
.force_adb_forward = false, \
.disable_screensaver = false, \
.forward_game_controllers = true, \
.forward_key_repeat = true, \
.forward_all_clicks = false, \
.legacy_paste = false, \
Expand Down
4 changes: 3 additions & 1 deletion app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
#define SERVER_FILENAME "scrcpy-server"

#define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FILENAME
#define DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
#define DEVICE_SERVER_DIR "/data/local/tmp"
#define DEVICE_SERVER_PATH DEVICE_SERVER_DIR "/scrcpy-server.jar"

static char *
get_server_path(void) {
Expand Down Expand Up @@ -281,6 +282,7 @@ execute_server(struct server *server, const struct server_params *params) {
"shell",
"CLASSPATH=" DEVICE_SERVER_PATH,
"app_process",
"-Djna.boot.library.path=/data/local/tmp",
#ifdef SERVER_DEBUGGER
# define SERVER_DEBUGGER_PORT "5005"
# ifdef SERVER_DEBUGGER_METHOD_NEW
Expand Down
7 changes: 5 additions & 2 deletions app/src/v4l2_sink.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,11 @@ sc_v4l2_sink_open(struct sc_v4l2_sink *vs) {
goto error_mutex_destroy;
}

// FIXME
const AVOutputFormat *format = find_muxer("video4linux2,v4l2");
const AVOutputFormat *format = find_muxer("v4l2");
if (!format) {
// Alternative name
format = find_muxer("video4linux2");
}
if (!format) {
LOGE("Could not find v4l2 muxer");
goto error_cond_destroy;
Expand Down
Loading