Merge lp:~hile/mixxx/hotplug into lp:~mixxxdevelopers/mixxx/trunk
- hotplug
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~hile/mixxx/hotplug |
Merge into: | lp:~mixxxdevelopers/mixxx/trunk |
Diff against target: |
1973 lines (+745/-312) 17 files modified
mixxx/build/features.py (+2/-2) mixxx/lib/hidapi/hidapi/hidapi.h (+1/-1) mixxx/lib/hidapi/linux/hid-libusb.c (+85/-60) mixxx/lib/hidapi/linux/hid.c (+296/-100) mixxx/lib/hidapi/mac/hid.c (+68/-38) mixxx/lib/hidapi/windows/Makefile.mingw (+5/-2) mixxx/lib/hidapi/windows/hid.c (+81/-34) mixxx/src/controllers/controller.h (+8/-0) mixxx/src/controllers/controllermanager.cpp (+116/-55) mixxx/src/controllers/controllermanager.h (+9/-4) mixxx/src/controllers/hid/hidcontroller.cpp (+12/-2) mixxx/src/controllers/hid/hidcontroller.h (+2/-0) mixxx/src/controllers/hid/hidenumerator.cpp (+46/-11) mixxx/src/controllers/hid/hidenumerator.h (+1/-0) mixxx/src/controllers/midi/portmidicontroller.cpp (+7/-0) mixxx/src/controllers/midi/portmidienumerator.cpp (+5/-2) mixxx/src/mixxx.cpp (+1/-1) |
To merge this branch: | bzr merge lp:~hile/mixxx/hotplug |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mixxx Development Team | Pending | ||
Review via email: mp+118293@code.launchpad.net |
Commit message
Description of the change
This branch changes controller manager device setup code so that the device setup function can be called on running system to implement hotplugging of controllers, and adds basic support to implement hotplugging in the backends.
It also implements HID hotplugging, removing and adding devices with a timer when devices are disconnected and connected. I have been running this hotplug code now for months, and after I fixed an error in the controller manager (preset was loaded every time changes were detected, causing multiple instances of same preset), it now seems to be very solid for HID devices.
For MIDI devices, the portmidi enumerator is changed to just return if devices were already loaded: until portmidi supports hotplug or we implement a new MIDI backend with something else than portmidi, this is the only thing we can do for MIDI.
If this is considered too late for 1.11, please merge the code at least to trunk.
- 3293. By Ilkka Tuohela
-
Merge from trunk
Unmerged revisions
- 3293. By Ilkka Tuohela
-
Merge from trunk
- 3292. By Ilkka Tuohela
-
Merge from trunk
- 3291. By Ilkka Tuohela
-
Import changes to HID API
Preview Diff
1 | === modified file 'mixxx/build/features.py' |
2 | --- mixxx/build/features.py 2012-07-08 07:30:32 +0000 |
3 | +++ mixxx/build/features.py 2012-08-26 04:54:21 +0000 |
4 | @@ -51,7 +51,7 @@ |
5 | 'controllers/midi/hss1394enumerator.cpp'] |
6 | |
7 | class HID(Feature): |
8 | - HIDAPI_INTERNAL_PATH = '#lib/hidapi-0.7.0' |
9 | + HIDAPI_INTERNAL_PATH = '#lib/hidapi' |
10 | def description(self): |
11 | return "HID controller support" |
12 | |
13 | @@ -97,7 +97,7 @@ |
14 | if build.platform_is_windows: |
15 | # Requires setupapi.lib which is included by the above check for |
16 | # setupapi. |
17 | - sources.append("#lib/hidapi-0.7.0/windows/hid.c") |
18 | + sources.append("#lib/hidapi/windows/hid.c") |
19 | return sources |
20 | elif build.platform_is_linux: |
21 | sources.append(os.path.join(self.HIDAPI_INTERNAL_PATH, 'linux/hid-libusb.c')) |
22 | |
23 | === renamed directory 'mixxx/lib/hidapi-0.7.0' => 'mixxx/lib/hidapi' |
24 | === modified file 'mixxx/lib/hidapi/hidapi/hidapi.h' |
25 | --- mixxx/lib/hidapi-0.7.0/hidapi/hidapi.h 2012-01-28 09:23:48 +0000 |
26 | +++ mixxx/lib/hidapi/hidapi/hidapi.h 2012-08-26 04:54:21 +0000 |
27 | @@ -155,7 +155,7 @@ |
28 | This function returns a pointer to a #hid_device object on |
29 | success or NULL on failure. |
30 | */ |
31 | - HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number); |
32 | + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); |
33 | |
34 | /** @brief Open a HID device by its path name. |
35 | |
36 | |
37 | === modified file 'mixxx/lib/hidapi/linux/hid-libusb.c' |
38 | --- mixxx/lib/hidapi-0.7.0/linux/hid-libusb.c 2012-05-12 10:43:43 +0000 |
39 | +++ mixxx/lib/hidapi/linux/hid-libusb.c 2012-08-26 04:54:21 +0000 |
40 | @@ -8,6 +8,7 @@ |
41 | 8/22/2009 |
42 | Linux Version - 6/2/2010 |
43 | Libusb Version - 8/13/2010 |
44 | + FreeBSD Version - 11/1/2011 |
45 | |
46 | Copyright 2009, All Rights Reserved. |
47 | |
48 | @@ -58,11 +59,15 @@ |
49 | #define LOG(...) do {} while (0) |
50 | #endif |
51 | |
52 | +#ifndef __FreeBSD__ |
53 | +#define DETACH_KERNEL_DRIVER |
54 | +#endif |
55 | |
56 | /* Uncomment to enable the retrieval of Usage and Usage Page in |
57 | -hid_enumerate(). Warning, this is very invasive as it requires the detach |
58 | +hid_enumerate(). Warning, on platforms different from FreeBSD |
59 | +this is very invasive as it requires the detach |
60 | and re-attach of the kernel driver. See comments inside hid_enumerate(). |
61 | -Linux/libusb HIDAPI programs are encouraged to use the interface number |
62 | +libusb HIDAPI programs are encouraged to use the interface number |
63 | instead to differentiate between interfaces on a composite HID device. */ |
64 | /*#define INVASIVE_GET_USAGE*/ |
65 | |
66 | @@ -106,7 +111,7 @@ |
67 | struct input_report *input_reports; |
68 | }; |
69 | |
70 | -static int initialized = 0; |
71 | +static libusb_context *usb_context = NULL; |
72 | |
73 | uint16_t get_usb_code_for_current_locale(void); |
74 | static int return_data(hid_device *dev, unsigned char *data, size_t length); |
75 | @@ -114,18 +119,7 @@ |
76 | static hid_device *new_hid_device(void) |
77 | { |
78 | hid_device *dev = calloc(1, sizeof(hid_device)); |
79 | - dev->device_handle = NULL; |
80 | - dev->input_endpoint = 0; |
81 | - dev->output_endpoint = 0; |
82 | - dev->input_ep_max_packet_size = 0; |
83 | - dev->interface = 0; |
84 | - dev->manufacturer_index = 0; |
85 | - dev->product_index = 0; |
86 | - dev->serial_index = 0; |
87 | dev->blocking = 1; |
88 | - dev->shutdown_thread = 0; |
89 | - dev->transfer = NULL; |
90 | - dev->input_reports = NULL; |
91 | |
92 | pthread_mutex_init(&dev->mutex, NULL); |
93 | pthread_cond_init(&dev->condition, NULL); |
94 | @@ -146,7 +140,7 @@ |
95 | } |
96 | |
97 | #if 0 |
98 | -//TODO: Implement this funciton on Linux. |
99 | +//TODO: Implement this funciton on hidapi/libusb.. |
100 | static void register_error(hid_device *device, const char *op) |
101 | { |
102 | |
103 | @@ -255,6 +249,27 @@ |
104 | } |
105 | #endif // INVASIVE_GET_USAGE |
106 | |
107 | +#ifdef __FreeBSD__ |
108 | +/* The FreeBSD version of libusb doesn't have this funciton. In mainline |
109 | + libusb, it's inlined in libusb.h. This function will bear a striking |
110 | + resemblence to that one, because there's about one way to code it. |
111 | + |
112 | + Note that the data parameter is Unicode in UTF-16LE encoding. |
113 | + Return value is the number of bytes in data, or LIBUSB_ERROR_*. |
114 | + */ |
115 | +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, |
116 | + uint8_t descriptor_index, uint16_t lang_id, |
117 | + unsigned char *data, int length) |
118 | +{ |
119 | + return libusb_control_transfer(dev, |
120 | + LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ |
121 | + LIBUSB_REQUEST_GET_DESCRIPTOR, |
122 | + (LIBUSB_DT_STRING << 8) | descriptor_index, |
123 | + lang_id, data, (uint16_t) length, 1000); |
124 | +} |
125 | + |
126 | +#endif |
127 | + |
128 | |
129 | /* Get the first language the device says it reports. This comes from |
130 | USB string #0. */ |
131 | @@ -317,7 +332,11 @@ |
132 | size_t inbytes; |
133 | size_t outbytes; |
134 | size_t res; |
135 | +#ifdef __FreeBSD__ |
136 | + const char *inptr; |
137 | +#else |
138 | char *inptr; |
139 | +#endif |
140 | char *outptr; |
141 | |
142 | /* Determine which language to use. */ |
143 | @@ -335,25 +354,27 @@ |
144 | if (len < 0) |
145 | return NULL; |
146 | |
147 | - buf[sizeof(buf)-1] = '\0'; |
148 | - |
149 | - if (len+1 < sizeof(buf)) |
150 | - buf[len+1] = '\0'; |
151 | + /* buf does not need to be explicitly NULL-terminated because |
152 | + it is only passed into iconv() which does not need it. */ |
153 | |
154 | /* Initialize iconv. */ |
155 | - ic = iconv_open("UTF-32", "UTF-16"); |
156 | - if (ic == (iconv_t)-1) |
157 | + ic = iconv_open("WCHAR_T", "UTF-16LE"); |
158 | + if (ic == (iconv_t)-1) { |
159 | + LOG("iconv_open() failed\n"); |
160 | return NULL; |
161 | + } |
162 | |
163 | - /* Convert to UTF-32 (wchar_t on glibc systems). |
164 | + /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). |
165 | Skip the first character (2-bytes). */ |
166 | inptr = buf+2; |
167 | inbytes = len-2; |
168 | outptr = (char*) wbuf; |
169 | outbytes = sizeof(wbuf); |
170 | res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); |
171 | - if (res == (size_t)-1) |
172 | + if (res == (size_t)-1) { |
173 | + LOG("iconv() failed\n"); |
174 | goto err; |
175 | + } |
176 | |
177 | /* Write the terminating NULL. */ |
178 | wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; |
179 | @@ -361,7 +382,7 @@ |
180 | *((wchar_t*)outptr) = 0x00000000; |
181 | |
182 | /* Allocate and copy the string. */ |
183 | - str = wcsdup(wbuf+1); |
184 | + str = wcsdup(wbuf); |
185 | |
186 | err: |
187 | iconv_close(ic); |
188 | @@ -384,10 +405,17 @@ |
189 | |
190 | int HID_API_EXPORT hid_init(void) |
191 | { |
192 | - if (!initialized) { |
193 | - if (libusb_init(NULL)) |
194 | + if (!usb_context) { |
195 | + const char *locale; |
196 | + |
197 | + /* Init Libusb */ |
198 | + if (libusb_init(&usb_context)) |
199 | return -1; |
200 | - initialized = 1; |
201 | + |
202 | + /* Set the locale if it's not set. */ |
203 | + locale = setlocale(LC_CTYPE, NULL); |
204 | + if (!locale) |
205 | + setlocale(LC_CTYPE, ""); |
206 | } |
207 | |
208 | return 0; |
209 | @@ -395,9 +423,9 @@ |
210 | |
211 | int HID_API_EXPORT hid_exit(void) |
212 | { |
213 | - if (initialized) { |
214 | - libusb_exit(NULL); |
215 | - initialized = 0; |
216 | + if (usb_context) { |
217 | + libusb_exit(usb_context); |
218 | + usb_context = NULL; |
219 | } |
220 | |
221 | return 0; |
222 | @@ -414,12 +442,9 @@ |
223 | struct hid_device_info *root = NULL; // return object |
224 | struct hid_device_info *cur_dev = NULL; |
225 | |
226 | - setlocale(LC_ALL,""); |
227 | - |
228 | - if (!initialized) |
229 | - hid_init(); |
230 | + hid_init(); |
231 | |
232 | - num_devs = libusb_get_device_list(NULL, &devs); |
233 | + num_devs = libusb_get_device_list(usb_context, &devs); |
234 | if (num_devs < 0) |
235 | return NULL; |
236 | while ((dev = devs[i++]) != NULL) { |
237 | @@ -499,9 +524,9 @@ |
238 | #if 0. For composite devices, use the interface |
239 | field in the hid_device_info struct to distinguish |
240 | between interfaces. */ |
241 | + unsigned char data[256]; |
242 | +#ifdef DETACH_KERNEL_DRIVER |
243 | int detached = 0; |
244 | - unsigned char data[256]; |
245 | - |
246 | /* Usage Page and Usage */ |
247 | res = libusb_kernel_driver_active(handle, interface_num); |
248 | if (res == 1) { |
249 | @@ -511,6 +536,7 @@ |
250 | else |
251 | detached = 1; |
252 | } |
253 | +#endif |
254 | res = libusb_claim_interface(handle, interface_num); |
255 | if (res >= 0) { |
256 | /* Get the HID Report Descriptor. */ |
257 | @@ -533,14 +559,16 @@ |
258 | } |
259 | else |
260 | LOG("Can't claim interface %d\n", res); |
261 | - |
262 | +#ifdef DETACH_KERNEL_DRIVER |
263 | /* Re-attach kernel driver if necessary. */ |
264 | if (detached) { |
265 | res = libusb_attach_kernel_driver(handle, interface_num); |
266 | if (res < 0) |
267 | LOG("Couldn't re-attach kernel driver.\n"); |
268 | } |
269 | -#endif /*******************/ |
270 | +#endif |
271 | + |
272 | +#endif // INVASIVE_GET_USAGE |
273 | |
274 | libusb_close(handle); |
275 | } |
276 | @@ -580,7 +608,7 @@ |
277 | } |
278 | } |
279 | |
280 | -hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) |
281 | +hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) |
282 | { |
283 | struct hid_device_info *devs, *cur_dev; |
284 | const char *path_to_open = NULL; |
285 | @@ -618,6 +646,7 @@ |
286 | static void read_callback(struct libusb_transfer *transfer) |
287 | { |
288 | hid_device *dev = transfer->user_data; |
289 | + int res; |
290 | |
291 | if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { |
292 | |
293 | @@ -670,7 +699,11 @@ |
294 | } |
295 | |
296 | /* Re-submit the transfer object. */ |
297 | - libusb_submit_transfer(transfer); |
298 | + res = libusb_submit_transfer(transfer); |
299 | + if (res != 0) { |
300 | + LOG("Unable to submit URB. libusb error code: %d\n", res); |
301 | + dev->shutdown_thread = 1; |
302 | + } |
303 | } |
304 | |
305 | |
306 | @@ -702,21 +735,16 @@ |
307 | /* Handle all the events. */ |
308 | while (!dev->shutdown_thread) { |
309 | int res; |
310 | - res = libusb_handle_events(NULL); |
311 | + res = libusb_handle_events(usb_context); |
312 | if (res < 0) { |
313 | /* There was an error. */ |
314 | - LOG("libusb reports error # %d\n", res); |
315 | - /* Should call libusb_error_name() to get the string version but |
316 | - the compiler doesn't recognize that function for some reason */ |
317 | + LOG("read_thread(): libusb reports error # %d\n", res); |
318 | |
319 | - /* Break out of this loop only on fatal error conditions. |
320 | - See the list at: |
321 | - http://libusb.sourceforge.net/api-1.0/group__misc.html#gab2323aa0f04bc22038e7e1740b2f29ef |
322 | - */ |
323 | + /* Break out of this loop only on fatal error.*/ |
324 | if (res != LIBUSB_ERROR_BUSY && |
325 | - res != LIBUSB_ERROR_TIMEOUT && |
326 | - res != LIBUSB_ERROR_OVERFLOW && |
327 | - res != LIBUSB_ERROR_INTERRUPTED) { |
328 | + res != LIBUSB_ERROR_TIMEOUT && |
329 | + res != LIBUSB_ERROR_OVERFLOW && |
330 | + res != LIBUSB_ERROR_INTERRUPTED) { |
331 | break; |
332 | } |
333 | } |
334 | @@ -726,7 +754,7 @@ |
335 | if no transfers are pending, but that's OK. */ |
336 | if (libusb_cancel_transfer(dev->transfer) == 0) { |
337 | /* The transfer was cancelled, so wait for its completion. */ |
338 | - libusb_handle_events(NULL); |
339 | + libusb_handle_events(usb_context); |
340 | } |
341 | |
342 | /* Now that the read thread is stopping, Wake any threads which are |
343 | @@ -763,12 +791,9 @@ |
344 | int d = 0; |
345 | int good_open = 0; |
346 | |
347 | - setlocale(LC_ALL,""); |
348 | - |
349 | - if (!initialized) |
350 | - hid_init(); |
351 | + hid_init(); |
352 | |
353 | - num_devs = libusb_get_device_list(NULL, &devs); |
354 | + num_devs = libusb_get_device_list(usb_context, &devs); |
355 | while ((usb_dev = devs[d++]) != NULL) { |
356 | struct libusb_device_descriptor desc; |
357 | struct libusb_config_descriptor *conf_desc = NULL; |
358 | @@ -795,7 +820,7 @@ |
359 | break; |
360 | } |
361 | good_open = 1; |
362 | - |
363 | +#ifdef DETACH_KERNEL_DRIVER |
364 | /* Detach the kernel driver, but only if the |
365 | device is managed by the kernel */ |
366 | if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { |
367 | @@ -808,7 +833,7 @@ |
368 | break; |
369 | } |
370 | } |
371 | - |
372 | +#endif |
373 | res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); |
374 | if (res < 0) { |
375 | LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); |
376 | |
377 | === modified file 'mixxx/lib/hidapi/linux/hid.c' |
378 | --- mixxx/lib/hidapi-0.7.0/linux/hid.c 2012-01-28 09:23:48 +0000 |
379 | +++ mixxx/lib/hidapi/linux/hid.c 2012-08-26 04:54:21 +0000 |
380 | @@ -40,6 +40,7 @@ |
381 | /* Linux */ |
382 | #include <linux/hidraw.h> |
383 | #include <linux/version.h> |
384 | +#include <linux/input.h> |
385 | #include <libudev.h> |
386 | |
387 | #include "hidapi.h" |
388 | @@ -53,6 +54,23 @@ |
389 | #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) |
390 | #endif |
391 | |
392 | + |
393 | +/* USB HID device property names */ |
394 | +const char *device_string_names[] = { |
395 | + "manufacturer", |
396 | + "product", |
397 | + "serial", |
398 | +}; |
399 | + |
400 | +/* Symbolic names for the properties above */ |
401 | +enum device_string_id { |
402 | + DEVICE_STRING_MANUFACTURER, |
403 | + DEVICE_STRING_PRODUCT, |
404 | + DEVICE_STRING_SERIAL, |
405 | + |
406 | + DEVICE_STRING_COUNT, |
407 | +}; |
408 | + |
409 | struct hid_device_ { |
410 | int device_handle; |
411 | int blocking; |
412 | @@ -77,22 +95,29 @@ |
413 | |
414 | } |
415 | |
416 | +/* The caller must free the returned string with free(). */ |
417 | +static wchar_t *utf8_to_wchar_t(const char *utf8) |
418 | +{ |
419 | + wchar_t *ret = NULL; |
420 | + |
421 | + if (utf8) { |
422 | + size_t wlen = mbstowcs(NULL, utf8, 0); |
423 | + if (wlen < 0) { |
424 | + return wcsdup(L""); |
425 | + } |
426 | + ret = calloc(wlen+1, sizeof(wchar_t)); |
427 | + mbstowcs(ret, utf8, wlen+1); |
428 | + ret[wlen] = 0x0000; |
429 | + } |
430 | + |
431 | + return ret; |
432 | +} |
433 | + |
434 | /* Get an attribute value from a udev_device and return it as a whar_t |
435 | string. The returned string must be freed with free() when done.*/ |
436 | static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) |
437 | { |
438 | - const char *str; |
439 | - wchar_t *ret = NULL; |
440 | - str = udev_device_get_sysattr_value(dev, udev_name); |
441 | - if (str) { |
442 | - /* Convert the string from UTF-8 to wchar_t */ |
443 | - size_t wlen = mbstowcs(NULL, str, 0); |
444 | - ret = calloc(wlen+1, sizeof(wchar_t)); |
445 | - mbstowcs(ret, str, wlen+1); |
446 | - ret[wlen] = 0x0000; |
447 | - } |
448 | - |
449 | - return ret; |
450 | + return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); |
451 | } |
452 | |
453 | /* uses_numbered_reports() returns 1 if report_descriptor describes a device |
454 | @@ -157,14 +182,70 @@ |
455 | return 0; |
456 | } |
457 | |
458 | -static int get_device_string(hid_device *dev, const char *key, wchar_t *string, size_t maxlen) |
459 | +/* |
460 | + * The caller is responsible for free()ing the (newly-allocated) character |
461 | + * strings pointed to by serial_number_utf8 and product_name_utf8 after use. |
462 | + */ |
463 | +int |
464 | +parse_uevent_info(const char *uevent, int *bus_type, |
465 | + unsigned short *vendor_id, unsigned short *product_id, |
466 | + char **serial_number_utf8, char **product_name_utf8) |
467 | +{ |
468 | + char *tmp = strdup(uevent); |
469 | + char *saveptr = NULL; |
470 | + char *line; |
471 | + char *key; |
472 | + char *value; |
473 | + |
474 | + int found_id = 0; |
475 | + int found_serial = 0; |
476 | + int found_name = 0; |
477 | + |
478 | + line = strtok_r(tmp, "\n", &saveptr); |
479 | + while (line != NULL) { |
480 | + /* line: "KEY=value" */ |
481 | + key = line; |
482 | + value = strchr(line, '='); |
483 | + if (!value) { |
484 | + goto next_line; |
485 | + } |
486 | + *value = '\0'; |
487 | + value++; |
488 | + |
489 | + if (strcmp(key, "HID_ID") == 0) { |
490 | + /** |
491 | + * type vendor product |
492 | + * HID_ID=0003:000005AC:00008242 |
493 | + **/ |
494 | + int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); |
495 | + if (ret == 3) { |
496 | + found_id = 1; |
497 | + } |
498 | + } else if (strcmp(key, "HID_NAME") == 0) { |
499 | + /* The caller has to free the product name */ |
500 | + *product_name_utf8 = strdup(value); |
501 | + found_name = 1; |
502 | + } else if (strcmp(key, "HID_UNIQ") == 0) { |
503 | + /* The caller has to free the serial number */ |
504 | + *serial_number_utf8 = strdup(value); |
505 | + found_serial = 1; |
506 | + } |
507 | + |
508 | +next_line: |
509 | + line = strtok_r(NULL, "\n", &saveptr); |
510 | + } |
511 | + |
512 | + free(tmp); |
513 | + return (found_id && found_name && found_serial); |
514 | +} |
515 | + |
516 | + |
517 | +static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen) |
518 | { |
519 | struct udev *udev; |
520 | - struct udev_device *udev_dev, *parent; |
521 | + struct udev_device *udev_dev, *parent, *hid_dev; |
522 | struct stat s; |
523 | int ret = -1; |
524 | - |
525 | - setlocale(LC_ALL,""); |
526 | |
527 | /* Create the udev object */ |
528 | udev = udev_new(); |
529 | @@ -178,26 +259,80 @@ |
530 | /* Open a udev device from the dev_t. 'c' means character device. */ |
531 | udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); |
532 | if (udev_dev) { |
533 | - const char *str; |
534 | - /* Find the parent USB Device */ |
535 | - parent = udev_device_get_parent_with_subsystem_devtype( |
536 | - udev_dev, |
537 | - "usb", |
538 | - "usb_device"); |
539 | - if (parent) { |
540 | - str = udev_device_get_sysattr_value(parent, key); |
541 | - if (str) { |
542 | - /* Convert the string from UTF-8 to wchar_t */ |
543 | - ret = mbstowcs(string, str, maxlen); |
544 | - goto end; |
545 | - } |
546 | + hid_dev = udev_device_get_parent_with_subsystem_devtype( |
547 | + udev_dev, |
548 | + "hid", |
549 | + NULL); |
550 | + if (hid_dev) { |
551 | + unsigned short dev_vid; |
552 | + unsigned short dev_pid; |
553 | + char *serial_number_utf8 = NULL; |
554 | + char *product_name_utf8 = NULL; |
555 | + int bus_type; |
556 | + |
557 | + ret = parse_uevent_info( |
558 | + udev_device_get_sysattr_value(hid_dev, "uevent"), |
559 | + &bus_type, |
560 | + &dev_vid, |
561 | + &dev_pid, |
562 | + &serial_number_utf8, |
563 | + &product_name_utf8); |
564 | + |
565 | + if (bus_type == BUS_BLUETOOTH) { |
566 | + switch (key) { |
567 | + case DEVICE_STRING_MANUFACTURER: |
568 | + wcsncpy(string, L"", maxlen); |
569 | + ret = 0; |
570 | + break; |
571 | + case DEVICE_STRING_PRODUCT: |
572 | + ret = mbstowcs(string, product_name_utf8, maxlen); |
573 | + ret = (ret == (size_t)-1)? -1: 0; |
574 | + break; |
575 | + case DEVICE_STRING_SERIAL: |
576 | + ret = mbstowcs(string, serial_number_utf8, maxlen); |
577 | + ret = (ret == (size_t)-1)? -1: 0; |
578 | + break; |
579 | + default: |
580 | + ret = -1; |
581 | + break; |
582 | + } |
583 | + } |
584 | + else { |
585 | + /* This is a USB device. Find its parent USB Device node. */ |
586 | + parent = udev_device_get_parent_with_subsystem_devtype( |
587 | + udev_dev, |
588 | + "usb", |
589 | + "usb_device"); |
590 | + if (parent) { |
591 | + const char *str; |
592 | + const char *key_str = NULL; |
593 | + |
594 | + if (key >= 0 && key < DEVICE_STRING_COUNT) { |
595 | + key_str = device_string_names[key]; |
596 | + } else { |
597 | + ret = -1; |
598 | + goto end; |
599 | + } |
600 | + |
601 | + str = udev_device_get_sysattr_value(parent, key_str); |
602 | + if (str) { |
603 | + /* Convert the string from UTF-8 to wchar_t */ |
604 | + ret = mbstowcs(string, str, maxlen); |
605 | + ret = (ret == (size_t)-1)? -1: 0; |
606 | + goto end; |
607 | + } |
608 | + } |
609 | + } |
610 | + |
611 | + free(serial_number_utf8); |
612 | + free(product_name_utf8); |
613 | } |
614 | } |
615 | |
616 | end: |
617 | udev_device_unref(udev_dev); |
618 | - // parent doesn't need to be (and can't be) unref'd. |
619 | - // I'm not sure why, but it'll throw double-free() errors. |
620 | + // parent and hid_dev don't need to be (and can't be) unref'd. |
621 | + // I'm not sure why, but they'll throw double-free() errors. |
622 | udev_unref(udev); |
623 | |
624 | return ret; |
625 | @@ -205,7 +340,13 @@ |
626 | |
627 | int HID_API_EXPORT hid_init(void) |
628 | { |
629 | - /* Nothing to do for this in the Linux/hidraw implementation. */ |
630 | + const char *locale; |
631 | + |
632 | + /* Set the locale if it's not set. */ |
633 | + locale = setlocale(LC_CTYPE, NULL); |
634 | + if (!locale) |
635 | + setlocale(LC_CTYPE, ""); |
636 | + |
637 | return 0; |
638 | } |
639 | |
640 | @@ -215,6 +356,7 @@ |
641 | return 0; |
642 | } |
643 | |
644 | + |
645 | struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) |
646 | { |
647 | struct udev *udev; |
648 | @@ -223,8 +365,9 @@ |
649 | |
650 | struct hid_device_info *root = NULL; // return object |
651 | struct hid_device_info *cur_dev = NULL; |
652 | - |
653 | - setlocale(LC_ALL,""); |
654 | + struct hid_device_info *prev_dev = NULL; // previous device |
655 | + |
656 | + hid_init(); |
657 | |
658 | /* Create the udev object */ |
659 | udev = udev_new(); |
660 | @@ -244,46 +387,57 @@ |
661 | const char *sysfs_path; |
662 | const char *dev_path; |
663 | const char *str; |
664 | + struct udev_device *raw_dev; // The device's hidraw udev node. |
665 | struct udev_device *hid_dev; // The device's HID udev node. |
666 | - struct udev_device *dev; // The actual hardware device. |
667 | + struct udev_device *usb_dev; // The device's USB udev node. |
668 | struct udev_device *intf_dev; // The device's interface (in the USB sense). |
669 | unsigned short dev_vid; |
670 | unsigned short dev_pid; |
671 | - |
672 | + char *serial_number_utf8 = NULL; |
673 | + char *product_name_utf8 = NULL; |
674 | + int bus_type; |
675 | + int result; |
676 | + |
677 | /* Get the filename of the /sys entry for the device |
678 | and create a udev_device object (dev) representing it */ |
679 | sysfs_path = udev_list_entry_get_name(dev_list_entry); |
680 | - hid_dev = udev_device_new_from_syspath(udev, sysfs_path); |
681 | - dev_path = udev_device_get_devnode(hid_dev); |
682 | - |
683 | - /* The device pointed to by hid_dev contains information about |
684 | - the hidraw device. In order to get information about the |
685 | - USB device, get the parent device with the |
686 | - subsystem/devtype pair of "usb"/"usb_device". This will |
687 | - be several levels up the tree, but the function will find |
688 | - it.*/ |
689 | - dev = udev_device_get_parent_with_subsystem_devtype( |
690 | - hid_dev, |
691 | - "usb", |
692 | - "usb_device"); |
693 | - if (!dev) { |
694 | - /* Unable to find parent usb device. */ |
695 | - goto next; |
696 | - } |
697 | - |
698 | - /* Get the VID/PID of the device */ |
699 | - str = udev_device_get_sysattr_value(dev,"idVendor"); |
700 | - dev_vid = (str)? strtol(str, NULL, 16): 0x0; |
701 | - str = udev_device_get_sysattr_value(dev, "idProduct"); |
702 | - dev_pid = (str)? strtol(str, NULL, 16): 0x0; |
703 | + raw_dev = udev_device_new_from_syspath(udev, sysfs_path); |
704 | + dev_path = udev_device_get_devnode(raw_dev); |
705 | + |
706 | + hid_dev = udev_device_get_parent_with_subsystem_devtype( |
707 | + raw_dev, |
708 | + "hid", |
709 | + NULL); |
710 | + |
711 | + if (!hid_dev) { |
712 | + /* Unable to find parent hid device. */ |
713 | + goto next; |
714 | + } |
715 | + |
716 | + result = parse_uevent_info( |
717 | + udev_device_get_sysattr_value(hid_dev, "uevent"), |
718 | + &bus_type, |
719 | + &dev_vid, |
720 | + &dev_pid, |
721 | + &serial_number_utf8, |
722 | + &product_name_utf8); |
723 | + |
724 | + if (!result) { |
725 | + /* parse_uevent_info() failed for at least one field. */ |
726 | + goto next; |
727 | + } |
728 | + |
729 | + if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) { |
730 | + /* We only know how to handle USB and BT devices. */ |
731 | + goto next; |
732 | + } |
733 | |
734 | /* Check the VID/PID against the arguments */ |
735 | if ((vendor_id == 0x0 && product_id == 0x0) || |
736 | (vendor_id == dev_vid && product_id == dev_pid)) { |
737 | struct hid_device_info *tmp; |
738 | - size_t len; |
739 | |
740 | - /* VID/PID match. Create the record. */ |
741 | + /* VID/PID match. Create the record. */ |
742 | tmp = malloc(sizeof(struct hid_device_info)); |
743 | if (cur_dev) { |
744 | cur_dev->next = tmp; |
745 | @@ -291,56 +445,96 @@ |
746 | else { |
747 | root = tmp; |
748 | } |
749 | + prev_dev = cur_dev; |
750 | cur_dev = tmp; |
751 | - |
752 | + |
753 | /* Fill out the record */ |
754 | cur_dev->next = NULL; |
755 | - str = dev_path; |
756 | - if (str) { |
757 | - len = strlen(str); |
758 | - cur_dev->path = calloc(len+1, sizeof(char)); |
759 | - strncpy(cur_dev->path, str, len+1); |
760 | - cur_dev->path[len] = '\0'; |
761 | - } |
762 | - else |
763 | - cur_dev->path = NULL; |
764 | - |
765 | - /* Serial Number */ |
766 | - cur_dev->serial_number |
767 | - = copy_udev_string(dev, "serial"); |
768 | + cur_dev->path = dev_path? strdup(dev_path): NULL; |
769 | |
770 | - /* Manufacturer and Product strings */ |
771 | - cur_dev->manufacturer_string |
772 | - = copy_udev_string(dev, "manufacturer"); |
773 | - cur_dev->product_string |
774 | - = copy_udev_string(dev, "product"); |
775 | - |
776 | /* VID/PID */ |
777 | cur_dev->vendor_id = dev_vid; |
778 | cur_dev->product_id = dev_pid; |
779 | |
780 | + /* Serial Number */ |
781 | + cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); |
782 | + |
783 | /* Release Number */ |
784 | - str = udev_device_get_sysattr_value(dev, "bcdDevice"); |
785 | - cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; |
786 | - |
787 | + cur_dev->release_number = 0x0; |
788 | + |
789 | /* Interface Number */ |
790 | cur_dev->interface_number = -1; |
791 | - /* Get a handle to the interface's udev node. */ |
792 | - intf_dev = udev_device_get_parent_with_subsystem_devtype( |
793 | - hid_dev, |
794 | - "usb", |
795 | - "usb_interface"); |
796 | - if (intf_dev) { |
797 | - str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); |
798 | - cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; |
799 | + |
800 | + switch (bus_type) { |
801 | + case BUS_USB: |
802 | + /* The device pointed to by raw_dev contains information about |
803 | + the hidraw device. In order to get information about the |
804 | + USB device, get the parent device with the |
805 | + subsystem/devtype pair of "usb"/"usb_device". This will |
806 | + be several levels up the tree, but the function will find |
807 | + it. */ |
808 | + usb_dev = udev_device_get_parent_with_subsystem_devtype( |
809 | + raw_dev, |
810 | + "usb", |
811 | + "usb_device"); |
812 | + |
813 | + if (!usb_dev) { |
814 | + /* Free this device */ |
815 | + free(cur_dev->serial_number); |
816 | + free(cur_dev->path); |
817 | + free(cur_dev); |
818 | + |
819 | + /* Take it off the device list. */ |
820 | + if (prev_dev) { |
821 | + prev_dev->next = NULL; |
822 | + cur_dev = prev_dev; |
823 | + } |
824 | + else { |
825 | + cur_dev = root = NULL; |
826 | + } |
827 | + |
828 | + goto next; |
829 | + } |
830 | + |
831 | + /* Manufacturer and Product strings */ |
832 | + cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]); |
833 | + cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]); |
834 | + |
835 | + /* Release Number */ |
836 | + str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); |
837 | + cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; |
838 | + |
839 | + /* Get a handle to the interface's udev node. */ |
840 | + intf_dev = udev_device_get_parent_with_subsystem_devtype( |
841 | + raw_dev, |
842 | + "usb", |
843 | + "usb_interface"); |
844 | + if (intf_dev) { |
845 | + str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); |
846 | + cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; |
847 | + } |
848 | + |
849 | + break; |
850 | + |
851 | + case BUS_BLUETOOTH: |
852 | + /* Manufacturer and Product strings */ |
853 | + cur_dev->manufacturer_string = wcsdup(L""); |
854 | + cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); |
855 | + |
856 | + break; |
857 | + |
858 | + default: |
859 | + /* Unknown device type - this should never happen, as we |
860 | + * check for USB and Bluetooth devices above */ |
861 | + break; |
862 | } |
863 | } |
864 | - else |
865 | - goto next; |
866 | |
867 | next: |
868 | - udev_device_unref(hid_dev); |
869 | - /* dev and intf_dev don't need to be (and can't be) |
870 | + free(serial_number_utf8); |
871 | + free(product_name_utf8); |
872 | + udev_device_unref(raw_dev); |
873 | + /* hid_dev, usb_dev and intf_dev don't need to be (and can't be) |
874 | unref()d. It will cause a double-free() error. I'm not |
875 | sure why. */ |
876 | } |
877 | @@ -365,7 +559,7 @@ |
878 | } |
879 | } |
880 | |
881 | -hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) |
882 | +hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) |
883 | { |
884 | struct hid_device_info *devs, *cur_dev; |
885 | const char *path_to_open = NULL; |
886 | @@ -404,6 +598,8 @@ |
887 | { |
888 | hid_device *dev = NULL; |
889 | |
890 | + hid_init(); |
891 | + |
892 | dev = new_hid_device(); |
893 | |
894 | if (kernel_version == 0) { |
895 | @@ -570,17 +766,17 @@ |
896 | |
897 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) |
898 | { |
899 | - return get_device_string(dev, "manufacturer", string, maxlen); |
900 | + return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen); |
901 | } |
902 | |
903 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) |
904 | { |
905 | - return get_device_string(dev, "product", string, maxlen); |
906 | + return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen); |
907 | } |
908 | |
909 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) |
910 | { |
911 | - return get_device_string(dev, "serial", string, maxlen); |
912 | + return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen); |
913 | } |
914 | |
915 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) |
916 | |
917 | === modified file 'mixxx/lib/hidapi/mac/hid.c' |
918 | --- mixxx/lib/hidapi-0.7.0/mac/hid.c 2012-01-28 09:23:48 +0000 |
919 | +++ mixxx/lib/hidapi/mac/hid.c 2012-08-26 04:54:21 +0000 |
920 | @@ -263,16 +263,25 @@ |
921 | |
922 | static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) |
923 | { |
924 | - CFStringRef str = IOHIDDeviceGetProperty(device, prop); |
925 | - |
926 | - buf[0] = 0x0000; |
927 | + CFStringRef str; |
928 | + |
929 | + if (!len) |
930 | + return 0; |
931 | + |
932 | + str = IOHIDDeviceGetProperty(device, prop); |
933 | + |
934 | + buf[0] = 0; |
935 | |
936 | if (str) { |
937 | + len --; |
938 | + |
939 | + CFIndex str_len = CFStringGetLength(str); |
940 | CFRange range; |
941 | range.location = 0; |
942 | - range.length = len; |
943 | + range.length = (str_len > len)? len: str_len; |
944 | CFIndex used_buf_len; |
945 | - CFStringGetBytes(str, |
946 | + CFIndex chars_copied; |
947 | + chars_copied = CFStringGetBytes(str, |
948 | range, |
949 | kCFStringEncodingUTF32LE, |
950 | (char)'?', |
951 | @@ -280,26 +289,35 @@ |
952 | (UInt8*)buf, |
953 | len, |
954 | &used_buf_len); |
955 | - buf[len-1] = 0x00000000; |
956 | - return used_buf_len; |
957 | + |
958 | + buf[chars_copied] = 0; |
959 | + return 0; |
960 | } |
961 | else |
962 | - return 0; |
963 | + return -1; |
964 | |
965 | } |
966 | |
967 | static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len) |
968 | { |
969 | - CFStringRef str = IOHIDDeviceGetProperty(device, prop); |
970 | - |
971 | - buf[0] = 0x0000; |
972 | + CFStringRef str; |
973 | + if (!len) |
974 | + return 0; |
975 | + |
976 | + str = IOHIDDeviceGetProperty(device, prop); |
977 | + |
978 | + buf[0] = 0; |
979 | |
980 | if (str) { |
981 | + len--; |
982 | + |
983 | + CFIndex str_len = CFStringGetLength(str); |
984 | CFRange range; |
985 | range.location = 0; |
986 | - range.length = len; |
987 | + range.length = (str_len > len)? len: str_len; |
988 | CFIndex used_buf_len; |
989 | - CFStringGetBytes(str, |
990 | + CFIndex chars_copied; |
991 | + chars_copied = CFStringGetBytes(str, |
992 | range, |
993 | kCFStringEncodingUTF8, |
994 | (char)'?', |
995 | @@ -307,12 +325,12 @@ |
996 | (UInt8*)buf, |
997 | len, |
998 | &used_buf_len); |
999 | - buf[len-1] = 0x00000000; |
1000 | + |
1001 | + buf[chars_copied] = 0; |
1002 | return used_buf_len; |
1003 | } |
1004 | else |
1005 | return 0; |
1006 | - |
1007 | } |
1008 | |
1009 | |
1010 | @@ -369,26 +387,32 @@ |
1011 | return res+1; |
1012 | } |
1013 | |
1014 | +/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ |
1015 | static int init_hid_manager(void) |
1016 | { |
1017 | IOReturn res; |
1018 | |
1019 | /* Initialize all the HID Manager Objects */ |
1020 | hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); |
1021 | - IOHIDManagerSetDeviceMatching(hid_mgr, NULL); |
1022 | - IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); |
1023 | - res = IOHIDManagerOpen(hid_mgr, kIOHIDOptionsTypeNone); |
1024 | - return (res == kIOReturnSuccess)? 0: -1; |
1025 | + if (hid_mgr) { |
1026 | + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); |
1027 | + IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); |
1028 | + return 0; |
1029 | + } |
1030 | + |
1031 | + return -1; |
1032 | } |
1033 | |
1034 | +/* Initialize the IOHIDManager if necessary. This is the public function, and |
1035 | + it is safe to call this function repeatedly. Return 0 for success and -1 |
1036 | + for failure. */ |
1037 | int HID_API_EXPORT hid_init(void) |
1038 | { |
1039 | if (!hid_mgr) { |
1040 | - if (init_hid_manager() < 0) { |
1041 | - hid_exit(); |
1042 | - return -1; |
1043 | - } |
1044 | + return init_hid_manager(); |
1045 | } |
1046 | + |
1047 | + /* Already initialized. */ |
1048 | return 0; |
1049 | } |
1050 | |
1051 | @@ -404,6 +428,13 @@ |
1052 | return 0; |
1053 | } |
1054 | |
1055 | +static void process_pending_events() { |
1056 | + SInt32 res; |
1057 | + do { |
1058 | + res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); |
1059 | + } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); |
1060 | +} |
1061 | + |
1062 | struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) |
1063 | { |
1064 | struct hid_device_info *root = NULL; // return object |
1065 | @@ -411,11 +442,13 @@ |
1066 | CFIndex num_devices; |
1067 | int i; |
1068 | |
1069 | - setlocale(LC_ALL,""); |
1070 | + /* Set up the HID Manager if it hasn't been done */ |
1071 | + if (hid_init() < 0) |
1072 | + return NULL; |
1073 | + |
1074 | + /* give the IOHIDManager a chance to update itself */ |
1075 | + process_pending_events(); |
1076 | |
1077 | - /* Set up the HID Manager if it hasn't been done */ |
1078 | - hid_init(); |
1079 | - |
1080 | /* Get a list of the Devices */ |
1081 | CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); |
1082 | |
1083 | @@ -508,7 +541,7 @@ |
1084 | } |
1085 | } |
1086 | |
1087 | -hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) |
1088 | +hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) |
1089 | { |
1090 | /* This function is identical to the Linux version. Platform independent. */ |
1091 | struct hid_device_info *devs, *cur_dev; |
1092 | @@ -676,13 +709,6 @@ |
1093 | pthread_cond_broadcast(&dev->condition); |
1094 | pthread_mutex_unlock(&dev->mutex); |
1095 | |
1096 | - /* Close the OS handle to the device, but only if it's not |
1097 | - been unplugged. If it's been unplugged, then calling |
1098 | - IOHIDDeviceClose() will crash. */ |
1099 | - if (!dev->disconnected) { |
1100 | - IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone); |
1101 | - } |
1102 | - |
1103 | /* Wait here until hid_close() is called and makes it past |
1104 | the call to CFRunLoopWakeUp(). This thread still needs to |
1105 | be valid when that function is called on the other thread. */ |
1106 | @@ -700,7 +726,11 @@ |
1107 | dev = new_hid_device(); |
1108 | |
1109 | /* Set up the HID Manager if it hasn't been done */ |
1110 | - hid_init(); |
1111 | + if (hid_init() < 0) |
1112 | + return NULL; |
1113 | + |
1114 | + /* give the IOHIDManager a chance to update itself */ |
1115 | + process_pending_events(); |
1116 | |
1117 | CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); |
1118 | |
1119 | @@ -720,6 +750,7 @@ |
1120 | char str[32]; |
1121 | |
1122 | free(device_array); |
1123 | + CFRetain(os_dev); |
1124 | CFRelease(device_set); |
1125 | dev->device_handle = os_dev; |
1126 | |
1127 | @@ -1012,6 +1043,7 @@ |
1128 | return_data(dev, NULL, 0); |
1129 | } |
1130 | pthread_mutex_unlock(&dev->mutex); |
1131 | + CFRelease(dev->device_handle); |
1132 | |
1133 | free_hid_device(dev); |
1134 | } |
1135 | @@ -1096,8 +1128,6 @@ |
1136 | IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); |
1137 | CFSetGetValues(device_set, (const void **) device_array); |
1138 | |
1139 | - setlocale(LC_ALL, ""); |
1140 | - |
1141 | for (i = 0; i < num_devices; i++) { |
1142 | IOHIDDeviceRef dev = device_array[i]; |
1143 | printf("Device: %p\n", dev); |
1144 | |
1145 | === modified file 'mixxx/lib/hidapi/windows/Makefile.mingw' |
1146 | --- mixxx/lib/hidapi-0.7.0/windows/Makefile.mingw 2012-01-28 09:23:48 +0000 |
1147 | +++ mixxx/lib/hidapi/windows/Makefile.mingw 2012-08-26 04:54:21 +0000 |
1148 | @@ -6,7 +6,7 @@ |
1149 | # 2010-06-01 |
1150 | ########################################### |
1151 | |
1152 | -all: hidtest |
1153 | +all: hidtest libhidapi.dll |
1154 | |
1155 | CC=gcc |
1156 | CXX=g++ |
1157 | @@ -15,11 +15,14 @@ |
1158 | OBJS=$(COBJS) $(CPPOBJS) |
1159 | CFLAGS=-I../hidapi -g -c |
1160 | LIBS= -lsetupapi |
1161 | - |
1162 | +DLL_LDFLAGS = -mwindows -lsetupapi |
1163 | |
1164 | hidtest: $(OBJS) |
1165 | g++ -g $^ $(LIBS) -o hidtest |
1166 | |
1167 | +libhidapi.dll: $(OBJS) |
1168 | + $(CC) -g $^ $(DLL_LDFLAGS) -o libhidapi.dll |
1169 | + |
1170 | $(COBJS): %.o: %.c |
1171 | $(CC) $(CFLAGS) $< -o $@ |
1172 | |
1173 | |
1174 | === modified file 'mixxx/lib/hidapi/windows/hid.c' |
1175 | --- mixxx/lib/hidapi-0.7.0/windows/hid.c 2012-01-28 09:23:48 +0000 |
1176 | +++ mixxx/lib/hidapi/windows/hid.c 2012-08-26 04:54:21 +0000 |
1177 | @@ -93,8 +93,8 @@ |
1178 | USHORT Reserved[17]; |
1179 | USHORT fields_not_used_by_hidapi[10]; |
1180 | } HIDP_CAPS, *PHIDP_CAPS; |
1181 | - typedef char* HIDP_PREPARSED_DATA; |
1182 | - #define HIDP_STATUS_SUCCESS 0x0 |
1183 | + typedef void* PHIDP_PREPARSED_DATA; |
1184 | + #define HIDP_STATUS_SUCCESS 0x110000 |
1185 | |
1186 | typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); |
1187 | typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); |
1188 | @@ -103,9 +103,9 @@ |
1189 | typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); |
1190 | typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); |
1191 | typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); |
1192 | - typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, HIDP_PREPARSED_DATA **preparsed_data); |
1193 | - typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(HIDP_PREPARSED_DATA *preparsed_data); |
1194 | - typedef BOOLEAN (__stdcall *HidP_GetCaps_)(HIDP_PREPARSED_DATA *preparsed_data, HIDP_CAPS *caps); |
1195 | + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); |
1196 | + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); |
1197 | + typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); |
1198 | |
1199 | static HidD_GetAttributes_ HidD_GetAttributes; |
1200 | static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; |
1201 | @@ -125,6 +125,7 @@ |
1202 | struct hid_device_ { |
1203 | HANDLE device_handle; |
1204 | BOOL blocking; |
1205 | + USHORT output_report_length; |
1206 | size_t input_report_length; |
1207 | void *last_error_str; |
1208 | DWORD last_error_num; |
1209 | @@ -138,6 +139,7 @@ |
1210 | hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); |
1211 | dev->device_handle = INVALID_HANDLE_VALUE; |
1212 | dev->blocking = TRUE; |
1213 | + dev->output_report_length = 0; |
1214 | dev->input_report_length = 0; |
1215 | dev->last_error_str = NULL; |
1216 | dev->last_error_num = 0; |
1217 | @@ -205,34 +207,22 @@ |
1218 | } |
1219 | #endif |
1220 | |
1221 | -static HANDLE open_device(const char *path) |
1222 | +static HANDLE open_device(const char *path, BOOL enumerate) |
1223 | { |
1224 | HANDLE handle; |
1225 | + DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); |
1226 | + DWORD share_mode = (enumerate)? |
1227 | + FILE_SHARE_READ|FILE_SHARE_WRITE: |
1228 | + FILE_SHARE_READ; |
1229 | |
1230 | - /* First, try to open with sharing mode turned off. This will make it so |
1231 | - that a HID device can only be opened once. This is to be consistent |
1232 | - with the behavior on the other platforms. */ |
1233 | handle = CreateFileA(path, |
1234 | - GENERIC_WRITE |GENERIC_READ, |
1235 | - 0, /*share mode*/ |
1236 | + desired_access, |
1237 | + share_mode, |
1238 | NULL, |
1239 | OPEN_EXISTING, |
1240 | FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, |
1241 | 0); |
1242 | |
1243 | - if (handle == INVALID_HANDLE_VALUE) { |
1244 | - /* Couldn't open the device. Some devices must be opened |
1245 | - with sharing enabled (even though they are only opened once), |
1246 | - so try it here. */ |
1247 | - handle = CreateFileA(path, |
1248 | - GENERIC_WRITE |GENERIC_READ, |
1249 | - FILE_SHARE_READ|FILE_SHARE_WRITE, /*share mode*/ |
1250 | - NULL, |
1251 | - OPEN_EXISTING, |
1252 | - FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, |
1253 | - 0); |
1254 | - } |
1255 | - |
1256 | return handle; |
1257 | } |
1258 | |
1259 | @@ -274,11 +264,13 @@ |
1260 | SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; |
1261 | HDEVINFO device_info_set = INVALID_HANDLE_VALUE; |
1262 | int device_index = 0; |
1263 | + int i; |
1264 | |
1265 | if (hid_init() < 0) |
1266 | return NULL; |
1267 | |
1268 | // Initialize the Windows objects. |
1269 | + memset(&devinfo_data, 0x0, sizeof(devinfo_data)); |
1270 | devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); |
1271 | device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
1272 | |
1273 | @@ -334,10 +326,35 @@ |
1274 | goto cont; |
1275 | } |
1276 | |
1277 | + // Make sure this device is of Setup Class "HIDClass" and has a |
1278 | + // driver bound to it. |
1279 | + for (i = 0; ; i++) { |
1280 | + char driver_name[256]; |
1281 | + |
1282 | + // Populate devinfo_data. This function will return failure |
1283 | + // when there are no more interfaces left. |
1284 | + res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); |
1285 | + if (!res) |
1286 | + goto cont; |
1287 | + |
1288 | + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, |
1289 | + SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); |
1290 | + if (!res) |
1291 | + goto cont; |
1292 | + |
1293 | + if (strcmp(driver_name, "HIDClass") == 0) { |
1294 | + // See if there's a driver bound. |
1295 | + res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, |
1296 | + SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); |
1297 | + if (res) |
1298 | + break; |
1299 | + } |
1300 | + } |
1301 | + |
1302 | //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); |
1303 | |
1304 | // Open a handle to the device |
1305 | - write_handle = open_device(device_interface_detail_data->DevicePath); |
1306 | + write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); |
1307 | |
1308 | // Check validity of write_handle. |
1309 | if (write_handle == INVALID_HANDLE_VALUE) { |
1310 | @@ -360,7 +377,7 @@ |
1311 | #define WSTR_LEN 512 |
1312 | const char *str; |
1313 | struct hid_device_info *tmp; |
1314 | - HIDP_PREPARSED_DATA *pp_data = NULL; |
1315 | + PHIDP_PREPARSED_DATA pp_data = NULL; |
1316 | HIDP_CAPS caps; |
1317 | BOOLEAN res; |
1318 | NTSTATUS nt_res; |
1319 | @@ -482,7 +499,7 @@ |
1320 | } |
1321 | |
1322 | |
1323 | -HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) |
1324 | +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) |
1325 | { |
1326 | // TODO: Merge this functions with the Linux version. This function should be platform independent. |
1327 | struct hid_device_info *devs, *cur_dev; |
1328 | @@ -522,7 +539,7 @@ |
1329 | { |
1330 | hid_device *dev; |
1331 | HIDP_CAPS caps; |
1332 | - HIDP_PREPARSED_DATA *pp_data = NULL; |
1333 | + PHIDP_PREPARSED_DATA pp_data = NULL; |
1334 | BOOLEAN res; |
1335 | NTSTATUS nt_res; |
1336 | |
1337 | @@ -533,7 +550,7 @@ |
1338 | dev = new_hid_device(); |
1339 | |
1340 | // Open a handle to the device |
1341 | - dev->device_handle = open_device(path); |
1342 | + dev->device_handle = open_device(path, FALSE); |
1343 | |
1344 | // Check validity of write_handle. |
1345 | if (dev->device_handle == INVALID_HANDLE_VALUE) { |
1346 | @@ -553,6 +570,7 @@ |
1347 | register_error(dev, "HidP_GetCaps"); |
1348 | goto err_pp_data; |
1349 | } |
1350 | + dev->output_report_length = caps.OutputReportByteLength; |
1351 | dev->input_report_length = caps.InputReportByteLength; |
1352 | HidD_FreePreparsedData(pp_data); |
1353 | |
1354 | @@ -574,15 +592,35 @@ |
1355 | BOOL res; |
1356 | |
1357 | OVERLAPPED ol; |
1358 | + unsigned char *buf; |
1359 | memset(&ol, 0, sizeof(ol)); |
1360 | |
1361 | - res = WriteFile(dev->device_handle, data, length, NULL, &ol); |
1362 | + /* Make sure the right number of bytes are passed to WriteFile. Windows |
1363 | + expects the number of bytes which are in the _longest_ report (plus |
1364 | + one for the report number) bytes even if the data is a report |
1365 | + which is shorter than that. Windows gives us this value in |
1366 | + caps.OutputReportByteLength. If a user passes in fewer bytes than this, |
1367 | + create a temporary buffer which is the proper size. */ |
1368 | + if (length >= dev->output_report_length) { |
1369 | + /* The user passed the right number of bytes. Use the buffer as-is. */ |
1370 | + buf = (unsigned char *) data; |
1371 | + } else { |
1372 | + /* Create a temporary buffer and copy the user's data |
1373 | + into it, padding the rest with zeros. */ |
1374 | + buf = (unsigned char *) malloc(dev->output_report_length); |
1375 | + memcpy(buf, data, length); |
1376 | + memset(buf + length, 0, dev->output_report_length - length); |
1377 | + length = dev->output_report_length; |
1378 | + } |
1379 | + |
1380 | + res = WriteFile(dev->device_handle, buf, length, NULL, &ol); |
1381 | |
1382 | if (!res) { |
1383 | if (GetLastError() != ERROR_IO_PENDING) { |
1384 | // WriteFile() failed. Return error. |
1385 | register_error(dev, "WriteFile"); |
1386 | - return -1; |
1387 | + bytes_written = -1; |
1388 | + goto end_of_function; |
1389 | } |
1390 | } |
1391 | |
1392 | @@ -592,9 +630,14 @@ |
1393 | if (!res) { |
1394 | // The Write operation failed. |
1395 | register_error(dev, "WriteFile"); |
1396 | - return -1; |
1397 | + bytes_written = -1; |
1398 | + goto end_of_function; |
1399 | } |
1400 | |
1401 | +end_of_function: |
1402 | + if (buf != data) |
1403 | + free(buf); |
1404 | + |
1405 | return bytes_written; |
1406 | } |
1407 | |
1408 | @@ -610,6 +653,7 @@ |
1409 | if (!dev->read_pending) { |
1410 | // Start an Overlapped I/O read. |
1411 | dev->read_pending = TRUE; |
1412 | + memset(dev->read_buf, 0, dev->input_report_length); |
1413 | ResetEvent(ev); |
1414 | res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); |
1415 | |
1416 | @@ -648,12 +692,15 @@ |
1417 | number (0x0) on the beginning of the report anyway. To make this |
1418 | work like the other platforms, and to make it work more like the |
1419 | HID spec, we'll skip over this byte. */ |
1420 | + size_t copy_len; |
1421 | bytes_read--; |
1422 | - memcpy(data, dev->read_buf+1, length); |
1423 | + copy_len = length > bytes_read ? bytes_read : length; |
1424 | + memcpy(data, dev->read_buf+1, copy_len); |
1425 | } |
1426 | else { |
1427 | /* Copy the whole buffer, report number and all. */ |
1428 | - memcpy(data, dev->read_buf, length); |
1429 | + size_t copy_len = length > bytes_read ? bytes_read : length; |
1430 | + memcpy(data, dev->read_buf, copy_len); |
1431 | } |
1432 | } |
1433 | |
1434 | |
1435 | === modified file 'mixxx/src/controllers/controller.h' |
1436 | --- mixxx/src/controllers/controller.h 2012-08-09 04:18:27 +0000 |
1437 | +++ mixxx/src/controllers/controller.h 2012-08-26 04:54:21 +0000 |
1438 | @@ -46,6 +46,12 @@ |
1439 | inline bool isOpen() const { |
1440 | return m_bIsOpen; |
1441 | } |
1442 | + inline bool isConnected() const { |
1443 | + return m_bIsConnected; |
1444 | + } |
1445 | + inline void setConnected(bool connected) { |
1446 | + m_bIsConnected = connected; |
1447 | + } |
1448 | inline bool isOutputDevice() const { |
1449 | return m_bIsOutputDevice; |
1450 | } |
1451 | @@ -160,6 +166,8 @@ |
1452 | bool m_bIsInputDevice; |
1453 | // Indicates whether or not the device has been opened for input/output. |
1454 | bool m_bIsOpen; |
1455 | + // Indicates whether or not the device is connected (hotplug). |
1456 | + bool m_bIsConnected; |
1457 | // Specifies whether or not we should dump incoming data to the console at |
1458 | // runtime. This is useful for end-user debugging and script-writing. |
1459 | bool m_bDebug; |
1460 | |
1461 | === modified file 'mixxx/src/controllers/controllermanager.cpp' |
1462 | --- mixxx/src/controllers/controllermanager.cpp 2012-07-08 23:27:47 +0000 |
1463 | +++ mixxx/src/controllers/controllermanager.cpp 2012-08-26 04:54:21 +0000 |
1464 | @@ -31,6 +31,7 @@ |
1465 | #else |
1466 | const int kPollIntervalMillis = 1; |
1467 | #endif |
1468 | +const int kUpdateIntervalMillis = 3000; |
1469 | |
1470 | QString firstAvailableFilename(QSet<QString>& filenames, |
1471 | const QString originalFilename) { |
1472 | @@ -48,6 +49,26 @@ |
1473 | return a->getName() < b->getName(); |
1474 | } |
1475 | |
1476 | +bool controllersModified(QList<Controller*> c_old, QList<Controller*> c_new) { |
1477 | + if (c_old.length()!=c_new.length()) |
1478 | + return true; |
1479 | + |
1480 | + foreach (Controller *a, c_old) { |
1481 | + bool device_found = false; |
1482 | + if (!a->isConnected()) |
1483 | + return true; |
1484 | + foreach (Controller *b, c_new) { |
1485 | + if (a->getName()==b->getName()) { |
1486 | + device_found=true; |
1487 | + break; |
1488 | + } |
1489 | + } |
1490 | + if (!device_found) |
1491 | + return true; |
1492 | + } |
1493 | + return false; |
1494 | +} |
1495 | + |
1496 | ControllerManager::ControllerManager(ConfigObject<ConfigValue> * pConfig) : |
1497 | QObject(), |
1498 | m_pConfig(pConfig), |
1499 | @@ -85,6 +106,10 @@ |
1500 | connect(&m_pollTimer, SIGNAL(timeout()), |
1501 | this, SLOT(pollDevices())); |
1502 | |
1503 | + m_updateTimer.setInterval(kUpdateIntervalMillis); |
1504 | + connect(&m_updateTimer, SIGNAL(timeout()), |
1505 | + this, SLOT(slotUpdateDevices())); |
1506 | + |
1507 | m_pThread = new QThread; |
1508 | m_pThread->setObjectName("Controller"); |
1509 | |
1510 | @@ -95,8 +120,8 @@ |
1511 | // audio directly, like when scratching |
1512 | m_pThread->start(QThread::HighPriority); |
1513 | |
1514 | - connect(this, SIGNAL(requestSetUpDevices()), |
1515 | - this, SLOT(slotSetUpDevices())); |
1516 | + connect(this, SIGNAL(requestUpdateDevices()), |
1517 | + this, SLOT(slotUpdateDevices())); |
1518 | connect(this, SIGNAL(requestShutdown()), |
1519 | this, SLOT(slotShutdown())); |
1520 | connect(this, SIGNAL(requestSave(bool)), |
1521 | @@ -133,12 +158,15 @@ |
1522 | m_pThread->quit(); |
1523 | } |
1524 | |
1525 | -void ControllerManager::updateControllerList() { |
1526 | +bool ControllerManager::updateControllerList() { |
1527 | QMutexLocker locker(&m_mutex); |
1528 | if (m_enumerators.isEmpty()) { |
1529 | - qWarning() << "updateControllerList called but no enumerators have been added!"; |
1530 | - return; |
1531 | + // qWarning() << |
1532 | + // "updateControllerList called but no enumerators have been added!"; |
1533 | + return false; |
1534 | } |
1535 | + |
1536 | + |
1537 | QList<ControllerEnumerator*> enumerators = m_enumerators; |
1538 | locker.unlock(); |
1539 | |
1540 | @@ -147,12 +175,15 @@ |
1541 | newDeviceList.append(pEnumerator->queryDevices()); |
1542 | } |
1543 | |
1544 | + // List of controllers did not change |
1545 | + if (!controllersModified(m_controllers,newDeviceList)) { |
1546 | + return false; |
1547 | + } |
1548 | + |
1549 | locker.relock(); |
1550 | - if (newDeviceList != m_controllers) { |
1551 | - m_controllers = newDeviceList; |
1552 | - locker.unlock(); |
1553 | - emit(devicesChanged()); |
1554 | - } |
1555 | + m_controllers = newDeviceList; |
1556 | + locker.unlock(); |
1557 | + return true; |
1558 | } |
1559 | |
1560 | QList<Controller*> ControllerManager::getControllers() const { |
1561 | @@ -161,7 +192,7 @@ |
1562 | } |
1563 | |
1564 | QList<Controller*> ControllerManager::getControllerList(bool bOutputDevices, bool bInputDevices) { |
1565 | - qDebug() << "ControllerManager::getControllerList"; |
1566 | + // qDebug() << "ControllerManager::getControllerList"; |
1567 | |
1568 | QMutexLocker locker(&m_mutex); |
1569 | QList<Controller*> controllers = m_controllers; |
1570 | @@ -180,51 +211,79 @@ |
1571 | return filteredDeviceList; |
1572 | } |
1573 | |
1574 | -int ControllerManager::slotSetUpDevices() { |
1575 | - qDebug() << "ControllerManager: Setting up devices"; |
1576 | - |
1577 | - updateControllerList(); |
1578 | - QList<Controller*> deviceList = getControllerList(false, true); |
1579 | - |
1580 | - QSet<QString> filenames; |
1581 | +void ControllerManager::startDeviceUpdateTimer() { |
1582 | + if (!m_updateTimer.isActive()) { |
1583 | + m_updateTimer.start(); |
1584 | + qDebug() << "Controller update polling started."; |
1585 | + } |
1586 | +} |
1587 | + |
1588 | +int ControllerManager::slotUpdateDevices() { |
1589 | + |
1590 | + bool devices_modified; |
1591 | int error = 0; |
1592 | - |
1593 | + int device_error = 0; |
1594 | + startDeviceUpdateTimer(); |
1595 | + |
1596 | + devices_modified = updateControllerList(); |
1597 | + if (!devices_modified) { |
1598 | + return 0; |
1599 | + } |
1600 | + |
1601 | + QList<Controller*> deviceList = getControllerList(false, true); |
1602 | foreach (Controller* pController, deviceList) { |
1603 | - QString name = pController->getName(); |
1604 | - |
1605 | - if (pController->isOpen()) { |
1606 | - pController->close(); |
1607 | - } |
1608 | - |
1609 | - // The filename for this device name. |
1610 | - const QString ofilename = presetFilenameFromName(name); |
1611 | - // The first unique filename for this device (appends numbers at the end |
1612 | - // if we have already seen a controller by this name on this run of |
1613 | - // Mixxx. |
1614 | - QString filename = firstAvailableFilename(filenames, ofilename); |
1615 | - |
1616 | - if (!loadPreset(pController, filename, true)) { |
1617 | - // TODO(XXX) : auto load midi preset here. |
1618 | - continue; |
1619 | - } |
1620 | - |
1621 | - if (m_pConfig->getValueString(ConfigKey("[Controller]", ofilename)) != "1") { |
1622 | - continue; |
1623 | - } |
1624 | - |
1625 | - qDebug() << "Opening controller:" << name; |
1626 | - |
1627 | - int value = pController->open(); |
1628 | - if (value != 0) { |
1629 | - qWarning() << "There was a problem opening" << name; |
1630 | - if (error == 0) { |
1631 | - error = value; |
1632 | - } |
1633 | - continue; |
1634 | - } |
1635 | - pController->applyPreset(m_pConfig->getResourcePath()); |
1636 | - } |
1637 | - |
1638 | + if (!pController->isConnected()) { |
1639 | + devices_modified = true; |
1640 | + continue; |
1641 | + } |
1642 | + |
1643 | + device_error = setupController(pController); |
1644 | + if (device_error!=-1) |
1645 | + error = device_error; |
1646 | + } |
1647 | + if (devices_modified) |
1648 | + emit(devicesChanged()); |
1649 | + |
1650 | + maybeStartOrStopPolling(); |
1651 | + return error; |
1652 | +} |
1653 | + |
1654 | +int ControllerManager::setupController(Controller *pController) { |
1655 | + QSet<QString> filenames; |
1656 | + int error = -1; |
1657 | + |
1658 | + // Don't reopen existing devices |
1659 | + if (!pController || pController->isOpen()) |
1660 | + return error; |
1661 | + |
1662 | + QString name = pController->getName(); |
1663 | + // The filename for this device name. |
1664 | + const QString ofilename = presetFilenameFromName(name); |
1665 | + |
1666 | + // Controller not enabled |
1667 | + if (m_pConfig->getValueString(ConfigKey("[Controller]", ofilename)) != "1") { |
1668 | + return error; |
1669 | + } |
1670 | + |
1671 | + // The first unique filename for this device (appends numbers at the end |
1672 | + // if we have already seen a controller by this name on this run of |
1673 | + // Mixxx. |
1674 | + QString filename = firstAvailableFilename(filenames, ofilename); |
1675 | + if (!loadPreset(pController, filename, true)) { |
1676 | + // TODO(XXX) : auto load midi preset here. |
1677 | + return error; |
1678 | + } |
1679 | + |
1680 | + qDebug() << "Opening controller:" << name; |
1681 | + int value = pController->open(); |
1682 | + if (value != 0) { |
1683 | + qWarning() << "There was a problem opening" << name; |
1684 | + if (error == 0) { |
1685 | + error = value; |
1686 | + } |
1687 | + return error; |
1688 | + } |
1689 | + pController->applyPreset(m_pConfig->getResourcePath()); |
1690 | maybeStartOrStopPolling(); |
1691 | return error; |
1692 | } |
1693 | @@ -266,7 +325,9 @@ |
1694 | do { |
1695 | eventsProcessed = false; |
1696 | foreach (Controller* pDevice, m_controllers) { |
1697 | - if (pDevice->isOpen() && pDevice->isPolling()) { |
1698 | + if (!pDevice->isConnected()) { |
1699 | + eventsProcessed = false || eventsProcessed; |
1700 | + } else if (pDevice->isOpen() && pDevice->isPolling()) { |
1701 | eventsProcessed = pDevice->poll() || eventsProcessed; |
1702 | } |
1703 | } |
1704 | |
1705 | === modified file 'mixxx/src/controllers/controllermanager.h' |
1706 | --- mixxx/src/controllers/controllermanager.h 2012-05-31 06:38:14 +0000 |
1707 | +++ mixxx/src/controllers/controllermanager.h 2012-08-26 04:54:21 +0000 |
1708 | @@ -20,6 +20,8 @@ |
1709 | // Function to sort controllers by name |
1710 | bool controllerCompare(Controller *a,Controller *b); |
1711 | |
1712 | +bool controllersModified(QList<Controller*> c_old, QList<Controller*> c_new); |
1713 | + |
1714 | /** Manages enumeration/operation/deletion of hardware controllers. */ |
1715 | class ControllerManager : public QObject { |
1716 | Q_OBJECT |
1717 | @@ -33,18 +35,18 @@ |
1718 | PresetInfoEnumerator* getPresetInfoManager(); |
1719 | |
1720 | // Prevent other parts of Mixxx from having to manually connect to our slots |
1721 | - void setUpDevices() { emit(requestSetUpDevices()); }; |
1722 | + void updateDevices() { emit(requestUpdateDevices()); }; |
1723 | void savePresets(bool onlyActive=false) { emit(requestSave(onlyActive)); }; |
1724 | void shutdown() { emit(requestShutdown()); }; |
1725 | |
1726 | signals: |
1727 | void devicesChanged(); |
1728 | - void requestSetUpDevices(); |
1729 | + void requestUpdateDevices(); |
1730 | void requestShutdown(); |
1731 | void requestSave(bool onlyActive); |
1732 | |
1733 | public slots: |
1734 | - void updateControllerList(); |
1735 | + bool updateControllerList(); |
1736 | |
1737 | void openController(Controller* pController); |
1738 | void closeController(Controller* pController); |
1739 | @@ -56,7 +58,7 @@ |
1740 | // Open whatever controllers are selected in the preferences. This currently |
1741 | // only runs on start-up but maybe should instead be signaled by the |
1742 | // preferences dialog on apply, and only open/close changed devices |
1743 | - int slotSetUpDevices(); |
1744 | + int slotUpdateDevices(); |
1745 | void slotShutdown(); |
1746 | bool loadPreset(Controller* pController, |
1747 | ControllerPresetPointer preset); |
1748 | @@ -67,6 +69,8 @@ |
1749 | void startPolling(); |
1750 | void stopPolling(); |
1751 | void maybeStartOrStopPolling(); |
1752 | + void startDeviceUpdateTimer(); |
1753 | + int setupController(Controller *pController); |
1754 | |
1755 | static QString presetFilenameFromName(QString name) { |
1756 | return name.replace(" ", "_"); |
1757 | @@ -76,6 +80,7 @@ |
1758 | ConfigObject<ConfigValue> *m_pConfig; |
1759 | ControllerLearningEventFilter* m_pControllerLearningEventFilter; |
1760 | QTimer m_pollTimer; |
1761 | + QTimer m_updateTimer; |
1762 | mutable QMutex m_mutex; |
1763 | QList<ControllerEnumerator*> m_enumerators; |
1764 | QList<Controller*> m_controllers; |
1765 | |
1766 | === modified file 'mixxx/src/controllers/hid/hidcontroller.cpp' |
1767 | --- mixxx/src/controllers/hid/hidcontroller.cpp 2012-08-09 04:18:27 +0000 |
1768 | +++ mixxx/src/controllers/hid/hidcontroller.cpp 2012-08-26 04:54:21 +0000 |
1769 | @@ -115,6 +115,8 @@ |
1770 | setInputDevice(true); |
1771 | setOutputDevice(true); |
1772 | m_pReader = NULL; |
1773 | + // Device is marked connected (but not open) when initialized |
1774 | + setConnected(true); |
1775 | } |
1776 | |
1777 | HidController::~HidController() { |
1778 | @@ -325,15 +327,23 @@ |
1779 | // Append the Report ID to the beginning of data[] per the API.. |
1780 | data.prepend(reportID); |
1781 | |
1782 | + if (!isConnected()) |
1783 | + return; |
1784 | + |
1785 | int result = hid_write(m_pHidDevice, (unsigned char*)data.constData(), data.size()); |
1786 | if (result == -1) { |
1787 | + const wchar_t* error = hid_error(m_pHidDevice); |
1788 | + if (error==NULL) { |
1789 | + setConnected(false); |
1790 | + return; |
1791 | + } |
1792 | if (debugging()) { |
1793 | qWarning() << "Unable to send data to" << getName() |
1794 | << "serial #" << hid_serial << ":" |
1795 | - << QString::fromWCharArray(hid_error(m_pHidDevice)); |
1796 | + << QString::fromWCharArray(error); |
1797 | } else { |
1798 | qWarning() << "Unable to send data to" << getName() << ":" |
1799 | - << QString::fromWCharArray(hid_error(m_pHidDevice)); |
1800 | + << QString::fromWCharArray(error); |
1801 | } |
1802 | } else if (debugging()) { |
1803 | qDebug() << result << "bytes sent to" << getName() |
1804 | |
1805 | === modified file 'mixxx/src/controllers/hid/hidcontroller.h' |
1806 | --- mixxx/src/controllers/hid/hidcontroller.h 2012-08-09 04:18:27 +0000 |
1807 | +++ mixxx/src/controllers/hid/hidcontroller.h 2012-08-26 04:54:21 +0000 |
1808 | @@ -68,6 +68,8 @@ |
1809 | virtual bool matchProductInfo(QHash <QString,QString >); |
1810 | virtual void guessDeviceCategory(); |
1811 | |
1812 | + const inline QString getPath() { return QString((const char *)hid_path);} |
1813 | + |
1814 | protected: |
1815 | Q_INVOKABLE void send(QList<int> data, unsigned int length, unsigned int reportID = 0); |
1816 | |
1817 | |
1818 | === modified file 'mixxx/src/controllers/hid/hidenumerator.cpp' |
1819 | --- mixxx/src/controllers/hid/hidenumerator.cpp 2012-05-27 03:32:05 +0000 |
1820 | +++ mixxx/src/controllers/hid/hidenumerator.cpp 2012-08-26 04:54:21 +0000 |
1821 | @@ -61,24 +61,37 @@ |
1822 | return false; |
1823 | } |
1824 | |
1825 | +bool HidEnumerator::isDeviceLoaded(struct hid_device_info *device) { |
1826 | + HidController* m_dev; |
1827 | + QString device_path, match_path; |
1828 | + if (!device) |
1829 | + return false; |
1830 | + device_path = QString(device->path); |
1831 | + |
1832 | + foreach (Controller *c_ptr, m_devices) { |
1833 | + m_dev = (HidController*) c_ptr; |
1834 | + match_path = m_dev->getPath(); |
1835 | + if (device_path==match_path) { |
1836 | + return true; |
1837 | + } |
1838 | + } |
1839 | + return false; |
1840 | +} |
1841 | + |
1842 | QList<Controller*> HidEnumerator::queryDevices() { |
1843 | - qDebug() << "Scanning HID devices:"; |
1844 | + // qeebug() << "Scanning HID devices:"; |
1845 | |
1846 | struct hid_device_info *devs, *cur_dev; |
1847 | devs = hid_enumerate(0x0, 0x0); |
1848 | |
1849 | for (cur_dev = devs; cur_dev; cur_dev = cur_dev->next) { |
1850 | if (isDeviceBlacklisted(cur_dev)) { |
1851 | - // OS/X and windows use usage_page/usage not interface_number |
1852 | - qDebug() << "Blacklisting" |
1853 | - << cur_dev->manufacturer_string |
1854 | - << cur_dev->product_string |
1855 | - << QString("r%1").arg(cur_dev->release_number) |
1856 | - << "S/N" << cur_dev->serial_number |
1857 | - << (cur_dev->interface_number == -1 ? QString("Usage Page %1 Usage %2").arg( |
1858 | - QString::number(cur_dev->usage_page), |
1859 | - QString::number(cur_dev->usage)) : |
1860 | - QString("Interface %1").arg(cur_dev->interface_number)); |
1861 | + // Do not print blacklisted devices (would be printed |
1862 | + // each time hotplug devices are queried) |
1863 | + continue; |
1864 | + } |
1865 | + |
1866 | + if (isDeviceLoaded(cur_dev)) { |
1867 | continue; |
1868 | } |
1869 | |
1870 | @@ -101,6 +114,28 @@ |
1871 | HidController* currentDevice = new HidController(*cur_dev); |
1872 | m_devices.push_back(currentDevice); |
1873 | } |
1874 | + |
1875 | + // Check for removed / disconnected devices |
1876 | + foreach (Controller* c_ptr, m_devices) { |
1877 | + HidController* m_dev = (HidController *) c_ptr; |
1878 | + QString device_path, match_path; |
1879 | + bool device_found = false; |
1880 | + |
1881 | + device_path = m_dev->getPath(); |
1882 | + for (cur_dev = devs; cur_dev; cur_dev = cur_dev->next) { |
1883 | + match_path = QString(cur_dev->path); |
1884 | + if (device_path==match_path) { |
1885 | + device_found = true; |
1886 | + break; |
1887 | + } |
1888 | + } |
1889 | + if (!device_found) { |
1890 | + qDebug() << "Disconnected HID device " << m_dev->getPath(); |
1891 | + m_dev->setConnected(false); |
1892 | + m_devices.removeOne(m_dev); |
1893 | + } |
1894 | + } |
1895 | + |
1896 | hid_free_enumeration(devs); |
1897 | |
1898 | return m_devices; |
1899 | |
1900 | === modified file 'mixxx/src/controllers/hid/hidenumerator.h' |
1901 | --- mixxx/src/controllers/hid/hidenumerator.h 2012-04-23 03:42:12 +0000 |
1902 | +++ mixxx/src/controllers/hid/hidenumerator.h 2012-08-26 04:54:21 +0000 |
1903 | @@ -19,6 +19,7 @@ |
1904 | |
1905 | private: |
1906 | QList<Controller*> m_devices; |
1907 | + bool isDeviceLoaded(struct hid_device_info *device); |
1908 | }; |
1909 | |
1910 | #endif |
1911 | |
1912 | === modified file 'mixxx/src/controllers/midi/portmidicontroller.cpp' |
1913 | --- mixxx/src/controllers/midi/portmidicontroller.cpp 2012-06-14 17:42:13 +0000 |
1914 | +++ mixxx/src/controllers/midi/portmidicontroller.cpp 2012-08-26 04:54:21 +0000 |
1915 | @@ -31,6 +31,10 @@ |
1916 | if (m_pOutputDeviceInfo) { |
1917 | setOutputDevice(m_pOutputDeviceInfo->output); |
1918 | } |
1919 | + |
1920 | + // PortMIDI devices are always connected, until we implement hotplug |
1921 | + // for portMIDI |
1922 | + setConnected(true); |
1923 | } |
1924 | |
1925 | PortMidiController::~PortMidiController() { |
1926 | @@ -221,6 +225,9 @@ |
1927 | } |
1928 | |
1929 | void PortMidiController::send(QByteArray data) { |
1930 | + if (!isConnected()) { |
1931 | + return; |
1932 | + } |
1933 | if (m_pOutputStream) { |
1934 | PmError err = Pm_WriteSysEx(m_pOutputStream, 0, (unsigned char*)data.constData()); |
1935 | if (err != pmNoError) { |
1936 | |
1937 | === modified file 'mixxx/src/controllers/midi/portmidienumerator.cpp' |
1938 | --- mixxx/src/controllers/midi/portmidienumerator.cpp 2012-04-24 05:42:04 +0000 |
1939 | +++ mixxx/src/controllers/midi/portmidienumerator.cpp 2012-08-26 04:54:21 +0000 |
1940 | @@ -28,6 +28,11 @@ |
1941 | * To help simplify a lot of code, we're going to aggregate these two streams into a single full-duplex device. |
1942 | */ |
1943 | QList<Controller*> PortMidiEnumerator::queryDevices() { |
1944 | + // Portmidi does not support hotplug: if any devices were already |
1945 | + // loaded, return current list |
1946 | + if (m_devices.length()>0) |
1947 | + return m_devices; |
1948 | + |
1949 | qDebug() << "Scanning PortMIDI devices:"; |
1950 | |
1951 | int iNumDevices = Pm_CountDevices(); |
1952 | @@ -37,8 +42,6 @@ |
1953 | delete dev_it.next(); |
1954 | } |
1955 | |
1956 | - m_devices.clear(); |
1957 | - |
1958 | const PmDeviceInfo *deviceInfo, *inputDeviceInfo, *outputDeviceInfo; |
1959 | int inputDevIndex, outputDevIndex; |
1960 | QMap<int,QString> unassignedOutputDevices; |
1961 | |
1962 | === modified file 'mixxx/src/mixxx.cpp' |
1963 | --- mixxx/src/mixxx.cpp 2012-07-22 09:31:52 +0000 |
1964 | +++ mixxx/src/mixxx.cpp 2012-08-26 04:54:21 +0000 |
1965 | @@ -506,7 +506,7 @@ |
1966 | |
1967 | // Wait until all other ControlObjects are set up |
1968 | // before initializing controllers |
1969 | - m_pControllerManager->setUpDevices(); |
1970 | + m_pControllerManager->updateDevices(); |
1971 | } |
1972 | |
1973 | MixxxApp::~MixxxApp() |