Ticket #2017: appleremote0.20_friendly.patch

File appleremote0.20_friendly.patch, 16.9 KB (added by mythtv@…, 15 years ago)

'release remote' version for 0.20

  • libs/libmyth/AppleRemote.h

     
     1#ifndef APPLEREMOTE
     2#define APPLEREMOTE
     3
     4#include <string>
     5#include <vector>
     6#include <map>
     7
     8#include <IOKit/IOKitLib.h>
     9#include <IOKit/IOCFPlugIn.h>
     10#include <IOKit/hid/IOHIDLib.h>
     11#include <IOKit/hid/IOHIDKeys.h>
     12#include <CoreFoundation/CoreFoundation.h>
     13
     14class AppleRemote {
     15public:
     16  enum Event {
     17    VolumePlus = 0,
     18    VolumeMinus,
     19    Menu,
     20    Play,
     21    Right,
     22    Left,
     23    RightHold,
     24    LeftHold,
     25    MenuHold,
     26    PlaySleep,
     27    ControlSwitched
     28  };
     29
     30  class Listener {
     31  public:
     32    virtual ~Listener();
     33    virtual void appleRemoteButton(Event button, bool pressedDown) = 0;
     34  };
     35
     36  static AppleRemote& instance();
     37  ~AppleRemote();
     38
     39  bool isRemoteAvailable();
     40  bool isListeningToRemote();
     41
     42  void setListener(Listener* listener);
     43  Listener* listener() { return _listener; }
     44  void setOpenInExclusiveMode(bool in) { openInExclusiveMode = in; };
     45  bool isOpenInExclusiveMode() { return openInExclusiveMode; };
     46  void startListening();
     47  void stopListening();
     48  void runLoop();
     49
     50protected:
     51  AppleRemote(); // will be a singleton class
     52
     53  static AppleRemote* _instance;
     54  static const char* const AppleRemoteDeviceName;
     55  static const int REMOTE_SWITCH_COOKIE;
     56
     57
     58private:
     59  bool openInExclusiveMode;
     60  IOHIDDeviceInterface** hidDeviceInterface;
     61  IOHIDQueueInterface** queue;
     62  std::vector<int> cookies;
     63  std::map< std::string, Event > cookieToButtonMapping;
     64  int remoteId;
     65  Listener* _listener;
     66
     67  void _initCookieMap();
     68  io_object_t _findAppleRemoteDevice();
     69  bool _initCookies();
     70  bool _createDeviceInterface(io_object_t hidDevice);
     71  bool _openDevice();
     72
     73  static void QueueCallbackFunction(void* target, IOReturn result,
     74                                    void* refcon, void* sender);
     75  void _queueCallbackFunction(IOReturn result, void* refcon, void* sender);
     76  void _handleEventWithCookieString(std::string cookieString, SInt32 sumOfValues);
     77};
     78
     79#endif // APPLEREMOTE
  • libs/libmyth/AppleRemoteListener.cpp

     
     1#include <qapplication.h>
     2#include <qkeysequence.h>
     3#include "lircevent.h"
     4
     5#include "AppleRemoteListener.h"
     6
     7AppleRemoteListener::AppleRemoteListener(QObject* mainWindow_): mainWindow(mainWindow_) {
     8}
     9
     10void
     11AppleRemoteListener::appleRemoteButton(AppleRemote::Event button, bool pressedDown) {
     12  char* code = 0;
     13  bool separateRelease = false;
     14  switch (button) {
     15    case AppleRemote::VolumePlus: {
     16        code="Up";
     17        separateRelease=true;
     18        break;
     19    }
     20    case AppleRemote::VolumeMinus: {
     21        code="Down";
     22        separateRelease=true;
     23        break;
     24    }
     25    case AppleRemote::Menu: {
     26        code="Esc";
     27        break;
     28    }
     29    case AppleRemote::Play: {
     30        code="Enter";
     31        break;
     32    }
     33    case AppleRemote::Right: {
     34        code="Right";
     35        break;
     36    }
     37    case AppleRemote::Left: {
     38        code="Left";
     39        break;
     40    }
     41    case AppleRemote::RightHold: {
     42        code="End";
     43        separateRelease=true;
     44        break;
     45    }
     46    case AppleRemote::LeftHold: {
     47        code="Home";
     48        separateRelease=true;
     49        break;
     50    }
     51    case AppleRemote::MenuHold: {
     52        code="M";
     53        break;
     54    }
     55    case AppleRemote::PlaySleep: {
     56        code="P";
     57        break;
     58    }
     59    case AppleRemote::ControlSwitched: {
     60        return;
     61    }
     62  }
     63  QKeySequence a(code);
     64  int keycode = 0;
     65  for (unsigned int i = 0; i < a.count(); i++)
     66           {
     67                keycode = a[i];
     68
     69                QApplication::postEvent(mainWindow, new LircKeycodeEvent(code,
     70                                        keycode, pressedDown));
     71                if (!separateRelease) {
     72                        QApplication::postEvent(mainWindow, new LircKeycodeEvent(code,
     73                                        keycode, false));
     74                }
     75            }
     76 
     77}
  • libs/libmyth/AppleRemote.cpp

     
     1#include <stdio.h>
     2#include <unistd.h>
     3#include <stdlib.h>
     4#include <ctype.h>
     5#include <sys/errno.h>
     6#include <sysexits.h>
     7#include <mach/mach.h>
     8#include <mach/mach_error.h>
     9#include <IOKit/IOKitLib.h>
     10#include <IOKit/IOCFPlugIn.h>
     11#include <IOKit/hid/IOHIDLib.h>
     12#include <IOKit/hid/IOHIDKeys.h>
     13#include <CoreFoundation/CoreFoundation.h>
     14
     15
     16#include <map>
     17#include <sstream>
     18#include <iostream>
     19
     20#include "AppleRemote.h"
     21
     22// static
     23AppleRemote* AppleRemote::_instance = 0;
     24// static
     25const char* const AppleRemote::AppleRemoteDeviceName = "AppleIRController";
     26// static
     27const int AppleRemote::REMOTE_SWITCH_COOKIE=19;
     28
     29AppleRemote::Listener::~Listener() {
     30}
     31
     32// public
     33AppleRemote&
     34AppleRemote::instance() {
     35  if (_instance==0) {
     36    _instance = new AppleRemote();
     37  }
     38  return *_instance;
     39}
     40
     41AppleRemote::~AppleRemote() {
     42  stopListening();
     43}
     44
     45bool
     46AppleRemote::isRemoteAvailable() {
     47  io_object_t hidDevice = _findAppleRemoteDevice();
     48  if (hidDevice != 0) {
     49    IOObjectRelease(hidDevice);
     50    return true;
     51  }
     52  return false;
     53}
     54
     55bool
     56AppleRemote::isListeningToRemote() {
     57  return (hidDeviceInterface != NULL && !cookies.empty() && queue != NULL);
     58}
     59
     60void
     61AppleRemote::setListener(AppleRemote::Listener* listener) {
     62  _listener = listener;
     63}
     64
     65void
     66AppleRemote::startListening() {
     67  if (queue != NULL) {
     68        // already listening
     69        return;
     70  }
     71  io_object_t hidDevice = _findAppleRemoteDevice();
     72  if (hidDevice == 0) goto error;
     73  if (!_createDeviceInterface(hidDevice)) goto error;
     74  if (!_initCookies()) goto error;
     75  if (!_openDevice()) goto error;
     76  goto cleanup;
     77
     78error:
     79  stopListening();
     80
     81cleanup:
     82  IOObjectRelease(hidDevice);
     83}
     84
     85void
     86AppleRemote::stopListening() {
     87  if (queue != NULL) {
     88    (*queue)->stop(queue);
     89    (*queue)->dispose(queue);
     90    (*queue)->Release(queue);
     91    queue = NULL;
     92  }
     93
     94  if (!cookies.empty()) {
     95    cookies.clear();
     96  }
     97
     98  if (hidDeviceInterface != NULL) {
     99    (*hidDeviceInterface)->close(hidDeviceInterface);
     100    (*hidDeviceInterface)->Release(hidDeviceInterface);
     101    hidDeviceInterface = NULL;
     102  }
     103}
     104
     105void
     106AppleRemote::runLoop() {
     107  CFRunLoopRun();
     108}
     109
     110// protected
     111AppleRemote::AppleRemote():
     112    openInExclusiveMode(true),
     113    hidDeviceInterface(0),
     114    queue(0),
     115    _listener(0) {
     116  _initCookieMap();
     117}
     118
     119// private
     120void
     121AppleRemote::_initCookieMap() {
     122  cookieToButtonMapping["14_12_11_6_5_"] = VolumePlus;
     123  cookieToButtonMapping["14_13_11_6_5_"] = VolumeMinus;
     124  cookieToButtonMapping["14_7_6_5_14_7_6_5_"] = Menu;
     125  cookieToButtonMapping["14_8_6_5_14_8_6_5_"] = Play;
     126  cookieToButtonMapping["14_9_6_5_14_9_6_5_"] = Right;
     127  cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left;
     128  cookieToButtonMapping["14_6_5_4_2_"] = RightHold;
     129  cookieToButtonMapping["14_6_5_3_2_"] = LeftHold;
     130  cookieToButtonMapping["14_6_5_14_6_5_"] = MenuHold;
     131  cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlaySleep;
     132  cookieToButtonMapping["19_"] = ControlSwitched;
     133}
     134
     135// private
     136io_object_t
     137AppleRemote::_findAppleRemoteDevice() {
     138  CFMutableDictionaryRef hidMatchDictionary = 0;
     139  io_iterator_t hidObjectIterator = 0;
     140  io_object_t hidDevice = 0;
     141
     142  hidMatchDictionary = IOServiceMatching(AppleRemoteDeviceName);
     143
     144  // check for matching devices
     145  IOReturn ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault,
     146                                               hidMatchDictionary,
     147                                               &hidObjectIterator);
     148  if ((ioReturnValue == kIOReturnSuccess)
     149                       && (hidObjectIterator != 0)) {
     150    hidDevice = IOIteratorNext(hidObjectIterator);
     151  }
     152
     153  // IOServiceGetMatchingServices consumes a reference to the dictionary, so we
     154  // don't need to release the dictionary ref.
     155  hidMatchDictionary = 0;
     156  return hidDevice;
     157}
     158
     159// private
     160bool
     161AppleRemote::_initCookies() {
     162  IOHIDDeviceInterface122** handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
     163
     164  CFArrayRef elements;
     165  IOReturn success =
     166  (*handle)->copyMatchingElements(handle,NULL,(CFArrayRef*)&elements);
     167
     168  if (success == kIOReturnSuccess) {
     169    for (CFIndex i = 0; i < CFArrayGetCount(elements); i++) {
     170      CFDictionaryRef element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
     171
     172      CFTypeRef object = CFDictionaryGetValue(element,
     173                                            CFSTR(kIOHIDElementCookieKey));
     174      if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
     175      long number;
     176      if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType, &number)) {
     177        continue;
     178      }
     179      IOHIDElementCookie cookie = (IOHIDElementCookie)number;
     180
     181      cookies.push_back((int)cookie);
     182    }
     183    return true;
     184  }
     185  return false;
     186}
     187
     188// private
     189bool
     190AppleRemote::_createDeviceInterface(io_object_t hidDevice) {
     191  SInt32 score = 0;
     192  IOCFPlugInInterface** plugInInterface = NULL;
     193
     194  IOReturn ioReturnValue = IOCreatePlugInInterfaceForService(hidDevice,
     195                                                    kIOHIDDeviceUserClientTypeID,
     196                                                    kIOCFPlugInInterfaceID,
     197                                                    &plugInInterface,
     198                                                    &score);
     199  if (ioReturnValue == kIOReturnSuccess) {
     200    HRESULT plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
     201                                CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
     202                                (LPVOID*) (&hidDeviceInterface));
     203    if (plugInResult != S_OK) {
     204      std::cerr << "AppleRemote Error: couldn't create device interface " << std::endl;
     205    }
     206    // Release
     207    if (plugInInterface) (*plugInInterface)->Release(plugInInterface);
     208  }
     209  return hidDeviceInterface != 0;
     210}
     211
     212// private
     213bool
     214AppleRemote::_openDevice() {
     215  IOHIDOptionsType openMode = kIOHIDOptionsTypeNone;
     216  if (openInExclusiveMode) openMode = kIOHIDOptionsTypeSeizeDevice;
     217  IOReturn ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
     218
     219  if (ioReturnValue != KERN_SUCCESS) {
     220    std::cerr << "AppleRemote Error: when opening device" << std::endl;
     221    return false;
     222  }
     223  queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
     224  if (!queue) {
     225    std::cerr << "AppleRemote Error allocating queue" << std::endl;
     226    return false;
     227  }
     228
     229  HRESULT result = (*queue)->create(queue, 0, 12);
     230  if (result != S_OK || !queue) {
     231        std::cerr << "AppleRemote Error creating queue" << std::endl;
     232  }
     233
     234  for (std::vector<int>::iterator iter = cookies.begin();
     235       iter != cookies.end();
     236       iter++) {
     237    IOHIDElementCookie cookie = (IOHIDElementCookie)(*iter);
     238    (*queue)->addElement(queue, cookie, 0);
     239  }
     240
     241  CFRunLoopSourceRef eventSource;
     242  ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
     243  if (ioReturnValue != KERN_SUCCESS) {
     244    std::cerr << "AppleRemote Error creating async event source" << std::endl;
     245    return false;
     246  }
     247
     248  ioReturnValue = (*queue)->setEventCallout(queue,QueueCallbackFunction, this, NULL);
     249  if (ioReturnValue != KERN_SUCCESS) {
     250    std::cerr << "AppleRemote Error registering callback" << std::endl;
     251    return false;
     252  }
     253  CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
     254  (*queue)->start(queue);
     255  return true;
     256}
     257
     258// static
     259void
     260AppleRemote::QueueCallbackFunction(void* target, IOReturn result,
     261                                    void* refcon, void* sender) {
     262  AppleRemote* remote = (AppleRemote*)target;
     263  remote->_queueCallbackFunction(result,refcon,sender);
     264}
     265
     266void
     267AppleRemote::_queueCallbackFunction(IOReturn result, void* /*refcon*/, void* /*sender*/) {
     268  AbsoluteTime zeroTime = {0,0};
     269  SInt32 sumOfValues = 0;
     270  std::stringstream cookieString;
     271
     272  while (result == kIOReturnSuccess) {
     273    IOHIDEventStruct event;
     274    result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
     275    if (result != kIOReturnSuccess) break;
     276
     277    if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie) {
     278      remoteId=event.value;
     279      _handleEventWithCookieString("19_",0);
     280    } else {
     281      sumOfValues+=event.value;
     282      cookieString << std::dec << (int)event.elementCookie << "_";
     283    }
     284  }
     285
     286  _handleEventWithCookieString(cookieString.str(), sumOfValues);
     287}
     288
     289void
     290AppleRemote::_handleEventWithCookieString(std::string cookieString,
     291                                          SInt32 sumOfValues) {
     292  std::map<std::string,AppleRemote::Event>::iterator ii = cookieToButtonMapping.find(cookieString);
     293  if (ii  != cookieToButtonMapping.end() && _listener) {
     294    AppleRemote::Event buttonid = ii->second;
     295    if (_listener) _listener->appleRemoteButton(buttonid, sumOfValues>0);
     296  }
     297}
  • libs/libmyth/libmyth.pro

     
    7575macx {
    7676    HEADERS += audiooutputca.h   screensaver-osx.h   DisplayResOSX.h
    7777    SOURCES += audiooutputca.cpp screensaver-osx.cpp DisplayResOSX.cpp
    78     HEADERS += util-osx.h
    79     SOURCES += util-osx.cpp
     78    HEADERS += util-osx.h AppleRemote.h AppleRemoteListener.h lircevent.h
     79    SOURCES += util-osx.cpp AppleRemote.cpp AppleRemoteListener.cpp lircevent.cpp
    8080
    8181    # Mac OS X Frameworks
    8282    FWKS = ApplicationServices AudioUnit Carbon CoreAudio IOKit
  • libs/libmyth/AppleRemoteListener.h

     
     1#ifndef APPLEREMOTELISTENER
     2#define APPLEREMOTELISTENER
     3
     4#include "AppleRemote.h"
     5
     6#include <iostream>
     7
     8class AppleRemoteListener: public AppleRemote::Listener {
     9public:
     10        AppleRemoteListener(QObject* mainWindow_);
     11    // virtual
     12    void appleRemoteButton(AppleRemote::Event button, bool pressedDown);
     13private:
     14        QObject *mainWindow;
     15};
     16
     17#endif // APPLEREMOTELISTENER
     18 No newline at end of file
  • libs/libmythui/mythmainwindow.cpp

     
    1010#include <qwindowsystem_qws.h>
    1111#endif
    1212#ifdef Q_WS_MACX
    13 #import <HIToolbox/Menus.h>   // For GetMBarHeight()
     13#include <HIToolbox/Menus.h>   // For GetMBarHeight()
     14#include <pthread.h>
     15#include "lircevent.h"
     16#include "AppleRemote.h"
     17#include "AppleRemoteListener.h"
    1418#endif
    1519
    1620#ifdef USE_LIRC
     
    4650}
    4751#endif
    4852
     53#ifdef Q_WS_MACX
     54static void* SpawnAppleRemote(void* param)
     55{
     56        MythMainWindow *main_window = (MythMainWindow *)param;
     57        AppleRemoteListener *arl = new AppleRemoteListener(main_window);
     58        AppleRemote& remote(AppleRemote::instance());
     59        remote.setListener(arl);
     60        remote.startListening();
     61        if (!remote.isListeningToRemote()) {
     62                return NULL;
     63        }
     64        remote.runLoop();
     65        return NULL;
     66}
     67#endif
     68
    4969class KeyContext
    5070{
    5171  public:
     
    214234    pthread_create(&lirc_tid, &attr, SpawnLirc, this);
    215235#endif
    216236
     237#ifdef Q_WS_MACX
     238    pthread_t appleremote_tid;
     239    pthread_attr_t arattr;
     240    pthread_attr_init(&arattr);
     241    pthread_attr_setdetachstate(&arattr, PTHREAD_CREATE_DETACHED);
     242
     243    pthread_create(&appleremote_tid, &arattr, SpawnAppleRemote, this);
     244#endif
     245
    217246    d->keyContexts.setAutoDelete(true);
    218247
    219248    RegisterKey("Global", "UP", "Up Arrow", "Up");
     
    357386    d->repaintRegion = d->repaintRegion.unite(pe->region());
    358387}
    359388
     389#ifdef Q_WS_MACX
     390bool MythMainWindow::event(QEvent* e) {
     391        switch (e->type()) {
     392                case QEvent::WindowActivate: {
     393                        AppleRemote::instance().startListening();
     394                }break;
     395                case QEvent::WindowDeactivate: {
     396                        // relinquish the remote
     397                        AppleRemote::instance().stopListening();
     398                }break;
     399        }
     400        return  QWidget::event(e);
     401}
     402#endif
     403
    360404void MythMainWindow::Init(void)
    361405{
    362406    gContext->GetScreenSettings(d->xbase, d->screenwidth, d->wmult,
     
    901945            }
    902946        }
    903947    }
    904 #ifdef USE_LIRC
     948#if defined(USE_LIRC) || defined(Q_WS_MACX)
    905949    else if (ce->type() == kLircKeycodeEventType && !d->ignore_lirc_keys)
    906950    {
    907951        LircKeycodeEvent *lke = (LircKeycodeEvent *)ce;
  • libs/libmythui/mythmainwindow.h

     
    9494    void customEvent(QCustomEvent *ce);
    9595    void closeEvent(QCloseEvent *e);
    9696    void paintEvent(QPaintEvent *e);
     97#ifdef Q_WS_MACX
     98    bool event(QEvent* e);
     99#endif
    97100   
    98101    void ExitToMainMenu();
    99102