Updated with upstream suggestions in https://github.com/libusb/hidapi/pull/582

This commit is contained in:
Sam Lantinga
2023-06-01 08:27:56 -07:00
parent 20182eed7c
commit b36679b90e
6 changed files with 2400 additions and 2353 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -53,185 +53,7 @@ static int SDL_libusb_get_string_descriptor(libusb_device_handle *dev,
#define libusb_get_string_descriptor SDL_libusb_get_string_descriptor
#endif /* __FreeBSD__ */
#define HIDAPI_THREAD_STATE_DEFINED
/* Barrier implementation because Android/Bionic don't have pthread_barrier.
This implementation came from Brent Priddy and was posted on
StackOverflow. It is used with his permission. */
typedef struct _SDL_ThreadBarrier
{
SDL_Mutex *mutex;
SDL_Condition *cond;
Uint32 count;
Uint32 trip_count;
} SDL_ThreadBarrier;
static int SDL_CreateThreadBarrier(SDL_ThreadBarrier *barrier, Uint32 count)
{
SDL_assert(barrier != NULL);
SDL_assert(count != 0);
barrier->mutex = SDL_CreateMutex();
if (barrier->mutex == NULL) {
return -1; /* Error set by CreateMutex */
}
barrier->cond = SDL_CreateCondition();
if (barrier->cond == NULL) {
return -1; /* Error set by CreateCond */
}
barrier->trip_count = count;
barrier->count = 0;
return 0;
}
static void SDL_DestroyThreadBarrier(SDL_ThreadBarrier *barrier)
{
SDL_DestroyCondition(barrier->cond);
SDL_DestroyMutex(barrier->mutex);
}
static int SDL_WaitThreadBarrier(SDL_ThreadBarrier *barrier)
{
SDL_LockMutex(barrier->mutex);
barrier->count += 1;
if (barrier->count >= barrier->trip_count) {
barrier->count = 0;
SDL_BroadcastCondition(barrier->cond);
SDL_UnlockMutex(barrier->mutex);
return 1;
}
SDL_WaitCondition(barrier->cond, barrier->mutex);
SDL_UnlockMutex(barrier->mutex);
return 0;
}
#include "../thread/SDL_systhread.h"
#define THREAD_STATE_WAIT_TIMED_OUT SDL_MUTEX_TIMEDOUT
typedef struct
{
SDL_Thread *thread;
SDL_Mutex *mutex; /* Protects input_reports */
SDL_Condition *condition;
SDL_ThreadBarrier barrier; /* Ensures correct startup sequence */
} hid_device_thread_state;
static void thread_state_init(hid_device_thread_state *state)
{
state->mutex = SDL_CreateMutex();
state->condition = SDL_CreateCondition();
SDL_CreateThreadBarrier(&state->barrier, 2);
}
static void thread_state_free(hid_device_thread_state *state)
{
SDL_DestroyThreadBarrier(&state->barrier);
SDL_DestroyCondition(state->condition);
SDL_DestroyMutex(state->mutex);
}
static void thread_state_push_cleanup(void (*routine)(void *), void *arg)
{
/* There isn't an equivalent in SDL, and it's only useful for threads calling hid_read_timeout() */
}
static void thread_state_pop_cleanup(int execute)
{
}
static void thread_state_lock(hid_device_thread_state *state)
{
SDL_LockMutex(state->mutex);
}
static void thread_state_unlock(hid_device_thread_state *state)
{
SDL_UnlockMutex(state->mutex);
}
static void thread_state_wait_condition(hid_device_thread_state *state)
{
SDL_WaitCondition(state->condition, state->mutex);
}
static int thread_state_wait_condition_timeout(hid_device_thread_state *state, struct timespec *ts)
{
Uint64 end_time;
Sint64 timeout_ns;
Sint32 timeout_ms;
end_time = ts->tv_sec;
end_time *= 1000000000L;
end_time += ts->tv_nsec;
timeout_ns = (Sint64)(end_time - SDL_GetTicksNS());
if (timeout_ns <= 0) {
timeout_ms = 0;
} else {
timeout_ms = (Sint32)SDL_NS_TO_MS(timeout_ns);
}
return SDL_WaitConditionTimeout(state->condition, state->mutex, timeout_ms);
}
static void thread_state_signal_condition(hid_device_thread_state *state)
{
SDL_SignalCondition(state->condition);
}
static void thread_state_broadcast_condition(hid_device_thread_state *state)
{
SDL_BroadcastCondition(state->condition);
}
static void thread_state_wait_barrier(hid_device_thread_state *state)
{
SDL_WaitThreadBarrier(&state->barrier);
}
typedef struct
{
void *(*func)(void*);
void *func_arg;
} RunInputThreadParam;
static int RunInputThread(void *param)
{
RunInputThreadParam *data = (RunInputThreadParam *)param;
void *(*func)(void*) = data->func;
void *func_arg = data->func_arg;
SDL_free(data);
func(func_arg);
return 0;
}
static void thread_state_create_thread(hid_device_thread_state *state, void *(*func)(void*), void *func_arg)
{
RunInputThreadParam *param = (RunInputThreadParam *)malloc(sizeof(*param));
/* Note that the hidapi code didn't check for thread creation failure.
* We'll crash if malloc() fails
*/
param->func = func;
param->func_arg = func_arg;
state->thread = SDL_CreateThreadInternal(RunInputThread, "libusb", 0, param);
}
static void thread_state_join_thread(hid_device_thread_state *state)
{
SDL_WaitThread(state->thread, NULL);
}
static void thread_state_get_current_time(struct timespec *ts)
{
Uint64 ns = SDL_GetTicksNS();
ts->tv_sec = ns / 1000000000L;
ts->tv_nsec = ns % 1000000000L;
}
#define HIDAPI_THREAD_MODEL_INCLUDE "hidapi_thread_sdl.h"
#undef HIDAPI_H__
#include "libusb/hid.c"

View File

@@ -52,6 +52,11 @@
#include "hidapi_libusb.h"
#ifndef HIDAPI_THREAD_MODEL_INCLUDE
#define HIDAPI_THREAD_MODEL_INCLUDE "hidapi_thread_pthread.h"
#endif
#include HIDAPI_THREAD_MODEL_INCLUDE
#ifdef __cplusplus
extern "C" {
#endif
@@ -81,150 +86,6 @@ struct input_report {
struct input_report *next;
};
#ifndef HIDAPI_THREAD_STATE_DEFINED
#include <pthread.h>
#if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__
/* Barrier implementation because Android/Bionic don't have pthread_barrier.
This implementation came from Brent Priddy and was posted on
StackOverflow. It is used with his permission. */
typedef int pthread_barrierattr_t;
typedef struct pthread_barrier {
pthread_mutex_t mutex;
pthread_cond_t cond;
int count;
int trip_count;
} pthread_barrier_t;
static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
{
if(count == 0) {
errno = EINVAL;
return -1;
}
if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
return -1;
}
if(pthread_cond_init(&barrier->cond, 0) < 0) {
pthread_mutex_destroy(&barrier->mutex);
return -1;
}
barrier->trip_count = count;
barrier->count = 0;
return 0;
}
static int pthread_barrier_destroy(pthread_barrier_t *barrier)
{
pthread_cond_destroy(&barrier->cond);
pthread_mutex_destroy(&barrier->mutex);
return 0;
}
static int pthread_barrier_wait(pthread_barrier_t *barrier)
{
pthread_mutex_lock(&barrier->mutex);
++(barrier->count);
if(barrier->count >= barrier->trip_count)
{
barrier->count = 0;
pthread_cond_broadcast(&barrier->cond);
pthread_mutex_unlock(&barrier->mutex);
return 1;
}
else
{
pthread_cond_wait(&barrier->cond, &(barrier->mutex));
pthread_mutex_unlock(&barrier->mutex);
return 0;
}
}
#endif
#define THREAD_STATE_WAIT_TIMED_OUT ETIMEDOUT
typedef struct
{
pthread_t thread;
pthread_mutex_t mutex; /* Protects input_reports */
pthread_cond_t condition;
pthread_barrier_t barrier; /* Ensures correct startup sequence */
} hid_device_thread_state;
static void thread_state_init(hid_device_thread_state *state)
{
pthread_mutex_init(&state->mutex, NULL);
pthread_cond_init(&state->condition, NULL);
pthread_barrier_init(&state->barrier, NULL, 2);
}
static void thread_state_free(hid_device_thread_state *state)
{
pthread_barrier_destroy(&state->barrier);
pthread_cond_destroy(&state->condition);
pthread_mutex_destroy(&state->mutex);
}
#define thread_state_push_cleanup pthread_cleanup_push
#define thread_state_pop_cleanup pthread_cleanup_pop
static void thread_state_lock(hid_device_thread_state *state)
{
pthread_mutex_lock(&state->mutex);
}
static void thread_state_unlock(hid_device_thread_state *state)
{
pthread_mutex_unlock(&state->mutex);
}
static void thread_state_wait_condition(hid_device_thread_state *state)
{
pthread_cond_wait(&state->condition, &state->mutex);
}
static int thread_state_wait_condition_timeout(hid_device_thread_state *state, struct timespec *ts)
{
return pthread_cond_timedwait(&state->condition, &state->mutex, ts);
}
static void thread_state_signal_condition(hid_device_thread_state *state)
{
pthread_cond_signal(&state->condition);
}
static void thread_state_broadcast_condition(hid_device_thread_state *state)
{
pthread_cond_broadcast(&state->condition);
}
static void thread_state_wait_barrier(hid_device_thread_state *state)
{
pthread_barrier_wait(&state->barrier);
}
static void thread_state_create_thread(hid_device_thread_state *state, void *(*func)(void*), void *func_arg)
{
pthread_create(&state->thread, NULL, func, func_arg);
}
static void thread_state_join_thread(hid_device_thread_state *state)
{
pthread_join(state->thread, NULL);
}
static void thread_state_get_current_time(struct timespec *ts)
{
clock_gettime(CLOCK_REALTIME, ts);
}
#endif /* !HIDAPI_THREAD_STATE_DEFINED */
struct hid_device_ {
/* Handle to the actual device. */
@@ -255,7 +116,7 @@ struct hid_device_ {
int blocking; /* boolean */
/* Read thread objects */
hid_device_thread_state thread_state;
hidapi_thread_state thread_state;
int shutdown_thread;
int transfer_loop_finished;
struct libusb_transfer *transfer;
@@ -289,7 +150,7 @@ static hid_device *new_hid_device(void)
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
dev->blocking = 1;
thread_state_init(&dev->thread_state);
hidapi_thread_state_init(&dev->thread_state);
return dev;
}
@@ -297,7 +158,7 @@ static hid_device *new_hid_device(void)
static void free_hid_device(hid_device *dev)
{
/* Clean up the thread objects */
thread_state_free(&dev->thread_state);
hidapi_thread_state_destroy(&dev->thread_state);
hid_free_enumeration(dev->device_info);
@@ -1229,13 +1090,13 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer)
rpt->len = transfer->actual_length;
rpt->next = NULL;
thread_state_lock(&dev->thread_state);
hidapi_thread_mutex_lock(&dev->thread_state);
/* Attach the new report object to the end of the list. */
if (dev->input_reports == NULL) {
/* The list is empty. Put it at the root. */
dev->input_reports = rpt;
thread_state_signal_condition(&dev->thread_state);
hidapi_thread_cond_signal(&dev->thread_state);
}
else {
/* Find the end of the list and attach. */
@@ -1254,7 +1115,7 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer)
return_data(dev, NULL, 0);
}
}
thread_state_unlock(&dev->thread_state);
hidapi_thread_mutex_unlock(&dev->thread_state);
}
else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
dev->shutdown_thread = 1;
@@ -1313,7 +1174,7 @@ static void *read_thread(void *param)
}
/* Notify the main thread that the read thread is up and running. */
thread_state_wait_barrier(&dev->thread_state);
hidapi_thread_barrier_wait(&dev->thread_state);
/* Handle all the events. */
while (!dev->shutdown_thread) {
@@ -1345,15 +1206,15 @@ static void *read_thread(void *param)
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. */
thread_state_lock(&dev->thread_state);
thread_state_broadcast_condition(&dev->thread_state);
thread_state_unlock(&dev->thread_state);
hidapi_thread_mutex_lock(&dev->thread_state);
hidapi_thread_cond_broadcast(&dev->thread_state);
hidapi_thread_mutex_unlock(&dev->thread_state);
/* The dev->transfer->buffer and dev->transfer objects are cleaned up
in hid_close(). They are not cleaned up here because this thread
could end either due to a disconnect or due to a user
call to hid_close(). In both cases the objects can be safely
cleaned up after the call to thread_state_join() (in hid_close()), but
cleaned up after the call to hidapi_thread_join() (in hid_close()), but
since hid_close() calls libusb_cancel_transfer(), on these objects,
they can not be cleaned up here. */
@@ -1536,10 +1397,10 @@ static int hidapi_initialize_device(hid_device *dev, const struct libusb_interfa
calculate_device_quirks(dev, desc.idVendor, desc.idProduct);
thread_state_create_thread(&dev->thread_state, read_thread, dev);
hidapi_thread_create(&dev->thread_state, read_thread, dev);
/* Wait here for the read thread to be initialized. */
thread_state_wait_barrier(&dev->thread_state);
hidapi_thread_barrier_wait(&dev->thread_state);
return 1;
}
@@ -1764,7 +1625,7 @@ static int return_data(hid_device *dev, unsigned char *data, size_t length)
static void cleanup_mutex(void *param)
{
hid_device *dev = param;
thread_state_unlock(&dev->thread_state);
hidapi_thread_mutex_unlock(&dev->thread_state);
}
@@ -1780,8 +1641,8 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
/* error: variable bytes_read might be clobbered by longjmp or vfork [-Werror=clobbered] */
int bytes_read; /* = -1; */
thread_state_lock(&dev->thread_state);
thread_state_push_cleanup(cleanup_mutex, dev);
hidapi_thread_mutex_lock(&dev->thread_state);
hidapi_thread_cleanup_push(cleanup_mutex, dev);
bytes_read = -1;
@@ -1802,7 +1663,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
if (milliseconds == -1) {
/* Blocking */
while (!dev->input_reports && !dev->shutdown_thread) {
thread_state_wait_condition(&dev->thread_state);
hidapi_thread_cond_wait(&dev->thread_state);
}
if (dev->input_reports) {
bytes_read = return_data(dev, data, length);
@@ -1812,7 +1673,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
/* Non-blocking, but called with timeout. */
int res;
struct timespec ts;
thread_state_get_current_time(&ts);
hidapi_thread_gettime(&ts);
ts.tv_sec += milliseconds / 1000;
ts.tv_nsec += (milliseconds % 1000) * 1000000;
if (ts.tv_nsec >= 1000000000L) {
@@ -1821,7 +1682,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
}
while (!dev->input_reports && !dev->shutdown_thread) {
res = thread_state_wait_condition_timeout(&dev->thread_state, &ts);
res = hidapi_thread_cond_timedwait(&dev->thread_state, &ts);
if (res == 0) {
if (dev->input_reports) {
bytes_read = return_data(dev, data, length);
@@ -1832,7 +1693,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
or the read thread was shutdown. Run the
loop again (ie: don't break). */
}
else if (res == THREAD_STATE_WAIT_TIMED_OUT) {
else if (res == HIDAPI_THREAD_TIMED_OUT) {
/* Timed out. */
bytes_read = 0;
break;
@@ -1850,8 +1711,8 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t
}
ret:
thread_state_unlock(&dev->thread_state);
thread_state_pop_cleanup(0);
hidapi_thread_mutex_unlock(&dev->thread_state);
hidapi_thread_cleanup_pop(0);
return bytes_read;
}
@@ -1969,7 +1830,7 @@ void HID_API_EXPORT hid_close(hid_device *dev)
libusb_cancel_transfer(dev->transfer);
/* Wait for read_thread() to end. */
thread_state_join_thread(&dev->thread_state);
hidapi_thread_join(&dev->thread_state);
/* Clean up the Transfer objects allocated in read_thread(). */
free(dev->transfer->buffer);
@@ -1992,11 +1853,11 @@ void HID_API_EXPORT hid_close(hid_device *dev)
libusb_close(dev->device_handle);
/* Clear out the queue of received reports. */
thread_state_lock(&dev->thread_state);
hidapi_thread_mutex_lock(&dev->thread_state);
while (dev->input_reports) {
return_data(dev, NULL, 0);
}
thread_state_unlock(&dev->thread_state);
hidapi_thread_mutex_unlock(&dev->thread_state);
free_hid_device(dev);
}

View File

@@ -0,0 +1,163 @@
/*******************************************************
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 <pthread.h>
#if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__
/* Barrier implementation because Android/Bionic don't have pthread_barrier.
This implementation came from Brent Priddy and was posted on
StackOverflow. It is used with his permission. */
typedef int pthread_barrierattr_t;
typedef struct pthread_barrier {
pthread_mutex_t mutex;
pthread_cond_t cond;
int count;
int trip_count;
} pthread_barrier_t;
static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
{
if(count == 0) {
errno = EINVAL;
return -1;
}
if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
return -1;
}
if(pthread_cond_init(&barrier->cond, 0) < 0) {
pthread_mutex_destroy(&barrier->mutex);
return -1;
}
barrier->trip_count = count;
barrier->count = 0;
return 0;
}
static int pthread_barrier_destroy(pthread_barrier_t *barrier)
{
pthread_cond_destroy(&barrier->cond);
pthread_mutex_destroy(&barrier->mutex);
return 0;
}
static int pthread_barrier_wait(pthread_barrier_t *barrier)
{
pthread_mutex_lock(&barrier->mutex);
++(barrier->count);
if(barrier->count >= barrier->trip_count)
{
barrier->count = 0;
pthread_cond_broadcast(&barrier->cond);
pthread_mutex_unlock(&barrier->mutex);
return 1;
}
else
{
pthread_cond_wait(&barrier->cond, &(barrier->mutex));
pthread_mutex_unlock(&barrier->mutex);
return 0;
}
}
#endif
#define HIDAPI_THREAD_TIMED_OUT ETIMEDOUT
typedef struct
{
pthread_t thread;
pthread_mutex_t mutex; /* Protects input_reports */
pthread_cond_t condition;
pthread_barrier_t barrier; /* Ensures correct startup sequence */
} hidapi_thread_state;
static void hidapi_thread_state_init(hidapi_thread_state *state)
{
pthread_mutex_init(&state->mutex, NULL);
pthread_cond_init(&state->condition, NULL);
pthread_barrier_init(&state->barrier, NULL, 2);
}
static void hidapi_thread_state_destroy(hidapi_thread_state *state)
{
pthread_barrier_destroy(&state->barrier);
pthread_cond_destroy(&state->condition);
pthread_mutex_destroy(&state->mutex);
}
#define hidapi_thread_cleanup_push pthread_cleanup_push
#define hidapi_thread_cleanup_pop pthread_cleanup_pop
static void hidapi_thread_mutex_lock(hidapi_thread_state *state)
{
pthread_mutex_lock(&state->mutex);
}
static void hidapi_thread_mutex_unlock(hidapi_thread_state *state)
{
pthread_mutex_unlock(&state->mutex);
}
static void hidapi_thread_cond_wait(hidapi_thread_state *state)
{
pthread_cond_wait(&state->condition, &state->mutex);
}
static int hidapi_thread_cond_timedwait(hidapi_thread_state *state, struct timespec *ts)
{
return pthread_cond_timedwait(&state->condition, &state->mutex, ts);
}
static void hidapi_thread_cond_signal(hidapi_thread_state *state)
{
pthread_cond_signal(&state->condition);
}
static void hidapi_thread_cond_broadcast(hidapi_thread_state *state)
{
pthread_cond_broadcast(&state->condition);
}
static void hidapi_thread_barrier_wait(hidapi_thread_state *state)
{
pthread_barrier_wait(&state->barrier);
}
static void hidapi_thread_create(hidapi_thread_state *state, void *(*func)(void*), void *func_arg)
{
pthread_create(&state->thread, NULL, func, func_arg);
}
static void hidapi_thread_join(hidapi_thread_state *state)
{
pthread_join(state->thread, NULL);
}
static void hidapi_thread_gettime(struct timespec *ts)
{
clock_gettime(CLOCK_REALTIME, ts);
}

View File

@@ -0,0 +1,198 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 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.
*/
/* Barrier implementation because Android/Bionic don't have pthread_barrier.
This implementation came from Brent Priddy and was posted on
StackOverflow. It is used with his permission. */
typedef struct _SDL_ThreadBarrier
{
SDL_Mutex *mutex;
SDL_Condition *cond;
Uint32 count;
Uint32 trip_count;
} SDL_ThreadBarrier;
static int SDL_CreateThreadBarrier(SDL_ThreadBarrier *barrier, Uint32 count)
{
SDL_assert(barrier != NULL);
SDL_assert(count != 0);
barrier->mutex = SDL_CreateMutex();
if (barrier->mutex == NULL) {
return -1; /* Error set by CreateMutex */
}
barrier->cond = SDL_CreateCondition();
if (barrier->cond == NULL) {
return -1; /* Error set by CreateCond */
}
barrier->trip_count = count;
barrier->count = 0;
return 0;
}
static void SDL_DestroyThreadBarrier(SDL_ThreadBarrier *barrier)
{
SDL_DestroyCondition(barrier->cond);
SDL_DestroyMutex(barrier->mutex);
}
static int SDL_WaitThreadBarrier(SDL_ThreadBarrier *barrier)
{
SDL_LockMutex(barrier->mutex);
barrier->count += 1;
if (barrier->count >= barrier->trip_count) {
barrier->count = 0;
SDL_BroadcastCondition(barrier->cond);
SDL_UnlockMutex(barrier->mutex);
return 1;
}
SDL_WaitCondition(barrier->cond, barrier->mutex);
SDL_UnlockMutex(barrier->mutex);
return 0;
}
#include "../../thread/SDL_systhread.h"
#define HIDAPI_THREAD_TIMED_OUT SDL_MUTEX_TIMEDOUT
typedef struct
{
SDL_Thread *thread;
SDL_Mutex *mutex; /* Protects input_reports */
SDL_Condition *condition;
SDL_ThreadBarrier barrier; /* Ensures correct startup sequence */
} hidapi_thread_state;
static void hidapi_thread_state_init(hidapi_thread_state *state)
{
state->mutex = SDL_CreateMutex();
state->condition = SDL_CreateCondition();
SDL_CreateThreadBarrier(&state->barrier, 2);
}
static void hidapi_thread_state_destroy(hidapi_thread_state *state)
{
SDL_DestroyThreadBarrier(&state->barrier);
SDL_DestroyCondition(state->condition);
SDL_DestroyMutex(state->mutex);
}
static void hidapi_thread_cleanup_push(void (*routine)(void *), void *arg)
{
/* There isn't an equivalent in SDL, and it's only useful for threads calling hid_read_timeout() */
}
static void hidapi_thread_cleanup_pop(int execute)
{
}
static void hidapi_thread_mutex_lock(hidapi_thread_state *state)
{
SDL_LockMutex(state->mutex);
}
static void hidapi_thread_mutex_unlock(hidapi_thread_state *state)
{
SDL_UnlockMutex(state->mutex);
}
static void hidapi_thread_cond_wait(hidapi_thread_state *state)
{
SDL_WaitCondition(state->condition, state->mutex);
}
static int hidapi_thread_cond_timedwait(hidapi_thread_state *state, struct timespec *ts)
{
Uint64 end_time;
Sint64 timeout_ns;
Sint32 timeout_ms;
end_time = ts->tv_sec;
end_time *= 1000000000L;
end_time += ts->tv_nsec;
timeout_ns = (Sint64)(end_time - SDL_GetTicksNS());
if (timeout_ns <= 0) {
timeout_ms = 0;
} else {
timeout_ms = (Sint32)SDL_NS_TO_MS(timeout_ns);
}
return SDL_WaitConditionTimeout(state->condition, state->mutex, timeout_ms);
}
static void hidapi_thread_cond_signal(hidapi_thread_state *state)
{
SDL_SignalCondition(state->condition);
}
static void hidapi_thread_cond_broadcast(hidapi_thread_state *state)
{
SDL_BroadcastCondition(state->condition);
}
static void hidapi_thread_barrier_wait(hidapi_thread_state *state)
{
SDL_WaitThreadBarrier(&state->barrier);
}
typedef struct
{
void *(*func)(void*);
void *func_arg;
} RunInputThreadParam;
static int RunInputThread(void *param)
{
RunInputThreadParam *data = (RunInputThreadParam *)param;
void *(*func)(void*) = data->func;
void *func_arg = data->func_arg;
SDL_free(data);
func(func_arg);
return 0;
}
static void hidapi_thread_create(hidapi_thread_state *state, void *(*func)(void*), void *func_arg)
{
RunInputThreadParam *param = (RunInputThreadParam *)malloc(sizeof(*param));
/* Note that the hidapi code didn't check for thread creation failure.
* We'll crash if malloc() fails
*/
param->func = func;
param->func_arg = func_arg;
state->thread = SDL_CreateThreadInternal(RunInputThread, "libusb", 0, param);
}
static void hidapi_thread_join(hidapi_thread_state *state)
{
SDL_WaitThread(state->thread, NULL);
}
static void hidapi_thread_gettime(struct timespec *ts)
{
Uint64 ns = SDL_GetTicksNS();
ts->tv_sec = ns / 1000000000L;
ts->tv_nsec = ns % 1000000000L;
}