mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-06-05 22:30:29 +00:00
Merge b35d4bd9b7 into 79de30a345
This commit is contained in:
commit
5375567a82
9 changed files with 1470 additions and 0 deletions
|
|
@ -1256,6 +1256,35 @@ if(SDL_JOYSTICK)
|
|||
file(GLOB JOYSTICK_VIRTUAL_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/virtual/*.c)
|
||||
list(APPEND SOURCE_FILES ${JOYSTICK_VIRTUAL_SOURCES})
|
||||
endif()
|
||||
|
||||
# DSU (DualShock UDP) client support
|
||||
option(SDL_DSU_JOYSTICK "Enable DSU client joystick support" ON)
|
||||
if(SDL_DSU_JOYSTICK)
|
||||
set(SDL_JOYSTICK_DSU 1)
|
||||
file(GLOB JOYSTICK_DSU_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/dsu/*.c)
|
||||
list(APPEND SOURCE_FILES ${JOYSTICK_DSU_SOURCES})
|
||||
|
||||
# DSU requires network libraries on Unix-like systems
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
if(NOT WIN32)
|
||||
# Check if we need to link against socket libraries
|
||||
include(CheckFunctionExists)
|
||||
check_function_exists(socket HAVE_SOCKET_IN_LIBC)
|
||||
if(NOT HAVE_SOCKET_IN_LIBC)
|
||||
# Try to find socket in libsocket (Solaris)
|
||||
check_library_exists(socket socket "" HAVE_LIBSOCKET)
|
||||
if(HAVE_LIBSOCKET)
|
||||
list(APPEND EXTRA_LIBS socket)
|
||||
endif()
|
||||
# Try to find inet_addr in libnsl (Solaris)
|
||||
check_library_exists(nsl inet_addr "" HAVE_LIBNSL)
|
||||
if(HAVE_LIBNSL)
|
||||
list(APPEND EXTRA_LIBS nsl)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SDL_VIDEO)
|
||||
|
|
@ -1995,6 +2024,10 @@ elseif(WINDOWS)
|
|||
# Libraries for Win32 native and MinGW
|
||||
if(NOT WINDOWS_STORE)
|
||||
list(APPEND EXTRA_LIBS kernel32 user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32)
|
||||
# Add Winsock library for DSU support
|
||||
if(SDL_DSU_JOYSTICK)
|
||||
list(APPEND EXTRA_LIBS ws2_32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WINDOWS_STORE)
|
||||
|
|
@ -2479,6 +2512,11 @@ elseif(HAIKU)
|
|||
|
||||
CheckPTHREAD()
|
||||
list(APPEND EXTRA_LIBS root be media game device textencoding)
|
||||
|
||||
# Add network library for DSU support on Haiku
|
||||
if(SDL_DSU_JOYSTICK)
|
||||
list(APPEND EXTRA_LIBS network)
|
||||
endif()
|
||||
|
||||
elseif(RISCOS)
|
||||
if(SDL_MISC)
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@
|
|||
#cmakedefine SDL_JOYSTICK_RAWINPUT @SDL_JOYSTICK_RAWINPUT@
|
||||
#cmakedefine SDL_JOYSTICK_EMSCRIPTEN @SDL_JOYSTICK_EMSCRIPTEN@
|
||||
#cmakedefine SDL_JOYSTICK_VIRTUAL @SDL_JOYSTICK_VIRTUAL@
|
||||
#cmakedefine SDL_JOYSTICK_DSU @SDL_JOYSTICK_DSU@
|
||||
#cmakedefine SDL_JOYSTICK_VITA @SDL_JOYSTICK_VITA@
|
||||
#cmakedefine SDL_JOYSTICK_PSP @SDL_JOYSTICK_PSP@
|
||||
#cmakedefine SDL_JOYSTICK_PS2 @SDL_JOYSTICK_PS2@
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ typedef unsigned int uintptr_t;
|
|||
#define SDL_JOYSTICK_RAWINPUT 1
|
||||
#endif
|
||||
#define SDL_JOYSTICK_VIRTUAL 1
|
||||
#define SDL_JOYSTICK_DSU 1
|
||||
#ifdef HAVE_WINDOWS_GAMING_INPUT_H
|
||||
#define SDL_JOYSTICK_WGI 1
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1247,6 +1247,40 @@ extern "C" {
|
|||
*/
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED "SDL_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED"
|
||||
|
||||
/**
|
||||
* A variable controlling whether the DSU (DualShock UDP) joystick driver should be used.
|
||||
*
|
||||
* This variable can be set to the following values:
|
||||
*
|
||||
* - "0": DSU driver is disabled
|
||||
* - "1": DSU driver is enabled (default)
|
||||
*
|
||||
* The DSU driver allows SDL to connect to DSU servers (DS4Windows, BetterJoy, etc.)
|
||||
* to receive controller data over UDP, including motion sensors and touchpad data.
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_DSU "SDL_JOYSTICK_DSU"
|
||||
|
||||
/**
|
||||
* A variable controlling the DSU server address.
|
||||
*
|
||||
* The default value is "127.0.0.1"
|
||||
*/
|
||||
#define SDL_HINT_DSU_SERVER "SDL_DSU_SERVER"
|
||||
|
||||
/**
|
||||
* A variable controlling the DSU server port.
|
||||
*
|
||||
* The default value is "26760"
|
||||
*/
|
||||
#define SDL_HINT_DSU_SERVER_PORT "SDL_DSU_SERVER_PORT"
|
||||
|
||||
/**
|
||||
* A variable controlling the DSU client port.
|
||||
*
|
||||
* The default value is "0" (auto-select)
|
||||
*/
|
||||
#define SDL_HINT_DSU_CLIENT_PORT "SDL_DSU_CLIENT_PORT"
|
||||
|
||||
/**
|
||||
* A variable controlling whether IOKit should be used for controller
|
||||
* handling.
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@
|
|||
#include "./virtual/SDL_virtualjoystick_c.h"
|
||||
#endif
|
||||
|
||||
#ifdef SDL_JOYSTICK_DSU
|
||||
#include "./dsu/SDL_dsujoystick_c.h"
|
||||
#endif
|
||||
|
||||
static SDL_JoystickDriver *SDL_joystick_drivers[] = {
|
||||
#ifdef SDL_JOYSTICK_HIDAPI /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */
|
||||
&SDL_HIDAPI_JoystickDriver,
|
||||
|
|
@ -100,6 +104,9 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
|
|||
#ifdef SDL_JOYSTICK_VIRTUAL
|
||||
&SDL_VIRTUAL_JoystickDriver,
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_DSU
|
||||
&SDL_DSU_JoystickDriver,
|
||||
#endif
|
||||
#ifdef SDL_JOYSTICK_VITA
|
||||
&SDL_VITA_JoystickDriver,
|
||||
#endif
|
||||
|
|
|
|||
465
src/joystick/dsu/SDL_dsujoystick.c
Normal file
465
src/joystick/dsu/SDL_dsujoystick.c
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_DSU
|
||||
|
||||
/* DSU (DualShock UDP) client joystick driver - Main Implementation */
|
||||
|
||||
#include "SDL_joystick.h"
|
||||
#include "SDL_endian.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "SDL_atomic.h"
|
||||
#include "../SDL_sysjoystick.h"
|
||||
#include "../SDL_joystick_c.h"
|
||||
#include "SDL_dsujoystick_c.h"
|
||||
|
||||
/* Platform-specific socket includes */
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
typedef int socklen_t;
|
||||
#define DSU_SOCKET_ERROR SOCKET_ERROR
|
||||
#define DSU_INVALID_SOCKET INVALID_SOCKET
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#define DSU_SOCKET_ERROR -1
|
||||
#define DSU_INVALID_SOCKET -1
|
||||
#define closesocket close
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/* Constants */
|
||||
#define SERVER_REREGISTER_INTERVAL 1000 /* ms */
|
||||
#define SERVER_TIMEOUT_INTERVAL 2000 /* ms */
|
||||
#define GRAVITY_ACCELERATION 9.80665f /* m/s² */
|
||||
|
||||
/* Global DSU context - defined in SDL_dsujoystick_driver.c */
|
||||
extern DSU_Context *g_dsu_context;
|
||||
|
||||
/* Forward declarations */
|
||||
void DSU_RequestControllerInfo(DSU_Context *ctx, Uint8 slot);
|
||||
void DSU_RequestControllerData(DSU_Context *ctx, Uint8 slot);
|
||||
|
||||
/* Socket helpers implementation */
|
||||
int DSU_InitSockets(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
return WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void DSU_QuitSockets(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
int DSU_CreateSocket(Uint16 port)
|
||||
{
|
||||
int sock;
|
||||
struct sockaddr_in addr;
|
||||
int reuse = 1;
|
||||
#ifdef _WIN32
|
||||
u_long mode = 1;
|
||||
#else
|
||||
int flags;
|
||||
#endif
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sock == DSU_INVALID_SOCKET) {
|
||||
return DSU_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
/* Allow address reuse */
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse));
|
||||
|
||||
/* Set socket to non-blocking */
|
||||
#ifdef _WIN32
|
||||
ioctlsocket(sock, FIONBIO, &mode);
|
||||
#else
|
||||
flags = fcntl(sock, F_GETFL, 0);
|
||||
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
/* Bind to client port if specified */
|
||||
if (port != 0) {
|
||||
SDL_memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
/* Bind failure is not fatal, continue anyway */
|
||||
}
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void DSU_CloseSocket(int socket)
|
||||
{
|
||||
if (socket != DSU_INVALID_SOCKET) {
|
||||
closesocket(socket);
|
||||
}
|
||||
}
|
||||
|
||||
/* Complete CRC32 table */
|
||||
static const Uint32 crc32_table[256] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
Uint32 DSU_CalculateCRC32(const Uint8 *data, size_t length)
|
||||
{
|
||||
Uint32 crc = 0xFFFFFFFF;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
crc = crc32_table[(crc ^ data[i]) & 0xFF] ^ (crc >> 8);
|
||||
}
|
||||
|
||||
return crc ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
/* Send a packet to the DSU server */
|
||||
static int DSU_SendPacket(DSU_Context *ctx, void *packet, size_t size)
|
||||
{
|
||||
struct sockaddr_in server;
|
||||
DSU_Header *header = (DSU_Header *)packet;
|
||||
|
||||
/* Set header fields */
|
||||
SDL_memcpy(header->magic, DSU_MAGIC_CLIENT, 4);
|
||||
header->version = SDL_SwapLE16(DSU_PROTOCOL_VERSION);
|
||||
header->length = SDL_SwapLE16((Uint16)(size - sizeof(DSU_Header)));
|
||||
header->client_id = SDL_SwapLE32(ctx->client_id);
|
||||
|
||||
/* Calculate CRC32 */
|
||||
header->crc32 = 0;
|
||||
header->crc32 = SDL_SwapLE32(DSU_CalculateCRC32((Uint8*)packet, size));
|
||||
|
||||
/* Send to server */
|
||||
SDL_memset(&server, 0, sizeof(server));
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(ctx->server_port);
|
||||
server.sin_addr.s_addr = inet_addr(ctx->server_address);
|
||||
|
||||
return sendto(ctx->socket, (const char*)packet, (int)size, 0,
|
||||
(struct sockaddr *)&server, sizeof(server));
|
||||
}
|
||||
|
||||
/* Request controller information */
|
||||
void DSU_RequestControllerInfo(DSU_Context *ctx, Uint8 slot)
|
||||
{
|
||||
DSU_PortRequest request;
|
||||
|
||||
SDL_memset(&request, 0, sizeof(request));
|
||||
request.header.message_type = SDL_SwapLE32(DSU_MSG_PORTS_INFO);
|
||||
request.flags = 0;
|
||||
request.slot_id = slot; /* 0xFF for all slots */
|
||||
/* MAC is zeros for all controllers */
|
||||
|
||||
DSU_SendPacket(ctx, &request, sizeof(request));
|
||||
}
|
||||
|
||||
/* Request controller data */
|
||||
void DSU_RequestControllerData(DSU_Context *ctx, Uint8 slot)
|
||||
{
|
||||
DSU_PortRequest request;
|
||||
|
||||
SDL_memset(&request, 0, sizeof(request));
|
||||
request.header.message_type = SDL_SwapLE32(DSU_MSG_DATA);
|
||||
request.flags = 0; /* Subscribe to data */
|
||||
request.slot_id = slot;
|
||||
|
||||
DSU_SendPacket(ctx, &request, sizeof(request));
|
||||
}
|
||||
|
||||
/* Process incoming controller data */
|
||||
void DSU_ProcessControllerData(DSU_Context *ctx, DSU_ControllerData *data)
|
||||
{
|
||||
DSU_ControllerSlot *slot;
|
||||
int slot_id;
|
||||
SDL_bool was_connected;
|
||||
|
||||
/* Get slot ID */
|
||||
slot_id = data->info.slot;
|
||||
if (slot_id >= DSU_MAX_SLOTS) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_LockMutex(ctx->slots_mutex);
|
||||
slot = &ctx->slots[slot_id];
|
||||
|
||||
/* Update connection state */
|
||||
was_connected = slot->connected;
|
||||
slot->connected = (data->info.slot_state == DSU_STATE_CONNECTED);
|
||||
|
||||
if (slot->connected) {
|
||||
/* Update controller info */
|
||||
SDL_memcpy(slot->mac, data->info.mac, 6);
|
||||
slot->battery = data->info.battery;
|
||||
slot->model = data->info.device_model;
|
||||
slot->connection = data->info.connection_type;
|
||||
slot->slot_id = slot_id;
|
||||
|
||||
/* Generate name */
|
||||
SDL_snprintf(slot->name, sizeof(slot->name), "DSUClient/%d", slot_id);
|
||||
|
||||
/* Update button states */
|
||||
slot->buttons = 0;
|
||||
|
||||
/* Map DSU buttons to SDL buttons */
|
||||
if (data->button_states_2 & DSU_BUTTON_CROSS) slot->buttons |= (1 << 0);
|
||||
if (data->button_states_2 & DSU_BUTTON_CIRCLE) slot->buttons |= (1 << 1);
|
||||
if (data->button_states_2 & DSU_BUTTON_SQUARE) slot->buttons |= (1 << 2);
|
||||
if (data->button_states_2 & DSU_BUTTON_TRIANGLE) slot->buttons |= (1 << 3);
|
||||
if (data->button_states_2 & DSU_BUTTON_L1) slot->buttons |= (1 << 4);
|
||||
if (data->button_states_2 & DSU_BUTTON_R1) slot->buttons |= (1 << 5);
|
||||
if (data->button_states_1 & DSU_BUTTON_SHARE) slot->buttons |= (1 << 6);
|
||||
if (data->button_states_1 & DSU_BUTTON_OPTIONS) slot->buttons |= (1 << 7);
|
||||
if (data->button_states_1 & DSU_BUTTON_L3) slot->buttons |= (1 << 8);
|
||||
if (data->button_states_1 & DSU_BUTTON_R3) slot->buttons |= (1 << 9);
|
||||
if (data->button_ps) slot->buttons |= (1 << 10);
|
||||
if (data->button_touch) slot->buttons |= (1 << 11);
|
||||
|
||||
/* Update analog sticks */
|
||||
slot->axes[0] = ((Sint16)data->left_stick_x - 128) * 257;
|
||||
slot->axes[1] = ((Sint16)data->left_stick_y - 128) * -257;
|
||||
slot->axes[2] = ((Sint16)data->right_stick_x - 128) * 257;
|
||||
slot->axes[3] = ((Sint16)data->right_stick_y - 128) * -257;
|
||||
|
||||
/* Triggers */
|
||||
slot->axes[4] = ((Sint16)data->analog_trigger_l2) * 128;
|
||||
slot->axes[5] = ((Sint16)data->analog_trigger_r2) * 128;
|
||||
|
||||
/* D-Pad as hat */
|
||||
slot->hat = SDL_HAT_CENTERED;
|
||||
if (data->button_states_1 & DSU_BUTTON_DPAD_UP) slot->hat |= SDL_HAT_UP;
|
||||
if (data->button_states_1 & DSU_BUTTON_DPAD_DOWN) slot->hat |= SDL_HAT_DOWN;
|
||||
if (data->button_states_1 & DSU_BUTTON_DPAD_LEFT) slot->hat |= SDL_HAT_LEFT;
|
||||
if (data->button_states_1 & DSU_BUTTON_DPAD_RIGHT) slot->hat |= SDL_HAT_RIGHT;
|
||||
|
||||
/* Motion data */
|
||||
if (data->motion_timestamp != 0) {
|
||||
slot->has_gyro = SDL_TRUE;
|
||||
slot->has_accel = SDL_TRUE;
|
||||
slot->motion_timestamp = SDL_SwapLE64(data->motion_timestamp);
|
||||
|
||||
/* Convert gyro from deg/s to rad/s (handling endianness) */
|
||||
slot->gyro[0] = SDL_SwapFloatLE(data->gyro_pitch) * (M_PI / 180.0f);
|
||||
slot->gyro[1] = SDL_SwapFloatLE(data->gyro_yaw) * (M_PI / 180.0f);
|
||||
slot->gyro[2] = SDL_SwapFloatLE(data->gyro_roll) * (M_PI / 180.0f);
|
||||
|
||||
/* Convert accel from g to m/s² (handling endianness) */
|
||||
slot->accel[0] = SDL_SwapFloatLE(data->accel_x) * GRAVITY_ACCELERATION;
|
||||
slot->accel[1] = SDL_SwapFloatLE(data->accel_y) * GRAVITY_ACCELERATION;
|
||||
slot->accel[2] = SDL_SwapFloatLE(data->accel_z) * GRAVITY_ACCELERATION;
|
||||
}
|
||||
|
||||
/* Update last packet time */
|
||||
slot->last_packet_time = SDL_GetTicks64();
|
||||
|
||||
/* Touch data */
|
||||
slot->has_touchpad = SDL_TRUE;
|
||||
slot->touch1_active = data->touch1_active;
|
||||
slot->touch2_active = data->touch2_active;
|
||||
slot->touch1_id = data->touch1_id;
|
||||
slot->touch2_id = data->touch2_id;
|
||||
slot->touch1_x = SDL_SwapLE16(data->touch1_x);
|
||||
slot->touch1_y = SDL_SwapLE16(data->touch1_y);
|
||||
slot->touch2_x = SDL_SwapLE16(data->touch2_x);
|
||||
slot->touch2_y = SDL_SwapLE16(data->touch2_y);
|
||||
|
||||
/* Update timing */
|
||||
slot->last_packet_time = SDL_GetTicks64();
|
||||
slot->packet_number = SDL_SwapLE32(data->packet_number);
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(ctx->slots_mutex);
|
||||
|
||||
/* Handle connection state changes */
|
||||
if (!was_connected && slot->connected) {
|
||||
Uint16 vendor;
|
||||
Uint16 product;
|
||||
|
||||
/* New controller connected */
|
||||
slot->instance_id = SDL_GetNextJoystickInstanceID();
|
||||
|
||||
/* Update controller ID for SDL */
|
||||
vendor = 0x054C; /* Sony vendor ID */
|
||||
product = 0x05C4; /* DS4 product ID by default */
|
||||
if (slot->model == DSU_MODEL_FULL_GYRO) {
|
||||
product = 0x09CC;
|
||||
}
|
||||
slot->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_BLUETOOTH, vendor, product, 0,
|
||||
NULL, slot->name, 'd', 0);
|
||||
|
||||
/* Subscribe to controller data updates */
|
||||
DSU_RequestControllerData(ctx, slot_id);
|
||||
|
||||
SDL_PrivateJoystickAdded(slot->instance_id);
|
||||
} else if (was_connected && !slot->connected) {
|
||||
/* Controller disconnected */
|
||||
SDL_PrivateJoystickRemoved(slot->instance_id);
|
||||
slot->instance_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Receiver thread implementation */
|
||||
int DSU_ReceiverThread(void *data)
|
||||
{
|
||||
DSU_Context *ctx = (DSU_Context *)data;
|
||||
Uint8 buffer[1024];
|
||||
struct sockaddr_in sender;
|
||||
socklen_t sender_len = sizeof(sender);
|
||||
DSU_Header *header;
|
||||
int received;
|
||||
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH);
|
||||
|
||||
while (SDL_AtomicGet(&ctx->running)) {
|
||||
received = recvfrom(ctx->socket, (char*)buffer, sizeof(buffer), 0,
|
||||
(struct sockaddr *)&sender, &sender_len);
|
||||
|
||||
if (received > (int)sizeof(DSU_Header)) {
|
||||
header = (DSU_Header *)buffer;
|
||||
|
||||
/* Validate magic */
|
||||
if (SDL_memcmp(header->magic, DSU_MAGIC_SERVER, 4) == 0) {
|
||||
Uint32 received_crc;
|
||||
Uint32 calculated_crc;
|
||||
|
||||
/* Validate CRC32 */
|
||||
received_crc = SDL_SwapLE32(header->crc32);
|
||||
header->crc32 = 0;
|
||||
calculated_crc = DSU_CalculateCRC32(buffer, received);
|
||||
|
||||
if (received_crc == calculated_crc) {
|
||||
Uint32 msg_type = SDL_SwapLE32(header->message_type);
|
||||
|
||||
switch (msg_type) {
|
||||
case DSU_MSG_VERSION:
|
||||
/* Version info received */
|
||||
break;
|
||||
|
||||
case DSU_MSG_PORTS_INFO: {
|
||||
/* Port info response - tells us which slots have controllers */
|
||||
if (received >= (int)(sizeof(DSU_Header) + 4)) {
|
||||
Uint8 *data_ptr;
|
||||
Uint8 slot_id;
|
||||
Uint8 slot_state;
|
||||
|
||||
/* Parse port info */
|
||||
data_ptr = buffer + sizeof(DSU_Header);
|
||||
slot_id = data_ptr[0];
|
||||
slot_state = data_ptr[1];
|
||||
/* Skip device_model = data_ptr[2] and connection_type = data_ptr[3] - not used */
|
||||
|
||||
/* If controller is connected in this slot, request data */
|
||||
if (slot_state == DSU_STATE_CONNECTED && slot_id < DSU_MAX_SLOTS) {
|
||||
DSU_RequestControllerData(ctx, slot_id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DSU_MSG_DATA:
|
||||
/* Controller data */
|
||||
if (received >= (int)sizeof(DSU_ControllerData)) {
|
||||
DSU_ProcessControllerData(ctx, (DSU_ControllerData *)buffer);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unknown message type */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (received < 0) {
|
||||
/* Check for real errors (not just EWOULDBLOCK) */
|
||||
#ifdef _WIN32
|
||||
int error = WSAGetLastError();
|
||||
if (error != WSAEWOULDBLOCK && error != WSAEINTR && error != WSAECONNRESET) {
|
||||
SDL_Delay(100); /* Back off on errors */
|
||||
}
|
||||
#else
|
||||
if (errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) {
|
||||
SDL_Delay(100);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Small delay to prevent CPU spinning */
|
||||
SDL_Delay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_JOYSTICK_DSU */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
108
src/joystick/dsu/SDL_dsujoystick_c.h
Normal file
108
src/joystick/dsu/SDL_dsujoystick_c.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_dsujoystick_c_h_
|
||||
#define SDL_dsujoystick_c_h_
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_DSU
|
||||
|
||||
#include "../SDL_sysjoystick.h"
|
||||
#include "SDL_dsuprotocol.h"
|
||||
|
||||
/* DSU Joystick driver */
|
||||
extern SDL_JoystickDriver SDL_DSU_JoystickDriver;
|
||||
|
||||
/* Internal structures */
|
||||
typedef struct DSU_ControllerSlot {
|
||||
SDL_bool connected;
|
||||
SDL_JoystickID instance_id;
|
||||
SDL_JoystickGUID guid;
|
||||
char name[128];
|
||||
|
||||
/* DSU protocol data */
|
||||
Uint8 slot_id;
|
||||
Uint8 mac[6];
|
||||
Uint8 battery;
|
||||
DSU_DeviceModel model;
|
||||
DSU_ConnectionType connection;
|
||||
|
||||
/* Controller state */
|
||||
Uint16 buttons;
|
||||
Sint16 axes[6]; /* LX, LY, RX, RY, L2, R2 */
|
||||
Uint8 hat;
|
||||
|
||||
/* Motion data */
|
||||
SDL_bool has_gyro;
|
||||
SDL_bool has_accel;
|
||||
float gyro[3]; /* Pitch, Yaw, Roll in rad/s */
|
||||
float accel[3]; /* X, Y, Z in m/s² */
|
||||
Uint64 motion_timestamp;
|
||||
|
||||
/* Touch data */
|
||||
SDL_bool has_touchpad;
|
||||
SDL_bool touch1_active;
|
||||
SDL_bool touch2_active;
|
||||
Uint8 touch1_id;
|
||||
Uint8 touch2_id;
|
||||
Uint16 touch1_x, touch1_y;
|
||||
Uint16 touch2_x, touch2_y;
|
||||
|
||||
/* Timing */
|
||||
Uint64 last_packet_time;
|
||||
Uint32 packet_number;
|
||||
} DSU_ControllerSlot;
|
||||
|
||||
typedef struct DSU_Context {
|
||||
/* Network */
|
||||
int socket;
|
||||
SDL_Thread *receiver_thread;
|
||||
SDL_atomic_t running;
|
||||
|
||||
/* Server configuration */
|
||||
char server_address[256];
|
||||
Uint16 server_port;
|
||||
Uint16 client_port;
|
||||
Uint32 client_id;
|
||||
|
||||
/* Controller slots (4 max per DSU protocol) */
|
||||
DSU_ControllerSlot slots[DSU_MAX_SLOTS];
|
||||
SDL_mutex *slots_mutex;
|
||||
|
||||
/* Timing for periodic updates */
|
||||
Uint64 last_request_time;
|
||||
} DSU_Context;
|
||||
|
||||
/* Socket helpers */
|
||||
int DSU_InitSockets(void);
|
||||
void DSU_QuitSockets(void);
|
||||
int DSU_CreateSocket(Uint16 port);
|
||||
void DSU_CloseSocket(int socket);
|
||||
|
||||
/* CRC32 calculation */
|
||||
Uint32 DSU_CalculateCRC32(const Uint8 *data, size_t length);
|
||||
|
||||
#endif /* SDL_JOYSTICK_DSU */
|
||||
|
||||
#endif /* SDL_dsujoystick_c_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
615
src/joystick/dsu/SDL_dsujoystick_driver.c
Normal file
615
src/joystick/dsu/SDL_dsujoystick_driver.c
Normal file
|
|
@ -0,0 +1,615 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_JOYSTICK_DSU
|
||||
|
||||
/* DSU Joystick Driver - SDL Driver Interface Implementation */
|
||||
|
||||
#include "SDL_joystick.h"
|
||||
#include "SDL_endian.h"
|
||||
#include "SDL_timer.h"
|
||||
#include "SDL_hints.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "SDL_atomic.h"
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_events.h"
|
||||
#include "../SDL_sysjoystick.h"
|
||||
#include "../SDL_joystick_c.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
#include "SDL_dsujoystick_c.h"
|
||||
|
||||
/* Platform-specific socket includes for rumble support */
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
/* Global DSU context (defined in SDL_dsujoystick.c) */
|
||||
DSU_Context *g_dsu_context = NULL;
|
||||
|
||||
/* Forward declarations */
|
||||
extern int DSU_ReceiverThread(void *data);
|
||||
extern void DSU_RequestControllerInfo(DSU_Context *ctx, Uint8 slot);
|
||||
extern void DSU_RequestControllerData(DSU_Context *ctx, Uint8 slot);
|
||||
|
||||
/* Driver functions */
|
||||
static int DSU_JoystickInit(void)
|
||||
{
|
||||
const char *enabled;
|
||||
const char *server;
|
||||
const char *server_port;
|
||||
const char *client_port;
|
||||
|
||||
/* Check if DSU is enabled */
|
||||
enabled = SDL_GetHint(SDL_HINT_JOYSTICK_DSU);
|
||||
if (enabled && SDL_atoi(enabled) == 0) {
|
||||
return 0; /* DSU disabled */
|
||||
}
|
||||
|
||||
/* Allocate context */
|
||||
g_dsu_context = (DSU_Context *)SDL_calloc(1, sizeof(DSU_Context));
|
||||
if (!g_dsu_context) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* Get configuration from hints with fallbacks */
|
||||
server = SDL_GetHint(SDL_HINT_DSU_SERVER);
|
||||
if (!server || !*server) {
|
||||
server = DSU_SERVER_ADDRESS_DEFAULT;
|
||||
}
|
||||
SDL_strlcpy(g_dsu_context->server_address, server,
|
||||
sizeof(g_dsu_context->server_address));
|
||||
|
||||
server_port = SDL_GetHint(SDL_HINT_DSU_SERVER_PORT);
|
||||
if (server_port && *server_port) {
|
||||
g_dsu_context->server_port = SDL_atoi(server_port);
|
||||
} else {
|
||||
g_dsu_context->server_port = DSU_SERVER_PORT_DEFAULT;
|
||||
}
|
||||
|
||||
client_port = SDL_GetHint(SDL_HINT_DSU_CLIENT_PORT);
|
||||
if (client_port && *client_port) {
|
||||
g_dsu_context->client_port = SDL_atoi(client_port);
|
||||
} else {
|
||||
g_dsu_context->client_port = DSU_CLIENT_PORT_DEFAULT;
|
||||
}
|
||||
|
||||
g_dsu_context->client_id = SDL_GetTicks();
|
||||
|
||||
/* Initialize sockets */
|
||||
if (DSU_InitSockets() != 0) {
|
||||
SDL_free(g_dsu_context);
|
||||
g_dsu_context = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create UDP socket */
|
||||
g_dsu_context->socket = DSU_CreateSocket(g_dsu_context->client_port);
|
||||
if (g_dsu_context->socket == -1) {
|
||||
DSU_QuitSockets();
|
||||
SDL_free(g_dsu_context);
|
||||
g_dsu_context = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Create mutex */
|
||||
g_dsu_context->slots_mutex = SDL_CreateMutex();
|
||||
if (!g_dsu_context->slots_mutex) {
|
||||
DSU_CloseSocket(g_dsu_context->socket);
|
||||
DSU_QuitSockets();
|
||||
SDL_free(g_dsu_context);
|
||||
g_dsu_context = NULL;
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* Start receiver thread */
|
||||
SDL_AtomicSet(&g_dsu_context->running, 1);
|
||||
g_dsu_context->receiver_thread = SDL_CreateThreadInternal(
|
||||
DSU_ReceiverThread, "DSU_Receiver", 0, g_dsu_context);
|
||||
if (!g_dsu_context->receiver_thread) {
|
||||
SDL_DestroyMutex(g_dsu_context->slots_mutex);
|
||||
DSU_CloseSocket(g_dsu_context->socket);
|
||||
DSU_QuitSockets();
|
||||
SDL_free(g_dsu_context);
|
||||
g_dsu_context = NULL;
|
||||
return SDL_SetError("Failed to create DSU receiver thread");
|
||||
}
|
||||
|
||||
/* Request controller info from all slots */
|
||||
DSU_RequestControllerInfo(g_dsu_context, 0xFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSU_JoystickGetCount(void)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
|
||||
if (!g_dsu_context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void DSU_JoystickDetect(void)
|
||||
{
|
||||
Uint64 now;
|
||||
int i;
|
||||
|
||||
if (!g_dsu_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Periodically request controller info and re-subscribe to data */
|
||||
now = SDL_GetTicks64();
|
||||
if (now - g_dsu_context->last_request_time >= 500) { /* Request more frequently */
|
||||
DSU_RequestControllerInfo(g_dsu_context, 0xFF);
|
||||
|
||||
/* Re-subscribe to data for connected controllers */
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected) {
|
||||
DSU_RequestControllerData(g_dsu_context, i);
|
||||
}
|
||||
}
|
||||
|
||||
g_dsu_context->last_request_time = now;
|
||||
}
|
||||
|
||||
/* Check for timeouts */
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected &&
|
||||
now - g_dsu_context->slots[i].last_packet_time > 5000) { /* Increased timeout */
|
||||
/* Controller timed out */
|
||||
g_dsu_context->slots[i].connected = SDL_FALSE;
|
||||
SDL_PrivateJoystickRemoved(g_dsu_context->slots[i].instance_id);
|
||||
g_dsu_context->slots[i].instance_id = 0;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
}
|
||||
|
||||
static const char *DSU_JoystickGetDeviceName(int device_index)
|
||||
{
|
||||
int i, count = 0;
|
||||
|
||||
if (!g_dsu_context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected) {
|
||||
if (count == device_index) {
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
return g_dsu_context->slots[i].name;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *DSU_JoystickGetDevicePath(int device_index)
|
||||
{
|
||||
return NULL; /* No path for network devices */
|
||||
}
|
||||
|
||||
static int DSU_JoystickGetDevicePlayerIndex(int device_index)
|
||||
{
|
||||
int i, count = 0;
|
||||
|
||||
if (!g_dsu_context) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected) {
|
||||
if (count == device_index) {
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
return i; /* Return slot ID as player index */
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void DSU_JoystickSetDevicePlayerIndex(int device_index, int player_index)
|
||||
{
|
||||
/* DSU controllers have fixed slots, can't change */
|
||||
}
|
||||
|
||||
static SDL_JoystickGUID DSU_JoystickGetDeviceGUID(int device_index)
|
||||
{
|
||||
SDL_JoystickGUID guid;
|
||||
int i, count = 0;
|
||||
|
||||
SDL_zero(guid);
|
||||
|
||||
if (!g_dsu_context) {
|
||||
return guid;
|
||||
}
|
||||
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected) {
|
||||
if (count == device_index) {
|
||||
guid = g_dsu_context->slots[i].guid;
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
return guid;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
|
||||
return guid;
|
||||
}
|
||||
|
||||
static SDL_JoystickID DSU_JoystickGetDeviceInstanceID(int device_index)
|
||||
{
|
||||
int i, count = 0;
|
||||
|
||||
if (!g_dsu_context) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected) {
|
||||
if (count == device_index) {
|
||||
SDL_JoystickID id = g_dsu_context->slots[i].instance_id;
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
return id;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int DSU_JoystickOpen(SDL_Joystick *joystick, int device_index)
|
||||
{
|
||||
DSU_ControllerSlot *slot = NULL;
|
||||
int i, count = 0;
|
||||
|
||||
if (!g_dsu_context) {
|
||||
return SDL_SetError("DSU not initialized");
|
||||
}
|
||||
|
||||
/* Find the slot for this device */
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
for (i = 0; i < DSU_MAX_SLOTS; i++) {
|
||||
if (g_dsu_context->slots[i].connected) {
|
||||
if (count == device_index) {
|
||||
slot = &g_dsu_context->slots[i];
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
|
||||
if (!slot) {
|
||||
return SDL_SetError("DSU device not found");
|
||||
}
|
||||
|
||||
joystick->instance_id = slot->instance_id;
|
||||
joystick->hwdata = (struct joystick_hwdata *)slot;
|
||||
joystick->nbuttons = 12; /* Standard PS4 buttons */
|
||||
joystick->naxes = 6; /* LX, LY, RX, RY, L2, R2 */
|
||||
joystick->nhats = 1; /* D-Pad */
|
||||
|
||||
/* Set up touchpad if available */
|
||||
if (slot->has_touchpad) {
|
||||
joystick->ntouchpads = 1;
|
||||
joystick->touchpads = (SDL_JoystickTouchpadInfo *)SDL_calloc(1, sizeof(SDL_JoystickTouchpadInfo));
|
||||
if (joystick->touchpads) {
|
||||
joystick->touchpads[0].nfingers = 2; /* DSU supports 2 fingers */
|
||||
} else {
|
||||
joystick->ntouchpads = 0; /* Failed to allocate, disable touchpad */
|
||||
}
|
||||
}
|
||||
|
||||
/* Register sensors if available */
|
||||
if (slot->has_gyro || (slot->model == DSU_MODEL_FULL_GYRO) || (slot->model == DSU_MODEL_PARTIAL_GYRO)) {
|
||||
/* DSU reports gyro at varying rates, but typically 250-1000Hz for DS4/DS5 */
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f);
|
||||
slot->has_gyro = SDL_TRUE;
|
||||
}
|
||||
if (slot->has_accel || (slot->model == DSU_MODEL_FULL_GYRO)) {
|
||||
/* DSU reports accelerometer at same rate as gyro */
|
||||
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f);
|
||||
slot->has_accel = SDL_TRUE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSU_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble)
|
||||
{
|
||||
DSU_ControllerSlot *slot = (DSU_ControllerSlot *)joystick->hwdata;
|
||||
DSU_RumblePacket packet;
|
||||
struct sockaddr_in server;
|
||||
|
||||
if (!g_dsu_context || !slot || !slot->connected) {
|
||||
return SDL_SetError("DSU controller not available");
|
||||
}
|
||||
|
||||
/* Build rumble packet */
|
||||
SDL_memset(&packet, 0, sizeof(packet));
|
||||
SDL_memcpy(packet.header.magic, DSU_MAGIC_CLIENT, 4);
|
||||
packet.header.version = SDL_SwapLE16(DSU_PROTOCOL_VERSION);
|
||||
packet.header.length = SDL_SwapLE16((Uint16)(sizeof(packet) - sizeof(DSU_Header)));
|
||||
packet.header.client_id = SDL_SwapLE32(g_dsu_context->client_id);
|
||||
packet.header.message_type = SDL_SwapLE32(DSU_MSG_RUMBLE);
|
||||
|
||||
/* Set rumble values */
|
||||
packet.slot = slot->slot_id;
|
||||
packet.motor_left = (Uint8)(low_frequency_rumble >> 8); /* Convert from 16-bit to 8-bit */
|
||||
packet.motor_right = (Uint8)(high_frequency_rumble >> 8);
|
||||
|
||||
/* Calculate CRC32 */
|
||||
packet.header.crc32 = 0;
|
||||
packet.header.crc32 = SDL_SwapLE32(DSU_CalculateCRC32((Uint8*)&packet, sizeof(packet)));
|
||||
|
||||
/* Send to server */
|
||||
SDL_memset(&server, 0, sizeof(server));
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons(g_dsu_context->server_port);
|
||||
server.sin_addr.s_addr = inet_addr(g_dsu_context->server_address);
|
||||
|
||||
if (sendto(g_dsu_context->socket, (const char*)&packet, sizeof(packet), 0,
|
||||
(struct sockaddr *)&server, sizeof(server)) < 0) {
|
||||
return SDL_SetError("Failed to send rumble packet");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSU_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static Uint32 DSU_JoystickGetCapabilities(SDL_Joystick *joystick)
|
||||
{
|
||||
Uint32 caps = 0;
|
||||
|
||||
/* DSU protocol supports rumble through unofficial extensions */
|
||||
caps |= SDL_JOYCAP_RUMBLE;
|
||||
|
||||
/* Note: SDL doesn't have a capability flag for motion sensors yet,
|
||||
* but they're supported through SDL_JoystickGetSensor* APIs */
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static int DSU_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int DSU_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static int DSU_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled)
|
||||
{
|
||||
DSU_ControllerSlot *slot = (DSU_ControllerSlot *)joystick->hwdata;
|
||||
|
||||
/* Sensors are always enabled if available */
|
||||
return (slot->has_gyro || slot->has_accel) ? 0 : SDL_Unsupported();
|
||||
}
|
||||
|
||||
static void DSU_JoystickUpdate(SDL_Joystick *joystick)
|
||||
{
|
||||
DSU_ControllerSlot *slot = (DSU_ControllerSlot *)joystick->hwdata;
|
||||
int i;
|
||||
|
||||
if (!slot || !slot->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_LockMutex(g_dsu_context->slots_mutex);
|
||||
|
||||
/* Update buttons */
|
||||
for (i = 0; i < 12; i++) {
|
||||
SDL_PrivateJoystickButton(joystick, i, (slot->buttons & (1 << i)) ? 1 : 0);
|
||||
}
|
||||
|
||||
/* Update axes */
|
||||
for (i = 0; i < 6; i++) {
|
||||
SDL_PrivateJoystickAxis(joystick, i, slot->axes[i]);
|
||||
}
|
||||
|
||||
/* Update hat */
|
||||
SDL_PrivateJoystickHat(joystick, 0, slot->hat);
|
||||
|
||||
/* Update touchpad if available */
|
||||
if (slot->has_touchpad && joystick->ntouchpads > 0) {
|
||||
/* DS4/DS5 touchpad resolution is typically 1920x943 */
|
||||
const float TOUCHPAD_WIDTH = 1920.0f;
|
||||
const float TOUCHPAD_HEIGHT = 943.0f;
|
||||
|
||||
/* First touch point */
|
||||
Uint8 touchpad_state = slot->touch1_active ? SDL_PRESSED : SDL_RELEASED;
|
||||
float touchpad_x = (float)slot->touch1_x / TOUCHPAD_WIDTH;
|
||||
float touchpad_y = (float)slot->touch1_y / TOUCHPAD_HEIGHT;
|
||||
|
||||
/* Clamp to valid range */
|
||||
if (touchpad_x < 0.0f) touchpad_x = 0.0f;
|
||||
if (touchpad_x > 1.0f) touchpad_x = 1.0f;
|
||||
if (touchpad_y < 0.0f) touchpad_y = 0.0f;
|
||||
if (touchpad_y > 1.0f) touchpad_y = 1.0f;
|
||||
|
||||
SDL_PrivateJoystickTouchpad(joystick, 0, 0, touchpad_state,
|
||||
touchpad_x, touchpad_y,
|
||||
touchpad_state ? 1.0f : 0.0f);
|
||||
|
||||
/* Second touch point */
|
||||
touchpad_state = slot->touch2_active ? SDL_PRESSED : SDL_RELEASED;
|
||||
touchpad_x = (float)slot->touch2_x / TOUCHPAD_WIDTH;
|
||||
touchpad_y = (float)slot->touch2_y / TOUCHPAD_HEIGHT;
|
||||
|
||||
/* Clamp to valid range */
|
||||
if (touchpad_x < 0.0f) touchpad_x = 0.0f;
|
||||
if (touchpad_x > 1.0f) touchpad_x = 1.0f;
|
||||
if (touchpad_y < 0.0f) touchpad_y = 0.0f;
|
||||
if (touchpad_y > 1.0f) touchpad_y = 1.0f;
|
||||
|
||||
SDL_PrivateJoystickTouchpad(joystick, 0, 1, touchpad_state,
|
||||
touchpad_x, touchpad_y,
|
||||
touchpad_state ? 1.0f : 0.0f);
|
||||
}
|
||||
|
||||
/* Update battery level */
|
||||
switch (slot->battery) {
|
||||
case DSU_BATTERY_DYING:
|
||||
case DSU_BATTERY_LOW:
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
|
||||
break;
|
||||
case DSU_BATTERY_MEDIUM:
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
|
||||
break;
|
||||
case DSU_BATTERY_HIGH:
|
||||
case DSU_BATTERY_FULL:
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
|
||||
break;
|
||||
case DSU_BATTERY_CHARGING:
|
||||
case DSU_BATTERY_CHARGED:
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED);
|
||||
break;
|
||||
default:
|
||||
SDL_PrivateJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_UNKNOWN);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update sensors if available */
|
||||
if (slot->has_gyro) {
|
||||
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_GYRO,
|
||||
slot->motion_timestamp, slot->gyro, 3);
|
||||
}
|
||||
if (slot->has_accel) {
|
||||
SDL_PrivateJoystickSensor(joystick, SDL_SENSOR_ACCEL,
|
||||
slot->motion_timestamp, slot->accel, 3);
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(g_dsu_context->slots_mutex);
|
||||
}
|
||||
|
||||
static void DSU_JoystickClose(SDL_Joystick *joystick)
|
||||
{
|
||||
/* Free touchpad info if allocated */
|
||||
if (joystick->touchpads) {
|
||||
SDL_free(joystick->touchpads);
|
||||
joystick->touchpads = NULL;
|
||||
joystick->ntouchpads = 0;
|
||||
}
|
||||
|
||||
joystick->hwdata = NULL;
|
||||
}
|
||||
|
||||
static void DSU_JoystickQuit(void)
|
||||
{
|
||||
if (!g_dsu_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop receiver thread */
|
||||
SDL_AtomicSet(&g_dsu_context->running, 0);
|
||||
if (g_dsu_context->receiver_thread) {
|
||||
SDL_WaitThread(g_dsu_context->receiver_thread, NULL);
|
||||
}
|
||||
|
||||
/* Close socket */
|
||||
DSU_CloseSocket(g_dsu_context->socket);
|
||||
DSU_QuitSockets();
|
||||
|
||||
/* Clean up mutex */
|
||||
if (g_dsu_context->slots_mutex) {
|
||||
SDL_DestroyMutex(g_dsu_context->slots_mutex);
|
||||
}
|
||||
|
||||
/* Free context */
|
||||
SDL_free(g_dsu_context);
|
||||
g_dsu_context = NULL;
|
||||
}
|
||||
|
||||
static SDL_bool DSU_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out)
|
||||
{
|
||||
/* DSU controllers map well to standard gamepad layout */
|
||||
return SDL_FALSE; /* Use default mapping */
|
||||
}
|
||||
|
||||
/* Export the driver */
|
||||
SDL_JoystickDriver SDL_DSU_JoystickDriver = {
|
||||
DSU_JoystickInit,
|
||||
DSU_JoystickGetCount,
|
||||
DSU_JoystickDetect,
|
||||
DSU_JoystickGetDeviceName,
|
||||
DSU_JoystickGetDevicePath,
|
||||
NULL, /* GetDeviceSteamVirtualGamepadSlot */
|
||||
DSU_JoystickGetDevicePlayerIndex,
|
||||
DSU_JoystickSetDevicePlayerIndex,
|
||||
DSU_JoystickGetDeviceGUID,
|
||||
DSU_JoystickGetDeviceInstanceID,
|
||||
DSU_JoystickOpen,
|
||||
DSU_JoystickRumble,
|
||||
DSU_JoystickRumbleTriggers,
|
||||
DSU_JoystickGetCapabilities,
|
||||
DSU_JoystickSetLED,
|
||||
DSU_JoystickSendEffect,
|
||||
DSU_JoystickSetSensorsEnabled,
|
||||
DSU_JoystickUpdate,
|
||||
DSU_JoystickClose,
|
||||
DSU_JoystickQuit,
|
||||
DSU_JoystickGetGamepadMapping
|
||||
};
|
||||
|
||||
#endif /* SDL_JOYSTICK_DSU */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
201
src/joystick/dsu/SDL_dsuprotocol.h
Normal file
201
src/joystick/dsu/SDL_dsuprotocol.h
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#ifndef SDL_dsuprotocol_h_
|
||||
#define SDL_dsuprotocol_h_
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
/* DSU (DualShock UDP) Protocol Constants - Based on CemuHook */
|
||||
|
||||
#define DSU_PROTOCOL_VERSION 1001
|
||||
#define DSU_SERVER_PORT_DEFAULT 26760
|
||||
#define DSU_CLIENT_PORT_DEFAULT 26761
|
||||
#define DSU_SERVER_ADDRESS_DEFAULT "127.0.0.1"
|
||||
|
||||
/* Magic strings */
|
||||
#define DSU_MAGIC_CLIENT "DSUC"
|
||||
#define DSU_MAGIC_SERVER "DSUS"
|
||||
|
||||
/* Message types */
|
||||
typedef enum {
|
||||
DSU_MSG_VERSION = 0x100000,
|
||||
DSU_MSG_PORTS_INFO = 0x100001,
|
||||
DSU_MSG_DATA = 0x100002,
|
||||
DSU_MSG_RUMBLE_INFO = 0x110001, /* Unofficial */
|
||||
DSU_MSG_RUMBLE = 0x110002 /* Unofficial */
|
||||
} DSU_MessageType;
|
||||
|
||||
/* Controller states */
|
||||
typedef enum {
|
||||
DSU_STATE_DISCONNECTED = 0,
|
||||
DSU_STATE_RESERVED = 1,
|
||||
DSU_STATE_CONNECTED = 2
|
||||
} DSU_SlotState;
|
||||
|
||||
/* Device models */
|
||||
typedef enum {
|
||||
DSU_MODEL_NONE = 0,
|
||||
DSU_MODEL_PARTIAL_GYRO = 1,
|
||||
DSU_MODEL_FULL_GYRO = 2, /* DS4, DS5 */
|
||||
DSU_MODEL_NO_GYRO = 3
|
||||
} DSU_DeviceModel;
|
||||
|
||||
/* Connection types */
|
||||
typedef enum {
|
||||
DSU_CONN_NONE = 0,
|
||||
DSU_CONN_USB = 1,
|
||||
DSU_CONN_BLUETOOTH = 2
|
||||
} DSU_ConnectionType;
|
||||
|
||||
/* Battery states */
|
||||
typedef enum {
|
||||
DSU_BATTERY_NONE = 0x00,
|
||||
DSU_BATTERY_DYING = 0x01, /* 0-10% */
|
||||
DSU_BATTERY_LOW = 0x02, /* 10-40% */
|
||||
DSU_BATTERY_MEDIUM = 0x03, /* 40-70% */
|
||||
DSU_BATTERY_HIGH = 0x04, /* 70-100% */
|
||||
DSU_BATTERY_FULL = 0x05, /* 100% */
|
||||
DSU_BATTERY_CHARGING = 0xEE,
|
||||
DSU_BATTERY_CHARGED = 0xEF
|
||||
} DSU_BatteryState;
|
||||
|
||||
/* Packet structures */
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct {
|
||||
char magic[4]; /* DSUC or DSUS */
|
||||
Uint16 version; /* Protocol version (1001) */
|
||||
Uint16 length; /* Packet length after header */
|
||||
Uint32 crc32; /* CRC32 of packet (with this field zeroed) */
|
||||
Uint32 client_id; /* Random client ID */
|
||||
Uint32 message_type; /* Message type enum */
|
||||
} DSU_Header;
|
||||
|
||||
typedef struct {
|
||||
DSU_Header header;
|
||||
Uint8 flags; /* Slot registration flags */
|
||||
Uint8 slot_id; /* 0-3 for specific slot, 0xFF for all */
|
||||
Uint8 mac[6]; /* MAC address filter (zeros for all) */
|
||||
} DSU_PortRequest;
|
||||
|
||||
typedef struct {
|
||||
Uint8 slot; /* Controller slot 0-3 */
|
||||
Uint8 slot_state; /* DSU_SlotState */
|
||||
Uint8 device_model; /* DSU_DeviceModel */
|
||||
Uint8 connection_type; /* DSU_ConnectionType */
|
||||
Uint8 mac[6]; /* Controller MAC address */
|
||||
Uint8 battery; /* DSU_BatteryState */
|
||||
Uint8 is_active; /* 0 or 1 */
|
||||
} DSU_ControllerInfo;
|
||||
|
||||
typedef struct {
|
||||
DSU_Header header;
|
||||
Uint8 slot; /* Controller slot 0-3 */
|
||||
Uint8 motor_left; /* Left/Low frequency motor intensity (0-255) */
|
||||
Uint8 motor_right; /* Right/High frequency motor intensity (0-255) */
|
||||
} DSU_RumblePacket;
|
||||
|
||||
typedef struct {
|
||||
DSU_Header header;
|
||||
DSU_ControllerInfo info;
|
||||
|
||||
/* Controller data */
|
||||
Uint32 packet_number; /* Incremental counter */
|
||||
|
||||
/* Digital buttons */
|
||||
Uint8 button_states_1; /* Share, L3, R3, Options, DPad */
|
||||
Uint8 button_states_2; /* L2, R2, L1, R1, Triangle, Circle, Cross, Square */
|
||||
Uint8 button_ps; /* PS/Home button */
|
||||
Uint8 button_touch; /* Touchpad button */
|
||||
|
||||
/* Analog sticks (0-255, 128=center) */
|
||||
Uint8 left_stick_x;
|
||||
Uint8 left_stick_y;
|
||||
Uint8 right_stick_x;
|
||||
Uint8 right_stick_y;
|
||||
|
||||
/* Analog buttons (0-255, pressure sensitive) */
|
||||
Uint8 analog_dpad_left;
|
||||
Uint8 analog_dpad_down;
|
||||
Uint8 analog_dpad_right;
|
||||
Uint8 analog_dpad_up;
|
||||
Uint8 analog_button_square;
|
||||
Uint8 analog_button_cross;
|
||||
Uint8 analog_button_circle;
|
||||
Uint8 analog_button_triangle;
|
||||
Uint8 analog_button_r1;
|
||||
Uint8 analog_button_l1;
|
||||
Uint8 analog_trigger_r2;
|
||||
Uint8 analog_trigger_l2;
|
||||
|
||||
/* Touch data (2 points max) */
|
||||
Uint8 touch1_active;
|
||||
Uint8 touch1_id;
|
||||
Uint16 touch1_x;
|
||||
Uint16 touch1_y;
|
||||
|
||||
Uint8 touch2_active;
|
||||
Uint8 touch2_id;
|
||||
Uint16 touch2_x;
|
||||
Uint16 touch2_y;
|
||||
|
||||
/* Motion data (optional) */
|
||||
Uint64 motion_timestamp; /* Microseconds */
|
||||
float accel_x; /* In g units */
|
||||
float accel_y;
|
||||
float accel_z;
|
||||
float gyro_pitch; /* In degrees/second */
|
||||
float gyro_yaw;
|
||||
float gyro_roll;
|
||||
} DSU_ControllerData;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
/* Button masks for button_states_1 */
|
||||
#define DSU_BUTTON_SHARE 0x01
|
||||
#define DSU_BUTTON_L3 0x02
|
||||
#define DSU_BUTTON_R3 0x04
|
||||
#define DSU_BUTTON_OPTIONS 0x08
|
||||
#define DSU_BUTTON_DPAD_UP 0x10
|
||||
#define DSU_BUTTON_DPAD_RIGHT 0x20
|
||||
#define DSU_BUTTON_DPAD_DOWN 0x40
|
||||
#define DSU_BUTTON_DPAD_LEFT 0x80
|
||||
|
||||
/* Button masks for button_states_2 */
|
||||
#define DSU_BUTTON_L2 0x01
|
||||
#define DSU_BUTTON_R2 0x02
|
||||
#define DSU_BUTTON_L1 0x04
|
||||
#define DSU_BUTTON_R1 0x08
|
||||
#define DSU_BUTTON_TRIANGLE 0x10
|
||||
#define DSU_BUTTON_CIRCLE 0x20
|
||||
#define DSU_BUTTON_CROSS 0x40
|
||||
#define DSU_BUTTON_SQUARE 0x80
|
||||
|
||||
/* Maximum number of DSU slots per server */
|
||||
#define DSU_MAX_SLOTS 4
|
||||
|
||||
/* We can support up to 8 controllers by using 2 server connections */
|
||||
#define DSU_MAX_CONTROLLERS 8
|
||||
|
||||
#endif /* SDL_dsuprotocol_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
Loading…
Add table
Add a link
Reference in a new issue