00001 #include <stdio.h>
00002 #include <unistd.h>
00003 #include <stdlib.h>
00004 #include <ctype.h>
00005 #include <sys/errno.h>
00006 #include <sysexits.h>
00007 #include <mach/mach.h>
00008 #include <mach/mach_error.h>
00009 #include <IOKit/IOKitLib.h>
00010 #include <IOKit/IOCFPlugIn.h>
00011 #include <IOKit/hid/IOHIDLib.h>
00012 #include <IOKit/hid/IOHIDKeys.h>
00013 #include <CoreFoundation/CoreFoundation.h>
00014
00015 #include <map>
00016 #include <sstream>
00017 #include <iostream>
00018
00019 #include "AppleRemote.h"
00020
00021 AppleRemote* AppleRemote::_instance = 0;
00022 const char* const AppleRemote::AppleRemoteDeviceName = "AppleIRController";
00023 const int AppleRemote::REMOTE_SWITCH_COOKIE = 19;
00024
00025
00026 AppleRemote::Listener::~Listener()
00027 {
00028 }
00029
00030 AppleRemote&
00031 AppleRemote::instance()
00032 {
00033 if (_instance == 0)
00034 _instance = new AppleRemote();
00035
00036 return *_instance;
00037 }
00038
00039 AppleRemote::~AppleRemote()
00040 {
00041 stopListening();
00042 }
00043
00044 bool
00045 AppleRemote::isListeningToRemote()
00046 {
00047 return (hidDeviceInterface != NULL && !cookies.empty() && queue != NULL);
00048 }
00049
00050 void
00051 AppleRemote::setListener(AppleRemote::Listener* listener)
00052 {
00053 _listener = listener;
00054 }
00055
00056 void
00057 AppleRemote::startListening()
00058 {
00059 if (queue != NULL)
00060 return;
00061
00062 io_object_t hidDevice = _findAppleRemoteDevice();
00063
00064 if (hidDevice == 0) goto error;
00065 if (!_createDeviceInterface(hidDevice)) goto error;
00066 if (!_initCookies()) goto error;
00067 if (!_openDevice()) goto error;
00068 goto cleanup;
00069
00070 error:
00071 stopListening();
00072
00073 cleanup:
00074 IOObjectRelease(hidDevice);
00075 }
00076
00077 void
00078 AppleRemote::stopListening()
00079 {
00080 if (queue != NULL)
00081 {
00082 (*queue)->stop(queue);
00083 (*queue)->dispose(queue);
00084 (*queue)->Release(queue);
00085 queue = NULL;
00086 }
00087
00088 if (!cookies.empty())
00089 cookies.clear();
00090
00091 if (hidDeviceInterface != NULL)
00092 {
00093 (*hidDeviceInterface)->close(hidDeviceInterface);
00094 (*hidDeviceInterface)->Release(hidDeviceInterface);
00095 hidDeviceInterface = NULL;
00096 }
00097 }
00098
00099 void
00100 AppleRemote::runLoop()
00101 {
00102 CFRunLoopRun();
00103 }
00104
00105
00106 AppleRemote::AppleRemote() : openInExclusiveMode(true),
00107 hidDeviceInterface(0),
00108 queue(0),
00109 _listener(0)
00110 {
00111 _initCookieMap();
00112 }
00113
00114
00115 void
00116 AppleRemote::_initCookieMap()
00117 {
00118
00119 cookieToButtonMapping["14_12_11_6_5_"] = VolumePlus;
00120 cookieToButtonMapping["14_13_11_6_5_"] = VolumeMinus;
00121 cookieToButtonMapping["14_7_6_5_14_7_6_5_"] = Menu;
00122 cookieToButtonMapping["14_8_6_5_14_8_6_5_"] = Play;
00123 cookieToButtonMapping["14_9_6_5_14_9_6_5_"] = Right;
00124 cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left;
00125 cookieToButtonMapping["14_6_5_4_2_"] = RightHold;
00126 cookieToButtonMapping["14_6_5_3_2_"] = LeftHold;
00127 cookieToButtonMapping["14_6_5_14_6_5_"] = MenuHold;
00128 cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlaySleep;
00129 cookieToButtonMapping["19_"] = ControlSwitched;
00130
00131
00132 cookieToButtonMapping["31_29_28_18_"] = VolumePlus;
00133 cookieToButtonMapping["31_30_28_18_"] = VolumeMinus;
00134 cookieToButtonMapping["31_20_18_31_20_18_"] = Menu;
00135 cookieToButtonMapping["31_21_18_31_21_18_"] = Play;
00136 cookieToButtonMapping["31_22_18_31_22_18_"] = Right;
00137 cookieToButtonMapping["31_23_18_31_23_18_"] = Left;
00138 cookieToButtonMapping["31_18_4_2_"] = RightHold;
00139 cookieToButtonMapping["31_18_3_2_"] = LeftHold;
00140 cookieToButtonMapping["31_18_31_18_"] = MenuHold;
00141 cookieToButtonMapping["35_31_18_35_31_18_"] = PlaySleep;
00142 cookieToButtonMapping["39_"] = ControlSwitched;
00143 }
00144
00145
00146 io_object_t
00147 AppleRemote::_findAppleRemoteDevice()
00148 {
00149 CFMutableDictionaryRef hidMatchDictionary = 0;
00150 io_iterator_t hidObjectIterator = 0;
00151 io_object_t hidDevice = 0;
00152 IOReturn ioReturnValue;
00153
00154
00155 hidMatchDictionary = IOServiceMatching(AppleRemoteDeviceName);
00156
00157
00158 ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault,
00159 hidMatchDictionary,
00160 &hidObjectIterator);
00161
00162 if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0))
00163 hidDevice = IOIteratorNext(hidObjectIterator);
00164
00165
00166
00167 hidMatchDictionary = 0;
00168 return hidDevice;
00169 }
00170
00171 bool
00172 AppleRemote::_initCookies()
00173 {
00174 IOHIDDeviceInterface122** handle;
00175 CFArrayRef elements;
00176 IOReturn success;
00177
00178 handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
00179 success = (*handle)->copyMatchingElements(handle,
00180 NULL,
00181 (CFArrayRef*)&elements);
00182
00183 if (success == kIOReturnSuccess)
00184 {
00185 for (CFIndex i = 0; i < CFArrayGetCount(elements); i++)
00186 {
00187 CFDictionaryRef element;
00188 CFTypeRef object;
00189 long number;
00190 IOHIDElementCookie cookie;
00191
00192 element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
00193 object = CFDictionaryGetValue(element,
00194 CFSTR(kIOHIDElementCookieKey));
00195
00196 if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
00197 continue;
00198
00199 if (!CFNumberGetValue((CFNumberRef)object,
00200 kCFNumberLongType, &number))
00201 continue;
00202
00203 cookie = (IOHIDElementCookie)number;
00204
00205 cookies.push_back((int)cookie);
00206 }
00207 return true;
00208 }
00209 return false;
00210 }
00211
00212 bool
00213 AppleRemote::_createDeviceInterface(io_object_t hidDevice)
00214 {
00215 IOReturn ioReturnValue;
00216 IOCFPlugInInterface** plugInInterface = NULL;
00217 SInt32 score = 0;
00218
00219
00220 ioReturnValue
00221 = IOCreatePlugInInterfaceForService(hidDevice,
00222 kIOHIDDeviceUserClientTypeID,
00223 kIOCFPlugInInterfaceID,
00224 &plugInInterface, &score);
00225
00226 if (ioReturnValue == kIOReturnSuccess)
00227 {
00228 HRESULT plugInResult = (*plugInInterface)->QueryInterface
00229 (plugInInterface,
00230 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
00231 (LPVOID*) (&hidDeviceInterface));
00232
00233 if (plugInResult != S_OK)
00234 std::cerr << "AppleRemote Error: couldn't create device interface " << std::endl;
00235
00236
00237 if (plugInInterface)
00238 (*plugInInterface)->Release(plugInInterface);
00239 }
00240 return hidDeviceInterface != 0;
00241 }
00242
00243 bool
00244 AppleRemote::_openDevice()
00245 {
00246 CFRunLoopSourceRef eventSource;
00247 IOReturn ioReturnValue;
00248 IOHIDOptionsType openMode;
00249
00250
00251 if (openInExclusiveMode)
00252 openMode = kIOHIDOptionsTypeSeizeDevice;
00253 else
00254 openMode = kIOHIDOptionsTypeNone;
00255
00256 ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
00257
00258 if (ioReturnValue != KERN_SUCCESS)
00259 {
00260 std::cerr << "AppleRemote Error: when opening device" << std::endl;
00261 return false;
00262 }
00263 queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
00264 if (!queue)
00265 {
00266 std::cerr << "AppleRemote Error allocating queue" << std::endl;
00267 return false;
00268 }
00269
00270 HRESULT result = (*queue)->create(queue, 0, 12);
00271 if (result != S_OK || !queue)
00272 std::cerr << "AppleRemote Error creating queue" << std::endl;
00273
00274 for (std::vector<int>::iterator iter = cookies.begin();
00275 iter != cookies.end();
00276 iter++)
00277 {
00278 IOHIDElementCookie cookie = (IOHIDElementCookie)(*iter);
00279 (*queue)->addElement(queue, cookie, 0);
00280 }
00281
00282 ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
00283 if (ioReturnValue != KERN_SUCCESS)
00284 {
00285 std::cerr << "AppleRemote Error creating async event source" << std::endl;
00286 return false;
00287 }
00288
00289 ioReturnValue = (*queue)->setEventCallout(queue, QueueCallbackFunction,
00290 this, NULL);
00291 if (ioReturnValue != KERN_SUCCESS)
00292 {
00293 std::cerr << "AppleRemote Error registering callback" << std::endl;
00294 return false;
00295 }
00296
00297 CFRunLoopAddSource(CFRunLoopGetCurrent(),
00298 eventSource, kCFRunLoopDefaultMode);
00299 (*queue)->start(queue);
00300 return true;
00301 }
00302
00303 void
00304 AppleRemote::QueueCallbackFunction(void* target, IOReturn result,
00305 void* refcon, void* sender)
00306 {
00307 AppleRemote* remote = (AppleRemote*)target;
00308
00309 remote->_queueCallbackFunction(result,refcon,sender);
00310 }
00311
00312 void
00313 AppleRemote::_queueCallbackFunction(IOReturn result,
00314 void* , void* )
00315 {
00316 AbsoluteTime zeroTime = {0,0};
00317 SInt32 sumOfValues = 0;
00318 std::stringstream cookieString;
00319
00320 while (result == kIOReturnSuccess)
00321 {
00322 IOHIDEventStruct event;
00323
00324 result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
00325 if (result != kIOReturnSuccess)
00326 break;
00327
00328 if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie)
00329 {
00330 remoteId=event.value;
00331 _handleEventWithCookieString("19_",0);
00332 }
00333 else
00334 {
00335 sumOfValues+=event.value;
00336 cookieString << std::dec << (int)event.elementCookie << "_";
00337 }
00338 }
00339
00340 _handleEventWithCookieString(cookieString.str(), sumOfValues);
00341 }
00342
00343 void
00344 AppleRemote::_handleEventWithCookieString(std::string cookieString,
00345 SInt32 sumOfValues)
00346 {
00347 std::map<std::string,AppleRemote::Event>::iterator ii;
00348
00349 ii = cookieToButtonMapping.find(cookieString);
00350 if (ii != cookieToButtonMapping.end() && _listener)
00351 {
00352 AppleRemote::Event buttonid = ii->second;
00353 if (_listener)
00354 _listener->appleRemoteButton(buttonid, sumOfValues>0);
00355 }
00356 }