VirtualBox

Changeset 49289 in vbox for trunk


Ignore:
Timestamp:
Oct 25, 2013 1:28:04 PM (11 years ago)
Author:
vboxsync
Message:

OS X host: LEDs sync: introduce FIFO event queue between IOKit and Carbon callbacks (intermittent commit).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/src/platform/darwin/DarwinKeyboard.cpp

    r49090 r49289  
    313313    void             *pParentContainer;     /** A pointer to a VBoxHidsState_t instance where VBoxKbdState_t instance is stored */
    314314    CFIndex           idxPosition;          /** Position in global storage (used to simplify CFArray navigation when removing detached device) */
     315    uint64_t          cCapsLockTimeout;     /** KBD CAPS LOCK key hold timeout (some Apple keyboards only) */
    315316} VBoxKbdState_t;
    316317
     
    321322    VBoxLedState_t      guestState;         /** LED states that were stored during last broadcast and reflect a guest LED states */
    322323
     324    CFMutableArrayRef   pFifoEventQueue;    /** This queue will be appended in IOKit input callback. Carbon input callback will extract data from it */
     325    RTSEMMUTEX          fifoEventQueueLock; /** Lock for pFifoEventQueue */
     326
    323327    /* Carbon events data */
    324328    CFMachPortRef       pTapRef;
    325329    CFRunLoopSourceRef  pLoopSourceRef;
    326330} VBoxHidsState_t;
    327 
    328 /* A *sync* between IOKit and Carbon callbacks. */
    329 static VBoxKbdState_t *g_LastTouchedState;
    330331#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
    331332
     
    13971398}
    13981399
     1400/** Get HID Vendor ID */
     1401static uint32_t darwinHidVendorId(IOHIDDeviceRef pHidDeviceRef)
     1402{
     1403    CFTypeRef pNumberRef;
     1404    uint32_t  vendorId = 0;
     1405
     1406    AssertReturn(pHidDeviceRef, 0);
     1407
     1408    pNumberRef = IOHIDDeviceGetProperty(pHidDeviceRef, CFSTR(kIOHIDVendorIDKey));
     1409    if (pNumberRef)
     1410    {
     1411        if (CFGetTypeID(pNumberRef) == CFNumberGetTypeID())
     1412        {
     1413            if (CFNumberGetValue((CFNumberRef)pNumberRef, kCFNumberSInt32Type, &vendorId))
     1414                return vendorId;
     1415        }
     1416    }
     1417
     1418    return 0;
     1419}
     1420
    13991421/** Some keyboard devices might freeze after LEDs manipulation. We filter out such devices here.
    14001422 * In the list below, devices that known to have such issues. If you want to add new device,
     
    14041426{
    14051427#ifndef VBOX_WITHOUT_KBD_LEDS_SYNC_FILTERING
    1406     bool      fSupported = true;
    1407     CFTypeRef pNumberRef;
    1408     uint32_t  vendorId = 0;
    1409 
    1410     AssertReturn(pHidDeviceRef, false);
    1411 
    1412     pNumberRef = IOHIDDeviceGetProperty(pHidDeviceRef, CFSTR(kIOHIDVendorIDKey));
    1413     if (pNumberRef)
    1414     {
    1415         if (CFGetTypeID(pNumberRef) == CFNumberGetTypeID())
    1416         {
    1417             if (CFNumberGetValue((CFNumberRef)pNumberRef, kCFNumberSInt32Type, &vendorId))
    1418             {
    1419                 switch (vendorId)
    1420                 {
    1421                     case 0x05D5: /** Genius (detected with GK-04008/C keyboard) */
    1422                         fSupported = false;
    1423                         break;
    1424                 }
    1425 
    1426                 Log2(("HID device Vendor ID 0x%X %s in the list of supported devices.\n", vendorId, (fSupported ? "is" : "is not")));
    1427             }
    1428         }
    1429     }
     1428    bool     fSupported = true;
     1429    uint32_t vendorId = darwinHidVendorId(pHidDeviceRef);
     1430
     1431    switch (vendorId)
     1432    {
     1433        case 0x05D5: /** Genius (detected with GK-04008/C keyboard) */
     1434            fSupported = false;
     1435            break;
     1436    }
     1437
     1438    Log2(("HID device Vendor ID 0x%X %s in the list of supported devices.\n", vendorId, (fSupported ? "is" : "is not")));
    14301439
    14311440    return fSupported;
     
    14351444}
    14361445
     1446typedef struct VBoxKbdEvent_t {
     1447    VBoxKbdState_t *pKbd;
     1448    uint32_t        iKeyCode;
     1449    uint64_t        tsKeyDown;
     1450    uint64_t        tsKeyUp;
     1451} VBoxKbdEvent_t;
     1452
    14371453/** IOKit key press callback. Triggered before Carbon callback. We remember which keyboard produced a keypress here. */
    14381454static void darwinHidInputCallback(void *pData, IOReturn unused, void *unused1, IOHIDValueRef pValueRef)
     
    14461462    AssertReturnVoid(pElementRef);
    14471463
    1448     if (IOHIDElementGetUsagePage(pElementRef) == kHIDPage_KeyboardOrKeypad)        /* Keyboard or keypad event */
    1449         if (IOHIDValueGetIntegerValue(pValueRef) == 1)                             /* key has been pressed down */
    1450             if (IOHIDElementGetUsage(pElementRef) == kHIDUsage_KeyboardCapsLock || /* CapsLock key has been pressed */
    1451                 IOHIDElementGetUsage(pElementRef) == kHIDUsage_KeypadNumLock)      /* ... or NumLock key has been pressed */
     1464    uint32_t usage = IOHIDElementGetUsage(pElementRef);
     1465
     1466    if (IOHIDElementGetUsagePage(pElementRef) == kHIDPage_KeyboardOrKeypad)    /* Keyboard or keypad event */
     1467        if (usage == kHIDUsage_KeyboardCapsLock ||                             /* CapsLock key has been pressed */
     1468            usage == kHIDUsage_KeypadNumLock)                                  /* ... or NumLock key has been pressed */
     1469        {
     1470            VBoxKbdState_t *pKbd = (VBoxKbdState_t *)pData;
     1471
     1472            if (pKbd && pKbd->pParentContainer)
    14521473            {
    1453                 Log2(("A modifier key has been pressed\n"));
    1454                 g_LastTouchedState = (VBoxKbdState_t *)pData;
     1474                bool fKeyDown = (IOHIDValueGetIntegerValue(pValueRef) == 1);
     1475
     1476                VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
     1477                VBoxKbdEvent_t  *pEvent = NULL;;
     1478                CFIndex          iQueue = 0;
     1479
     1480                AssertReturnVoid(pHidState);
     1481
     1482                if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
     1483                    return ;
     1484
     1485                /* Allocate new event data on Key-Down, find a cached one on Key-Up */
     1486                if (fKeyDown)
     1487                    pEvent = (VBoxKbdEvent_t *)malloc(sizeof(VBoxKbdEvent_t));
     1488                else
     1489                {
     1490                    for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pFifoEventQueue); i++)
     1491                    {
     1492                        VBoxKbdEvent_t *pCachedEvent = (VBoxKbdEvent_t *)CFArrayGetValueAtIndex(pHidState->pFifoEventQueue, i);
     1493                        if (pCachedEvent && pCachedEvent->pKbd == pKbd && pCachedEvent->iKeyCode == usage)
     1494                        {
     1495                            pEvent = pCachedEvent;
     1496                            iQueue = i;
     1497                            break;
     1498                        }
     1499                    }
     1500                }
     1501
     1502                if (pEvent)
     1503                {
     1504                    if (usage == kHIDUsage_KeyboardCapsLock && pKbd->cCapsLockTimeout)
     1505                    {
     1506                        if (fKeyDown)
     1507                            pEvent->tsKeyDown = RTTimeSystemMilliTS();
     1508                        else
     1509                            pEvent->tsKeyUp = RTTimeSystemMilliTS();
     1510                    }
     1511
     1512                    if (fKeyDown)
     1513                    {
     1514                        Log2(("IOHID: KBD %d: Modifier Key-Down\n", (int)pKbd->idxPosition));
     1515
     1516                        pEvent->pKbd = pKbd;
     1517                        pEvent->iKeyCode = usage;
     1518                        pEvent->tsKeyUp = 0;
     1519
     1520                        /* Append FIFO event queue */
     1521                        CFArrayAppendValue(pHidState->pFifoEventQueue, (void *)pEvent);
     1522                    }
     1523                    else
     1524                    {
     1525                        uint64_t tsDiff = pEvent->tsKeyUp - pEvent->tsKeyDown;
     1526                        if (tsDiff < pKbd->cCapsLockTimeout)
     1527                        {
     1528                            free(pEvent);
     1529                            CFArrayRemoveValueAtIndex(pHidState->pFifoEventQueue, iQueue);
     1530                            Log2(("IOHID: KBD %d: Modifier Key-Up (Key-Down %llu ms ago). Apple keyboard, removed from queue\n", (int)pKbd->idxPosition, tsDiff));
     1531                        }
     1532                        else
     1533                            Log2(("IOHID: KBD %d: Modifier Key-Up (Key-Down %llu ms ago)\n", (int)pKbd->idxPosition, tsDiff));
     1534                    }
     1535                }
     1536                else
     1537                {
     1538                    if (fKeyDown)
     1539                        Log2(("IOHID: unable to find memory to queue KBD %d event\n", (int)pKbd->idxPosition));
     1540                    else
     1541                        Log2(("IOHID: unable to find KBD %d event in queue\n", (int)pKbd->idxPosition));
     1542                }
     1543
     1544                RTSemMutexRelease(pHidState->fifoEventQueueLock);
    14551545            }
    1456 
     1546            else
     1547                Log2(("IOHID: No KBD: A modifier key has been pressed\n"));
     1548        }
    14571549}
    14581550
    14591551/** Carbon key press callback. Triggered after IOKit callback. We update keyboard (catched in darwinHidInputCallback()) internal state here. */
    1460 static CGEventRef darwinCarbonGlobalKeyPressCallback(CGEventTapProxy unused, CGEventType type, CGEventRef pEventRef, void *unused1)
     1552static CGEventRef darwinCarbonGlobalKeyPressCallback(CGEventTapProxy unused, CGEventType unused1, CGEventRef pEventRef, void *pData)
    14611553{
    14621554    (void)unused;
    14631555    (void)unused1;
    1464 
    1465     /* Skip events we are not interested in. */
    1466     if (type != kCGEventKeyDown && type != kCGEventFlagsChanged)
    1467         return pEventRef;
    14681556
    14691557    CGEventFlags fMask = CGEventGetFlags(pEventRef);
     
    14721560    CGKeyCode    key   = CGEventGetIntegerValueField(pEventRef, kCGKeyboardEventKeycode);
    14731561
     1562    VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pData; AssertReturn(pHidState, pEventRef);
     1563    AssertReturn(pHidState, pEventRef);
     1564
     1565    if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
     1566        return pEventRef;
     1567
    14741568    if (key == kHIDUsage_KeyboardCapsLock ||
    14751569        key == kHIDUsage_KeypadNumLock)
    14761570    {
    1477         Log2(("carbon event: caps=%s, num=%s\n", VBOX_BOOL_TO_STR_STATE(fCaps), VBOX_BOOL_TO_STR_STATE(fNum)));
    1478 
    1479         VBoxKbdState_t  *pKbd = g_LastTouchedState;
    1480         if (pKbd)
    1481         {
     1571        /* Extract last touched state from FIFO event queue */
     1572        VBoxKbdEvent_t *pEvent = NULL;
     1573        CFIndex         iEvent = 0;
     1574
     1575        /* Find last occured KBD event in queue (ignoring those events which not match CAPS LOCK timeout criteria). */
     1576        for (CFIndex i = 0; i < CFArrayGetCount(pHidState->pFifoEventQueue); i++)
     1577        {
     1578            pEvent = (VBoxKbdEvent_t *)CFArrayGetValueAtIndex(pHidState->pFifoEventQueue, i);
     1579            if (!pEvent) continue;
     1580            if (!pEvent->pKbd) continue;
     1581
     1582            if (pEvent->iKeyCode == kHIDUsage_KeypadNumLock
     1583             || (pEvent->iKeyCode == kHIDUsage_KeyboardCapsLock &&
     1584                 (RTTimeSystemMilliTS() - pEvent->tsKeyDown) > pEvent->pKbd->cCapsLockTimeout))
     1585            {
     1586                /* Found one. Keep its index in queue in order to remove it later. */
     1587                iEvent = i;
     1588                Log2(("CARBON: Found event in queue: %d (KBD %d, tsKeyDown=%llu, pressed %llu ms ago)\n", (int)i, (int)pEvent->pKbd->idxPosition, pEvent->tsKeyDown, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
     1589                break;
     1590            }
     1591            else
     1592                Log2(("CARBON: skip CAPS LOCK event %d (KBD %d) (tsKeyDown=%llu, pressed %llu ms ago)\n", (int)i, (int)pEvent->pKbd->idxPosition, pEvent->tsKeyDown, RTTimeSystemMilliTS() - pEvent->tsKeyDown));
     1593
     1594            pEvent = NULL;
     1595        }
     1596
     1597        if (pEvent)
     1598        {
     1599            VBoxKbdState_t *pKbd = pEvent->pKbd;
     1600
     1601            Log2(("CARBON: KBD %d: caps=%s, num=%s. tsKeyDown=%llu, tsKeyUp=%llu [tsDiff=%llu ms]. %d events in queue.\n",
     1602                (int)pKbd->idxPosition, VBOX_BOOL_TO_STR_STATE(fCaps), VBOX_BOOL_TO_STR_STATE(fNum),
     1603                pEvent->tsKeyDown, pEvent->tsKeyUp, pEvent->tsKeyUp - pEvent->tsKeyDown,
     1604                CFArrayGetCount(pHidState->pFifoEventQueue)));
     1605
    14821606            pKbd->LED.fCapsLockOn = fCaps;
    14831607            pKbd->LED.fNumLockOn  = fNum;
    14841608
    14851609            /* Silently resync last touched KBD device */
    1486             VBoxHidsState_t *pHidState = (VBoxHidsState_t *)pKbd->pParentContainer;
    14871610            if (pHidState)
    14881611            {
     
    14971620            }
    14981621
    1499             /* Forget device */
    1500             g_LastTouchedState = NULL;
    1501         }
    1502     }
     1622            /* Forget event */
     1623            CFArrayRemoveValueAtIndex(pHidState->pFifoEventQueue, iEvent);
     1624            free(pEvent);
     1625        }
     1626        else
     1627            Log2(("CARBON: No KBD to take care when modifier key has been pressed: caps=%s, num=%s\n", VBOX_BOOL_TO_STR_STATE(fCaps), VBOX_BOOL_TO_STR_STATE(fNum)));
     1628    }
     1629
     1630    RTSemMutexRelease(pHidState->fifoEventQueueLock);
    15031631
    15041632    return pEventRef;
     
    15171645
    15181646    Log2(("Forget KBD %d\n", (int)pKbd->idxPosition));
     1647
     1648    //if (RT_FAILURE(RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT)))
     1649    //    return ;
     1650
    15191651    CFArrayRemoveValueAtIndex(pHidState->pDeviceCollection, pKbd->idxPosition);
    15201652    free(pKbd);
     1653
     1654    //RTSemMutexRelease(pHidState->fifoEventQueueLock);
    15211655}
    15221656
     
    15531687                pKbd->pParentContainer = (void *)pHidState;
    15541688                pKbd->idxPosition = CFArrayGetCount(pHidState->pDeviceCollection);
     1689
     1690                /* Some Apple keyboards have CAPS LOCK key timeout. According to corresponding
     1691                 * kext plist files, it is equals to 75 ms. For such devices we only add info into our FIFO event
     1692                 * queue if the time between Key-Down and Key-Up events >= 75 ms. */
     1693                pKbd->cCapsLockTimeout = (darwinHidVendorId(pKbd->pDevice) == kIOUSBVendorIDAppleComputer) ? 75 : 0;
    15551694
    15561695                CFDictionaryRef elementMatchingDict = darwinQueryLedElementMatchingDictionary();
     
    16241763    CGEventMask   fMask = CGEventMaskBit(kCGEventFlagsChanged);
    16251764
    1626     g_LastTouchedState = NULL;
    1627 
    1628     pTapRef = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, fMask, darwinCarbonGlobalKeyPressCallback, NULL);
     1765    AssertReturn(pHidState, kIOReturnError);
     1766
     1767    /* Create FIFO event queue for keyboard events */
     1768    pHidState->pFifoEventQueue = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
     1769    AssertReturn(pHidState->pFifoEventQueue, kIOReturnError);
     1770
     1771    /* Create Lock for FIFO event queue */
     1772    if (RT_FAILURE(RTSemMutexCreate(&pHidState->fifoEventQueueLock)))
     1773    {
     1774        Log2(("Unable to create Lock for FIFO event queue\n"));
     1775        CFRelease(pHidState->pFifoEventQueue);
     1776        pHidState->pFifoEventQueue = NULL;
     1777        return kIOReturnError;
     1778    }
     1779
     1780    pTapRef = CGEventTapCreate(kCGSessionEventTap, kCGTailAppendEventTap, 0, fMask, darwinCarbonGlobalKeyPressCallback, (void *)pHidState);
    16291781    if (pTapRef)
    16301782    {
     
    16581810    AssertReturnVoid(pHidState->pTapRef);
    16591811    AssertReturnVoid(pHidState->pLoopSourceRef);
    1660 
    1661     g_LastTouchedState = NULL;
     1812    AssertReturnVoid(pHidState->pFifoEventQueue);
    16621813
    16631814    CGEventTapEnable(pHidState->pTapRef, false);
     
    16651816    CFRelease(pHidState->pLoopSourceRef);
    16661817    CFRelease(pHidState->pTapRef);
     1818
     1819    RTSemMutexRequest(pHidState->fifoEventQueueLock, RT_INDEFINITE_WAIT);
     1820    CFRelease(pHidState->pFifoEventQueue);
     1821    pHidState->pFifoEventQueue = NULL;
     1822    RTSemMutexRelease(pHidState->fifoEventQueueLock);
     1823
     1824    RTSemMutexDestroy(pHidState->fifoEventQueueLock);
    16671825}
    16681826#endif /* !VBOX_WITH_KBD_LEDS_SYNC */
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette