summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/hidapi/mac
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/hidapi/mac')
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/mac/CMakeLists.txt48
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/mac/Makefile-manual27
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/mac/Makefile.am9
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/mac/hid.c1594
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/mac/hidapi_darwin.h98
5 files changed, 1776 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/hidapi/mac/CMakeLists.txt b/contrib/SDL-3.2.8/src/hidapi/mac/CMakeLists.txt
new file mode 100644
index 0000000..0a1c1d9
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/mac/CMakeLists.txt
@@ -0,0 +1,48 @@
1cmake_minimum_required(VERSION 3.4.3...3.25 FATAL_ERROR)
2
3list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_darwin.h")
4
5add_library(hidapi_darwin
6 ${HIDAPI_PUBLIC_HEADERS}
7 hid.c
8)
9
10find_package(Threads REQUIRED)
11
12target_link_libraries(hidapi_darwin
13 PUBLIC hidapi_include
14 PRIVATE Threads::Threads
15 PRIVATE "-framework IOKit" "-framework CoreFoundation"
16)
17
18set_target_properties(hidapi_darwin
19 PROPERTIES
20 EXPORT_NAME "darwin"
21 OUTPUT_NAME "hidapi"
22 VERSION ${PROJECT_VERSION}
23 SOVERSION ${PROJECT_VERSION_MAJOR}
24 MACHO_COMPATIBILITY_VERSION ${PROJECT_VERSION_MAJOR}
25 FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR}
26 PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}"
27)
28
29# compatibility with find_package()
30add_library(hidapi::darwin ALIAS hidapi_darwin)
31# compatibility with raw library link
32add_library(hidapi ALIAS hidapi_darwin)
33
34set(PUBLIC_HEADER_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
35if(NOT CMAKE_FRAMEWORK)
36 set(PUBLIC_HEADER_DESTINATION "${PUBLIC_HEADER_DESTINATION}/hidapi")
37endif()
38
39if(HIDAPI_INSTALL_TARGETS)
40 install(TARGETS hidapi_darwin EXPORT hidapi
41 LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
42 ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
43 FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}"
44 PUBLIC_HEADER DESTINATION "${PUBLIC_HEADER_DESTINATION}"
45 )
46endif()
47
48hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in")
diff --git a/contrib/SDL-3.2.8/src/hidapi/mac/Makefile-manual b/contrib/SDL-3.2.8/src/hidapi/mac/Makefile-manual
new file mode 100644
index 0000000..667c863
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/mac/Makefile-manual
@@ -0,0 +1,27 @@
1###########################################
2# Simple Makefile for HIDAPI test program
3#
4# Alan Ott
5# Signal 11 Software
6# 2010-07-03
7###########################################
8
9all: hidtest
10
11CC=gcc
12COBJS=hid.o ../hidtest/test.o
13OBJS=$(COBJS)
14CFLAGS+=-I../hidapi -I. -Wall -g -c
15LIBS=-framework IOKit -framework CoreFoundation
16
17
18hidtest: $(OBJS)
19 $(CC) -Wall -g $^ $(LIBS) -o hidtest
20
21$(COBJS): %.o: %.c
22 $(CC) $(CFLAGS) $< -o $@
23
24clean:
25 rm -f *.o hidtest
26
27.PHONY: clean
diff --git a/contrib/SDL-3.2.8/src/hidapi/mac/Makefile.am b/contrib/SDL-3.2.8/src/hidapi/mac/Makefile.am
new file mode 100644
index 0000000..23d96e0
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/mac/Makefile.am
@@ -0,0 +1,9 @@
1lib_LTLIBRARIES = libhidapi.la
2libhidapi_la_SOURCES = hid.c
3libhidapi_la_LDFLAGS = $(LTLDFLAGS)
4AM_CPPFLAGS = -I$(top_srcdir)/hidapi/
5
6hdrdir = $(includedir)/hidapi
7hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h
8
9EXTRA_DIST = Makefile-manual
diff --git a/contrib/SDL-3.2.8/src/hidapi/mac/hid.c b/contrib/SDL-3.2.8/src/hidapi/mac/hid.c
new file mode 100644
index 0000000..8acb6da
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/mac/hid.c
@@ -0,0 +1,1594 @@
1/*******************************************************
2 HIDAPI - Multi-Platform library for
3 communication with HID devices.
4
5 Alan Ott
6 Signal 11 Software
7
8 libusb/hidapi Team
9
10 Copyright 2022, All Rights Reserved.
11
12 At the discretion of the user of this library,
13 this software may be licensed under the terms of the
14 GNU General Public License v3, a BSD-Style license, or the
15 original HIDAPI license as outlined in the LICENSE.txt,
16 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
17 files located at the root of the source distribution.
18 These files may also be found in the public source
19 code repository located at:
20 https://github.com/libusb/hidapi .
21********************************************************/
22
23/* See Apple Technical Note TN2187 for details on IOHidManager. */
24
25#include <IOKit/hid/IOHIDManager.h>
26#include <IOKit/hid/IOHIDKeys.h>
27#include <IOKit/IOKitLib.h>
28#include <IOKit/usb/USBSpec.h>
29#include <CoreFoundation/CoreFoundation.h>
30#include <mach/mach_error.h>
31#include <stdbool.h>
32#include <wchar.h>
33#include <locale.h>
34#include <pthread.h>
35#include <sys/time.h>
36#include <unistd.h>
37#include <dlfcn.h>
38
39#include "hidapi_darwin.h"
40
41/* Barrier implementation because Mac OSX doesn't have pthread_barrier.
42 It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
43 This implementation came from Brent Priddy and was posted on
44 StackOverflow. It is used with his permission. */
45typedef int pthread_barrierattr_t;
46typedef struct pthread_barrier {
47 pthread_mutex_t mutex;
48 pthread_cond_t cond;
49 int count;
50 int trip_count;
51} pthread_barrier_t;
52
53static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
54{
55 (void) attr;
56
57 if (count == 0) {
58 errno = EINVAL;
59 return -1;
60 }
61
62 if (pthread_mutex_init(&barrier->mutex, 0) < 0) {
63 return -1;
64 }
65 if (pthread_cond_init(&barrier->cond, 0) < 0) {
66 pthread_mutex_destroy(&barrier->mutex);
67 return -1;
68 }
69 barrier->trip_count = count;
70 barrier->count = 0;
71
72 return 0;
73}
74
75static int pthread_barrier_destroy(pthread_barrier_t *barrier)
76{
77 pthread_cond_destroy(&barrier->cond);
78 pthread_mutex_destroy(&barrier->mutex);
79 return 0;
80}
81
82static int pthread_barrier_wait(pthread_barrier_t *barrier)
83{
84 pthread_mutex_lock(&barrier->mutex);
85 ++(barrier->count);
86 if (barrier->count >= barrier->trip_count) {
87 barrier->count = 0;
88 pthread_mutex_unlock(&barrier->mutex);
89 pthread_cond_broadcast(&barrier->cond);
90 return 1;
91 }
92 else {
93 do {
94 pthread_cond_wait(&barrier->cond, &(barrier->mutex));
95 }
96 while (barrier->count != 0);
97
98 pthread_mutex_unlock(&barrier->mutex);
99 return 0;
100 }
101}
102
103static int return_data(hid_device *dev, unsigned char *data, size_t length);
104
105/* Linked List of input reports received from the device. */
106struct input_report {
107 uint8_t *data;
108 size_t len;
109 struct input_report *next;
110};
111
112static struct hid_api_version api_version = {
113 .major = HID_API_VERSION_MAJOR,
114 .minor = HID_API_VERSION_MINOR,
115 .patch = HID_API_VERSION_PATCH
116};
117
118/* - Run context - */
119static IOHIDManagerRef hid_mgr = 0x0;
120static int is_macos_10_10_or_greater = 0;
121static IOOptionBits device_open_options = 0;
122static wchar_t *last_global_error_str = NULL;
123/* --- */
124
125struct hid_device_ {
126 IOHIDDeviceRef device_handle;
127 IOOptionBits open_options;
128 int blocking;
129 int disconnected;
130 CFStringRef run_loop_mode;
131 CFRunLoopRef run_loop;
132 CFRunLoopSourceRef source;
133 uint8_t *input_report_buf;
134 CFIndex max_input_report_len;
135 struct input_report *input_reports;
136 struct hid_device_info* device_info;
137
138 pthread_t thread;
139 pthread_mutex_t mutex; /* Protects input_reports */
140 pthread_cond_t condition;
141 pthread_barrier_t barrier; /* Ensures correct startup sequence */
142 pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */
143 int shutdown_thread;
144 wchar_t *last_error_str;
145};
146
147static hid_device *new_hid_device(void)
148{
149 hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
150 if (dev == NULL) {
151 return NULL;
152 }
153
154 dev->device_handle = NULL;
155 dev->open_options = device_open_options;
156 dev->blocking = 1;
157 dev->disconnected = 0;
158 dev->run_loop_mode = NULL;
159 dev->run_loop = NULL;
160 dev->source = NULL;
161 dev->input_report_buf = NULL;
162 dev->input_reports = NULL;
163 dev->device_info = NULL;
164 dev->shutdown_thread = 0;
165 dev->last_error_str = NULL;
166
167 /* Thread objects */
168 pthread_mutex_init(&dev->mutex, NULL);
169 pthread_cond_init(&dev->condition, NULL);
170 pthread_barrier_init(&dev->barrier, NULL, 2);
171 pthread_barrier_init(&dev->shutdown_barrier, NULL, 2);
172
173 return dev;
174}
175
176static void free_hid_device(hid_device *dev)
177{
178 if (!dev)
179 return;
180
181 /* Delete any input reports still left over. */
182 struct input_report *rpt = dev->input_reports;
183 while (rpt) {
184 struct input_report *next = rpt->next;
185 free(rpt->data);
186 free(rpt);
187 rpt = next;
188 }
189
190 /* Free the string and the report buffer. The check for NULL
191 is necessary here as CFRelease() doesn't handle NULL like
192 free() and others do. */
193 if (dev->run_loop_mode)
194 CFRelease(dev->run_loop_mode);
195 if (dev->source)
196 CFRelease(dev->source);
197 free(dev->input_report_buf);
198 hid_free_enumeration(dev->device_info);
199
200 /* Clean up the thread objects */
201 pthread_barrier_destroy(&dev->shutdown_barrier);
202 pthread_barrier_destroy(&dev->barrier);
203 pthread_cond_destroy(&dev->condition);
204 pthread_mutex_destroy(&dev->mutex);
205
206 /* Free the structure itself. */
207 free(dev);
208}
209
210
211#ifndef HIDAPI_USING_SDL_RUNTIME
212/* The caller must free the returned string with free(). */
213static wchar_t *utf8_to_wchar_t(const char *utf8)
214{
215 wchar_t *ret = NULL;
216
217 if (utf8) {
218 size_t wlen = mbstowcs(NULL, utf8, 0);
219 if ((size_t) -1 == wlen) {
220 return wcsdup(L"");
221 }
222 ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t));
223 if (ret == NULL) {
224 /* as much as we can do at this point */
225 return NULL;
226 }
227 mbstowcs(ret, utf8, wlen+1);
228 ret[wlen] = 0x0000;
229 }
230
231 return ret;
232}
233#endif
234
235
236/* Makes a copy of the given error message (and decoded according to the
237 * currently locale) into the wide string pointer pointed by error_str.
238 * The last stored error string is freed.
239 * Use register_error_str(NULL) to free the error message completely. */
240static void register_error_str(wchar_t **error_str, const char *msg)
241{
242 free(*error_str);
243#ifdef HIDAPI_USING_SDL_RUNTIME
244 /* Thread-safe error handling */
245 if (msg) {
246 SDL_SetError("%s", msg);
247 } else {
248 SDL_ClearError();
249 }
250#else
251 *error_str = utf8_to_wchar_t(msg);
252#endif
253}
254
255/* Similar to register_error_str, but allows passing a format string with va_list args into this function. */
256static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args)
257{
258 char msg[1024];
259 vsnprintf(msg, sizeof(msg), format, args);
260
261 register_error_str(error_str, msg);
262}
263
264/* Set the last global error to be reported by hid_error(NULL).
265 * The given error message will be copied (and decoded according to the
266 * currently locale, so do not pass in string constants).
267 * The last stored global error message is freed.
268 * Use register_global_error(NULL) to indicate "no error". */
269static void register_global_error(const char *msg)
270{
271 register_error_str(&last_global_error_str, msg);
272}
273
274/* Similar to register_global_error, but allows passing a format string into this function. */
275static void register_global_error_format(const char *format, ...)
276{
277 va_list args;
278 va_start(args, format);
279 register_error_str_vformat(&last_global_error_str, format, args);
280 va_end(args);
281}
282
283/* Set the last error for a device to be reported by hid_error(dev).
284 * The given error message will be copied (and decoded according to the
285 * currently locale, so do not pass in string constants).
286 * The last stored device error message is freed.
287 * Use register_device_error(dev, NULL) to indicate "no error". */
288static void register_device_error(hid_device *dev, const char *msg)
289{
290 register_error_str(&dev->last_error_str, msg);
291}
292
293/* Similar to register_device_error, but you can pass a format string into this function. */
294static void register_device_error_format(hid_device *dev, const char *format, ...)
295{
296 va_list args;
297 va_start(args, format);
298 register_error_str_vformat(&dev->last_error_str, format, args);
299 va_end(args);
300}
301
302
303static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key)
304{
305 CFTypeRef ref = IOHIDDeviceGetProperty(device, key);
306 if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) {
307 return (CFArrayRef)ref;
308 } else {
309 return NULL;
310 }
311}
312
313static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
314{
315 CFTypeRef ref;
316 int32_t value = 0;
317
318 ref = IOHIDDeviceGetProperty(device, key);
319 if (ref) {
320 if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
321 CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value);
322 return value;
323 }
324 }
325 return 0;
326}
327
328static bool try_get_int_property(IOHIDDeviceRef device, CFStringRef key, int32_t *out_val)
329{
330 bool result = false;
331 CFTypeRef ref;
332
333 ref = IOHIDDeviceGetProperty(device, key);
334 if (ref) {
335 if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
336 result = CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, out_val);
337 }
338 }
339 return result;
340}
341
342static bool try_get_ioregistry_int_property(io_service_t service, CFStringRef property, int32_t *out_val)
343{
344 bool result = false;
345 CFTypeRef ref = IORegistryEntryCreateCFProperty(service, property, kCFAllocatorDefault, 0);
346
347 if (ref) {
348 if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
349 result = CFNumberGetValue(ref, kCFNumberSInt32Type, out_val);
350 }
351
352 CFRelease(ref);
353 }
354
355 return result;
356}
357
358static CFArrayRef get_usage_pairs(IOHIDDeviceRef device)
359{
360 return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey));
361}
362
363static unsigned short get_vendor_id(IOHIDDeviceRef device)
364{
365 return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
366}
367
368static unsigned short get_product_id(IOHIDDeviceRef device)
369{
370 return get_int_property(device, CFSTR(kIOHIDProductIDKey));
371}
372
373static int32_t get_max_report_length(IOHIDDeviceRef device)
374{
375 return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
376}
377
378static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len)
379{
380 CFStringRef str;
381
382 if (!len)
383 return 0;
384
385 str = (CFStringRef) IOHIDDeviceGetProperty(device, prop);
386
387 buf[0] = 0;
388
389 if (str && CFGetTypeID(str) == CFStringGetTypeID()) {
390 CFIndex str_len = CFStringGetLength(str);
391 CFRange range;
392 CFIndex used_buf_len;
393 CFIndex chars_copied;
394
395 len --;
396
397 range.location = 0;
398 range.length = ((size_t) str_len > len)? len: (size_t) str_len;
399 chars_copied = CFStringGetBytes(str,
400 range,
401 kCFStringEncodingUTF32LE,
402 (char) '?',
403 FALSE,
404 (UInt8*)buf,
405 len * sizeof(wchar_t),
406 &used_buf_len);
407
408 if (chars_copied <= 0)
409 buf[0] = 0;
410 else
411 buf[chars_copied] = 0;
412
413 return 0;
414 }
415 else
416 return -1;
417
418}
419
420static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len)
421{
422 return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
423}
424
425static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
426{
427 return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len);
428}
429
430static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
431{
432 return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len);
433}
434
435
436/* Implementation of wcsdup() for Mac. */
437static wchar_t *dup_wcs(const wchar_t *s)
438{
439 size_t len = wcslen(s);
440 wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t));
441 wcscpy(ret, s);
442
443 return ret;
444}
445
446/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
447static int init_hid_manager(void)
448{
449 /* Initialize all the HID Manager Objects */
450 hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
451 if (hid_mgr) {
452 IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
453 IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
454 return 0;
455 }
456
457 register_global_error("Failed to create IOHIDManager");
458 return -1;
459}
460
461HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
462{
463 return &api_version;
464}
465
466HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
467{
468 return HID_API_VERSION_STR;
469}
470
471/* Initialize the IOHIDManager if necessary. This is the public function, and
472 it is safe to call this function repeatedly. Return 0 for success and -1
473 for failure. */
474int HID_API_EXPORT hid_init(void)
475{
476 register_global_error(NULL);
477
478 if (!hid_mgr) {
479 is_macos_10_10_or_greater = (kCFCoreFoundationVersionNumber >= 1151.16); /* kCFCoreFoundationVersionNumber10_10 */
480 hid_darwin_set_open_exclusive(1); /* Backward compatibility */
481 return init_hid_manager();
482 }
483
484 /* Already initialized. */
485 return 0;
486}
487
488int HID_API_EXPORT hid_exit(void)
489{
490 if (hid_mgr) {
491 /* Close the HID manager. */
492 IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
493 CFRelease(hid_mgr);
494 hid_mgr = NULL;
495 }
496
497 /* Free global error message */
498 register_global_error(NULL);
499
500 return 0;
501}
502
503static void process_pending_events(void) {
504 SInt32 res;
505 do {
506 res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE);
507 } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
508}
509
510static int read_usb_interface_from_hid_service_parent(io_service_t hid_service)
511{
512 int32_t result = -1;
513 bool success = false;
514 io_registry_entry_t current = IO_OBJECT_NULL;
515 kern_return_t res;
516 int parent_number = 0;
517
518 res = IORegistryEntryGetParentEntry(hid_service, kIOServicePlane, &current);
519 while (KERN_SUCCESS == res
520 /* Only search up to 3 parent entries.
521 * With the default driver - the parent-of-interest supposed to be the first one,
522 * but lets assume some custom drivers or so, with deeper tree. */
523 && parent_number < 3) {
524 io_registry_entry_t parent = IO_OBJECT_NULL;
525 int32_t interface_number = -1;
526 parent_number++;
527
528 success = try_get_ioregistry_int_property(current, CFSTR(kUSBInterfaceNumber), &interface_number);
529 if (success) {
530 result = interface_number;
531 break;
532 }
533
534 res = IORegistryEntryGetParentEntry(current, kIOServicePlane, &parent);
535 if (parent) {
536 IOObjectRelease(current);
537 current = parent;
538 }
539
540 }
541
542 if (current) {
543 IOObjectRelease(current);
544 current = IO_OBJECT_NULL;
545 }
546
547 return result;
548}
549
550#ifdef HIDAPI_IGNORE_DEVICE
551static hid_bus_type get_bus_type(IOHIDDeviceRef dev)
552{
553 hid_bus_type bus_type = HID_API_BUS_UNKNOWN;
554
555 CFTypeRef transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey));
556
557 if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) {
558 if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) {
559 bus_type = HID_API_BUS_USB;
560 } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) {
561 bus_type = HID_API_BUS_BLUETOOTH;
562 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) {
563 bus_type = HID_API_BUS_I2C;
564 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) {
565 bus_type = HID_API_BUS_SPI;
566 }
567 }
568 return bus_type;
569}
570#endif /* HIDAPI_IGNORE_DEVICE */
571
572static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage)
573{
574 unsigned short dev_vid;
575 unsigned short dev_pid;
576 int BUF_LEN = 256;
577 wchar_t buf[BUF_LEN];
578 CFTypeRef transport_prop;
579
580 struct hid_device_info *cur_dev;
581 io_service_t hid_service;
582 kern_return_t res;
583 uint64_t entry_id = 0;
584
585 if (dev == NULL) {
586 return NULL;
587 }
588
589 cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
590 if (cur_dev == NULL) {
591 return NULL;
592 }
593
594 dev_vid = get_vendor_id(dev);
595 dev_pid = get_product_id(dev);
596
597#ifdef HIDAPI_IGNORE_DEVICE
598 /* See if there are any devices we should skip in enumeration */
599 if (HIDAPI_IGNORE_DEVICE(get_bus_type(dev), dev_vid, dev_pid, usage_page, usage)) {
600 free(cur_dev);
601 return NULL;
602 }
603#endif
604
605 cur_dev->usage_page = usage_page;
606 cur_dev->usage = usage;
607
608 /* Fill out the record */
609 cur_dev->next = NULL;
610
611 /* Fill in the path (as a unique ID of the service entry) */
612 cur_dev->path = NULL;
613 hid_service = IOHIDDeviceGetService(dev);
614 if (hid_service != MACH_PORT_NULL) {
615 res = IORegistryEntryGetRegistryEntryID(hid_service, &entry_id);
616 }
617 else {
618 res = KERN_INVALID_ARGUMENT;
619 }
620
621 if (res == KERN_SUCCESS) {
622 /* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long,
623 so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need
624 9+1+20+1=31 bytes buffer, but allocate 32 for simple alignment */
625 const size_t path_len = 32;
626 cur_dev->path = calloc(1, path_len);
627 if (cur_dev->path != NULL) {
628 snprintf(cur_dev->path, path_len, "DevSrvsID:%llu", entry_id);
629 }
630 }
631
632 if (cur_dev->path == NULL) {
633 /* for whatever reason, trying to keep it a non-NULL string */
634 cur_dev->path = strdup("");
635 }
636
637 /* Serial Number */
638 get_serial_number(dev, buf, BUF_LEN);
639 cur_dev->serial_number = dup_wcs(buf);
640
641 /* Manufacturer and Product strings */
642 get_manufacturer_string(dev, buf, BUF_LEN);
643 cur_dev->manufacturer_string = dup_wcs(buf);
644 get_product_string(dev, buf, BUF_LEN);
645 cur_dev->product_string = dup_wcs(buf);
646
647 /* VID/PID */
648 cur_dev->vendor_id = dev_vid;
649 cur_dev->product_id = dev_pid;
650
651 /* Release Number */
652 cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
653
654 /* Interface Number.
655 * We can only retrieve the interface number for USB HID devices.
656 * See below */
657 cur_dev->interface_number = -1;
658
659 /* Bus Type */
660 transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey));
661
662 if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) {
663 if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) {
664 int32_t interface_number = -1;
665 cur_dev->bus_type = HID_API_BUS_USB;
666
667 /* A IOHIDDeviceRef used to have this simple property,
668 * until macOS 13.3 - we will try to use it. */
669 if (try_get_int_property(dev, CFSTR(kUSBInterfaceNumber), &interface_number)) {
670 cur_dev->interface_number = interface_number;
671 } else {
672 /* Otherwise fallback to io_service_t property.
673 * (of one of the parent services). */
674 cur_dev->interface_number = read_usb_interface_from_hid_service_parent(hid_service);
675
676 /* If the above doesn't work -
677 * no (known) fallback exists at this point. */
678 }
679
680 /* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */
681 } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) {
682 cur_dev->bus_type = HID_API_BUS_BLUETOOTH;
683 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) {
684 cur_dev->bus_type = HID_API_BUS_I2C;
685 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) {
686 cur_dev->bus_type = HID_API_BUS_SPI;
687 }
688 }
689
690 return cur_dev;
691}
692
693static struct hid_device_info *create_device_info(IOHIDDeviceRef device)
694{
695 const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
696 const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
697
698 /* Primary should always be first, to match previous behavior. */
699 struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage);
700 struct hid_device_info *cur = root;
701
702 CFArrayRef usage_pairs = get_usage_pairs(device);
703
704 if (usage_pairs != NULL) {
705 struct hid_device_info *next = NULL;
706 for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) {
707 CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i);
708 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
709 continue;
710 }
711
712 CFTypeRef usage_page_ref, usage_ref;
713 int32_t usage_page, usage;
714
715 if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) ||
716 !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) ||
717 CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() ||
718 CFGetTypeID(usage_ref) != CFNumberGetTypeID() ||
719 !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) ||
720 !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) {
721 continue;
722 }
723 if (usage_page == primary_usage_page && usage == primary_usage)
724 continue; /* Already added. */
725
726 next = create_device_info_with_usage(device, usage_page, usage);
727 if (cur) {
728 if (next != NULL) {
729 cur->next = next;
730 cur = next;
731 }
732 } else {
733 root = cur = next;
734 }
735 }
736 }
737
738 return root;
739}
740
741struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
742{
743 struct hid_device_info *root = NULL; /* return object */
744 struct hid_device_info *cur_dev = NULL;
745 CFIndex num_devices;
746 int i;
747
748 /* Set up the HID Manager if it hasn't been done */
749 if (hid_init() < 0) {
750 return NULL;
751 }
752 /* register_global_error: global error is set/reset by hid_init */
753
754 /* give the IOHIDManager a chance to update itself */
755 process_pending_events();
756
757 /* Get a list of the Devices */
758 CFMutableDictionaryRef matching = NULL;
759 if (vendor_id != 0 || product_id != 0) {
760 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
761
762 if (matching && vendor_id != 0) {
763 CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id);
764 CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v);
765 CFRelease(v);
766 }
767
768 if (matching && product_id != 0) {
769 CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id);
770 CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p);
771 CFRelease(p);
772 }
773 }
774 IOHIDManagerSetDeviceMatching(hid_mgr, matching);
775 if (matching != NULL) {
776 CFRelease(matching);
777 }
778
779 CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
780
781 IOHIDDeviceRef *device_array = NULL;
782
783 if (device_set != NULL) {
784 /* Convert the list into a C array so we can iterate easily. */
785 num_devices = CFSetGetCount(device_set);
786 device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef));
787 CFSetGetValues(device_set, (const void **) device_array);
788 } else {
789 num_devices = 0;
790 }
791
792 /* Iterate over each device, making an entry for it. */
793 for (i = 0; i < num_devices; i++) {
794
795 IOHIDDeviceRef dev = device_array[i];
796 if (!dev) {
797 continue;
798 }
799
800 struct hid_device_info *tmp = create_device_info(dev);
801 if (tmp == NULL) {
802 continue;
803 }
804
805 if (cur_dev) {
806 cur_dev->next = tmp;
807 }
808 else {
809 root = tmp;
810 }
811 cur_dev = tmp;
812
813 /* move the pointer to the tail of returned list */
814 while (cur_dev->next != NULL) {
815 cur_dev = cur_dev->next;
816 }
817 }
818
819 free(device_array);
820 if (device_set != NULL)
821 CFRelease(device_set);
822
823 if (root == NULL) {
824 if (vendor_id == 0 && product_id == 0) {
825 register_global_error("No HID devices found in the system.");
826 } else {
827 register_global_error("No HID devices with requested VID/PID found in the system.");
828 }
829 }
830
831 return root;
832}
833
834void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
835{
836 /* This function is identical to the Linux version. Platform independent. */
837 struct hid_device_info *d = devs;
838 while (d) {
839 struct hid_device_info *next = d->next;
840 free(d->path);
841 free(d->serial_number);
842 free(d->manufacturer_string);
843 free(d->product_string);
844 free(d);
845 d = next;
846 }
847}
848
849hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
850{
851 /* This function is identical to the Linux version. Platform independent. */
852
853 struct hid_device_info *devs, *cur_dev;
854 const char *path_to_open = NULL;
855 hid_device * handle = NULL;
856
857 /* register_global_error: global error is reset by hid_enumerate/hid_init */
858 devs = hid_enumerate(vendor_id, product_id);
859 if (devs == NULL) {
860 /* register_global_error: global error is already set by hid_enumerate */
861 return NULL;
862 }
863
864 cur_dev = devs;
865 while (cur_dev) {
866 if (cur_dev->vendor_id == vendor_id &&
867 cur_dev->product_id == product_id) {
868 if (serial_number) {
869 if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
870 path_to_open = cur_dev->path;
871 break;
872 }
873 }
874 else {
875 path_to_open = cur_dev->path;
876 break;
877 }
878 }
879 cur_dev = cur_dev->next;
880 }
881
882 if (path_to_open) {
883 handle = hid_open_path(path_to_open);
884 } else {
885 register_global_error("Device with requested VID/PID/(SerialNumber) not found");
886 }
887
888 hid_free_enumeration(devs);
889
890 return handle;
891}
892
893static void hid_device_removal_callback(void *context, IOReturn result,
894 void *sender)
895{
896 (void) result;
897 (void) sender;
898
899 /* Stop the Run Loop for this device. */
900 hid_device *d = (hid_device*) context;
901
902 d->disconnected = 1;
903 CFRunLoopStop(d->run_loop);
904}
905
906/* The Run Loop calls this function for each input report received.
907 This function puts the data into a linked list to be picked up by
908 hid_read(). */
909static void hid_report_callback(void *context, IOReturn result, void *sender,
910 IOHIDReportType report_type, uint32_t report_id,
911 uint8_t *report, CFIndex report_length)
912{
913 (void) result;
914 (void) sender;
915 (void) report_type;
916 (void) report_id;
917
918 struct input_report *rpt;
919 hid_device *dev = (hid_device*) context;
920
921 /* Make a new Input Report object */
922 rpt = (struct input_report*) calloc(1, sizeof(struct input_report));
923 rpt->data = (uint8_t*) calloc(1, report_length);
924 memcpy(rpt->data, report, report_length);
925 rpt->len = report_length;
926 rpt->next = NULL;
927
928 /* Lock this section */
929 pthread_mutex_lock(&dev->mutex);
930
931 /* Attach the new report object to the end of the list. */
932 if (dev->input_reports == NULL) {
933 /* The list is empty. Put it at the root. */
934 dev->input_reports = rpt;
935 }
936 else {
937 /* Find the end of the list and attach. */
938 struct input_report *cur = dev->input_reports;
939 int num_queued = 0;
940 while (cur->next != NULL) {
941 cur = cur->next;
942 num_queued++;
943 }
944 cur->next = rpt;
945
946 /* Pop one off if we've reached 30 in the queue. This
947 way we don't grow forever if the user never reads
948 anything from the device. */
949 if (num_queued > 30) {
950 return_data(dev, NULL, 0);
951 }
952 }
953
954 /* Signal a waiting thread that there is data. */
955 pthread_cond_signal(&dev->condition);
956
957 /* Unlock */
958 pthread_mutex_unlock(&dev->mutex);
959
960}
961
962/* This gets called when the read_thread's run loop gets signaled by
963 hid_close(), and serves to stop the read_thread's run loop. */
964static void perform_signal_callback(void *context)
965{
966 hid_device *dev = (hid_device*) context;
967 CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/
968}
969
970static void *read_thread(void *param)
971{
972 hid_device *dev = (hid_device*) param;
973 SInt32 code;
974
975 /* Move the device's run loop to this thread. */
976 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
977
978 /* Create the RunLoopSource which is used to signal the
979 event loop to stop when hid_close() is called. */
980 CFRunLoopSourceContext ctx;
981 memset(&ctx, 0, sizeof(ctx));
982 ctx.version = 0;
983 ctx.info = dev;
984 ctx.perform = &perform_signal_callback;
985 dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
986 CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
987
988 /* Store off the Run Loop so it can be stopped from hid_close()
989 and on device disconnection. */
990 dev->run_loop = CFRunLoopGetCurrent();
991
992 /* Notify the main thread that the read thread is up and running. */
993 pthread_barrier_wait(&dev->barrier);
994
995 /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
996 reports into the hid_report_callback(). */
997 while (!dev->shutdown_thread && !dev->disconnected) {
998 code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
999 /* Return if the device has been disconnected */
1000 if (code == kCFRunLoopRunFinished || code == kCFRunLoopRunStopped) {
1001 dev->disconnected = 1;
1002 break;
1003 }
1004
1005
1006 /* Break if The Run Loop returns Finished or Stopped. */
1007 if (code != kCFRunLoopRunTimedOut &&
1008 code != kCFRunLoopRunHandledSource) {
1009 /* There was some kind of error. Setting
1010 shutdown seems to make sense, but
1011 there may be something else more appropriate */
1012 dev->shutdown_thread = 1;
1013 break;
1014 }
1015 }
1016
1017 /* Now that the read thread is stopping, Wake any threads which are
1018 waiting on data (in hid_read_timeout()). Do this under a mutex to
1019 make sure that a thread which is about to go to sleep waiting on
1020 the condition actually will go to sleep before the condition is
1021 signaled. */
1022 pthread_mutex_lock(&dev->mutex);
1023 pthread_cond_broadcast(&dev->condition);
1024 pthread_mutex_unlock(&dev->mutex);
1025
1026 /* Wait here until hid_close() is called and makes it past
1027 the call to CFRunLoopWakeUp(). This thread still needs to
1028 be valid when that function is called on the other thread. */
1029 pthread_barrier_wait(&dev->shutdown_barrier);
1030
1031 return NULL;
1032}
1033
1034/* \p path must be one of:
1035 - in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate);
1036 - a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath,
1037 e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver");
1038 Second format is for compatibility with paths accepted by older versions of HIDAPI.
1039*/
1040static io_registry_entry_t hid_open_service_registry_from_path(const char *path)
1041{
1042 if (path == NULL)
1043 return MACH_PORT_NULL;
1044
1045 /* Get the IORegistry entry for the given path */
1046 if (strncmp("DevSrvsID:", path, 10) == 0) {
1047 char *endptr;
1048 uint64_t entry_id = strtoull(path + 10, &endptr, 10);
1049 if (*endptr == '\0') {
1050 return IOServiceGetMatchingService((mach_port_t) 0, IORegistryEntryIDMatching(entry_id));
1051 }
1052 }
1053 else {
1054 /* Fallback to older format of the path */
1055 return IORegistryEntryFromPath((mach_port_t) 0, path);
1056 }
1057
1058 return MACH_PORT_NULL;
1059}
1060
1061hid_device * HID_API_EXPORT hid_open_path(const char *path)
1062{
1063 hid_device *dev = NULL;
1064 io_registry_entry_t entry = MACH_PORT_NULL;
1065 IOReturn ret = kIOReturnInvalid;
1066 char str[32];
1067
1068 /* Set up the HID Manager if it hasn't been done */
1069 if (hid_init() < 0) {
1070 return NULL;
1071 }
1072 /* register_global_error: global error is set/reset by hid_init */
1073
1074 dev = new_hid_device();
1075 if (!dev) {
1076 register_global_error("Couldn't allocate memory");
1077 return NULL;
1078 }
1079
1080 /* Get the IORegistry entry for the given path */
1081 entry = hid_open_service_registry_from_path(path);
1082 if (entry == MACH_PORT_NULL) {
1083 /* Path wasn't valid (maybe device was removed?) */
1084 register_global_error("hid_open_path: device mach entry not found with the given path");
1085 goto return_error;
1086 }
1087
1088 /* Create an IOHIDDevice for the entry */
1089 dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
1090 if (dev->device_handle == NULL) {
1091 /* Error creating the HID device */
1092 register_global_error("hid_open_path: failed to create IOHIDDevice from the mach entry");
1093 goto return_error;
1094 }
1095
1096 /* Open the IOHIDDevice */
1097 ret = IOHIDDeviceOpen(dev->device_handle, dev->open_options);
1098 if (ret != kIOReturnSuccess) {
1099 register_global_error_format("hid_open_path: failed to open IOHIDDevice from mach entry: (0x%08X) %s", ret, mach_error_string(ret));
1100 goto return_error;
1101 }
1102
1103 /* Create the buffers for receiving data */
1104 dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
1105 dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t));
1106
1107 /* Create the Run Loop Mode for this device.
1108 printing the reference seems to work. */
1109 snprintf(str, sizeof(str), "HIDAPI_%p", (void*) dev->device_handle);
1110 dev->run_loop_mode =
1111 CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
1112
1113 /* Attach the device to a Run Loop */
1114 IOHIDDeviceRegisterInputReportCallback(
1115 dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
1116 &hid_report_callback, dev);
1117 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
1118
1119 /* Start the read thread */
1120 pthread_create(&dev->thread, NULL, read_thread, dev);
1121
1122 /* Wait here for the read thread to be initialized. */
1123 pthread_barrier_wait(&dev->barrier);
1124
1125 IOObjectRelease(entry);
1126 return dev;
1127
1128return_error:
1129 if (dev->device_handle != NULL)
1130 CFRelease(dev->device_handle);
1131
1132 if (entry != MACH_PORT_NULL)
1133 IOObjectRelease(entry);
1134
1135 free_hid_device(dev);
1136 return NULL;
1137}
1138
1139static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
1140{
1141 const unsigned char *data_to_send = data;
1142 CFIndex length_to_send = length;
1143 IOReturn res;
1144 unsigned char report_id;
1145
1146 register_device_error(dev, NULL);
1147
1148 if (!data || (length == 0)) {
1149 register_device_error(dev, strerror(EINVAL));
1150 return -1;
1151 }
1152
1153 report_id = data[0];
1154
1155 if (report_id == 0x0) {
1156 /* Not using numbered Reports.
1157 Don't send the report number. */
1158 data_to_send = data+1;
1159 length_to_send = length-1;
1160 }
1161
1162 /* Avoid crash if the device has been unplugged. */
1163 if (dev->disconnected) {
1164 register_device_error(dev, "Device is disconnected");
1165 return -1;
1166 }
1167
1168 res = IOHIDDeviceSetReport(dev->device_handle,
1169 type,
1170 report_id,
1171 data_to_send, length_to_send);
1172
1173 if (res != kIOReturnSuccess) {
1174 register_device_error_format(dev, "IOHIDDeviceSetReport failed: (0x%08X) %s", res, mach_error_string(res));
1175 return -1;
1176 }
1177
1178 return (int) length;
1179}
1180
1181static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length)
1182{
1183 unsigned char *report = data;
1184 CFIndex report_length = length;
1185 IOReturn res = kIOReturnSuccess;
1186 const unsigned char report_id = data[0];
1187
1188 register_device_error(dev, NULL);
1189
1190 if (report_id == 0x0) {
1191 /* Not using numbered Reports.
1192 Don't send the report number. */
1193 report = data+1;
1194 report_length = length-1;
1195 }
1196
1197 /* Avoid crash if the device has been unplugged. */
1198 if (dev->disconnected) {
1199 register_device_error(dev, "Device is disconnected");
1200 return -1;
1201 }
1202
1203 res = IOHIDDeviceGetReport(dev->device_handle,
1204 type,
1205 report_id,
1206 report, &report_length);
1207
1208 if (res != kIOReturnSuccess) {
1209 register_device_error_format(dev, "IOHIDDeviceGetReport failed: (0x%08X) %s", res, mach_error_string(res));
1210 return -1;
1211 }
1212
1213 if (report_id == 0x0) { /* 0 report number still present at the beginning */
1214 report_length++;
1215 }
1216
1217 return (int) report_length;
1218}
1219
1220int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
1221{
1222 return set_report(dev, kIOHIDReportTypeOutput, data, length);
1223}
1224
1225/* Helper function, so that this isn't duplicated in hid_read(). */
1226static int return_data(hid_device *dev, unsigned char *data, size_t length)
1227{
1228 /* Copy the data out of the linked list item (rpt) into the
1229 return buffer (data), and delete the liked list item. */
1230 struct input_report *rpt = dev->input_reports;
1231 size_t len = (length < rpt->len)? length: rpt->len;
1232 if (data != NULL) {
1233 memcpy(data, rpt->data, len);
1234 }
1235 dev->input_reports = rpt->next;
1236 free(rpt->data);
1237 free(rpt);
1238 return (int) len;
1239}
1240
1241static int cond_wait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
1242{
1243 while (!dev->input_reports) {
1244 int res = pthread_cond_wait(cond, mutex);
1245 if (res != 0)
1246 return res;
1247
1248 /* A res of 0 means we may have been signaled or it may
1249 be a spurious wakeup. Check to see that there's actually
1250 data in the queue before returning, and if not, go back
1251 to sleep. See the pthread_cond_timedwait() man page for
1252 details. */
1253
1254 if (dev->shutdown_thread || dev->disconnected) {
1255 return -1;
1256 }
1257 }
1258
1259 return 0;
1260}
1261
1262static int cond_timedwait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
1263{
1264 while (!dev->input_reports) {
1265 int res = pthread_cond_timedwait(cond, mutex, abstime);
1266 if (res != 0)
1267 return res;
1268
1269 /* A res of 0 means we may have been signaled or it may
1270 be a spurious wakeup. Check to see that there's actually
1271 data in the queue before returning, and if not, go back
1272 to sleep. See the pthread_cond_timedwait() man page for
1273 details. */
1274
1275 if (dev->shutdown_thread || dev->disconnected) {
1276 return -1;
1277 }
1278 }
1279
1280 return 0;
1281
1282}
1283
1284int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
1285{
1286 int bytes_read = -1;
1287
1288 /* Lock the access to the report list. */
1289 pthread_mutex_lock(&dev->mutex);
1290
1291 /* There's an input report queued up. Return it. */
1292 if (dev->input_reports) {
1293 /* Return the first one */
1294 bytes_read = return_data(dev, data, length);
1295 goto ret;
1296 }
1297
1298 /* Return if the device has been disconnected. */
1299 if (dev->disconnected) {
1300 bytes_read = -1;
1301 register_device_error(dev, "hid_read_timeout: device disconnected");
1302 goto ret;
1303 }
1304
1305 if (dev->shutdown_thread) {
1306 /* This means the device has been closed (or there
1307 has been an error. An error code of -1 should
1308 be returned. */
1309 bytes_read = -1;
1310 register_device_error(dev, "hid_read_timeout: thread shutdown");
1311 goto ret;
1312 }
1313
1314 /* There is no data. Go to sleep and wait for data. */
1315
1316 if (milliseconds == -1) {
1317 /* Blocking */
1318 int res;
1319 res = cond_wait(dev, &dev->condition, &dev->mutex);
1320 if (res == 0)
1321 bytes_read = return_data(dev, data, length);
1322 else {
1323 /* There was an error, or a device disconnection. */
1324 register_device_error(dev, "hid_read_timeout: error waiting for more data");
1325 bytes_read = -1;
1326 }
1327 }
1328 else if (milliseconds > 0) {
1329 /* Non-blocking, but called with timeout. */
1330 int res;
1331 struct timespec ts;
1332 struct timeval tv;
1333 gettimeofday(&tv, NULL);
1334 TIMEVAL_TO_TIMESPEC(&tv, &ts);
1335 ts.tv_sec += milliseconds / 1000;
1336 ts.tv_nsec += (milliseconds % 1000) * 1000000;
1337 if (ts.tv_nsec >= 1000000000L) {
1338 ts.tv_sec++;
1339 ts.tv_nsec -= 1000000000L;
1340 }
1341
1342 res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
1343 if (res == 0) {
1344 bytes_read = return_data(dev, data, length);
1345 } else if (res == ETIMEDOUT) {
1346 bytes_read = 0;
1347 } else {
1348 register_device_error(dev, "hid_read_timeout: error waiting for more data");
1349 bytes_read = -1;
1350 }
1351 }
1352 else {
1353 /* Purely non-blocking */
1354 bytes_read = 0;
1355 }
1356
1357ret:
1358 /* Unlock */
1359 pthread_mutex_unlock(&dev->mutex);
1360 return bytes_read;
1361}
1362
1363int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
1364{
1365 return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
1366}
1367
1368int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
1369{
1370 /* All Nonblocking operation is handled by the library. */
1371 dev->blocking = !nonblock;
1372
1373 return 0;
1374}
1375
1376int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
1377{
1378 return set_report(dev, kIOHIDReportTypeFeature, data, length);
1379}
1380
1381int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
1382{
1383 return get_report(dev, kIOHIDReportTypeFeature, data, length);
1384}
1385
1386int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
1387{
1388 return get_report(dev, kIOHIDReportTypeInput, data, length);
1389}
1390
1391void HID_API_EXPORT hid_close(hid_device *dev)
1392{
1393 if (!dev)
1394 return;
1395
1396 /* Disconnect the report callback before close.
1397 See comment below.
1398 */
1399 if (is_macos_10_10_or_greater || !dev->disconnected) {
1400 IOHIDDeviceRegisterInputReportCallback(
1401 dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
1402 NULL, dev);
1403 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev);
1404 IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
1405 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
1406 }
1407
1408 /* Cause read_thread() to stop. */
1409 dev->shutdown_thread = 1;
1410
1411 /* Wake up the run thread's event loop so that the thread can exit. */
1412 CFRunLoopSourceSignal(dev->source);
1413 CFRunLoopWakeUp(dev->run_loop);
1414
1415 /* Notify the read thread that it can shut down now. */
1416 pthread_barrier_wait(&dev->shutdown_barrier);
1417
1418 /* Wait for read_thread() to end. */
1419 pthread_join(dev->thread, NULL);
1420
1421 /* Close the OS handle to the device, but only if it's not
1422 been unplugged. If it's been unplugged, then calling
1423 IOHIDDeviceClose() will crash.
1424
1425 UPD: The crash part was true in/until some version of macOS.
1426 Starting with macOS 10.15, there is an opposite effect in some environments:
1427 crash happenes if IOHIDDeviceClose() is not called.
1428 Not leaking a resource in all tested environments.
1429 */
1430 if (is_macos_10_10_or_greater || !dev->disconnected) {
1431 IOHIDDeviceClose(dev->device_handle, dev->open_options);
1432 }
1433
1434 /* Clear out the queue of received reports. */
1435 pthread_mutex_lock(&dev->mutex);
1436 while (dev->input_reports) {
1437 return_data(dev, NULL, 0);
1438 }
1439 pthread_mutex_unlock(&dev->mutex);
1440 CFRelease(dev->device_handle);
1441
1442 free_hid_device(dev);
1443}
1444
1445int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
1446{
1447 if (!string || !maxlen)
1448 {
1449 register_device_error(dev, "Zero buffer/length");
1450 return -1;
1451 }
1452
1453 struct hid_device_info *info = hid_get_device_info(dev);
1454 if (!info)
1455 {
1456 // hid_get_device_info will have set an error already
1457 return -1;
1458 }
1459
1460 wcsncpy(string, info->manufacturer_string, maxlen);
1461 string[maxlen - 1] = L'\0';
1462
1463 return 0;
1464}
1465
1466int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
1467{
1468 if (!string || !maxlen) {
1469 register_device_error(dev, "Zero buffer/length");
1470 return -1;
1471 }
1472
1473 struct hid_device_info *info = hid_get_device_info(dev);
1474 if (!info) {
1475 // hid_get_device_info will have set an error already
1476 return -1;
1477 }
1478
1479 wcsncpy(string, info->product_string, maxlen);
1480 string[maxlen - 1] = L'\0';
1481
1482 return 0;
1483}
1484
1485int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
1486{
1487 if (!string || !maxlen) {
1488 register_device_error(dev, "Zero buffer/length");
1489 return -1;
1490 }
1491
1492 struct hid_device_info *info = hid_get_device_info(dev);
1493 if (!info) {
1494 // hid_get_device_info will have set an error already
1495 return -1;
1496 }
1497
1498 wcsncpy(string, info->serial_number, maxlen);
1499 string[maxlen - 1] = L'\0';
1500
1501 return 0;
1502}
1503
1504HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) {
1505 if (!dev->device_info) {
1506 dev->device_info = create_device_info(dev->device_handle);
1507 if (!dev->device_info) {
1508 register_device_error(dev, "Failed to create hid_device_info");
1509 }
1510 }
1511
1512 return dev->device_info;
1513}
1514
1515int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
1516{
1517 (void) dev;
1518 (void) string_index;
1519 (void) string;
1520 (void) maxlen;
1521
1522 register_device_error(dev, "hid_get_indexed_string: not available on this platform");
1523 return -1;
1524}
1525
1526int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id)
1527{
1528 int res = get_int_property(dev->device_handle, CFSTR(kIOHIDLocationIDKey));
1529 if (res != 0) {
1530 *location_id = (uint32_t) res;
1531 return 0;
1532 } else {
1533 register_device_error(dev, "Failed to get IOHIDLocationID property");
1534 return -1;
1535 }
1536}
1537
1538void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive)
1539{
1540 device_open_options = (open_exclusive == 0) ? kIOHIDOptionsTypeNone : kIOHIDOptionsTypeSeizeDevice;
1541}
1542
1543int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void)
1544{
1545 return (device_open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
1546}
1547
1548int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev)
1549{
1550 if (!dev)
1551 return -1;
1552
1553 return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
1554}
1555
1556int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
1557{
1558 CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey));
1559 if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) {
1560 CFDataRef report_descriptor = (CFDataRef) ref;
1561 const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor);
1562 CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor);
1563 size_t copy_len = (size_t) descriptor_buf_len;
1564
1565 if (descriptor_buf == NULL || descriptor_buf_len < 0) {
1566 register_device_error(dev, "Zero buffer/length");
1567 return -1;
1568 }
1569
1570 if (buf_size < copy_len) {
1571 copy_len = buf_size;
1572 }
1573
1574 memcpy(buf, descriptor_buf, copy_len);
1575 return (int)copy_len;
1576 }
1577 else {
1578 register_device_error(dev, "Failed to get kIOHIDReportDescriptorKey property");
1579 return -1;
1580 }
1581}
1582
1583HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
1584{
1585 if (dev) {
1586 if (dev->last_error_str == NULL)
1587 return L"Success";
1588 return dev->last_error_str;
1589 }
1590
1591 if (last_global_error_str == NULL)
1592 return L"Success";
1593 return last_global_error_str;
1594}
diff --git a/contrib/SDL-3.2.8/src/hidapi/mac/hidapi_darwin.h b/contrib/SDL-3.2.8/src/hidapi/mac/hidapi_darwin.h
new file mode 100644
index 0000000..1465583
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/mac/hidapi_darwin.h
@@ -0,0 +1,98 @@
1/*******************************************************
2 HIDAPI - Multi-Platform library for
3 communication with HID devices.
4
5 libusb/hidapi Team
6
7 Copyright 2022, All Rights Reserved.
8
9 At the discretion of the user of this library,
10 this software may be licensed under the terms of the
11 GNU General Public License v3, a BSD-Style license, or the
12 original HIDAPI license as outlined in the LICENSE.txt,
13 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
14 files located at the root of the source distribution.
15 These files may also be found in the public source
16 code repository located at:
17 https://github.com/libusb/hidapi .
18********************************************************/
19
20/** @file
21 * @defgroup API hidapi API
22
23 * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
24 */
25
26#ifndef HIDAPI_DARWIN_H__
27#define HIDAPI_DARWIN_H__
28
29#include <stdint.h>
30
31#include "../hidapi/hidapi.h"
32
33#ifdef __cplusplus
34extern "C" {
35#endif
36
37 /** @brief Get the location ID for a HID device.
38
39 Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
40
41 @ingroup API
42 @param dev A device handle returned from hid_open().
43 @param location_id The device's location ID on return.
44
45 @returns
46 This function returns 0 on success and -1 on error.
47 */
48 int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id);
49
50
51 /** @brief Changes the behavior of all further calls to @ref hid_open or @ref hid_open_path.
52
53 By default on Darwin platform all devices opened by HIDAPI with @ref hid_open or @ref hid_open_path
54 are opened in exclusive mode (see kIOHIDOptionsTypeSeizeDevice).
55
56 Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
57
58 @ingroup API
59 @param open_exclusive When set to 0 - all further devices will be opened
60 in non-exclusive mode. Otherwise - all further devices will be opened
61 in exclusive mode.
62
63 @note During the initialisation by @ref hid_init - this property is set to 1 (TRUE).
64 This is done to preserve full backward compatibility with previous behavior.
65
66 @note Calling this function before @ref hid_init or after @ref hid_exit has no effect.
67 */
68 void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive);
69
70 /** @brief Getter for option set by @ref hid_darwin_set_open_exclusive.
71
72 Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
73
74 @ingroup API
75 @return 1 if all further devices will be opened in exclusive mode.
76
77 @note Value returned by this function before calling to @ref hid_init or after @ref hid_exit
78 is not reliable.
79 */
80 int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void);
81
82 /** @brief Check how the device was opened.
83
84 Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
85
86 @ingroup API
87 @param dev A device to get property from.
88
89 @return 1 if the device is opened in exclusive mode, 0 - opened in non-exclusive,
90 -1 - if dev is invalid.
91 */
92 int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev);
93
94#ifdef __cplusplus
95}
96#endif
97
98#endif