Handle SDL gamepad events
Introduce a gamepad processor trait, similar to the keyboard processor and mouse processor traits. Handle gamepad events received from SDL, convert them to scrcpy-specific gamepad events, and forward them to the gamepad processor. Further commits will provide AOA and UHID implementations of the gamepad processor trait. PR #5270 <https://github.com/Genymobile/scrcpy/pull/5270> Co-authored-by: Luiz Henrique Laurini <luizhenriquelaurini@gmail.com>
This commit is contained in:
parent
d24580f469
commit
6210b51dcc
@ -323,6 +323,38 @@ enum sc_mouse_button {
|
|||||||
SC_MOUSE_BUTTON_X2 = SDL_BUTTON(SDL_BUTTON_X2),
|
SC_MOUSE_BUTTON_X2 = SDL_BUTTON(SDL_BUTTON_X2),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use the naming from SDL3 for gamepad axis and buttons:
|
||||||
|
// <https://wiki.libsdl.org/SDL3/README/migration>
|
||||||
|
|
||||||
|
enum sc_gamepad_axis {
|
||||||
|
SC_GAMEPAD_AXIS_UNKNOWN = -1,
|
||||||
|
SC_GAMEPAD_AXIS_LEFTX = SDL_CONTROLLER_AXIS_LEFTX,
|
||||||
|
SC_GAMEPAD_AXIS_LEFTY = SDL_CONTROLLER_AXIS_LEFTY,
|
||||||
|
SC_GAMEPAD_AXIS_RIGHTX = SDL_CONTROLLER_AXIS_RIGHTX,
|
||||||
|
SC_GAMEPAD_AXIS_RIGHTY = SDL_CONTROLLER_AXIS_RIGHTY,
|
||||||
|
SC_GAMEPAD_AXIS_LEFT_TRIGGER = SDL_CONTROLLER_AXIS_TRIGGERLEFT,
|
||||||
|
SC_GAMEPAD_AXIS_RIGHT_TRIGGER = SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sc_gamepad_button {
|
||||||
|
SC_GAMEPAD_BUTTON_UNKNOWN = -1,
|
||||||
|
SC_GAMEPAD_BUTTON_SOUTH = SDL_CONTROLLER_BUTTON_A,
|
||||||
|
SC_GAMEPAD_BUTTON_EAST = SDL_CONTROLLER_BUTTON_B,
|
||||||
|
SC_GAMEPAD_BUTTON_WEST = SDL_CONTROLLER_BUTTON_X,
|
||||||
|
SC_GAMEPAD_BUTTON_NORTH = SDL_CONTROLLER_BUTTON_Y,
|
||||||
|
SC_GAMEPAD_BUTTON_BACK = SDL_CONTROLLER_BUTTON_BACK,
|
||||||
|
SC_GAMEPAD_BUTTON_GUIDE = SDL_CONTROLLER_BUTTON_GUIDE,
|
||||||
|
SC_GAMEPAD_BUTTON_START = SDL_CONTROLLER_BUTTON_START,
|
||||||
|
SC_GAMEPAD_BUTTON_LEFT_STICK = SDL_CONTROLLER_BUTTON_LEFTSTICK,
|
||||||
|
SC_GAMEPAD_BUTTON_RIGHT_STICK = SDL_CONTROLLER_BUTTON_RIGHTSTICK,
|
||||||
|
SC_GAMEPAD_BUTTON_LEFT_SHOULDER = SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
|
||||||
|
SC_GAMEPAD_BUTTON_RIGHT_SHOULDER = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
|
||||||
|
SC_GAMEPAD_BUTTON_DPAD_UP = SDL_CONTROLLER_BUTTON_DPAD_UP,
|
||||||
|
SC_GAMEPAD_BUTTON_DPAD_DOWN = SDL_CONTROLLER_BUTTON_DPAD_DOWN,
|
||||||
|
SC_GAMEPAD_BUTTON_DPAD_LEFT = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
|
||||||
|
SC_GAMEPAD_BUTTON_DPAD_RIGHT = SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
|
||||||
|
};
|
||||||
|
|
||||||
static_assert(sizeof(enum sc_mod) >= sizeof(SDL_Keymod),
|
static_assert(sizeof(enum sc_mod) >= sizeof(SDL_Keymod),
|
||||||
"SDL_Keymod must be convertible to sc_mod");
|
"SDL_Keymod must be convertible to sc_mod");
|
||||||
|
|
||||||
@ -380,6 +412,33 @@ struct sc_touch_event {
|
|||||||
float pressure;
|
float pressure;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum sc_gamepad_device_event_type {
|
||||||
|
SC_GAMEPAD_DEVICE_ADDED,
|
||||||
|
SC_GAMEPAD_DEVICE_REMOVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
// As documented in <https://wiki.libsdl.org/SDL2/SDL_JoystickID>:
|
||||||
|
// The ID value starts at 0 and increments from there. The value -1 is an
|
||||||
|
// invalid ID.
|
||||||
|
#define SC_GAMEPAD_ID_INVALID UINT32_C(-1)
|
||||||
|
|
||||||
|
struct sc_gamepad_device_event {
|
||||||
|
enum sc_gamepad_device_event_type type;
|
||||||
|
uint32_t gamepad_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_gamepad_button_event {
|
||||||
|
uint32_t gamepad_id;
|
||||||
|
enum sc_action action;
|
||||||
|
enum sc_gamepad_button button;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_gamepad_axis_event {
|
||||||
|
uint32_t gamepad_id;
|
||||||
|
enum sc_gamepad_axis axis;
|
||||||
|
int16_t value;
|
||||||
|
};
|
||||||
|
|
||||||
static inline uint16_t
|
static inline uint16_t
|
||||||
sc_mods_state_from_sdl(uint16_t mods_state) {
|
sc_mods_state_from_sdl(uint16_t mods_state) {
|
||||||
return mods_state;
|
return mods_state;
|
||||||
@ -444,4 +503,43 @@ sc_mouse_buttons_state_from_sdl(uint32_t buttons_state) {
|
|||||||
return buttons_state;
|
return buttons_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline enum sc_gamepad_device_event_type
|
||||||
|
sc_gamepad_device_event_type_from_sdl_type(uint32_t type) {
|
||||||
|
assert(type == SDL_CONTROLLERDEVICEADDED
|
||||||
|
|| type == SDL_CONTROLLERDEVICEREMOVED);
|
||||||
|
if (type == SDL_CONTROLLERDEVICEADDED) {
|
||||||
|
return SC_GAMEPAD_DEVICE_ADDED;
|
||||||
|
}
|
||||||
|
return SC_GAMEPAD_DEVICE_REMOVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum sc_gamepad_axis
|
||||||
|
sc_gamepad_axis_from_sdl(uint8_t axis) {
|
||||||
|
if (axis <= SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
|
||||||
|
// SC_GAMEPAD_AXIS_* constants are initialized from
|
||||||
|
// SDL_CONTROLLER_AXIS_*
|
||||||
|
return axis;
|
||||||
|
}
|
||||||
|
return SC_GAMEPAD_AXIS_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum sc_gamepad_button
|
||||||
|
sc_gamepad_button_from_sdl(uint8_t button) {
|
||||||
|
if (button <= SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
|
||||||
|
// SC_GAMEPAD_BUTTON_* constants are initialized from
|
||||||
|
// SDL_CONTROLLER_BUTTON_*
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
return SC_GAMEPAD_BUTTON_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline enum sc_action
|
||||||
|
sc_action_from_sdl_controllerbutton_type(uint32_t type) {
|
||||||
|
assert(type == SDL_CONTROLLERBUTTONDOWN || type == SDL_CONTROLLERBUTTONUP);
|
||||||
|
if (type == SDL_CONTROLLERBUTTONDOWN) {
|
||||||
|
return SC_ACTION_DOWN;
|
||||||
|
}
|
||||||
|
return SC_ACTION_UP;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -56,16 +56,18 @@ void
|
|||||||
sc_input_manager_init(struct sc_input_manager *im,
|
sc_input_manager_init(struct sc_input_manager *im,
|
||||||
const struct sc_input_manager_params *params) {
|
const struct sc_input_manager_params *params) {
|
||||||
// A key/mouse processor may not be present if there is no controller
|
// A key/mouse processor may not be present if there is no controller
|
||||||
assert((!params->kp && !params->mp) || params->controller);
|
assert((!params->kp && !params->mp && !params->gp) || params->controller);
|
||||||
// A processor must have ops initialized
|
// A processor must have ops initialized
|
||||||
assert(!params->kp || params->kp->ops);
|
assert(!params->kp || params->kp->ops);
|
||||||
assert(!params->mp || params->mp->ops);
|
assert(!params->mp || params->mp->ops);
|
||||||
|
assert(!params->gp || params->gp->ops);
|
||||||
|
|
||||||
im->controller = params->controller;
|
im->controller = params->controller;
|
||||||
im->fp = params->fp;
|
im->fp = params->fp;
|
||||||
im->screen = params->screen;
|
im->screen = params->screen;
|
||||||
im->kp = params->kp;
|
im->kp = params->kp;
|
||||||
im->mp = params->mp;
|
im->mp = params->mp;
|
||||||
|
im->gp = params->gp;
|
||||||
|
|
||||||
im->mouse_bindings = params->mouse_bindings;
|
im->mouse_bindings = params->mouse_bindings;
|
||||||
im->legacy_paste = params->legacy_paste;
|
im->legacy_paste = params->legacy_paste;
|
||||||
@ -920,6 +922,78 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
|||||||
im->mp->ops->process_mouse_scroll(im->mp, &evt);
|
im->mp->ops->process_mouse_scroll(im->mp, &evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_input_manager_process_gamepad_device(struct sc_input_manager *im,
|
||||||
|
const SDL_ControllerDeviceEvent *event) {
|
||||||
|
SDL_JoystickID id;
|
||||||
|
if (event->type == SDL_CONTROLLERDEVICEADDED) {
|
||||||
|
SDL_GameController *gc = SDL_GameControllerOpen(event->which);
|
||||||
|
if (!gc) {
|
||||||
|
LOGW("Could not open game controller");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(gc);
|
||||||
|
if (!joystick) {
|
||||||
|
LOGW("Could not get controller joystick");
|
||||||
|
SDL_GameControllerClose(gc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = SDL_JoystickInstanceID(joystick);
|
||||||
|
} else if (event->type == SDL_CONTROLLERDEVICEREMOVED) {
|
||||||
|
id = event->which;
|
||||||
|
|
||||||
|
SDL_GameController *gc = SDL_GameControllerFromInstanceID(id);
|
||||||
|
if (gc) {
|
||||||
|
SDL_GameControllerClose(gc);
|
||||||
|
} else {
|
||||||
|
LOGW("Unknown gamepad device removed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Nothing to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sc_gamepad_device_event evt = {
|
||||||
|
.type = sc_gamepad_device_event_type_from_sdl_type(event->type),
|
||||||
|
.gamepad_id = id,
|
||||||
|
};
|
||||||
|
im->gp->ops->process_gamepad_device(im->gp, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_input_manager_process_gamepad_axis(struct sc_input_manager *im,
|
||||||
|
const SDL_ControllerAxisEvent *event) {
|
||||||
|
enum sc_gamepad_axis axis = sc_gamepad_axis_from_sdl(event->axis);
|
||||||
|
if (axis == SC_GAMEPAD_AXIS_UNKNOWN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sc_gamepad_axis_event evt = {
|
||||||
|
.gamepad_id = event->which,
|
||||||
|
.axis = axis,
|
||||||
|
.value = event->value,
|
||||||
|
};
|
||||||
|
im->gp->ops->process_gamepad_axis(im->gp, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_input_manager_process_gamepad_button(struct sc_input_manager *im,
|
||||||
|
const SDL_ControllerButtonEvent *event) {
|
||||||
|
enum sc_gamepad_button button = sc_gamepad_button_from_sdl(event->button);
|
||||||
|
if (button == SC_GAMEPAD_BUTTON_UNKNOWN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sc_gamepad_button_event evt = {
|
||||||
|
.gamepad_id = event->which,
|
||||||
|
.action = sc_action_from_sdl_controllerbutton_type(event->type),
|
||||||
|
.button = button,
|
||||||
|
};
|
||||||
|
im->gp->ops->process_gamepad_button(im->gp, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_apk(const char *file) {
|
is_apk(const char *file) {
|
||||||
const char *ext = strrchr(file, '.');
|
const char *ext = strrchr(file, '.');
|
||||||
@ -992,6 +1066,27 @@ sc_input_manager_handle_event(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
sc_input_manager_process_touch(im, &event->tfinger);
|
sc_input_manager_process_touch(im, &event->tfinger);
|
||||||
break;
|
break;
|
||||||
|
case SDL_CONTROLLERDEVICEADDED:
|
||||||
|
case SDL_CONTROLLERDEVICEREMOVED:
|
||||||
|
// Handle device added or removed even if paused
|
||||||
|
if (!im->gp) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sc_input_manager_process_gamepad_device(im, &event->cdevice);
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
|
if (!im->gp || paused) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sc_input_manager_process_gamepad_axis(im, &event->caxis);
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
|
if (!im->gp || paused) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sc_input_manager_process_gamepad_button(im, &event->cbutton);
|
||||||
|
break;
|
||||||
case SDL_DROPFILE: {
|
case SDL_DROPFILE: {
|
||||||
if (!control) {
|
if (!control) {
|
||||||
break;
|
break;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "file_pusher.h"
|
#include "file_pusher.h"
|
||||||
#include "fps_counter.h"
|
#include "fps_counter.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "trait/gamepad_processor.h"
|
||||||
#include "trait/key_processor.h"
|
#include "trait/key_processor.h"
|
||||||
#include "trait/mouse_processor.h"
|
#include "trait/mouse_processor.h"
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ struct sc_input_manager {
|
|||||||
|
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
struct sc_mouse_processor *mp;
|
struct sc_mouse_processor *mp;
|
||||||
|
struct sc_gamepad_processor *gp;
|
||||||
|
|
||||||
struct sc_mouse_bindings mouse_bindings;
|
struct sc_mouse_bindings mouse_bindings;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
@ -50,6 +52,7 @@ struct sc_input_manager_params {
|
|||||||
struct sc_screen *screen;
|
struct sc_screen *screen;
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
struct sc_mouse_processor *mp;
|
struct sc_mouse_processor *mp;
|
||||||
|
struct sc_gamepad_processor *gp;
|
||||||
|
|
||||||
struct sc_mouse_bindings mouse_bindings;
|
struct sc_mouse_bindings mouse_bindings;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
|
@ -484,6 +484,11 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SDL_Init(SDL_INIT_GAMECONTROLLER)) {
|
||||||
|
LOGE("Could not initialize SDL gamepad: %s", SDL_GetError());
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
sdl_configure(options->video_playback, options->disable_screensaver);
|
sdl_configure(options->video_playback, options->disable_screensaver);
|
||||||
|
|
||||||
// Await for server without blocking Ctrl+C handling
|
// Await for server without blocking Ctrl+C handling
|
||||||
@ -734,6 +739,7 @@ aoa_complete:
|
|||||||
.fp = fp,
|
.fp = fp,
|
||||||
.kp = kp,
|
.kp = kp,
|
||||||
.mp = mp,
|
.mp = mp,
|
||||||
|
.gp = NULL,
|
||||||
.mouse_bindings = options->mouse_bindings,
|
.mouse_bindings = options->mouse_bindings,
|
||||||
.legacy_paste = options->legacy_paste,
|
.legacy_paste = options->legacy_paste,
|
||||||
.clipboard_autosync = options->clipboard_autosync,
|
.clipboard_autosync = options->clipboard_autosync,
|
||||||
|
@ -477,6 +477,7 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
.screen = screen,
|
.screen = screen,
|
||||||
.kp = params->kp,
|
.kp = params->kp,
|
||||||
.mp = params->mp,
|
.mp = params->mp,
|
||||||
|
.gp = params->gp,
|
||||||
.mouse_bindings = params->mouse_bindings,
|
.mouse_bindings = params->mouse_bindings,
|
||||||
.legacy_paste = params->legacy_paste,
|
.legacy_paste = params->legacy_paste,
|
||||||
.clipboard_autosync = params->clipboard_autosync,
|
.clipboard_autosync = params->clipboard_autosync,
|
||||||
|
@ -78,6 +78,7 @@ struct sc_screen_params {
|
|||||||
struct sc_file_pusher *fp;
|
struct sc_file_pusher *fp;
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
struct sc_mouse_processor *mp;
|
struct sc_mouse_processor *mp;
|
||||||
|
struct sc_gamepad_processor *gp;
|
||||||
|
|
||||||
struct sc_mouse_bindings mouse_bindings;
|
struct sc_mouse_bindings mouse_bindings;
|
||||||
bool legacy_paste;
|
bool legacy_paste;
|
||||||
|
50
app/src/trait/gamepad_processor.h
Normal file
50
app/src/trait/gamepad_processor.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef SC_GAMEPAD_PROCESSOR_H
|
||||||
|
#define SC_GAMEPAD_PROCESSOR_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "input_events.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gamepad processor trait.
|
||||||
|
*
|
||||||
|
* Component able to handle gamepads devices and inject buttons and axis events.
|
||||||
|
*/
|
||||||
|
struct sc_gamepad_processor {
|
||||||
|
const struct sc_gamepad_processor_ops *ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_gamepad_processor_ops {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a gamepad device added or removed
|
||||||
|
*
|
||||||
|
* This function is mandatory.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
(*process_gamepad_device)(struct sc_gamepad_processor *gp,
|
||||||
|
const struct sc_gamepad_device_event *event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a gamepad axis event
|
||||||
|
*
|
||||||
|
* This function is mandatory.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
(*process_gamepad_axis)(struct sc_gamepad_processor *gp,
|
||||||
|
const struct sc_gamepad_axis_event *event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a gamepad button event
|
||||||
|
*
|
||||||
|
* This function is mandatory.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
(*process_gamepad_button)(struct sc_gamepad_processor *gp,
|
||||||
|
const struct sc_gamepad_button_event *event);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user