mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-04-22 12:35:01 +00:00
Read controller and send control_msgs to device
This commit is contained in:
parent
1d49bf89ba
commit
0ba031b183
8 changed files with 286 additions and 1 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue