Merge lp:~hile/mixxx/hotplug into lp:~mixxxdevelopers/mixxx/trunk

Proposed by Ilkka Tuohela
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
Reviewer Review Type Date Requested Status
Mixxx Development Team Pending
Review via email: mp+118293@code.launchpad.net

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.

To post a comment you must log in.
lp:~hile/mixxx/hotplug updated
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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()