From 46bc83e1b1eb83c39aafa183a43c50f620752709 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Fri, 29 Oct 2010 10:15:33 +0000 Subject: [PATCH] Added class ControllerListener --- telldus-core/service/CMakeLists.txt | 3 + telldus-core/service/ControllerListener.h | 26 +++ .../service/ControllerListener_mac.cpp | 174 ++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 telldus-core/service/ControllerListener.h create mode 100644 telldus-core/service/ControllerListener_mac.cpp diff --git a/telldus-core/service/CMakeLists.txt b/telldus-core/service/CMakeLists.txt index 08ea9608..486749fd 100644 --- a/telldus-core/service/CMakeLists.txt +++ b/telldus-core/service/CMakeLists.txt @@ -45,6 +45,7 @@ SET( telldus-service_HDRS ClientCommunicationHandler.h ConnectionListener.h Controller.h + ControllerListener.h ControllerManager.h ControllerMessage.h Device.h @@ -70,6 +71,7 @@ INCLUDE_DIRECTORIES( IF (APPLE) #### Mac OS X #### SET(DEFAULT_FTDI_ENGINE "ftd2xx") SET( telldus-service_TARGET TelldusService ) + ADD_DEFINITIONS( -D_MACOSX ) FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) FIND_LIBRARY(IOKIT_LIBRARY IOKit) @@ -82,6 +84,7 @@ IF (APPLE) #### Mac OS X #### LIST(APPEND telldus-service_SRCS main_unix.cpp ConnectionListener_unix.cpp + ControllerListener_mac.cpp Event_unix.cpp EventHandler_unix.cpp SettingsCoreFoundationPreferences.cpp diff --git a/telldus-core/service/ControllerListener.h b/telldus-core/service/ControllerListener.h new file mode 100644 index 00000000..7fd7fbce --- /dev/null +++ b/telldus-core/service/ControllerListener.h @@ -0,0 +1,26 @@ +#ifndef CONTROLLERLISTENER_H +#define CONTROLLERLISTENER_H + +#include "Thread.h" +#include "Event.h" + +class ControllerChangeEventData : public EventDataBase { +public: + int vid, pid; + bool inserted; +}; + +class ControllerListener : public TelldusCore::Thread { +public: + ControllerListener(EventRef event); + virtual ~ControllerListener(); + +protected: + void run(); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif //CONTROLLERLISTENER_H diff --git a/telldus-core/service/ControllerListener_mac.cpp b/telldus-core/service/ControllerListener_mac.cpp new file mode 100644 index 00000000..c3749cb2 --- /dev/null +++ b/telldus-core/service/ControllerListener_mac.cpp @@ -0,0 +1,174 @@ +#include "ControllerListener.h" + +#include +#include +#include +#include +#include + +class TellStickData { +public: + io_object_t notification; + CFStringRef serialNumber; + UInt32 vid; + UInt32 pid; + EventRef event; +}; + +class ControllerListener::PrivateData { +public: + IONotificationPortRef gNotifyPort; + CFRunLoopRef gRunLoop; + io_iterator_t gAddedIter; + EventRef event; + + static void DeviceAdded(void *refCon, io_iterator_t iterator); + static void DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument); +}; + +ControllerListener::ControllerListener(EventRef event) +:Thread() +{ + d = new PrivateData; + d->event = event; + this->start(); +} + +ControllerListener::~ControllerListener() { + CFRunLoopStop(d->gRunLoop); + this->wait(); + delete d; +} + +void ControllerListener::run() { + CFMutableDictionaryRef matchingDict; + CFRunLoopSourceRef runLoopSource; + CFNumberRef numberRef; + kern_return_t kr; + long usbVendor = 0x1781; + long usbProduct = 0x0c30; + + matchingDict = IOServiceMatching(kIOUSBDeviceClassName); // Interested in instances of class + // IOUSBDevice and its subclasses + + if (matchingDict == NULL) { + return; + } + + // Create a CFNumber for the idVendor and set the value in the dictionary + numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbVendor); + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef); + CFRelease(numberRef); + + // Create a CFNumber for the idProduct and set the value in the dictionary + numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbProduct); + CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef); + CFRelease(numberRef); + numberRef = NULL; + + d->gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); + runLoopSource = IONotificationPortGetRunLoopSource(d->gNotifyPort); + + d->gRunLoop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(d->gRunLoop, runLoopSource, kCFRunLoopDefaultMode); + + // Now set up a notification to be called when a device is first matched by I/O Kit. + kr = IOServiceAddMatchingNotification(d->gNotifyPort, // notifyPort + kIOFirstMatchNotification, // notificationType + matchingDict, // matching + PrivateData::DeviceAdded, // callback + this, // refCon + &d->gAddedIter // notification + ); + + + // Iterate once to get already-present devices and arm the notification + PrivateData::DeviceAdded(this, d->gAddedIter); + CFRunLoopRun(); +} + +void ControllerListener::PrivateData::DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) { + kern_return_t kr; + + if (messageType != kIOMessageServiceIsTerminated) { + return; + } + + TellStickData *tsd = reinterpret_cast (refCon); + if (!tsd) { + return; + } + + CFIndex size = CFStringGetLength(tsd->serialNumber); + char *s = new char[size+1]; + CFStringGetCString(tsd->serialNumber, s, size+1, kCFStringEncodingASCII); + std::string serial(s); //Copy the string to the stack + delete[] s; + + ControllerChangeEventData *data = new ControllerChangeEventData; + data->vid = tsd->vid; + data->pid = tsd->pid; + data->inserted = false; + tsd->event->signal(data); + + // Free the data we're no longer using now that the device is going away + CFRelease(tsd->serialNumber); + + kr = IOObjectRelease(tsd->notification); + + delete tsd; +} + +void ControllerListener::PrivateData::DeviceAdded(void *refCon, io_iterator_t iterator) { + io_service_t usbDevice; + kern_return_t kr; + + ControllerListener *parent = reinterpret_cast (refCon); + + while ((usbDevice = IOIteratorNext(iterator))) { + TellStickData *tsd = new TellStickData; + tsd->event = parent->d->event; + + // Get the serial number + CFStringRef serialRef = reinterpret_cast(IORegistryEntryCreateCFProperty( usbDevice, CFSTR("USB Serial Number" ), kCFAllocatorDefault, 0 )); + if (serialRef == NULL) { + //No serial number, we cannot continue. Sorry + continue; + } + + + CFNumberRef vidRef = reinterpret_cast (IORegistryEntryCreateCFProperty(usbDevice, CFSTR("idVendor"), kCFAllocatorDefault, 0)); + if (vidRef) { + CFNumberGetValue(vidRef, kCFNumberIntType, &(tsd->vid)); + CFRelease(vidRef); + } + + CFNumberRef pidRef = reinterpret_cast (IORegistryEntryCreateCFProperty(usbDevice, CFSTR("idProduct"), kCFAllocatorDefault, 0)); + if (pidRef) { + CFNumberGetValue(pidRef, kCFNumberIntType, &(tsd->pid)); + CFRelease(pidRef); + } + + CFStringRef serialNumberAsCFString = CFStringCreateCopy(kCFAllocatorDefault, serialRef); + tsd->serialNumber = serialNumberAsCFString; + CFRelease(serialRef); + + // Register for an interest notification of this device being removed. Use a reference to our + // private data as the refCon which will be passed to the notification callback. + kr = IOServiceAddInterestNotification(parent->d->gNotifyPort, usbDevice, kIOGeneralInterest, DeviceNotification, tsd, &(tsd->notification)); + + CFIndex size = CFStringGetLength(serialNumberAsCFString); + char *s = new char[size+1]; + CFStringGetCString(serialNumberAsCFString, s, size+1, kCFStringEncodingASCII); + std::string serial(s); //Copy the string to the stack + delete[] s; + + kr = IOObjectRelease(usbDevice); + + ControllerChangeEventData *data = new ControllerChangeEventData; + data->vid = tsd->vid; + data->pid = tsd->pid; + data->inserted = true; + tsd->event->signal(data); + } +}