Read controller and send control_msgs to device

This commit is contained in:
Luiz Henrique Laurini 2021-02-14 21:12:15 -03:00
parent 1d49bf89ba
commit 0ba031b183
8 changed files with 286 additions and 1 deletions

View file

@ -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;

View file

@ -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 {
@ -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;
};
};

View file

@ -57,6 +57,7 @@ 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;
@ -846,7 +847,133 @@ 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 (!options->control) {
break;
}
input_manager_process_controller_axis(&input_manager, &event->caxis);
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
if (!options->control) {
break;
}
input_manager_process_controller_button(&input_manager, &event->cbutton);
break;
case SDL_CONTROLLERDEVICEADDED:
// case SDL_CONTROLLERDEVICEREMAPPED:
case SDL_CONTROLLERDEVICEREMOVED:
if (!options->control) {
break;
}
input_manager_process_controller_device(&input_manager, &event->cdevice);
break;
}
return false;
}
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);
}

View file

@ -12,9 +12,12 @@
#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.

View file

@ -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;

View file

@ -17,6 +17,9 @@ public final class ControlMessage {
public static final int TYPE_SET_CLIPBOARD = 9;
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
public static final int TYPE_ROTATE_DEVICE = 11;
public static final int TYPE_INJECT_GAME_CONTROLLER_AXIS = 12;
public static final int TYPE_INJECT_GAME_CONTROLLER_BUTTON = 13;
public static final int TYPE_INJECT_GAME_CONTROLLER_DEVICE = 14;
private int type;
private String text;
@ -31,6 +34,12 @@ public final class ControlMessage {
private int vScroll;
private boolean paste;
private int repeat;
private int gameControllerId;
private int gameControllerAxis;
private int gameControllerAxisValue;
private int gameControllerButton;
private int gameControllerButtonState;
private int gameControllerDeviceEvent;
private ControlMessage() {
}
@ -97,6 +106,32 @@ public final class ControlMessage {
return msg;
}
public static ControlMessage createInjectGameControllerAxis(int id, int axis, int value) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_INJECT_GAME_CONTROLLER_AXIS;
msg.gameControllerId = id;
msg.gameControllerAxis = axis;
msg.gameControllerAxisValue = value;
return msg;
}
public static ControlMessage createInjectGameControllerButton(int id, int button, int state) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_INJECT_GAME_CONTROLLER_BUTTON;
msg.gameControllerId = id;
msg.gameControllerButton = button;
msg.gameControllerButtonState = state;
return msg;
}
public static ControlMessage createInjectGameControllerDevice(int id, int event) {
ControlMessage msg = new ControlMessage();
msg.type = TYPE_INJECT_GAME_CONTROLLER_DEVICE;
msg.gameControllerId = id;
msg.gameControllerDeviceEvent = event;
return msg;
}
public static ControlMessage createEmpty(int type) {
ControlMessage msg = new ControlMessage();
msg.type = type;
@ -154,4 +189,29 @@ public final class ControlMessage {
public int getRepeat() {
return repeat;
}
public int getGameControllerId() {
return gameControllerId;
}
public int getGameControllerAxis() {
return gameControllerAxis;
}
public int getGameControllerAxisValue() {
return gameControllerAxisValue;
}
public int getGameControllerButton() {
return gameControllerButton;
}
public int getGameControllerButtonState() {
return gameControllerButtonState;
}
public int getGameControllerDeviceEvent() {
return gameControllerDeviceEvent;
}
}

View file

@ -14,6 +14,9 @@ public class ControlMessageReader {
static final int BACK_OR_SCREEN_ON_LENGTH = 1;
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 1;
static final int INJECT_GAME_CONTROLLER_AXIS_PAYLOAD_LENGTH = 5;
static final int INJECT_GAME_CONTROLLER_BUTTON_PAYLOAD_LENGTH = 4;
static final int INJECT_GAME_CONTROLLER_DEVICE_PAYLOAD_LENGTH = 3;
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
@ -83,6 +86,15 @@ public class ControlMessageReader {
case ControlMessage.TYPE_ROTATE_DEVICE:
msg = ControlMessage.createEmpty(type);
break;
case ControlMessage.TYPE_INJECT_GAME_CONTROLLER_AXIS:
msg = parseInjectGameControllerAxis();
break;
case ControlMessage.TYPE_INJECT_GAME_CONTROLLER_BUTTON:
msg = parseInjectGameControllerButton();
break;
case ControlMessage.TYPE_INJECT_GAME_CONTROLLER_DEVICE:
msg = parseInjectGameControllerDevice();
break;
default:
Ln.w("Unknown event type: " + type);
msg = null;
@ -182,6 +194,35 @@ public class ControlMessageReader {
return ControlMessage.createSetScreenPowerMode(mode);
}
private ControlMessage parseInjectGameControllerAxis() {
if (buffer.remaining() < INJECT_GAME_CONTROLLER_AXIS_PAYLOAD_LENGTH) {
return null;
}
int id = buffer.getShort();
int axis = buffer.get();
int value = buffer.getShort();
return ControlMessage.createInjectGameControllerAxis(id, axis, value);
}
private ControlMessage parseInjectGameControllerButton() {
if (buffer.remaining() < INJECT_GAME_CONTROLLER_BUTTON_PAYLOAD_LENGTH) {
return null;
}
int id = buffer.getShort();
int button = buffer.get();
int state = buffer.get();
return ControlMessage.createInjectGameControllerButton(id, button, state);
}
private ControlMessage parseInjectGameControllerDevice() {
if (buffer.remaining() < INJECT_GAME_CONTROLLER_DEVICE_PAYLOAD_LENGTH) {
return null;
}
int id = buffer.getShort();
int event = buffer.get();
return ControlMessage.createInjectGameControllerDevice(id, event);
}
private static Position readPosition(ByteBuffer buffer) {
int x = buffer.getInt();
int y = buffer.getInt();

View file

@ -135,6 +135,29 @@ public class Controller {
case ControlMessage.TYPE_ROTATE_DEVICE:
Device.rotateDevice();
break;
case ControlMessage.TYPE_INJECT_GAME_CONTROLLER_AXIS:
{
int id = msg.getGameControllerId();
int axis = msg.getGameControllerAxis();
int value = msg.getGameControllerAxisValue();
Ln.d(String.format("Received gc axis: %d %d %d", id, axis, value));
}
break;
case ControlMessage.TYPE_INJECT_GAME_CONTROLLER_BUTTON:
{
int id = msg.getGameControllerId();
int button = msg.getGameControllerButton();
int state = msg.getGameControllerButtonState();
Ln.d(String.format("Received gc button: %d %d %d", id, button, state));
}
break;
case ControlMessage.TYPE_INJECT_GAME_CONTROLLER_DEVICE:
{
int id = msg.getGameControllerId();
int event = msg.getGameControllerDeviceEvent();
Ln.d(String.format("Received gc device event: %d %d", id, event));
}
break;
default:
// do nothing
}