mirror of
https://github.com/encounter/SDL.git
synced 2025-12-09 21:47:44 +00:00
First pass at extending virtual controller functionality
Added the ability to specify a name and the product VID/PID for a virtual controller Also added a test case to testgamecontroller, if you pass --virtual as a parameter
This commit is contained in:
@@ -28,6 +28,10 @@
|
||||
#define SCREEN_WIDTH 512
|
||||
#define SCREEN_HEIGHT 320
|
||||
|
||||
#define BUTTON_SIZE 50
|
||||
#define AXIS_SIZE 50
|
||||
|
||||
|
||||
/* This is indexed by SDL_GameControllerButton. */
|
||||
static const struct { int x; int y; } button_positions[] = {
|
||||
{387, 167}, /* SDL_CONTROLLER_BUTTON_A */
|
||||
@@ -50,7 +54,9 @@ static const struct { int x; int y; } button_positions[] = {
|
||||
{330, 135}, /* SDL_CONTROLLER_BUTTON_PADDLE2 */
|
||||
{132, 175}, /* SDL_CONTROLLER_BUTTON_PADDLE3 */
|
||||
{330, 175}, /* SDL_CONTROLLER_BUTTON_PADDLE4 */
|
||||
{0, 0}, /* SDL_CONTROLLER_BUTTON_TOUCHPAD */
|
||||
};
|
||||
SDL_COMPILE_TIME_ASSERT(button_positions, SDL_arraysize(button_positions) == SDL_CONTROLLER_BUTTON_MAX);
|
||||
|
||||
/* This is indexed by SDL_GameControllerAxis. */
|
||||
static const struct { int x; int y; double angle; } axis_positions[] = {
|
||||
@@ -61,6 +67,7 @@ static const struct { int x; int y; double angle; } axis_positions[] = {
|
||||
{91, -20, 0.0}, /* TRIGGERLEFT */
|
||||
{375, -20, 0.0}, /* TRIGGERRIGHT */
|
||||
};
|
||||
SDL_COMPILE_TIME_ASSERT(axis_positions, SDL_arraysize(axis_positions) == SDL_CONTROLLER_AXIS_MAX);
|
||||
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *screen = NULL;
|
||||
@@ -72,6 +79,11 @@ static SDL_Texture *background_front, *background_back, *button, *axis;
|
||||
static SDL_GameController *gamecontroller;
|
||||
static SDL_GameController **gamecontrollers;
|
||||
static int num_controllers = 0;
|
||||
static SDL_Joystick *virtual_joystick = NULL;
|
||||
static SDL_GameControllerAxis virtual_axis_active = SDL_CONTROLLER_AXIS_INVALID;
|
||||
static int virtual_axis_start_x;
|
||||
static int virtual_axis_start_y;
|
||||
static SDL_GameControllerButton virtual_button_active = SDL_CONTROLLER_BUTTON_INVALID;
|
||||
|
||||
static void UpdateWindowTitle()
|
||||
{
|
||||
@@ -280,12 +292,170 @@ static void CyclePS5TriggerEffect()
|
||||
SDL_GameControllerSendEffect(gamecontroller, &state, sizeof(state));
|
||||
}
|
||||
|
||||
static SDL_bool ShowingFront()
|
||||
{
|
||||
SDL_bool showing_front = SDL_TRUE;
|
||||
int i;
|
||||
|
||||
if (gamecontroller) {
|
||||
/* Show the back of the controller if the paddles are being held */
|
||||
for (i = SDL_CONTROLLER_BUTTON_PADDLE1; i <= SDL_CONTROLLER_BUTTON_PADDLE4; ++i) {
|
||||
if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
|
||||
showing_front = SDL_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((SDL_GetModState() & KMOD_SHIFT) != 0) {
|
||||
showing_front = SDL_FALSE;
|
||||
}
|
||||
return showing_front;
|
||||
}
|
||||
|
||||
static int OpenVirtualController()
|
||||
{
|
||||
SDL_VirtualJoystickDesc desc;
|
||||
|
||||
SDL_zero(desc);
|
||||
desc.version = SDL_VIRTUAL_JOYSTICK_DESC_VERSION;
|
||||
desc.type = SDL_JOYSTICK_TYPE_GAMECONTROLLER;
|
||||
desc.naxes = SDL_CONTROLLER_AXIS_MAX;
|
||||
desc.nbuttons = SDL_CONTROLLER_BUTTON_MAX;
|
||||
return SDL_JoystickAttachVirtualEx(&desc);
|
||||
}
|
||||
|
||||
static SDL_GameControllerButton FindButtonAtPosition(int x, int y)
|
||||
{
|
||||
SDL_Point point;
|
||||
int i;
|
||||
SDL_bool showing_front = ShowingFront();
|
||||
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
for (i = 0; i < SDL_CONTROLLER_BUTTON_TOUCHPAD; ++i) {
|
||||
SDL_bool on_front = (i < SDL_CONTROLLER_BUTTON_PADDLE1 || i > SDL_CONTROLLER_BUTTON_PADDLE4);
|
||||
if (on_front == showing_front) {
|
||||
SDL_Rect rect;
|
||||
rect.x = button_positions[i].x;
|
||||
rect.y = button_positions[i].y;
|
||||
rect.w = BUTTON_SIZE;
|
||||
rect.h = BUTTON_SIZE;
|
||||
if (SDL_PointInRect(&point, &rect)) {
|
||||
return (SDL_GameControllerButton)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL_CONTROLLER_BUTTON_INVALID;
|
||||
}
|
||||
|
||||
static SDL_GameControllerAxis FindAxisAtPosition(int x, int y)
|
||||
{
|
||||
SDL_Point point;
|
||||
int i;
|
||||
SDL_bool showing_front = ShowingFront();
|
||||
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i) {
|
||||
if (showing_front) {
|
||||
SDL_Rect rect;
|
||||
rect.x = axis_positions[i].x;
|
||||
rect.y = axis_positions[i].y;
|
||||
rect.w = AXIS_SIZE;
|
||||
rect.h = AXIS_SIZE;
|
||||
if (SDL_PointInRect(&point, &rect)) {
|
||||
return (SDL_GameControllerAxis)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SDL_CONTROLLER_AXIS_INVALID;
|
||||
}
|
||||
|
||||
static void VirtualControllerMouseMotion(int x, int y)
|
||||
{
|
||||
if (virtual_button_active != SDL_CONTROLLER_BUTTON_INVALID) {
|
||||
if (virtual_axis_active != SDL_CONTROLLER_AXIS_INVALID) {
|
||||
const int MOVING_DISTANCE = 2;
|
||||
if (SDL_abs(x - virtual_axis_start_x) >= MOVING_DISTANCE ||
|
||||
SDL_abs(y - virtual_axis_start_y) >= MOVING_DISTANCE) {
|
||||
SDL_JoystickSetVirtualButton(virtual_joystick, virtual_button_active, SDL_RELEASED);
|
||||
virtual_button_active = SDL_CONTROLLER_BUTTON_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (virtual_axis_active != SDL_CONTROLLER_AXIS_INVALID) {
|
||||
if (virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
|
||||
virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
|
||||
int range = (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN);
|
||||
float distance = SDL_clamp(((float)y - virtual_axis_start_y) / AXIS_SIZE, 0.0f, 1.0f);
|
||||
Sint16 value = (Sint16)(SDL_JOYSTICK_AXIS_MIN + (distance * range));
|
||||
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, value);
|
||||
} else {
|
||||
float distanceX = SDL_clamp(((float)x - virtual_axis_start_x) / AXIS_SIZE, -1.0f, 1.0f);
|
||||
float distanceY = SDL_clamp(((float)y - virtual_axis_start_y) / AXIS_SIZE, -1.0f, 1.0f);
|
||||
Sint16 valueX, valueY;
|
||||
|
||||
if (distanceX >= 0) {
|
||||
valueX = (Sint16)(distanceX * SDL_JOYSTICK_AXIS_MAX);
|
||||
} else {
|
||||
valueX = (Sint16)(distanceX * -SDL_JOYSTICK_AXIS_MIN);
|
||||
}
|
||||
if (distanceY >= 0) {
|
||||
valueY = (Sint16)(distanceY * SDL_JOYSTICK_AXIS_MAX);
|
||||
} else {
|
||||
valueY = (Sint16)(distanceY * -SDL_JOYSTICK_AXIS_MIN);
|
||||
}
|
||||
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, valueX);
|
||||
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active+1, valueY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void VirtualControllerMouseDown(int x, int y)
|
||||
{
|
||||
SDL_GameControllerButton button;
|
||||
SDL_GameControllerAxis axis;
|
||||
|
||||
button = FindButtonAtPosition(x, y);
|
||||
if (button != SDL_CONTROLLER_BUTTON_INVALID) {
|
||||
virtual_button_active = button;
|
||||
SDL_JoystickSetVirtualButton(virtual_joystick, virtual_button_active, SDL_PRESSED);
|
||||
}
|
||||
|
||||
axis = FindAxisAtPosition(x, y);
|
||||
if (axis != SDL_CONTROLLER_AXIS_INVALID) {
|
||||
virtual_axis_active = axis;
|
||||
virtual_axis_start_x = x;
|
||||
virtual_axis_start_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
static void VirtualControllerMouseUp(int x, int y)
|
||||
{
|
||||
if (virtual_button_active != SDL_CONTROLLER_BUTTON_INVALID) {
|
||||
SDL_JoystickSetVirtualButton(virtual_joystick, virtual_button_active, SDL_RELEASED);
|
||||
virtual_button_active = SDL_CONTROLLER_BUTTON_INVALID;
|
||||
}
|
||||
|
||||
if (virtual_axis_active != SDL_CONTROLLER_AXIS_INVALID) {
|
||||
if (virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
|
||||
virtual_axis_active == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
|
||||
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, SDL_JOYSTICK_AXIS_MIN);
|
||||
} else {
|
||||
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active, 0);
|
||||
SDL_JoystickSetVirtualAxis(virtual_joystick, virtual_axis_active+1, 0);
|
||||
}
|
||||
virtual_axis_active = SDL_CONTROLLER_AXIS_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
loop(void *arg)
|
||||
{
|
||||
SDL_Event event;
|
||||
int i;
|
||||
SDL_bool showing_front = SDL_TRUE;
|
||||
SDL_bool showing_front;
|
||||
|
||||
/* Update to get the current event state */
|
||||
SDL_PumpEvents();
|
||||
@@ -356,6 +526,24 @@ loop(void *arg)
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
if (virtual_joystick) {
|
||||
VirtualControllerMouseDown(event.button.x, event.button.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (virtual_joystick) {
|
||||
VirtualControllerMouseUp(event.button.x, event.button.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
if (virtual_joystick) {
|
||||
VirtualControllerMouseMotion(event.motion.x, event.motion.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
if (event.key.keysym.sym >= SDLK_0 && event.key.keysym.sym <= SDLK_9) {
|
||||
if (gamecontroller) {
|
||||
@@ -377,15 +565,7 @@ loop(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
if (gamecontroller) {
|
||||
/* Show the back of the controller if the paddles are being held */
|
||||
for (i = SDL_CONTROLLER_BUTTON_PADDLE1; i <= SDL_CONTROLLER_BUTTON_PADDLE4; ++i) {
|
||||
if (SDL_GameControllerGetButton(gamecontroller, (SDL_GameControllerButton)i) == SDL_PRESSED) {
|
||||
showing_front = SDL_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
showing_front = ShowingFront();
|
||||
|
||||
/* blank screen, set up for drawing this frame. */
|
||||
SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
|
||||
@@ -401,8 +581,8 @@ loop(void *arg)
|
||||
SDL_Rect dst;
|
||||
dst.x = button_positions[i].x;
|
||||
dst.y = button_positions[i].y;
|
||||
dst.w = 50;
|
||||
dst.h = 50;
|
||||
dst.w = BUTTON_SIZE;
|
||||
dst.h = BUTTON_SIZE;
|
||||
SDL_RenderCopyEx(screen, button, NULL, &dst, 0, NULL, SDL_FLIP_NONE);
|
||||
}
|
||||
}
|
||||
@@ -417,16 +597,16 @@ loop(void *arg)
|
||||
SDL_Rect dst;
|
||||
dst.x = axis_positions[i].x;
|
||||
dst.y = axis_positions[i].y;
|
||||
dst.w = 50;
|
||||
dst.h = 50;
|
||||
dst.w = AXIS_SIZE;
|
||||
dst.h = AXIS_SIZE;
|
||||
SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
|
||||
} else if (value > deadzone) {
|
||||
const double angle = axis_positions[i].angle + 180.0;
|
||||
SDL_Rect dst;
|
||||
dst.x = axis_positions[i].x;
|
||||
dst.y = axis_positions[i].y;
|
||||
dst.w = 50;
|
||||
dst.h = 50;
|
||||
dst.w = AXIS_SIZE;
|
||||
dst.h = AXIS_SIZE;
|
||||
SDL_RenderCopyEx(screen, axis, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
|
||||
}
|
||||
}
|
||||
@@ -627,8 +807,22 @@ main(int argc, char *argv[])
|
||||
/* !!! FIXME: */
|
||||
/*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
|
||||
|
||||
if (argv[1] && *argv[1] != '-') {
|
||||
controller_index = SDL_atoi(argv[1]);
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (SDL_strcmp(argv[i], "--virtual") == 0) {
|
||||
int virtual_index = OpenVirtualController();
|
||||
if (virtual_index < 0) {
|
||||
SDL_Log("Couldn't open virtual device: %s\n", SDL_GetError());
|
||||
} else {
|
||||
virtual_joystick = SDL_JoystickOpen(virtual_index);
|
||||
if (!virtual_joystick) {
|
||||
SDL_Log("Couldn't open virtual device: %s\n", SDL_GetError());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (argv[i] && *argv[i] != '-') {
|
||||
controller_index = SDL_atoi(argv[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (controller_index < num_controllers) {
|
||||
gamecontroller = gamecontrollers[controller_index];
|
||||
|
||||
Reference in New Issue
Block a user