mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-06-05 22:30:29 +00:00
Merge 3eb59cb7a9 into fa2a726cc3
This commit is contained in:
commit
ded2fd8610
9 changed files with 607 additions and 5 deletions
2
.github/workflows/create-test-plan.py
vendored
2
.github/workflows/create-test-plan.py
vendored
|
|
@ -631,7 +631,7 @@ def spec_to_job(spec: JobSpec, key: str, trackmem_symbol_names: bool, ctest_args
|
|||
"-ffile-prefix-map=${PWD}=/SDL",
|
||||
))
|
||||
job.ldflags.extend((
|
||||
"--source-map-base", "/",
|
||||
"--source-map-base", "/", "-s", "ASYNCIFY",
|
||||
))
|
||||
pretest_cmd.extend((
|
||||
"# Start local HTTP server",
|
||||
|
|
|
|||
|
|
@ -1760,6 +1760,10 @@ elseif(EMSCRIPTEN)
|
|||
)
|
||||
endif()
|
||||
|
||||
if(SDL_HIDAPI)
|
||||
CheckHIDAPI()
|
||||
endif()
|
||||
|
||||
if(SDL_JOYSTICK)
|
||||
set(SDL_JOYSTICK_EMSCRIPTEN 1)
|
||||
sdl_glob_sources(
|
||||
|
|
|
|||
|
|
@ -595,6 +595,8 @@ typedef struct PLATFORM_hid_device_ PLATFORM_hid_device;
|
|||
#include "SDL_hidapi_android.h"
|
||||
#elif defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
|
||||
#include "SDL_hidapi_ios.h"
|
||||
#elif defined(SDL_PLATFORM_EMSCRIPTEN)
|
||||
#include "SDL_hidapi_emscripten.h"
|
||||
#endif
|
||||
|
||||
#undef api_version
|
||||
|
|
|
|||
25
src/hidapi/SDL_hidapi_emscripten.h
Normal file
25
src/hidapi/SDL_hidapi_emscripten.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2026 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.
|
||||
*/
|
||||
|
||||
#undef HIDAPI_H__
|
||||
#include "emscripten/hid.c"
|
||||
#define HAVE_PLATFORM_BACKEND 1
|
||||
#define udev_ctx 1
|
||||
479
src/hidapi/emscripten/hid.c
Normal file
479
src/hidapi/emscripten/hid.c
Normal file
|
|
@ -0,0 +1,479 @@
|
|||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
Alan Ott
|
||||
Signal 11 Software
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2022, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
|
||||
#include "../hidapi/hidapi.h"
|
||||
#include <stdio.h>
|
||||
#include <emscripten/html5.h>
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
EM_JS_DEPS(hidapi, "$stringToUTF32");
|
||||
|
||||
#if 0
|
||||
#define HIDAPI_WEBHID_DEBUG
|
||||
#endif
|
||||
|
||||
struct hid_device_ {
|
||||
int device_id;
|
||||
unsigned char *last_report;
|
||||
size_t last_report_length;
|
||||
struct hid_device_info* device_info;
|
||||
};
|
||||
|
||||
static struct hid_api_version api_version = {
|
||||
.major = HID_API_VERSION_MAJOR,
|
||||
.minor = HID_API_VERSION_MINOR,
|
||||
.patch = HID_API_VERSION_PATCH
|
||||
};
|
||||
|
||||
static int initialized = 0;
|
||||
static int webhid_supported = 0;
|
||||
|
||||
static hid_device *new_hid_device(void)
|
||||
{
|
||||
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
|
||||
if (dev == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->device_id = -1;
|
||||
dev->last_report = NULL;
|
||||
dev->last_report_length = 0;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
|
||||
{
|
||||
return &api_version;
|
||||
}
|
||||
|
||||
HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
|
||||
{
|
||||
return HID_API_VERSION_STR;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_init(void)
|
||||
{
|
||||
if (!initialized) {
|
||||
webhid_supported = MAIN_THREAD_EM_ASM_INT({
|
||||
return "hid" in navigator;
|
||||
});
|
||||
}
|
||||
return webhid_supported ? 0 : -1;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_exit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hid_device_info * create_device_info_for_device(int device_id)
|
||||
{
|
||||
struct hid_device_info *root = NULL;
|
||||
struct hid_device_info *cur_dev = NULL;
|
||||
char path[16];
|
||||
wchar_t product_string[128];
|
||||
int ignore = 0;
|
||||
int input_report_count;
|
||||
|
||||
ignore = MAIN_THREAD_EM_ASM_INT({
|
||||
let device = window._hidDeviceList[$0];
|
||||
if (!device) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}, device_id);
|
||||
|
||||
if (ignore)
|
||||
goto end;
|
||||
|
||||
/* Create the record. */
|
||||
root = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
|
||||
if (!root)
|
||||
goto end;
|
||||
|
||||
cur_dev = root;
|
||||
|
||||
snprintf(path, sizeof(path), "hid%d", device_id);
|
||||
cur_dev->path = strdup(path);
|
||||
|
||||
cur_dev->vendor_id = MAIN_THREAD_EM_ASM_INT({
|
||||
return window._hidDeviceList[$0].vendorId;
|
||||
}, device_id);
|
||||
cur_dev->product_id = MAIN_THREAD_EM_ASM_INT({
|
||||
return window._hidDeviceList[$0].productId;
|
||||
}, device_id);
|
||||
|
||||
cur_dev->serial_number = NULL;
|
||||
cur_dev->release_number = 0;
|
||||
cur_dev->manufacturer_string = NULL;
|
||||
|
||||
MAIN_THREAD_EM_ASM({
|
||||
stringToUTF32(window._hidDeviceList[$0].productName, $1, Number($2));
|
||||
}, device_id, product_string, sizeof(product_string));
|
||||
|
||||
cur_dev->product_string = wcsdup(product_string);
|
||||
|
||||
cur_dev->usage_page = MAIN_THREAD_EM_ASM_INT({
|
||||
return window._hidDeviceList[$0].collections[0].usagePage;
|
||||
}, device_id);
|
||||
cur_dev->usage = MAIN_THREAD_EM_ASM_INT({
|
||||
return window._hidDeviceList[$0].collections[0].usage;
|
||||
}, device_id);
|
||||
cur_dev->interface_number = 0;
|
||||
|
||||
input_report_count = MAIN_THREAD_EM_ASM_INT({
|
||||
return window._hidDeviceList[$0].collections[0].inputReports.length;
|
||||
}, device_id);
|
||||
|
||||
/* WebHID doesn't provide the bus type, so we have to guess it ourselves */
|
||||
if (input_report_count >= 5) {
|
||||
cur_dev->bus_type = HID_API_BUS_BLUETOOTH;
|
||||
} else {
|
||||
cur_dev->bus_type = HID_API_BUS_USB;
|
||||
}
|
||||
|
||||
cur_dev->next = NULL;
|
||||
|
||||
#ifdef HIDAPI_WEBHID_DEBUG
|
||||
printf("HIDAPI: New device found: %d %d\n", cur_dev->vendor_id, cur_dev->product_id);
|
||||
#endif
|
||||
end:
|
||||
return root;
|
||||
}
|
||||
|
||||
EM_ASYNC_JS(int, hid_js_get_device_count, (), {
|
||||
window._hidDeviceList = await navigator.hid.getDevices();
|
||||
return window._hidDeviceList.length;
|
||||
});
|
||||
|
||||
struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
||||
{
|
||||
int device_count;
|
||||
struct hid_device_info *root = NULL; /* return object */
|
||||
struct hid_device_info *cur_dev = NULL;
|
||||
|
||||
if (hid_init() < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_count = hid_js_get_device_count();
|
||||
|
||||
#ifdef HIDAPI_WEBHID_DEBUG
|
||||
printf("hid_enumerate, device_count=%d\n", device_count);
|
||||
#endif
|
||||
|
||||
int device_id;
|
||||
for (device_id = 0; device_id < device_count; device_id++) {
|
||||
struct hid_device_info * tmp;
|
||||
/* TODO: handle vendor_id and product_id */
|
||||
tmp = create_device_info_for_device(device_id);
|
||||
|
||||
if (tmp) {
|
||||
if (cur_dev) {
|
||||
cur_dev->next = tmp;
|
||||
}
|
||||
else {
|
||||
root = tmp;
|
||||
}
|
||||
cur_dev = tmp;
|
||||
|
||||
/* move the pointer to the tail of returned list */
|
||||
while (cur_dev->next != NULL) {
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
|
||||
{
|
||||
struct hid_device_info *d = devs;
|
||||
while (d) {
|
||||
struct hid_device_info *next = d->next;
|
||||
free(d->path);
|
||||
free(d->product_string);
|
||||
free(d);
|
||||
d = next;
|
||||
}
|
||||
}
|
||||
|
||||
hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
|
||||
{
|
||||
struct hid_device_info *devs, *cur_dev;
|
||||
const char *path_to_open = NULL;
|
||||
hid_device *handle = NULL;
|
||||
|
||||
/* register_global_error: global error is reset by hid_enumerate/hid_init */
|
||||
devs = hid_enumerate(vendor_id, product_id);
|
||||
if (devs == NULL) {
|
||||
/* register_global_error: global error is already set by hid_enumerate */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cur_dev = devs;
|
||||
while (cur_dev) {
|
||||
if (cur_dev->vendor_id == vendor_id &&
|
||||
cur_dev->product_id == product_id) {
|
||||
if (serial_number) {
|
||||
if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
|
||||
path_to_open = cur_dev->path;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
path_to_open = cur_dev->path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
|
||||
if (path_to_open) {
|
||||
/* Open the device */
|
||||
handle = hid_open_path(path_to_open);
|
||||
}
|
||||
|
||||
hid_free_enumeration(devs);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
EM_ASYNC_JS(void, hid_js_open, (int device_id, hid_device *dev, unsigned char **last_report_out, size_t *last_report_length_out), {
|
||||
let device = window._hidDeviceList[device_id];
|
||||
if (device) {
|
||||
try {
|
||||
await device.open();
|
||||
device.addEventListener("inputreport", function (event) {
|
||||
const { data, device, reportId } = event;
|
||||
|
||||
let dataLength = data['byteLength']+1;
|
||||
let pointer = _malloc(dataLength);
|
||||
HEAPU8[pointer] = reportId;
|
||||
for (let i = 0; i < data['byteLength']; i++) {
|
||||
HEAPU8[pointer + i + 1] = data['getUint8'](i);
|
||||
}
|
||||
|
||||
if (HEAP32[last_report_out >> 2] != 0) {
|
||||
_free(HEAP32[last_report_out >> 2]);
|
||||
}
|
||||
HEAP32[last_report_out >> 2] = pointer;
|
||||
HEAP32[last_report_length_out >> 2] = dataLength;
|
||||
});
|
||||
} catch (e) {
|
||||
// Pass?
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
||||
{
|
||||
hid_device *dev = NULL;
|
||||
int device_id = 0;
|
||||
|
||||
if (hid_init() < 0) {
|
||||
return NULL;
|
||||
}
|
||||
/* register_global_error: global error is reset by hid_init */
|
||||
|
||||
#ifdef HIDAPI_WEBHID_DEBUG
|
||||
printf("hid_open_path: %s\n", path);
|
||||
#endif
|
||||
dev = new_hid_device();
|
||||
if (!dev) {
|
||||
#ifdef HIDAPI_WEBHID_DEBUG
|
||||
printf("hid_open_path: no memory\n");
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sscanf(path, "hid%d", &device_id) != 1) {
|
||||
free(dev);
|
||||
#ifdef HIDAPI_WEBHID_DEBUG
|
||||
printf("hid_open_path: invalid path\n");
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->device_id = device_id;
|
||||
hid_js_open(device_id, dev, &dev->last_report, &dev->last_report_length);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
EM_ASYNC_JS(int, hid_js_write, (int device_id, int report_id, const unsigned char *data, size_t length), {
|
||||
let device = window._hidDeviceList[device_id];
|
||||
if (device) {
|
||||
let dataArray = new Uint8Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
dataArray[i] = HEAPU8[data+i];
|
||||
}
|
||||
await device.sendReport(report_id, dataArray);
|
||||
}
|
||||
});
|
||||
|
||||
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
|
||||
{
|
||||
if (!webhid_supported || length < 1)
|
||||
return 0;
|
||||
hid_js_write(dev->device_id, data[0], data+1, length-1);
|
||||
return length;
|
||||
}
|
||||
|
||||
EM_ASYNC_JS(int, hid_js_read_timeout, (int device_id, unsigned char *data, size_t length), {
|
||||
let device = window._hidDeviceList[device_id];
|
||||
if (device) {
|
||||
let [report_id, dataView] = await new Promise(function(resolve, reject) {
|
||||
device.addEventListener("inputreport", function (event) {
|
||||
const { data, device, report_id } = event;
|
||||
resolve([report_id, data]); // done
|
||||
}, {once: true});
|
||||
});
|
||||
HEAPU8[data] = report_id;
|
||||
for (let i = 0; i < dataView['byteLength'] && i < (length-1); i++) {
|
||||
HEAPU8[data + i + 1] = dataView['getUint8'](i);
|
||||
}
|
||||
return dataView['byteLength']+1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
/* TODO: timeout */
|
||||
if (!webhid_supported || length < 1)
|
||||
return -1;
|
||||
if (milliseconds == 0) {
|
||||
if (dev->last_report) {
|
||||
size_t return_size = length;
|
||||
if (dev->last_report_length < length) {
|
||||
return_size = dev->last_report_length;
|
||||
}
|
||||
memcpy(data, dev->last_report, return_size);
|
||||
free(dev->last_report);
|
||||
dev->last_report = NULL;
|
||||
return return_size;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return hid_js_read_timeout(dev->device_id, data, length);
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
{
|
||||
/* TODO: blocking */
|
||||
return hid_read_timeout(dev, data, length, -1);
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
EM_ASYNC_JS(void, hid_js_get_feature_report, (int device_id, int report_id, unsigned char *data, size_t length), {
|
||||
let device = window._hidDeviceList[device_id];
|
||||
if (device) {
|
||||
let dataView = await device['receiveFeatureReport'](report_id);
|
||||
for (let i = 0; i < dataView['byteLength']; i++) {
|
||||
HEAPU8[data + i] = dataView['getUint8'](i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
|
||||
{
|
||||
if (!webhid_supported || length < 1)
|
||||
return -1;
|
||||
int report_id = (int)data[0];
|
||||
hid_js_get_feature_report(dev->device_id, report_id, data, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
EM_ASYNC_JS(int, hid_js_close, (int device_id), {
|
||||
let device = window._hidDeviceList[device_id];
|
||||
if (device) {
|
||||
await device.close();
|
||||
}
|
||||
});
|
||||
|
||||
void HID_API_EXPORT hid_close(hid_device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
if (dev->last_report) {
|
||||
free(dev->last_report);
|
||||
}
|
||||
|
||||
hid_js_close(dev->device_id);
|
||||
hid_free_enumeration(dev->device_info);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@
|
|||
#include "SDL_sysjoystick_c.h"
|
||||
#include "../SDL_joystick_c.h"
|
||||
#include "../usb_ids.h"
|
||||
#include "../hidapi/SDL_hidapijoystick_c.h"
|
||||
|
||||
static SDL_joylist_item *JoystickByIndex(int index);
|
||||
|
||||
|
|
@ -120,6 +121,61 @@ static int SDL_GetEmscriptenOSID()
|
|||
});
|
||||
}
|
||||
|
||||
static EM_BOOL Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
|
||||
static void SDL_WebHID_DisconnectEmscriptenGamepad(int device_index)
|
||||
{
|
||||
int rc;
|
||||
EmscriptenGamepadEvent gamepadState;
|
||||
rc = emscripten_get_gamepad_status(device_index, &gamepadState);
|
||||
if (rc == EMSCRIPTEN_RESULT_SUCCESS) {
|
||||
Emscripten_JoyStickDisconnected(EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED,
|
||||
&gamepadState,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_RequestWebHIDDevice(Uint16 vendor, Uint16 product, int device_index)
|
||||
{
|
||||
Uint16 product2 = 0;
|
||||
if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
|
||||
product = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT;
|
||||
product2 = USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT;
|
||||
}
|
||||
MAIN_THREAD_EM_ASM({
|
||||
function timeout(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
if ("hid" in navigator) {
|
||||
async function handler() {
|
||||
while (true) {
|
||||
try {
|
||||
let devices = await navigator["hid"]["requestDevice"]({ "filters": [ { "vendorId": $0, "productId": $1, } ]});
|
||||
let device_length = devices["length"];
|
||||
if ($4) { // product2
|
||||
devices = await navigator["hid"]["requestDevice"]({ "filters": [ { "vendorId": $0, "productId": $4, } ]});
|
||||
device_length += devices["length"];
|
||||
}
|
||||
if (devices["length"]) {
|
||||
dynCall("vi", $2, [$3]);
|
||||
}
|
||||
return;
|
||||
} catch(e) {
|
||||
// Exception, most likely because the user hasn't interacted with the page yet.
|
||||
// Let's wait until they do, hopefully.
|
||||
await timeout(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
handler();
|
||||
}
|
||||
}, vendor, product, SDL_WebHID_DisconnectEmscriptenGamepad, device_index, product2);
|
||||
}
|
||||
#endif
|
||||
|
||||
static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
|
||||
{
|
||||
SDL_joylist_item *item;
|
||||
|
|
@ -148,6 +204,12 @@ static EM_BOOL Emscripten_JoyStickConnected(int eventType, const EmscriptenGamep
|
|||
product = SDL_GetEmscriptenJoystickProduct(gamepadEvent->index);
|
||||
is_xinput = SDL_IsEmscriptenJoystickXInput(gamepadEvent->index);
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
if (HIDAPI_IsDeviceSupported(vendor, product, 0, "")) {
|
||||
SDL_RequestWebHIDDevice(vendor, product, gamepadEvent->index);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use a generic VID/PID representing an XInput controller
|
||||
if (!vendor && !product && is_xinput) {
|
||||
vendor = USB_VENDOR_MICROSOFT;
|
||||
|
|
@ -287,7 +349,7 @@ done:
|
|||
return 1;
|
||||
}
|
||||
|
||||
static EM_BOOL Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
|
||||
EM_BOOL Emscripten_JoyStickDisconnected(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
|
||||
{
|
||||
SDL_joylist_item *item = SDL_joylist;
|
||||
SDL_joylist_item *prev = NULL;
|
||||
|
|
|
|||
|
|
@ -49,12 +49,15 @@ typedef struct SDL_HIDAPI_RumbleContext
|
|||
SDL_HIDAPI_RumbleRequest *requests_tail;
|
||||
} SDL_HIDAPI_RumbleContext;
|
||||
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
#ifndef SDL_THREAD_SAFETY_ANALYSIS
|
||||
static
|
||||
#endif
|
||||
SDL_Mutex *SDL_HIDAPI_rumble_lock;
|
||||
#endif
|
||||
static SDL_HIDAPI_RumbleContext rumble_context SDL_GUARDED_BY(SDL_HIDAPI_rumble_lock);
|
||||
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
static int SDLCALL SDL_HIDAPI_RumbleThread(void *data)
|
||||
{
|
||||
SDL_HIDAPI_RumbleContext *ctx = (SDL_HIDAPI_RumbleContext *)data;
|
||||
|
|
@ -161,9 +164,11 @@ static bool SDL_HIDAPI_StartRumbleThread(SDL_HIDAPI_RumbleContext *ctx)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SDL_HIDAPI_LockRumble(void)
|
||||
{
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
|
||||
|
||||
if (SDL_CompareAndSwapAtomicInt(&ctx->initialized, false, true)) {
|
||||
|
|
@ -173,11 +178,13 @@ bool SDL_HIDAPI_LockRumble(void)
|
|||
}
|
||||
|
||||
SDL_LockMutex(SDL_HIDAPI_rumble_lock);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data, int **size, int *maximum_size)
|
||||
{
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
|
||||
SDL_HIDAPI_RumbleRequest *request, *found;
|
||||
|
||||
|
|
@ -193,6 +200,7 @@ bool SDL_HIDAPI_GetPendingRumbleLocked(SDL_HIDAPI_Device *device, Uint8 **data,
|
|||
*maximum_size = sizeof(found->data);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -212,6 +220,7 @@ int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const
|
|||
return -1;
|
||||
}
|
||||
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
request = (SDL_HIDAPI_RumbleRequest *)SDL_calloc(1, sizeof(*request));
|
||||
if (!request) {
|
||||
SDL_HIDAPI_UnlockRumble();
|
||||
|
|
@ -234,6 +243,9 @@ int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const
|
|||
|
||||
// Make sure we unlock before posting the semaphore so the rumble thread can run immediately
|
||||
SDL_HIDAPI_UnlockRumble();
|
||||
#else
|
||||
SDL_hid_write(device->dev, data, size);
|
||||
#endif
|
||||
|
||||
SDL_SignalSemaphore(ctx->request_sem);
|
||||
|
||||
|
|
@ -242,7 +254,9 @@ int SDL_HIDAPI_SendRumbleWithCallbackAndUnlock(SDL_HIDAPI_Device *device, const
|
|||
|
||||
void SDL_HIDAPI_UnlockRumble(void)
|
||||
{
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
SDL_UnlockMutex(SDL_HIDAPI_rumble_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size)
|
||||
|
|
@ -273,11 +287,13 @@ int SDL_HIDAPI_SendRumble(SDL_HIDAPI_Device *device, const Uint8 *data, int size
|
|||
|
||||
void SDL_HIDAPI_QuitRumble(void)
|
||||
{
|
||||
#ifndef SDL_THREADS_DISABLED
|
||||
SDL_HIDAPI_RumbleContext *ctx = &rumble_context;
|
||||
|
||||
if (SDL_GetAtomicInt(&ctx->running)) {
|
||||
SDL_HIDAPI_StopRumbleThread(ctx);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // SDL_JOYSTICK_HIDAPI
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ static SDL_GamepadType SDL_GetJoystickGameControllerProtocol(const char *name, U
|
|||
return type;
|
||||
}
|
||||
|
||||
static bool HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
|
||||
bool HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
|
||||
{
|
||||
int i;
|
||||
SDL_GamepadType type = SDL_GetJoystickGameControllerProtocol(name, vendor_id, product_id, -1, 0, 0, 0);
|
||||
|
|
@ -676,6 +676,10 @@ static bool HIDAPI_SerialIsEmpty(SDL_HIDAPI_Device *device)
|
|||
|
||||
void HIDAPI_SetDeviceSerial(SDL_HIDAPI_Device *device, const char *serial)
|
||||
{
|
||||
#ifdef SDL_PLATFORM_EMSCRIPTEN
|
||||
// Don't include the serial number for the web to decrease the fingerprinting surface
|
||||
serial = NULL;
|
||||
#endif
|
||||
if (serial && *serial && (!device->serial || SDL_strcmp(serial, device->serial) != 0)) {
|
||||
SDL_free(device->serial);
|
||||
device->serial = SDL_strdup(serial);
|
||||
|
|
@ -700,6 +704,10 @@ static int wcstrcmp(const wchar_t *str1, const char *str2)
|
|||
|
||||
static void HIDAPI_SetDeviceSerialW(SDL_HIDAPI_Device *device, const wchar_t *serial)
|
||||
{
|
||||
#ifdef SDL_PLATFORM_EMSCRIPTEN
|
||||
// Don't include the serial number for the web to decrease the fingerprinting surface
|
||||
serial = NULL;
|
||||
#endif
|
||||
if (serial && *serial && (!device->serial || wcstrcmp(serial, device->serial) != 0)) {
|
||||
SDL_free(device->serial);
|
||||
device->serial = HIDAPI_ConvertString(serial);
|
||||
|
|
|
|||
|
|
@ -63,8 +63,12 @@
|
|||
#if defined(SDL_PLATFORM_ANDROID) || \
|
||||
defined(SDL_PLATFORM_IOS) || \
|
||||
defined(SDL_PLATFORM_TVOS) || \
|
||||
defined(SDL_PLATFORM_VISIONOS)
|
||||
// On Android, HIDAPI prompts for permissions and acquires exclusive access to the device, and on Apple mobile platforms it doesn't do anything except for handling Bluetooth Steam Controllers, so we'll leave it off by default.
|
||||
defined(SDL_PLATFORM_VISIONOS) || \
|
||||
defined(SDL_PLATFORM_EMSCRIPTEN)
|
||||
// On Android, HIDAPI prompts for permissions and acquires exclusive access to the device,
|
||||
// on Emscripten, it also prompts for permissions and it's not available in some browsers,
|
||||
// and on Apple mobile platforms it doesn't do anything except for handling Bluetooth Steam Controllers,
|
||||
// so we'll leave it off by default.
|
||||
#define SDL_HIDAPI_DEFAULT false
|
||||
#else
|
||||
#define SDL_HIDAPI_DEFAULT true
|
||||
|
|
@ -180,6 +184,8 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverZUIKI;
|
|||
(((Uint32)(C)) << 16) | \
|
||||
(((Uint32)(D)) << 24))
|
||||
|
||||
extern bool HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
|
||||
|
||||
// Return true if a HID device is present and supported as a joystick of the given type
|
||||
extern bool HIDAPI_IsDeviceTypePresent(SDL_GamepadType type);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue