/* Copyright (c) 2014 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "inputcontroller/HashTableAxisMotion2DKey.h" #include "inputcontroller/InputController.h" #include #include #include #define stemobject_implementation InputController v_begin(); v_func(dispose); v_func(startAction); v_func(stopAction); v_func(motion); v_end(); static Atom actionDownEventAtom, actionRepeatEventAtom, actionUpEventAtom, motionEventAtom; struct motion2DAxisPairValue { float valueX; float valueY; float adjustedValueX; float adjustedValueY; }; InputController * InputController_create(GamepadMap * gamepadMap, InputMap * inputMap, ...) { va_list args; va_start(args, inputMap); InputController * self = InputController_vcreate(gamepadMap, inputMap, args); va_end(args); return self; } InputController * InputController_vcreate(GamepadMap * gamepadMap, InputMap * inputMap, va_list args) { stemobject_create_implementation(vinit, gamepadMap, inputMap, args) } bool InputController_init(InputController * self, GamepadMap * gamepadMap, InputMap * inputMap, ...) { va_list args; va_start(args, inputMap); bool success = InputController_vinit(self, gamepadMap, inputMap, args); va_end(args); return success; } static bool gamepadLogicalButtonDown(Atom eventID, void * eventData, void * context) { InputController * self = context; GamepadLogicalInputController_buttonEvent * event = eventData; bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadLogicalMap.buttonActionBindingCount; bindingIndex++) { if (self->inputMap->gamepadLogicalMap.buttonActionBindings[bindingIndex].elementID == event->element) { handled |= call_virtual(startAction, self, self->inputMap->gamepadLogicalMap.buttonActionBindings[bindingIndex].actionID, event->timestamp, event->peek, InputMap_genericGamepadButtonBinding(self->inputMap->gamepadLogicalMap.buttonActionBindings[bindingIndex])); } } return handled; } static bool gamepadLogicalButtonUp(Atom eventID, void * eventData, void * context) { InputController * self = context; GamepadLogicalInputController_buttonEvent * event = eventData; bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadLogicalMap.buttonActionBindingCount; bindingIndex++) { if (self->inputMap->gamepadLogicalMap.buttonActionBindings[bindingIndex].elementID == event->element) { handled |= call_virtual(stopAction, self, self->inputMap->gamepadLogicalMap.buttonActionBindings[bindingIndex].actionID, event->timestamp, event->peek); } } return handled; } static bool gamepadLogicalAxisActivate(Atom eventID, void * eventData, void * context) { InputController * self = context; GamepadLogicalInputController_axisEvent * event = eventData; bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadLogicalMap.axisActionBindingCount; bindingIndex++) { if (self->inputMap->gamepadLogicalMap.axisActionBindings[bindingIndex].elementID == event->element && self->inputMap->gamepadLogicalMap.axisActionBindings[bindingIndex].direction == event->sign) { handled |= call_virtual(startAction, self, self->inputMap->gamepadLogicalMap.axisActionBindings[bindingIndex].actionID, event->timestamp, event->peek, InputMap_genericGamepadAxisActionBinding(self->inputMap->gamepadLogicalMap.axisActionBindings[bindingIndex])); } } return handled; } static bool gamepadLogicalAxisRelease(Atom eventID, void * eventData, void * context) { InputController * self = context; GamepadLogicalInputController_axisEvent * event = eventData; bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadLogicalMap.axisActionBindingCount; bindingIndex++) { if (self->inputMap->gamepadLogicalMap.axisActionBindings[bindingIndex].elementID == event->element && self->inputMap->gamepadLogicalMap.axisActionBindings[bindingIndex].direction == event->sign) { handled |= call_virtual(stopAction, self, self->inputMap->gamepadLogicalMap.axisActionBindings[bindingIndex].actionID, event->timestamp, event->peek); } } return handled; } static bool gamepadLogicalAxisMotion(Atom eventID, void * eventData, void * context) { InputController * self = context; GamepadLogicalInputController_axisEvent * event = eventData; bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadLogicalMap.axisMotion1DBindingCount; bindingIndex++) { if (self->inputMap->gamepadLogicalMap.axisMotion1DBindings[bindingIndex].elementID == event->element) { handled |= call_virtual(motion, self, self->inputMap->gamepadLogicalMap.axisMotion1DBindings[bindingIndex].actionID, event->timestamp, ftox(event->adjustedValue), 0x00000, ftox(event->value), 0x00000, event->peek); } } for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadLogicalMap.axisMotion2DBindingCount; bindingIndex++) { if (self->inputMap->gamepadLogicalMap.axisMotion2DBindings[bindingIndex].elementIDX == event->element || self->inputMap->gamepadLogicalMap.axisMotion2DBindings[bindingIndex].elementIDY == event->element) { HashTableAxisMotion2DKey keyObject; stemobject_assign_vtable(keyObject, HashTableAxisMotion2DKey); HashTableAxisMotion2DKey_init(&keyObject, event->device->deviceID, self->inputMap->gamepadLogicalMap.axisMotion2DBindings[bindingIndex].elementIDX, self->inputMap->gamepadLogicalMap.axisMotion2DBindings[bindingIndex].elementIDY); struct motion2DAxisPairValue * axisPairValue = NULL; if (self->motion2DAxisValueMemory == NULL) { self->motion2DAxisValueMemory = HashTable_create(sizeof(struct motion2DAxisPairValue)); } else { axisPairValue = HashTable_get(self->motion2DAxisValueMemory, HashTable_objectKey(&keyObject)); } if (axisPairValue == NULL) { struct motion2DAxisPairValue newAxisPairValue = {0.0f, 0.0f, 0.0f, 0.0f}; axisPairValue = HashTable_set(self->motion2DAxisValueMemory, HashTable_objectKey(&keyObject), &newAxisPairValue); } if (self->inputMap->gamepadLogicalMap.axisMotion2DBindings[bindingIndex].elementIDX == event->element) { axisPairValue->valueX = event->value; axisPairValue->adjustedValueX = event->adjustedValue; } else { axisPairValue->valueY = event->value; axisPairValue->adjustedValueY = event->adjustedValue; } handled |= call_virtual(motion, self, self->inputMap->gamepadLogicalMap.axisMotion2DBindings[bindingIndex].actionID, event->timestamp, ftox(axisPairValue->adjustedValueX), ftox(axisPairValue->adjustedValueY), ftox(axisPairValue->valueX), ftox(axisPairValue->valueY), event->peek); } } return handled; } bool InputController_vinit(InputController * self, GamepadMap * gamepadMap, InputMap * inputMap, va_list args) { call_super(init, self); self->eventDispatcher = EventDispatcher_create(); if (gamepadMap == NULL) { self->gamepadInputController = NULL; } else { self->gamepadInputController = GamepadLogicalInputController_create(gamepadMap); EventDispatcher_registerForEvent(self->gamepadInputController->eventDispatcher, ATOM(GAMEPAD_CONTROLLER_EVENT_BUTTON_DOWN), gamepadLogicalButtonDown, self); EventDispatcher_registerForEvent(self->gamepadInputController->eventDispatcher, ATOM(GAMEPAD_CONTROLLER_EVENT_BUTTON_UP), gamepadLogicalButtonUp, self); EventDispatcher_registerForEvent(self->gamepadInputController->eventDispatcher, ATOM(GAMEPAD_CONTROLLER_EVENT_AXIS_ACTIVATE), gamepadLogicalAxisActivate, self); EventDispatcher_registerForEvent(self->gamepadInputController->eventDispatcher, ATOM(GAMEPAD_CONTROLLER_EVENT_AXIS_RELEASE), gamepadLogicalAxisRelease, self); EventDispatcher_registerForEvent(self->gamepadInputController->eventDispatcher, ATOM(GAMEPAD_CONTROLLER_EVENT_AXIS_MOTION), gamepadLogicalAxisMotion, self); } self->inputMap = inputMap; self->actionCount = 0; self->actions = NULL; self->motion2DAxisValueMemory = NULL; self->repeatInitialDelay = 0.0; self->repeatInterval = 0.0; self->lastRepeatReferenceTime = 0.0; self->mouseSensitivityX = 1.0f; self->mouseSensitivityY = 1.0f; va_list argsCopy; va_copy(argsCopy, args); while (va_arg(argsCopy, const char *) != NULL) { self->actionCount++; } va_end(argsCopy); if (self->actionCount > 0) { self->actions = malloc(self->actionCount * sizeof(struct InputController_action)); for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { self->actions[actionIndex].actionID = Atom_fromString(va_arg(args, const char *)); self->actions[actionIndex].active = false; } } return true; } void InputController_dispose(InputController * self) { EventDispatcher_dispose(self->eventDispatcher); free(self->actions); if (self->motion2DAxisValueMemory != NULL) { HashTable_dispose(self->motion2DAxisValueMemory); } if (self->gamepadInputController != NULL) { GamepadLogicalInputController_dispose(self->gamepadInputController); } call_super(dispose, self); } bool InputController_keyDown(InputController * self, unsigned int keyCode, unsigned int modifiers, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->keyboardBindingCount; bindingIndex++) { if (self->inputMap->keyboardBindings[bindingIndex].keyCode == keyCode) { handled |= call_virtual(startAction, self, self->inputMap->keyboardBindings[bindingIndex].actionID, timestamp, peek, InputMap_genericKeyBinding(self->inputMap->keyboardBindings[bindingIndex])); } } for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->keyboardBindingWithModifiersCount; bindingIndex++) { if (self->inputMap->keyboardBindingsWithModifiers[bindingIndex].keyCode == keyCode && self->inputMap->keyboardBindingsWithModifiers[bindingIndex].modifierFlags == modifiers) { handled |= call_virtual(startAction, self, self->inputMap->keyboardBindingsWithModifiers[bindingIndex].actionID, timestamp, peek, InputMap_genericKeyBindingWithModifiers(self->inputMap->keyboardBindingsWithModifiers[bindingIndex])); } } return handled; } bool InputController_keyUp(InputController * self, unsigned int keyCode, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->keyboardBindingCount; bindingIndex++) { if (self->inputMap->keyboardBindings[bindingIndex].keyCode == keyCode) { handled |= call_virtual(stopAction, self, self->inputMap->keyboardBindings[bindingIndex].actionID, timestamp, peek); } } for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->keyboardBindingWithModifiersCount; bindingIndex++) { if (self->inputMap->keyboardBindingsWithModifiers[bindingIndex].keyCode == keyCode) { handled |= call_virtual(stopAction, self, self->inputMap->keyboardBindingsWithModifiers[bindingIndex].actionID, timestamp, peek); } } return handled; } bool InputController_keyModifiersChanged(InputController * self, unsigned int modifiers, unsigned int lastModifiers, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->keyModifierBindingCount; bindingIndex++) { unsigned int lastModifier = self->inputMap->keyModifierBindings[bindingIndex].modifierBit & lastModifiers; unsigned int modifier = self->inputMap->keyModifierBindings[bindingIndex].modifierBit & modifiers; if (lastModifier && !modifier) { handled |= call_virtual(stopAction, self, self->inputMap->keyModifierBindings[bindingIndex].actionID, timestamp, peek); } else if (modifier && !lastModifier) { handled |= call_virtual(startAction, self, self->inputMap->keyModifierBindings[bindingIndex].actionID, timestamp, peek, InputMap_genericKeyModifierBinding(self->inputMap->keyModifierBindings[bindingIndex])); } } return handled; } bool InputController_mouseDown(InputController * self, unsigned int buttonNumber, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseButtonBindingCount; bindingIndex++) { if (self->inputMap->mouseButtonBindings[bindingIndex].buttonNumber == buttonNumber) { handled |= call_virtual(startAction, self, self->inputMap->mouseButtonBindings[bindingIndex].actionID, timestamp, peek, InputMap_genericMouseButtonBinding(self->inputMap->mouseButtonBindings[bindingIndex])); } } return handled; } bool InputController_mouseUp(InputController * self, unsigned int buttonNumber, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseButtonBindingCount; bindingIndex++) { if (self->inputMap->mouseButtonBindings[bindingIndex].buttonNumber == buttonNumber) { handled |= call_virtual(stopAction, self, self->inputMap->mouseButtonBindings[bindingIndex].actionID, timestamp, peek); } } return handled; } bool InputController_mouseMoved(InputController * self, float deltaX, float deltaY, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; if (deltaX != 0.0f) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseMotion1DBindingCount; bindingIndex++) { if (self->inputMap->mouseMotion1DBindings[bindingIndex].axisNumber == 0) { handled |= call_virtual(motion, self, self->inputMap->mouseMotion1DBindings[bindingIndex].actionID, timestamp, ftox(deltaX * self->mouseSensitivityX), 0x00000, ftox(deltaX), 0x00000, peek); } } } if (deltaY != 0.0f) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseMotion1DBindingCount; bindingIndex++) { if (self->inputMap->mouseMotion1DBindings[bindingIndex].axisNumber == 1) { handled |= call_virtual(motion, self, self->inputMap->mouseMotion1DBindings[bindingIndex].actionID, timestamp, ftox(deltaY * self->mouseSensitivityY), 0x00000, ftox(deltaY), 0x00000, peek); } } } if (deltaX != 0.0f || deltaY != 0.0f) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseMotion2DBindingCount; bindingIndex++) { handled |= call_virtual(motion, self, self->inputMap->mouseMotion2DBindings[bindingIndex].actionID, timestamp, ftox(deltaX * self->mouseSensitivityX), ftox(deltaY * self->mouseSensitivityY), ftox(deltaX), ftox(deltaY), peek); } } return handled; } bool InputController_mouseScrollWheel(InputController * self, int deltaX, int deltaY, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; if (deltaX < 0) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseScrollBindingCount; bindingIndex++) { if (self->inputMap->mouseScrollBindings[bindingIndex].directionX < 0) { for (unsigned int unit = 0; unit < (unsigned int) -deltaX; unit++) { handled |= call_virtual(startAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek, InputMap_genericMouseScrollWheelBinding(self->inputMap->mouseScrollBindings[bindingIndex])); handled |= call_virtual(stopAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek); } } } } else if (deltaX > 0) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseScrollBindingCount; bindingIndex++) { if (self->inputMap->mouseScrollBindings[bindingIndex].directionX > 0) { for (unsigned int unit = 0; unit < (unsigned int) deltaX; unit++) { handled |= call_virtual(startAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek, InputMap_genericMouseScrollWheelBinding(self->inputMap->mouseScrollBindings[bindingIndex])); handled |= call_virtual(stopAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek); } } } } if (deltaY < 0) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseScrollBindingCount; bindingIndex++) { if (self->inputMap->mouseScrollBindings[bindingIndex].directionY < 0) { for (unsigned int unit = 0; unit < (unsigned int) -deltaY; unit++) { handled |= call_virtual(startAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek, InputMap_genericMouseScrollWheelBinding(self->inputMap->mouseScrollBindings[bindingIndex])); handled |= call_virtual(stopAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek); } } } } else if (deltaY > 0) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->mouseScrollBindingCount; bindingIndex++) { if (self->inputMap->mouseScrollBindings[bindingIndex].directionY > 0) { for (unsigned int unit = 0; unit < (unsigned int) deltaY; unit++) { handled |= call_virtual(startAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek, InputMap_genericMouseScrollWheelBinding(self->inputMap->mouseScrollBindings[bindingIndex])); handled |= call_virtual(stopAction, self, self->inputMap->mouseScrollBindings[bindingIndex].actionID, timestamp, peek); } } } } return handled; } bool InputController_gamepadButtonDown(InputController * self, struct Gamepad_device * device, unsigned int buttonID, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; // Physical button action bindings int vendorID = device->vendorID, productID = device->productID; InputMap_resolveGamepadAlias(self->inputMap, &vendorID, &productID); for (unsigned int gamepadIndex = 0; gamepadIndex < self->inputMap->gamepadPhysicalMapCount; gamepadIndex++) { if (self->inputMap->gamepadPhysicalMaps[gamepadIndex].vendorID == vendorID && self->inputMap->gamepadPhysicalMaps[gamepadIndex].productID == productID) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadPhysicalMaps[gamepadIndex].buttonActionBindingCount; bindingIndex++) { struct InputMap_gamepadPhysicalButtonActionBinding binding = self->inputMap->gamepadPhysicalMaps[gamepadIndex].buttonActionBindings[bindingIndex]; if (binding.buttonID == buttonID) { handled |= call_virtual(startAction, self, binding.actionID, timestamp, peek, InputMap_genericUnknownBinding()); } } break; } } if (self->gamepadInputController != NULL) { handled |= GamepadLogicalInputController_gamepadButtonDown(self->gamepadInputController, device, buttonID, timestamp, peek); } return handled; } bool InputController_gamepadButtonUp(InputController * self, struct Gamepad_device * device, unsigned int buttonID, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; // Physical button action bindings int vendorID = device->vendorID, productID = device->productID; InputMap_resolveGamepadAlias(self->inputMap, &vendorID, &productID); for (unsigned int gamepadIndex = 0; gamepadIndex < self->inputMap->gamepadPhysicalMapCount; gamepadIndex++) { if (self->inputMap->gamepadPhysicalMaps[gamepadIndex].vendorID == vendorID && self->inputMap->gamepadPhysicalMaps[gamepadIndex].productID == productID) { for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadPhysicalMaps[gamepadIndex].buttonActionBindingCount; bindingIndex++) { struct InputMap_gamepadPhysicalButtonActionBinding binding = self->inputMap->gamepadPhysicalMaps[gamepadIndex].buttonActionBindings[bindingIndex]; if (binding.buttonID == buttonID) { handled |= call_virtual(stopAction, self, binding.actionID, timestamp, peek); } } break; } } if (self->gamepadInputController != NULL) { handled |= GamepadLogicalInputController_gamepadButtonUp(self->gamepadInputController, device, buttonID, timestamp, peek); } return handled; } bool InputController_gamepadAxisMoved(InputController * self, struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, bool peek) { if (self->inputMap == NULL) { return false; } bool handled = false; int vendorID = device->vendorID, productID = device->productID; InputMap_resolveGamepadAlias(self->inputMap, &vendorID, &productID); for (unsigned int gamepadIndex = 0; gamepadIndex < self->inputMap->gamepadPhysicalMapCount; gamepadIndex++) { if (self->inputMap->gamepadPhysicalMaps[gamepadIndex].vendorID == vendorID && self->inputMap->gamepadPhysicalMaps[gamepadIndex].productID == productID) { // Physical axis action bindings for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadPhysicalMaps[gamepadIndex].axisActionBindingCount; bindingIndex++) { struct InputMap_gamepadPhysicalAxisActionBinding binding = self->inputMap->gamepadPhysicalMaps[gamepadIndex].axisActionBindings[bindingIndex]; if (binding.axisID == axisID) { int direction; if (binding.activateThreshold > binding.releaseThreshold) { direction = 1; } else if (binding.activateThreshold < binding.releaseThreshold) { direction = -1; } else if (binding.activateThreshold > 0) { direction = 1; } else { direction = -1; } if (lastValue * direction < binding.activateThreshold * direction && value * direction >= binding.activateThreshold * direction) { handled |= call_virtual(startAction, self, binding.actionID, timestamp, peek, InputMap_genericUnknownBinding()); } else if (lastValue * direction > binding.releaseThreshold * direction && value * direction <= binding.releaseThreshold * direction) { handled |= call_virtual(stopAction, self, binding.actionID, timestamp, peek); } } } // Physical axis motion bindings for (unsigned int bindingIndex = 0; bindingIndex < self->inputMap->gamepadPhysicalMaps[gamepadIndex].axisMotionBindingCount; bindingIndex++) { struct InputMap_gamepadPhysicalAxisMotionBinding binding = self->inputMap->gamepadPhysicalMaps[gamepadIndex].axisMotionBindings[bindingIndex]; if (binding.axisID == axisID) { handled |= call_virtual(motion, self, binding.actionID, timestamp, ftox(value), 0x00000, ftox(value), 0x00000, peek); } } break; } } if (self->gamepadInputController != NULL) { handled |= GamepadLogicalInputController_gamepadAxisMoved(self->gamepadInputController, device, axisID, value, lastValue, timestamp, peek); } return handled; } bool InputController_startAction(InputController * self, Atom actionID, double timestamp, bool peek, InputMap_bindingGeneric binding) { for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { if (actionID == self->actions[actionIndex].actionID) { if (!self->actions[actionIndex].active) { if (peek) { return true; } struct InputController_event eventData = {self->actions[actionIndex].actionID, timestamp, binding}; self->actions[actionIndex].active = true; self->actions[actionIndex].nextRepeatReferenceTime = timestamp + self->repeatInitialDelay; self->actions[actionIndex].timeUntilNextRepeat = self->repeatInitialDelay; if (actionDownEventAtom == NULL) { actionDownEventAtom = ATOM(INPUT_CONTROLLER_EVENT_ACTION_DOWN); } return EventDispatcher_dispatchEvent(self->eventDispatcher, actionDownEventAtom, &eventData); } break; } } return false; } bool InputController_stopAction(InputController * self, Atom actionID, double timestamp, bool peek) { for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { if (actionID == self->actions[actionIndex].actionID) { if (self->actions[actionIndex].active) { if (peek) { return true; } struct InputController_event eventData = {self->actions[actionIndex].actionID, timestamp, InputMap_genericUnknownBinding()}; self->actions[actionIndex].active = false; if (actionUpEventAtom == NULL) { actionUpEventAtom = ATOM(INPUT_CONTROLLER_EVENT_ACTION_UP); } return EventDispatcher_dispatchEvent(self->eventDispatcher, actionUpEventAtom, &eventData); } break; } } return false; } bool InputController_motion(InputController * self, Atom actionID, double timestamp, fixed16_16 valueX, fixed16_16 valueY, fixed16_16 rawValueX, fixed16_16 rawValueY, bool peek) { for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { if (actionID == self->actions[actionIndex].actionID) { if (peek) { return true; } struct InputController_motionEvent eventData = {.actionID = actionID, .timestamp = timestamp, .valueX = valueX, .valueY = valueY, .rawValueX = rawValueX, .rawValueY = rawValueY}; if (motionEventAtom == NULL) { motionEventAtom = ATOM(INPUT_CONTROLLER_EVENT_MOTION); } return EventDispatcher_dispatchEvent(self->eventDispatcher, motionEventAtom, &eventData); } } return false; } bool InputController_isActionActive(InputController * self, Atom actionID) { for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { if (actionID == self->actions[actionIndex].actionID) { return self->actions[actionIndex].active; } } return false; } void InputController_reset(InputController * self) { for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { self->actions[actionIndex].active = false; } } void InputController_setRepeatRate(InputController * self, double initialDelay, double repeatInterval) { self->repeatInitialDelay = initialDelay; self->repeatInterval = repeatInterval; } void InputController_processRepeats(InputController * self, double referenceTime, double timeElapsed) { if (self->repeatInterval != 0.0) { for (unsigned int actionIndex = 0; actionIndex < self->actionCount; actionIndex++) { if (self->actions[actionIndex].active) { if (self->lastRepeatReferenceTime != 0.0) { self->actions[actionIndex].nextRepeatReferenceTime += referenceTime - (self->lastRepeatReferenceTime + timeElapsed); } self->actions[actionIndex].timeUntilNextRepeat -= timeElapsed; if (self->actions[actionIndex].timeUntilNextRepeat <= 0.0) { struct InputController_event eventData = {self->actions[actionIndex].actionID, self->actions[actionIndex].nextRepeatReferenceTime, InputMap_genericUnknownBinding()}; if (self->lastRepeatReferenceTime == 0.0) { self->lastRepeatReferenceTime = referenceTime; } self->actions[actionIndex].nextRepeatReferenceTime += self->repeatInterval; self->actions[actionIndex].timeUntilNextRepeat += self->repeatInterval; if (actionRepeatEventAtom == NULL) { actionRepeatEventAtom = ATOM(INPUT_CONTROLLER_EVENT_ACTION_REPEAT); } EventDispatcher_dispatchEvent(self->eventDispatcher, actionRepeatEventAtom, &eventData); } } } } self->lastRepeatReferenceTime = referenceTime; }