void HIDGamepad::valueChanged(IOHIDValueRef value) { IOHIDElementCookie cookie = IOHIDElementGetCookie(IOHIDValueGetElement(value)); HIDGamepadElement* element = m_elementMap.get(cookie); // This might be an element we don't currently handle as input so we can skip it. if (!element) return; element->rawValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical); if (element->isButton()) { for (unsigned i = 0; i < m_buttons.size(); ++i) { if (m_buttons[i].get() == element) m_buttonValues[i] = element->normalizedValue(); } } else if (element->isAxis()) { for (unsigned i = 0; i < m_axes.size(); ++i) { if (m_axes[i].get() == element) m_axisValues[i] = element->normalizedValue(); } } else ASSERT_NOT_REACHED(); m_lastUpdateTime = monotonicallyIncreasingTime(); }
static void input_callback(void *ctx, IOReturn result, void *sender, IOHIDValueRef value) { struct osx_mouse_data *mdata = static_cast<struct osx_mouse_data *>(ctx); IOHIDElementRef elem = IOHIDValueGetElement(value); uint32_t page = IOHIDElementGetUsagePage(elem); uint32_t usage = IOHIDElementGetUsage(elem); uint32_t val = IOHIDValueGetIntegerValue(value); if (page == kHIDPage_GenericDesktop) { switch (usage) { case kHIDUsage_GD_X: SDL_LockMutex(mdata->mouse_mutex); mdata->mouse_x += val; SDL_UnlockMutex(mdata->mouse_mutex); break; case kHIDUsage_GD_Y: SDL_LockMutex(mdata->mouse_mutex); mdata->mouse_y += val; SDL_UnlockMutex(mdata->mouse_mutex); break; default: break; } } }
// NOTE: I pieced this together through trial and error, any corrections are welcome static void hid_device_input_callback(void* context, IOReturn result, void* sender, IOHIDValueRef value) { struct apple_pad_connection* connection = (struct apple_pad_connection*)context; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = IOHIDElementGetType(element); uint32_t page = IOHIDElementGetUsagePage(element); uint32_t use = IOHIDElementGetUsage(element); // Joystick handler: TODO: Can GamePad work the same? if (type == kIOHIDElementTypeInput_Button && page == kHIDPage_Button) { CFIndex state = IOHIDValueGetIntegerValue(value); if (state) g_current_input_data.pad_buttons[connection->slot] |= (1 << (use - 1)); else g_current_input_data.pad_buttons[connection->slot] &= ~(1 << (use - 1)); } else if (type == kIOHIDElementTypeInput_Misc && page == kHIDPage_GenericDesktop) { static const uint32_t axis_use_ids[4] = { 48, 49, 50, 53 }; for (int i = 0; i < 4; i ++) { if (use == axis_use_ids[i]) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex max = IOHIDElementGetPhysicalMax(element) - min; CFIndex state = IOHIDValueGetIntegerValue(value) - min; float val = (float)state / (float)max; g_current_input_data.pad_axis[connection->slot][i] = ((val * 2.0f) - 1.0f) * 32767.0f; } } } }
void osxHIDPointingDevice::hidQueueCallback(void *context, IOReturn /*result*/, void *sender) { // std::cerr << "osxHIDPointingDevice::hidQueueCallback" << std::endl ; osxHIDPointingDevice *self = (osxHIDPointingDevice*)context ; IOHIDQueueRef queue = (IOHIDQueueRef)sender ; while (true) { IOHIDValueRef hidvalue = IOHIDQueueCopyNextValueWithTimeout(queue, 0.) ; if (!hidvalue) break ; #if 1 TimeStamp::inttime uptime = AbsoluteTimeInNanoseconds(IOHIDValueGetTimeStamp(hidvalue)) ; TimeStamp::inttime timestamp = self->epoch + (uptime - self->epoch_mach)*TimeStamp::one_nanosecond ; #else TimeStamp::inttime timestamp = TimeStamp::createAsInt() ; #endif if (self->qreport.isOlderThan(timestamp)) { // Flush the old qreport before creating a new one self->report(self->qreport) ; self->qreport.clear() ; } IOHIDElementRef element = IOHIDValueGetElement(hidvalue) ; uint32_t usagepage = IOHIDElementGetUsagePage(element) ; uint32_t usage = IOHIDElementGetUsage(element) ; //std::cout << usagepage << std::endl; //std::cout << usage << std::endl; if (usagepage==kHIDPage_GenericDesktop) { if (usage==kHIDUsage_GD_X || usage==kHIDUsage_GD_Y) { // Could use IOHIDValueGetScaledValue(hidvalue, kIOHIDValueScaleTypePhysical) CFIndex d = IOHIDValueGetIntegerValue(hidvalue) ; //std::cout << IOHIDValueGetBytePtr(hidvalue) << std::endl; //std::cout << IOHIDValueGetLength(hidvalue) << std::endl; if (d) { if (usage==kHIDUsage_GD_X) self->qreport.dx = (int32_t)d ; else self->qreport.dy = (int32_t)d ; self->qreport.t = timestamp ; } } // FIXME: GD_Z, GD_Wheel, etc. } else if (usagepage==kHIDPage_Button) { // kHIDUsage_Button_1 is 1 self->qreport.setButton(usage-1, (uint32_t)IOHIDValueGetIntegerValue(hidvalue)) ; self->qreport.t = timestamp ; } CFRelease(hidvalue) ; } // Flush the qreport we were constructing, if any if (self->qreport.t!=TimeStamp::undef) self->report(self->qreport) ; self->qreport.clear() ; }
void CPH_HidMacOSX::InputValueCallback(IOHIDValueRef valueRef) { IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef); uint32 usage = IOHIDElementGetUsage(elementRef); uint32 usagePage = IOHIDElementGetUsagePage(elementRef); CFIndex state = IOHIDValueGetIntegerValue(valueRef); if(usagePage != kHIDPage_KeyboardOrKeypad) return; for(auto bindingIterator(std::begin(m_bindings)); bindingIterator != std::end(m_bindings); bindingIterator++) { const auto& binding = (*bindingIterator); if(!binding) continue; binding->ProcessEvent(usage, state); } }
void HIDGamepadProvider::valuesChanged(IOHIDValueRef value) { IOHIDDeviceRef device = IOHIDElementGetDevice(IOHIDValueGetElement(value)); HIDGamepad* gamepad = m_gamepadMap.get(device); // When starting monitoring we might get a value changed callback before we even know the device is connected. if (!gamepad) return; gamepad->valueChanged(value); // This isActive check is necessary as we want to delay input notifications from the time of the first input, // and not push the notification out on every subsequent input. if (!m_inputNotificationTimer.isActive()) m_inputNotificationTimer.startOneShot(InputNotificationDelay); }
HIDInputType HIDGamepad::valueChanged(IOHIDValueRef value) { IOHIDElementCookie cookie = IOHIDElementGetCookie(IOHIDValueGetElement(value)); HIDGamepadElement* element = m_elementMap.get(cookie); // This might be an element we don't currently handle as input so we can skip it. if (!element) return HIDInputType::NotAButtonPress; element->rawValue = IOHIDValueGetScaledValue(value, kIOHIDValueScaleTypePhysical); if (element->isButton()) { for (unsigned i = 0; i < m_buttons.size(); ++i) { if (&m_buttons[i].get() == element) { m_buttonValues[i] = element->normalizedValue(); break; } } } else if (element->isAxis()) { for (unsigned i = 0; i < m_axes.size(); ++i) { if (&m_axes[i].get() == element) { m_axisValues[i] = element->normalizedValue(); break; } } } else if (element->isDPad()) { int intValue = IOHIDValueGetIntegerValue(value) - element->min; for (unsigned i = 0; i < m_dPads.size(); ++i) { if (&m_dPads[i].get() != element) continue; // Each DPad represents 4 button values which are tacked on to the end of the values from non-DPad buttons. unsigned firstButtonValue = m_buttons.size() + i * 4; ASSERT(m_buttonValues.size() > firstButtonValue + 3); fillInButtonValues(intValue, m_buttonValues[firstButtonValue], m_buttonValues[firstButtonValue + 1], m_buttonValues[firstButtonValue + 2], m_buttonValues[firstButtonValue + 3]); } } else ASSERT_NOT_REACHED(); m_lastUpdateTime = monotonicallyIncreasingTime(); return element->isButton() ? HIDInputType::ButtonPress : HIDInputType::NotAButtonPress; }
static void hid_event(void* userdata, IOReturn result, void* sender, IOHIDValueRef value) { EventBridge* self = reinterpret_cast<EventBridge*>(userdata); IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t usage_page = IOHIDElementGetUsagePage(element); switch (usage_page) { case kHIDPage_KeyboardOrKeypad: self->key_event(result, element, value); break; case kHIDPage_GenericDesktop: self->analog_event(result, element, value); break; case kHIDPage_Button: self->button_event(result, element, value); break; default: sfz::print(sfz::io::err, sfz::format("{0}\n", usage_page)); break; } }
void CInputProviderMacOsHid::InputValueCallback(DEVICE_INFO* deviceInfo, IOReturn result, void* sender, IOHIDValueRef valueRef) { if(!OnInput) return; IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef); uint32 usagePage = IOHIDElementGetUsagePage(elementRef); if( (usagePage != kHIDPage_GenericDesktop) && (usagePage != kHIDPage_Button)) { return; } uint32 usage = IOHIDElementGetUsage(elementRef); CFIndex value = IOHIDValueGetIntegerValue(valueRef); IOHIDElementType type = IOHIDElementGetType(elementRef); BINDINGTARGET tgt; tgt.providerId = PROVIDER_ID; tgt.deviceId = deviceInfo->deviceId; tgt.keyId = usage; tgt.keyType = GetKeyType(usage, type); OnInput(tgt, value); }
static void onDeviceValueChanged(void * context, IOReturn result, void * sender, IOHIDValueRef value) { struct Gamepad_device * deviceRecord; struct Gamepad_devicePrivate * hidDeviceRecord; IOHIDElementRef element; IOHIDElementCookie cookie; unsigned int axisIndex, buttonIndex; static mach_timebase_info_data_t timebaseInfo; if (timebaseInfo.denom == 0) { mach_timebase_info(&timebaseInfo); } deviceRecord = context; hidDeviceRecord = deviceRecord->privateData; element = IOHIDValueGetElement(value); cookie = IOHIDElementGetCookie(element); for (axisIndex = 0; axisIndex < deviceRecord->numAxes; axisIndex++) { if (!hidDeviceRecord->axisElements[axisIndex].isHatSwitchSecondAxis && hidDeviceRecord->axisElements[axisIndex].cookie == cookie) { CFIndex integerValue; if (IOHIDValueGetLength(value) > 4) { // Workaround for a strange crash that occurs with PS3 controller; was getting lengths of 39 (!) continue; } integerValue = IOHIDValueGetIntegerValue(value); if (hidDeviceRecord->axisElements[axisIndex].isHatSwitch) { int x, y; // Fix for Saitek X52 if (!hidDeviceRecord->axisElements[axisIndex].hasNullState) { if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { integerValue = hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1; } else { integerValue--; } } hatValueToXY(integerValue, hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin + 1, &x, &y); if (x != deviceRecord->axisStates[axisIndex]) { queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex, x, deviceRecord->axisStates[axisIndex]); deviceRecord->axisStates[axisIndex] = x; } if (y != deviceRecord->axisStates[axisIndex + 1]) { queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex + 1, y, deviceRecord->axisStates[axisIndex + 1]); deviceRecord->axisStates[axisIndex + 1] = y; } } else { float floatValue; if (integerValue < hidDeviceRecord->axisElements[axisIndex].logicalMin) { hidDeviceRecord->axisElements[axisIndex].logicalMin = integerValue; } if (integerValue > hidDeviceRecord->axisElements[axisIndex].logicalMax) { hidDeviceRecord->axisElements[axisIndex].logicalMax = integerValue; } floatValue = (integerValue - hidDeviceRecord->axisElements[axisIndex].logicalMin) / (float) (hidDeviceRecord->axisElements[axisIndex].logicalMax - hidDeviceRecord->axisElements[axisIndex].logicalMin) * 2.0f - 1.0f; queueAxisEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, axisIndex, floatValue, deviceRecord->axisStates[axisIndex]); deviceRecord->axisStates[axisIndex] = floatValue; } return; } } for (buttonIndex = 0; buttonIndex < deviceRecord->numButtons; buttonIndex++) { if (hidDeviceRecord->buttonElements[buttonIndex].cookie == cookie) { bool down; down = IOHIDValueGetIntegerValue(value); queueButtonEvent(deviceRecord, IOHIDValueGetTimeStamp(value) * timebaseInfo.numer / timebaseInfo.denom * 0.000000001, buttonIndex, down); deviceRecord->buttonStates[buttonIndex] = down; return; } } }
static void input_callback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { struct input_data *input = (struct input_data*)context; IOHIDElementRef elem = IOHIDValueGetElement(value); uint32_t page = IOHIDElementGetUsagePage(elem); uint32_t usage = IOHIDElementGetUsage(elem); uint32_t val = IOHIDValueGetIntegerValue(value); if (page == kHIDPage_GenericDesktop) { if (input->ignore_mouse) { return; } switch (usage) { case kHIDUsage_GD_X: pthread_mutex_lock(&input->mouse_mutex); input->mouse_x += val; pthread_mutex_unlock(&input->mouse_mutex); break; case kHIDUsage_GD_Y: pthread_mutex_lock(&input->mouse_mutex); input->mouse_y += val; pthread_mutex_unlock(&input->mouse_mutex); break; case kHIDUsage_GD_Wheel: if ((int32_t)val > 0) { add_to_event_queue(input, K_MWHEELUP, true); add_to_event_queue(input, K_MWHEELUP, false); } else if ((int32_t)val < 0) { add_to_event_queue(input, K_MWHEELDOWN, true); add_to_event_queue(input, K_MWHEELDOWN, false); } break; default: break; } } else if (page == kHIDPage_Button) { if (input->ignore_mouse) { return; } if (usage < 1 || usage > 10) { usage = 10; } add_to_event_queue(input, K_MOUSE1 + usage - 1, val ? true : false); } else if (page == kHIDPage_KeyboardOrKeypad) { if (usage == kHIDUsage_KeyboardLeftGUI) { input->left_cmd_key_active = val ? true : false; } else if (usage == kHIDUsage_KeyboardRightGUI) { input->right_cmd_key_active = val ? true : false; } if (usage < sizeof(keytable) && (input->left_cmd_key_active || input->right_cmd_key_active)) { if (keytable[usage] == 'c' && val) { add_to_event_queue(input, K_COPY, true); add_to_event_queue(input, K_COPY, false); } else if (keytable[usage] == 'v' && val) { add_to_event_queue(input, K_PASTE, true); add_to_event_queue(input, K_PASTE, false); } return; } if (usage < sizeof(keytable)) { add_to_event_queue(input, keytable[usage], val ? true : false); pthread_mutex_lock(&input->key_mutex); if (val) { input->repeatkey = keytable[usage]; input->nextrepeattime = Sys_IntTime() + input->key_repeat_initial_delay; } else { input->repeatkey = 0; input->nextrepeattime = 0; } pthread_mutex_unlock(&input->key_mutex); } } else if (page == 0xFF) { if (usage == kHIDUsage_KeyboardErrorUndefined) { input->fn_key_active = val ? true : false; } } }
static void iohidmanager_hid_device_input_callback(void *data, IOReturn result, void* sender, IOHIDValueRef value) { iohidmanager_hid_t *hid = (iohidmanager_hid_t*)hid_driver_get_data(); struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*)data; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = (uint32_t)IOHIDElementGetType(element); uint32_t page = (uint32_t)IOHIDElementGetUsagePage(element); uint32_t use = (uint32_t)IOHIDElementGetUsage(element); uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element); apple_input_rec_t *tmp = NULL; if (type != kIOHIDElementTypeInput_Misc) if (type != kIOHIDElementTypeInput_Button) if (type != kIOHIDElementTypeInput_Axis) return; /* Joystick handler. * TODO: Can GamePad work the same? */ int pushed_button = 0; switch (page) { case kHIDPage_GenericDesktop: switch (type) { case kIOHIDElementTypeInput_Misc: switch (use) { case kHIDUsage_GD_Hatswitch: { tmp = adapter->hats; while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) tmp = tmp->next; if(tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex range = IOHIDElementGetLogicalMax(element) - IOHIDElementGetLogicalMin(element); CFIndex val = IOHIDValueGetIntegerValue(value); if(range == 3) val *= 2; switch(val) { case 0: /* pos = up */ hid->hats[adapter->slot][0] = 0; hid->hats[adapter->slot][1] = -1; break; case 1: /* pos = up+right */ hid->hats[adapter->slot][0] = 1; hid->hats[adapter->slot][1] = -1; break; case 2: /* pos = right */ hid->hats[adapter->slot][0] = 1; hid->hats[adapter->slot][1] = 0; break; case 3: /* pos = down+right */ hid->hats[adapter->slot][0] = 1; hid->hats[adapter->slot][1] = 1; break; case 4: /* pos = down */ hid->hats[adapter->slot][0] = 0; hid->hats[adapter->slot][1] = 1; break; case 5: /* pos = down+left */ hid->hats[adapter->slot][0] = -1; hid->hats[adapter->slot][1] = 1; break; case 6: /* pos = left */ hid->hats[adapter->slot][0] = -1; hid->hats[adapter->slot][1] = 0; break; case 7: /* pos = up_left */ hid->hats[adapter->slot][0] = -1; hid->hats[adapter->slot][1] = -1; break; default: /* pos = centered */ hid->hats[adapter->slot][0] = 0; hid->hats[adapter->slot][1] = 0; break; } } } break; default: tmp = adapter->axes; while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) tmp = tmp->next; if (tmp) { if(tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex state = IOHIDValueGetIntegerValue(value) - min; CFIndex max = IOHIDElementGetPhysicalMax(element) - min; float val = (float)state / (float)max; hid->axes[adapter->slot][tmp->id] = ((val * 2.0f) - 1.0f) * 32767.0f; } } else pushed_button = 1; break; } break; } break; case kHIDPage_Consumer: case kHIDPage_Button: switch (type) { case kIOHIDElementTypeInput_Misc: case kIOHIDElementTypeInput_Button: pushed_button = 1; break; } break; } if (pushed_button) { tmp = adapter->buttons; uint8_t bit = 0; while(tmp && tmp->cookie != (IOHIDElementCookie)cookie) { bit++; tmp = tmp->next; } if(tmp && tmp->cookie == (IOHIDElementCookie)cookie) { CFIndex state = IOHIDValueGetIntegerValue(value); if (state) BIT64_SET(hid->buttons[adapter->slot], bit); else BIT64_CLEAR(hid->buttons[adapter->slot], bit); } } }
void __deviceValueCallback (void * context, IOReturn result, void * sender, IOHIDValueRef value) { IOHIDElementRef element = IOHIDValueGetElement(value); printf("IOHIDDeviceRef[%p]: value=%p timestamp=%lld cookie=%d usagePage=0x%02X usage=0x%02X intValue=%ld\n", sender, value, IOHIDValueGetTimeStamp(value), (uint32_t)IOHIDElementGetCookie(element), IOHIDElementGetUsagePage(element), IOHIDElementGetUsage(element), IOHIDValueGetIntegerValue(value)); }
void GamepadManager::onDeviceValueChanged(IOHIDValueRef value) { IOHIDElementRef element = IOHIDValueGetElement(value); IOHIDDeviceRef device = IOHIDElementGetDevice(element); int vendorID = getIntDeviceProperty(device, CFSTR(kIOHIDVendorIDKey)); int productID = getIntDeviceProperty(device, CFSTR(kIOHIDProductIDKey)); uint32_t usagePage = IOHIDElementGetUsagePage(element); uint32_t usage = IOHIDElementGetUsage(element); // The following controller mapping is based on the Logitech F710, however we use it for // all Logitech devices on the assumption that they're likely to share the same mapping. if (vendorID == Logitech_F710_VendorID) { // Logitech F710 mapping. if (usagePage == kHIDPage_Button) { bool buttonState = IOHIDValueGetIntegerValue(value); switch(usage) { case kHIDUsage_Button_1: manipulateBitField(State.Buttons, Gamepad_X, buttonState); break; case kHIDUsage_Button_2: manipulateBitField(State.Buttons, Gamepad_A, buttonState); break; case kHIDUsage_Button_3: manipulateBitField(State.Buttons, Gamepad_B, buttonState); break; case kHIDUsage_Button_4: manipulateBitField(State.Buttons, Gamepad_Y, buttonState); break; case 0x05: manipulateBitField(State.Buttons, Gamepad_L1, buttonState); break; case 0x06: manipulateBitField(State.Buttons, Gamepad_R1, buttonState); break; case 0x07: State.LT = buttonState ? 1.0f:0.0f; break; case 0x08: State.RT = buttonState ? 1.0f:0.0f; break; case 0x09: manipulateBitField(State.Buttons, Gamepad_Back, buttonState); break; case 0x0A: manipulateBitField(State.Buttons, Gamepad_Start, buttonState); break; case 0x0B: manipulateBitField(State.Buttons, Gamepad_LStick, buttonState); break; case 0x0C: manipulateBitField(State.Buttons, Gamepad_RStick, buttonState); break; default: return; } } else if (usagePage == kHIDPage_GenericDesktop) { float v; switch(usage) { case kHIDUsage_GD_X: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LX, v)) return; break; case kHIDUsage_GD_Y: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LY, -v)) return; break; case kHIDUsage_GD_Z: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RX, v)) return; break; case kHIDUsage_GD_Rz: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RY, -v)) return; break; case kHIDUsage_GD_Hatswitch: { CFIndex integerValue = IOHIDValueGetIntegerValue(value); manipulateBitField(State.Buttons, Gamepad_Up, integerValue == 7 || integerValue == 0 || integerValue == 1); manipulateBitField(State.Buttons, Gamepad_Down, integerValue == 3 || integerValue == 4 || integerValue == 5); manipulateBitField(State.Buttons, Gamepad_Left, integerValue == 5 || integerValue == 6 || integerValue == 7); manipulateBitField(State.Buttons, Gamepad_Right, integerValue == 1 || integerValue == 2 || integerValue == 3); } break; default: return; } } } // The following controller mapping is based on the Sony DualShock3, however we use it for // all Sony devices on the assumption that they're likely to share the same mapping. else if (vendorID == Sony_DualShock3_VendorID) { // PS3 Controller. if (usagePage == kHIDPage_Button) { bool buttonState = IOHIDValueGetIntegerValue(value); switch(usage) { case kHIDUsage_Button_1: manipulateBitField(State.Buttons, Gamepad_Back, buttonState); break; case kHIDUsage_Button_2: manipulateBitField(State.Buttons, Gamepad_LStick, buttonState); break; case kHIDUsage_Button_3: manipulateBitField(State.Buttons, Gamepad_RStick, buttonState); break; case kHIDUsage_Button_4: manipulateBitField(State.Buttons, Gamepad_Start, buttonState); break; case 0x05: manipulateBitField(State.Buttons, Gamepad_Up, buttonState); break; case 0x06: manipulateBitField(State.Buttons, Gamepad_Right, buttonState); break; case 0x07: manipulateBitField(State.Buttons, Gamepad_Down, buttonState); break; case 0x08: manipulateBitField(State.Buttons, Gamepad_Left, buttonState); break; case 0x09: State.LT = buttonState ? 1.0f:0.0f; break; case 0x0A: State.RT = buttonState ? 1.0f:0.0f; break; case 0x0B: manipulateBitField(State.Buttons, Gamepad_L1, buttonState); break; case 0x0C: manipulateBitField(State.Buttons, Gamepad_R1, buttonState); break; case 0x0D: // PS3 Triangle. manipulateBitField(State.Buttons, Gamepad_TRIANGLE, buttonState); break; case 0x0E: // PS3 Circle manipulateBitField(State.Buttons, Gamepad_CIRCLE, buttonState); break; case 0x0F: // PS3 Cross manipulateBitField(State.Buttons, Gamepad_CROSS, buttonState); break; case 0x10: // PS3 Square manipulateBitField(State.Buttons, Gamepad_SQUARE, buttonState); break; default: return; } } else if (usagePage == kHIDPage_GenericDesktop) { float v; switch(usage) { case kHIDUsage_GD_X: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LX, v)) return; break; case kHIDUsage_GD_Y: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.LY, -v)) return; break; case kHIDUsage_GD_Z: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RX, v)) return; break; case kHIDUsage_GD_Rz: v = mapAnalogAxis(value, element); if (!setStateIfDifferent(State.RY, -v)) return; break; default: return; } } } bStateChanged = true; }
static void PsychHIDKbQueueCallbackFunction(void *target, IOReturn result, void *sender) { // This routine is executed each time the queue transitions from empty to non-empty // The CFRunLoop of the thread in KbQueueWorkerThreadMain() is the one that executes here: IOHIDQueueRef queue = (IOHIDQueueRef) sender; IOHIDValueRef valueRef = NULL; int deviceIndex = (int) target; double timestamp; int eventValue; long keysUsage = -1; PsychHIDEventRecord evt; result=kIOReturnError; if (!queue) return; // Nothing we can do because we can't access queue, (shouldn't happen) while (1) { // This function only gets called when queue transitions from empty to non-empty // Therefore, we must process all available events in this while loop before // it will be possible for this function to be notified again. if (valueRef) { CFRelease(valueRef); valueRef = NULL; } // Dequeue next event from queue in a polling non-blocking fashion: valueRef = IOHIDQueueCopyNextValueWithTimeout(queue, 0.0); // Done? Exit, if so: if (!valueRef) break; // Get event value, e.g., the key state of a key or button 1 = pressed, 0 = released: eventValue = IOHIDValueGetIntegerValue(valueRef); // Get usage value, ie., the identity of the key: IOHIDElementRef element = IOHIDValueGetElement(valueRef); keysUsage = IOHIDElementGetUsage(element); // Get double GetSecs timestamp, computed from returned uint64 nanoseconds timestamp: timestamp = convertTime(IOHIDValueGetTimeStamp(valueRef)); // Don't bother with keysUsage of 0 (meaningless) or 1 (ErrorRollOver) for keyboards: if ((queueIsAKeyboard[deviceIndex]) && (keysUsage <= 1)) continue; // Clear ringbuffer event: memset(&evt, 0 , sizeof(evt)); // Cooked key code defaults to "unhandled", and stays that way for anything but keyboards: evt.cookedEventCode = -1; // For real keyboards we can compute cooked key codes: Requires OSX 10.5 or later. if (queueIsAKeyboard[deviceIndex]) { // Keyboard(ish) device. We can handle this under some conditions. // Init to a default of handled, but unmappable/ignored keycode: evt.cookedEventCode = 0; // Keypress event code available in mapping table? if (keysUsage < kHID2VKCSize) { // Yes: We try to map this to a character code: // Step 1: Map HID usage value to virtual keycode via LUT: uint16_t vcKey = kHID2VKC[keysUsage]; // Keep track of SHIFT keys as modifier keys: Bits 0 == Command, 1 == Shift, 2 == CapsLock, 3 == Alt/Option, 4 == CTRL if ((vcKey == kVKC_Shift || vcKey == kVKC_rShift) && (eventValue != 0)) modifierKeyState[deviceIndex] |= (1 << 1); if ((vcKey == kVKC_Shift || vcKey == kVKC_rShift) && (eventValue == 0)) modifierKeyState[deviceIndex] &= ~(1 << 1); // Keep track of ALT keys as modifier keys: if ((vcKey == kVKC_Option || vcKey == kVKC_rOption) && (eventValue != 0)) modifierKeyState[deviceIndex] |= (1 << 3); if ((vcKey == kVKC_Option || vcKey == kVKC_rOption) && (eventValue == 0)) modifierKeyState[deviceIndex] &= ~(1 << 3); // Keep track of CTRL keys as modifier keys: if ((vcKey == kVKC_Control || vcKey == kVKC_rControl) && (eventValue != 0)) modifierKeyState[deviceIndex] |= (1 << 4); if ((vcKey == kVKC_Control || vcKey == kVKC_rControl) && (eventValue == 0)) modifierKeyState[deviceIndex] &= ~(1 << 4); // Was this a CTRL + C interrupt request? if ((eventValue != 0) && (vcKey == 0x08) && (modifierKeyState[deviceIndex] & (1 << 4))) { // Yes: Tell the console input helper about it, so it can send interrupt // signals to the runtime and reenable keyboard input if appropriate: // Note: Not sure if the mutex exclusion is needed here, but better safe than sorry. PsychLockMutex(&KbQueueMutex); ConsoleInputHelper(-1); PsychUnlockMutex(&KbQueueMutex); } // Key press? if (eventValue != 0) { // Step 2: Translate virtual key code into unicode char: // Ok, this is the usual horrifying complexity of Apple's system. We use code // snippets found on StackOverflow, modified to suit our needs, e.g., we track // modifier keys manually, at least left and right ALT and SHIFT keys. We don't // care about other modifiers. TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef uchr = (CFDataRef) ((currentKeyboard) ? TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData) : NULL); const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*) ((uchr) ? CFDataGetBytePtr(uchr) : NULL); if (keyboardLayout) { UInt32 deadKeyState = 0; UniCharCount maxStringLength = 255; UniCharCount actualStringLength = 0; UniChar unicodeString[maxStringLength]; OSStatus status = UCKeyTranslate(keyboardLayout, vcKey, kUCKeyActionDown, modifierKeyState[deviceIndex], LMGetKbdType(), 0, &deadKeyState, maxStringLength, &actualStringLength, unicodeString); if ((actualStringLength == 0) && deadKeyState) { status = UCKeyTranslate(keyboardLayout, kVK_Space, kUCKeyActionDown, 0, LMGetKbdType(), 0, &deadKeyState, maxStringLength, &actualStringLength, unicodeString); } if((actualStringLength > 0) && (status == noErr)) { // Assign final cooked / mapped keycode: evt.cookedEventCode = (int) unicodeString[0]; // Send same keystroke character to console input helper. // In kbqueue-based ListenChar(1) mode, the helper will // inject/forward the character into the runtime: // Note: ConsoleInputHelper() should be safe to call without // mutex protection for >= 0 event codes. ConsoleInputHelper(evt.cookedEventCode); } } } } } PsychLockMutex(&KbQueueMutex); // Update records of first and latest key presses and releases if (eventValue != 0) { if (psychHIDKbQueueFirstPress[deviceIndex]) { // First key press timestamp: if (psychHIDKbQueueFirstPress[deviceIndex][keysUsage-1] == 0) { psychHIDKbQueueFirstPress[deviceIndex][keysUsage-1] = timestamp; } } if (psychHIDKbQueueLastPress[deviceIndex]) { // Last key press timestamp: psychHIDKbQueueLastPress[deviceIndex][keysUsage-1] = timestamp; } evt.status |= (1 << 0); } else { if (psychHIDKbQueueFirstRelease[deviceIndex]) { // First key release timestamp: if (psychHIDKbQueueFirstRelease[deviceIndex][keysUsage-1] == 0) psychHIDKbQueueFirstRelease[deviceIndex][keysUsage-1] = timestamp; } if (psychHIDKbQueueLastRelease[deviceIndex]) { // Last key release timestamp: psychHIDKbQueueLastRelease[deviceIndex][keysUsage-1] = timestamp; } evt.status &= ~(1 << 0); } // Update event buffer: evt.timestamp = timestamp; evt.rawEventCode = keysUsage; PsychHIDAddEventToEventBuffer(deviceIndex, &evt); // Tell waiting userspace (under KbQueueMutxex protection for better scheduling) something interesting has changed: PsychSignalCondition(&KbQueueCondition); PsychUnlockMutex(&KbQueueMutex); // Next while loop iteration to dequeue potentially more events: } // Done for this queue transition. Return to runloop. }
static void apple_hid_device_input_callback(void *data, IOReturn result, void* sender, IOHIDValueRef value) { driver_t *driver = driver_get_ptr(); apple_input_data_t *apple = (apple_input_data_t*)driver->input_data; struct apple_hid_adapter *adapter = (struct apple_hid_adapter*)data; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = IOHIDElementGetType(element); uint32_t page = IOHIDElementGetUsagePage(element); uint32_t use = IOHIDElementGetUsage(element); if (type != kIOHIDElementTypeInput_Misc) if (type != kIOHIDElementTypeInput_Button) if (type != kIOHIDElementTypeInput_Axis) return; /* Joystick handler. * TODO: Can GamePad work the same? */ switch (page) { case kHIDPage_GenericDesktop: switch (type) { case kIOHIDElementTypeInput_Misc: switch (use) { case kHIDUsage_GD_Hatswitch: break; default: { int i; static const uint32_t axis_use_ids[4] = { 48, 49, 50, 53 }; for (i = 0; i < 4; i ++) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex max = IOHIDElementGetPhysicalMax(element) - min; CFIndex state = IOHIDValueGetIntegerValue(value) - min; float val = (float)state / (float)max; if (use != axis_use_ids[i]) continue; apple->axes[adapter->slot][i] = ((val * 2.0f) - 1.0f) * 32767.0f; } } break; } break; } break; case kHIDPage_Button: switch (type) { case kIOHIDElementTypeInput_Button: { CFIndex state = IOHIDValueGetIntegerValue(value); unsigned id = use - 1; if (state) BIT64_SET(apple->buttons[adapter->slot], id); else BIT64_CLEAR(apple->buttons[adapter->slot], id); } break; } break; } }
void Burger::Mouse::InputCallback(void *pData, int iReturn,void * /* pSender */,IOHIDValueRef pValue) { if (iReturn == kIOReturnSuccess) { Mouse *pMouse = static_cast<Mouse *>(pData); Word uCount = pMouse->m_uMiceCount; if (uCount) { IOHIDElementRef pElement = IOHIDValueGetElement(pValue); IOHIDDeviceRef pDevice = IOHIDElementGetDevice(pElement); #if 0 Word uRatNumber = BURGER_MAXUINT; const DeviceStruct *pRat = pMouse->m_Mice; do { if (pRat->m_pDevice == pDevice) { uRatNumber = pMouse->m_uMiceCount-uCount; break; } ++pRat; } while (--uCount); if (uRatNumber==BURGER_MAXUINT) { } #endif Word32 uTime = static_cast<Word32>(IOHIDValueGetTimeStamp(pValue)); CFIndex iValue = IOHIDValueGetIntegerValue(pValue); uint32_t uPage = IOHIDElementGetUsagePage(pElement); uint32_t uUsage = IOHIDElementGetUsage(pElement); switch (uPage) { case kHIDPage_GenericDesktop: if (iValue) { switch (uUsage) { case kHIDUsage_GD_X: pMouse->PostMouseMotion(static_cast<Int32>(iValue),0,uTime); break; case kHIDUsage_GD_Y: pMouse->PostMouseMotion(0,static_cast<Int32>(iValue),uTime); break; case kHIDUsage_GD_Wheel: pMouse->PostMouseWheel(0,static_cast<Int32>(iValue),uTime); break; default: printf("Unknown usage %u\n",uUsage); break; } } break; case kHIDPage_Button: // iValue == down // Usage = which 1.2.3.4 if (iValue) { pMouse->PostMouseDown(1<<(uUsage-1)); } else { pMouse->PostMouseUp(1<<(uUsage-1)); } break; // Ignore this one case kHIDPage_Consumer: break; default: printf("Unknown page found %u\n",uPage); break; } } } }
static void iohidmanager_hid_device_input_callback(void *data, IOReturn result, void* sender, IOHIDValueRef value) { iohidmanager_hid_t *hid = (iohidmanager_hid_t*)hid_driver_get_data(); struct iohidmanager_hid_adapter *adapter = (struct iohidmanager_hid_adapter*)data; IOHIDElementRef element = IOHIDValueGetElement(value); uint32_t type = IOHIDElementGetType(element); uint32_t page = IOHIDElementGetUsagePage(element); uint32_t use = IOHIDElementGetUsage(element); if (type != kIOHIDElementTypeInput_Misc) if (type != kIOHIDElementTypeInput_Button) if (type != kIOHIDElementTypeInput_Axis) return; /* Joystick handler. * TODO: Can GamePad work the same? */ switch (page) { case kHIDPage_GenericDesktop: switch (type) { case kIOHIDElementTypeInput_Misc: switch (use) { case kHIDUsage_GD_Hatswitch: break; default: { int i; // +0/-0 => Left Stick Horizontal => 48 // +1/-1 => Left Stick Vertical => 49 // +2/-2 => Right Stick Horizontal => 51 // +3/-3 => Right Stick Vertical => 52 // +4/-4 => Left Trigger (if exists) => 50 // +5/-5 => Right Trigger (if exists) => 53 static const uint32_t axis_use_ids[6] = { 48, 49, 51, 52, 50, 53 }; for (i = 0; i < 6; i ++) { CFIndex min = IOHIDElementGetPhysicalMin(element); CFIndex state = IOHIDValueGetIntegerValue(value) - min; CFIndex max = IOHIDElementGetPhysicalMax(element) - min; float val = (float)state / (float)max; if (use != axis_use_ids[i]) continue; hid->axes[adapter->slot][i] = ((val * 2.0f) - 1.0f) * 32767.0f; } } break; } break; } break; case kHIDPage_Button: switch (type) { case kIOHIDElementTypeInput_Button: { CFIndex state = IOHIDValueGetIntegerValue(value); unsigned id = use - 1; if (state) BIT64_SET(hid->buttons[adapter->slot], id); else BIT64_CLEAR(hid->buttons[adapter->slot], id); } break; } break; } }