mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-02 22:29:25 +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:
|
case CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||||
// no additional data
|
// no additional data
|
||||||
return 1;
|
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:
|
default:
|
||||||
LOGW("Unknown message type: %u", (unsigned) msg->type);
|
LOGW("Unknown message type: %u", (unsigned) msg->type);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -33,6 +33,9 @@ enum control_msg_type {
|
||||||
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
CONTROL_MSG_TYPE_SET_CLIPBOARD,
|
||||||
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
CONTROL_MSG_TYPE_SET_SCREEN_POWER_MODE,
|
||||||
CONTROL_MSG_TYPE_ROTATE_DEVICE,
|
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 {
|
enum screen_power_mode {
|
||||||
|
@ -76,6 +79,20 @@ struct control_msg {
|
||||||
struct {
|
struct {
|
||||||
enum screen_power_mode mode;
|
enum screen_power_mode mode;
|
||||||
} set_screen_power_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) {
|
const struct scrcpy_options *options) {
|
||||||
im->controller = controller;
|
im->controller = controller;
|
||||||
im->screen = screen;
|
im->screen = screen;
|
||||||
|
memset(im->game_controllers, 0, sizeof(im->game_controllers));
|
||||||
im->repeat = 0;
|
im->repeat = 0;
|
||||||
|
|
||||||
im->control = options->control;
|
im->control = options->control;
|
||||||
|
@ -846,7 +847,133 @@ input_manager_handle_event(struct input_manager *im, SDL_Event *event) {
|
||||||
case SDL_FINGERUP:
|
case SDL_FINGERUP:
|
||||||
input_manager_process_touch(im, &event->tfinger);
|
input_manager_process_touch(im, &event->tfinger);
|
||||||
return true;
|
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;
|
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 "scrcpy.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
|
#define MAX_GAME_CONTROLLERS 16
|
||||||
|
|
||||||
struct input_manager {
|
struct input_manager {
|
||||||
struct controller *controller;
|
struct controller *controller;
|
||||||
struct screen *screen;
|
struct screen *screen;
|
||||||
|
SDL_GameController *game_controllers[MAX_GAME_CONTROLLERS];
|
||||||
|
|
||||||
// SDL reports repeated events as a boolean, but Android expects the actual
|
// SDL reports repeated events as a boolean, but Android expects the actual
|
||||||
// number of repetitions. This variable keeps track of the count.
|
// number of repetitions. This variable keeps track of the count.
|
||||||
|
|
|
@ -59,7 +59,7 @@ BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
|
||||||
static bool
|
static bool
|
||||||
sdl_init_and_configure(bool display, const char *render_driver,
|
sdl_init_and_configure(bool display, const char *render_driver,
|
||||||
bool disable_screensaver) {
|
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)) {
|
if (SDL_Init(flags)) {
|
||||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -17,6 +17,9 @@ public final class ControlMessage {
|
||||||
public static final int TYPE_SET_CLIPBOARD = 9;
|
public static final int TYPE_SET_CLIPBOARD = 9;
|
||||||
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
public static final int TYPE_SET_SCREEN_POWER_MODE = 10;
|
||||||
public static final int TYPE_ROTATE_DEVICE = 11;
|
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 int type;
|
||||||
private String text;
|
private String text;
|
||||||
|
@ -31,6 +34,12 @@ public final class ControlMessage {
|
||||||
private int vScroll;
|
private int vScroll;
|
||||||
private boolean paste;
|
private boolean paste;
|
||||||
private int repeat;
|
private int repeat;
|
||||||
|
private int gameControllerId;
|
||||||
|
private int gameControllerAxis;
|
||||||
|
private int gameControllerAxisValue;
|
||||||
|
private int gameControllerButton;
|
||||||
|
private int gameControllerButtonState;
|
||||||
|
private int gameControllerDeviceEvent;
|
||||||
|
|
||||||
private ControlMessage() {
|
private ControlMessage() {
|
||||||
}
|
}
|
||||||
|
@ -97,6 +106,32 @@ public final class ControlMessage {
|
||||||
return msg;
|
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) {
|
public static ControlMessage createEmpty(int type) {
|
||||||
ControlMessage msg = new ControlMessage();
|
ControlMessage msg = new ControlMessage();
|
||||||
msg.type = type;
|
msg.type = type;
|
||||||
|
@ -154,4 +189,29 @@ public final class ControlMessage {
|
||||||
public int getRepeat() {
|
public int getRepeat() {
|
||||||
return repeat;
|
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 BACK_OR_SCREEN_ON_LENGTH = 1;
|
||||||
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
static final int SET_SCREEN_POWER_MODE_PAYLOAD_LENGTH = 1;
|
||||||
static final int SET_CLIPBOARD_FIXED_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
|
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
|
||||||
|
|
||||||
|
@ -83,6 +86,15 @@ public class ControlMessageReader {
|
||||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
msg = ControlMessage.createEmpty(type);
|
msg = ControlMessage.createEmpty(type);
|
||||||
break;
|
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:
|
default:
|
||||||
Ln.w("Unknown event type: " + type);
|
Ln.w("Unknown event type: " + type);
|
||||||
msg = null;
|
msg = null;
|
||||||
|
@ -182,6 +194,35 @@ public class ControlMessageReader {
|
||||||
return ControlMessage.createSetScreenPowerMode(mode);
|
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) {
|
private static Position readPosition(ByteBuffer buffer) {
|
||||||
int x = buffer.getInt();
|
int x = buffer.getInt();
|
||||||
int y = buffer.getInt();
|
int y = buffer.getInt();
|
||||||
|
|
|
@ -135,6 +135,29 @@ public class Controller {
|
||||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
Device.rotateDevice();
|
Device.rotateDevice();
|
||||||
break;
|
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:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue