diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index 8074c4b933..8ed5f19d17 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -1272,7 +1272,7 @@ macro(CheckHIDAPI) #include #include int main(int argc, char **argv) { - libusb_close(NULL); + libusb_interrupt_event_handler(NULL); return 0; }" HAVE_LIBUSB_H) cmake_pop_check_state() diff --git a/src/hidapi/SDL_hidapi.c b/src/hidapi/SDL_hidapi.c index 3f42b3da2c..b7ea2cf159 100644 --- a/src/hidapi/SDL_hidapi.c +++ b/src/hidapi/SDL_hidapi.c @@ -734,6 +734,10 @@ static SDL_LibUSBContext *libusb_ctx; #define libusb_bulk_transfer libusb_ctx->bulk_transfer #define libusb_handle_events libusb_ctx->handle_events #define libusb_handle_events_completed libusb_ctx->handle_events_completed +#define libusb_interrupt_event_handler libusb_ctx->interrupt_event_handler +#define libusb_has_capability libusb_ctx->has_capability +#define libusb_hotplug_register_callback libusb_ctx->hotplug_register_callback +#define libusb_hotplug_deregister_callback libusb_ctx->hotplug_deregister_callback #define libusb_error_name libusb_ctx->error_name struct LIBUSB_hid_device_; diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index 6c5126c7f3..8f7cf38c7d 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -600,6 +600,66 @@ HID_API_EXPORT const char* HID_API_CALL hid_version_str(void) return HID_API_VERSION_STR; } +#ifdef HIDAPI_USING_SDL_RUNTIME +static libusb_hotplug_callback_handle hotplug_callback_handle; +static int shutdown_event_thread; +static hidapi_thread_state event_thread_state; + +static int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *user_data) +{ + switch (event) { + case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: + ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; + break; + case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: + ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter; + break; + default: + break; + } + return 0; +} + + +static void *event_thread(void *param) +{ + while (!shutdown_event_thread) { + int res = libusb_handle_events(usb_context); + if (res < 0) { + /* There was an error. */ + LOG("event_thread(): (%d) %s\n", res, libusb_error_name(res)); + } + } + return NULL; +} + +static void start_event_thread() +{ + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + int res = libusb_hotplug_register_callback(usb_context, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, &hotplug_callback_handle); + if (res < 0) { + LOG("Couldn't register hotplug: (%d) %s\n", res, libusb_error_name(res)); + } + } + + hidapi_thread_create(&event_thread_state, event_thread, NULL); +} + +static void stop_event_thread() +{ + shutdown_event_thread = 1; + libusb_interrupt_event_handler(usb_context); + hidapi_thread_join(&event_thread_state); + shutdown_event_thread = 0; + + if (hotplug_callback_handle) { + libusb_hotplug_deregister_callback(usb_context, hotplug_callback_handle); + hotplug_callback_handle = 0; + } +} +#endif /* HIDAPI_USING_SDL_RUNTIME */ + + int HID_API_EXPORT hid_init(void) { if (!usb_context) { @@ -613,6 +673,10 @@ int HID_API_EXPORT hid_init(void) locale = setlocale(LC_CTYPE, NULL); if (!locale) (void) setlocale(LC_CTYPE, ""); + +#ifdef HIDAPI_USING_SDL_RUNTIME + start_event_thread(); +#endif } return 0; @@ -622,6 +686,10 @@ int HID_API_EXPORT hid_exit(void) { usb_string_cache_destroy(); +#ifdef HIDAPI_USING_SDL_RUNTIME + stop_event_thread(); +#endif + if (usb_context) { libusb_exit(usb_context); usb_context = NULL; @@ -1186,10 +1254,9 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer) } -static void *read_thread(void *param) +static void start_read_operations(hid_device *dev) { int res; - hid_device *dev = param; uint8_t *buf; const size_t length = dev->input_ep_max_packet_size; @@ -1209,10 +1276,37 @@ static void *read_thread(void *param) from inside read_callback() */ res = libusb_submit_transfer(dev->transfer); if(res < 0) { - LOG("libusb_submit_transfer failed: %d %s. Stopping read_thread from running\n", res, libusb_error_name(res)); - dev->shutdown_thread = 1; - dev->transfer_loop_finished = 1; + LOG("libusb_submit_transfer failed: %d %s. Stopping read_thread from running\n", res, libusb_error_name(res)); + dev->shutdown_thread = 1; + dev->transfer_loop_finished = 1; } +} + + +static void stop_read_operations(hid_device *dev) +{ + while (!dev->transfer_loop_finished) + libusb_handle_events_completed(usb_context, &dev->transfer_loop_finished); + + /* Now that the read operations are stopping, Wake any threads which are + waiting on data (in hid_read_timeout()). Do this under a mutex to + make sure that a thread which is about to go to sleep waiting on + the condition actually will go to sleep before the condition is + signaled. */ + hidapi_thread_mutex_lock(&dev->thread_state); + hidapi_thread_cond_broadcast(&dev->thread_state); + hidapi_thread_mutex_unlock(&dev->thread_state); +} + + +#ifdef HIDAPI_USING_SDL_RUNTIME +/* We have a separate thread handling all the events */ +#else +static void *read_thread(void *param) +{ + hid_device *dev = param; + + start_read_operations(dev); /* Notify the main thread that the read thread is up and running. */ hidapi_thread_barrier_wait(&dev->thread_state); @@ -1239,17 +1333,7 @@ static void *read_thread(void *param) if no transfers are pending, but that's OK. */ libusb_cancel_transfer(dev->transfer); - while (!dev->transfer_loop_finished) - libusb_handle_events_completed(usb_context, &dev->transfer_loop_finished); - - /* Now that the read thread is stopping, Wake any threads which are - waiting on data (in hid_read_timeout()). Do this under a mutex to - make sure that a thread which is about to go to sleep waiting on - the condition actually will go to sleep before the condition is - signaled. */ - hidapi_thread_mutex_lock(&dev->thread_state); - hidapi_thread_cond_broadcast(&dev->thread_state); - hidapi_thread_mutex_unlock(&dev->thread_state); + stop_read_operations(dev); /* The dev->transfer->buffer and dev->transfer objects are cleaned up in hid_close(). They are not cleaned up here because this thread @@ -1261,6 +1345,7 @@ static void *read_thread(void *param) return NULL; } +#endif /* HIDAPI_USING_SDL_RUNTIME */ static void init_xbox360(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, const struct libusb_config_descriptor *conf_desc) { @@ -1441,10 +1526,14 @@ static int hidapi_initialize_device(hid_device *dev, const struct libusb_interfa calculate_device_quirks(dev, desc.idVendor, desc.idProduct); +#ifdef HIDAPI_USING_SDL_RUNTIME + start_read_operations(dev); +#else hidapi_thread_create(&dev->thread_state, read_thread, dev); /* Wait here for the read thread to be initialized. */ hidapi_thread_barrier_wait(&dev->thread_state); +#endif return 1; } @@ -1868,8 +1957,12 @@ void HID_API_EXPORT hid_close(hid_device *dev) dev->shutdown_thread = 1; libusb_cancel_transfer(dev->transfer); +#ifdef HIDAPI_USING_SDL_RUNTIME + stop_read_operations(dev); +#else /* Wait for read_thread() to end. */ hidapi_thread_join(&dev->thread_state); +#endif /* Clean up the Transfer objects allocated in read_thread(). */ free(dev->transfer->buffer); diff --git a/src/hidapi/libusb/hidapi_thread_sdl.h b/src/hidapi/libusb/hidapi_thread_sdl.h index 8503999719..7aa9f24c05 100644 --- a/src/hidapi/libusb/hidapi_thread_sdl.h +++ b/src/hidapi/libusb/hidapi_thread_sdl.h @@ -53,10 +53,17 @@ static int SDL_CreateThreadBarrier(SDL_ThreadBarrier *barrier, Uint32 count) static void SDL_DestroyThreadBarrier(SDL_ThreadBarrier *barrier) { - SDL_DestroyCondition(barrier->cond); - SDL_DestroyMutex(barrier->mutex); + if (barrier->cond) { + SDL_DestroyCondition(barrier->cond); + barrier->cond = NULL; + } + if (barrier->mutex) { + SDL_DestroyMutex(barrier->mutex); + barrier->mutex = NULL; + } } +#if 0 // Not used static int SDL_WaitThreadBarrier(SDL_ThreadBarrier *barrier) { SDL_LockMutex(barrier->mutex); @@ -71,6 +78,7 @@ static int SDL_WaitThreadBarrier(SDL_ThreadBarrier *barrier) SDL_UnlockMutex(barrier->mutex); return 0; } +#endif #include "../../thread/SDL_systhread.h" @@ -97,8 +105,14 @@ static void hidapi_thread_state_init(hidapi_thread_state *state) static void hidapi_thread_state_destroy(hidapi_thread_state *state) { SDL_DestroyThreadBarrier(&state->barrier); - SDL_DestroyCondition(state->condition); - SDL_DestroyMutex(state->mutex); + if (state->condition) { + SDL_DestroyCondition(state->condition); + state->condition = NULL; + } + if (state->mutex) { + SDL_DestroyMutex(state->mutex); + state->mutex = NULL; + } } static void hidapi_thread_cleanup_push(void (*routine)(void *), void *arg) @@ -153,10 +167,12 @@ static void hidapi_thread_cond_broadcast(hidapi_thread_state *state) SDL_BroadcastCondition(state->condition); } +#if 0 // Not used static void hidapi_thread_barrier_wait(hidapi_thread_state *state) { SDL_WaitThreadBarrier(&state->barrier); } +#endif typedef struct { diff --git a/src/misc/SDL_libusb.c b/src/misc/SDL_libusb.c index 82fd95baf5..2a5aff44ae 100644 --- a/src/misc/SDL_libusb.c +++ b/src/misc/SDL_libusb.c @@ -90,6 +90,10 @@ bool SDL_InitLibUSB(SDL_LibUSBContext **ctx) LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, unsigned char, unsigned char *, int, int *, unsigned int), bulk_transfer) LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *), handle_events) LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *, int *), handle_events_completed) + LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(libusb_context *ctx), interrupt_event_handler) + LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(uint32_t), has_capability) + LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *, int, int, int, int, int, libusb_hotplug_callback_fn, void *, libusb_hotplug_callback_handle *), hotplug_register_callback) + LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(libusb_context *, libusb_hotplug_callback_handle), hotplug_deregister_callback) LOAD_LIBUSB_SYMBOL(const char * (LIBUSB_CALL *)(int), error_name) #undef LOAD_LIBUSB_SYMBOL } diff --git a/src/misc/SDL_libusb.h b/src/misc/SDL_libusb.h index 9cce444aed..cddbf6fd5f 100644 --- a/src/misc/SDL_libusb.h +++ b/src/misc/SDL_libusb.h @@ -91,6 +91,10 @@ typedef struct SDL_LibUSBContext ); int (LIBUSB_CALL *handle_events)(libusb_context *ctx); int (LIBUSB_CALL *handle_events_completed)(libusb_context *ctx, int *completed); + void (LIBUSB_CALL *interrupt_event_handler)(libusb_context *ctx); + int (LIBUSB_CALL *has_capability)(uint32_t capability); + int (LIBUSB_CALL *hotplug_register_callback)(libusb_context *ctx, int events, int flags, int vendor_id, int product_id, int dev_class, libusb_hotplug_callback_fn cb_fn, void *user_data, libusb_hotplug_callback_handle *callback_handle); + void (LIBUSB_CALL *hotplug_deregister_callback)(libusb_context *ctx, libusb_hotplug_callback_handle callback_handle); const char * (LIBUSB_CALL *error_name)(int errcode); /* *INDENT-ON* */ // clang-format on