static void onDeviceRemoved(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) {
  unsigned int deviceIndex;
	
  for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
    if (((struct Gamepad_devicePrivate *) devices[deviceIndex]->privateData)->deviceRef == device) {
      if (Gamepad_deviceRemoveCallback != NULL) {
        Gamepad_deviceRemoveCallback(devices[deviceIndex], Gamepad_deviceRemoveContext);
      }
			
      disposeDevice(devices[deviceIndex]);
      numDevices--;
      for (; deviceIndex < numDevices; deviceIndex++) {
        devices[deviceIndex] = devices[deviceIndex + 1];
      }
      return;
    }
  }
}
static void processQueuedEvent(struct Gamepad_queuedEvent event) {
  switch (event.eventType) {
  case GAMEPAD_EVENT_DEVICE_ATTACHED:
    if (Gamepad_deviceAttachCallback != NULL) {
      Gamepad_deviceAttachCallback(event.eventData, Gamepad_deviceAttachContext);
    }
    break;
			
  case GAMEPAD_EVENT_DEVICE_REMOVED:
    if (Gamepad_deviceRemoveCallback != NULL) {
      Gamepad_deviceRemoveCallback(event.eventData, Gamepad_deviceRemoveContext);
    }
    break;
			
  case GAMEPAD_EVENT_BUTTON_DOWN:
    if (Gamepad_buttonDownCallback != NULL) {
      struct Gamepad_buttonEvent * buttonEvent = event.eventData;
      Gamepad_buttonDownCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonDownContext);
    }
    break;
			
  case GAMEPAD_EVENT_BUTTON_UP:
    if (Gamepad_buttonUpCallback != NULL) {
      struct Gamepad_buttonEvent * buttonEvent = event.eventData;
      Gamepad_buttonUpCallback(buttonEvent->device, buttonEvent->buttonID, buttonEvent->timestamp, Gamepad_buttonUpContext);
    }
    break;
			
  case GAMEPAD_EVENT_AXIS_MOVED:
    if (Gamepad_axisMoveCallback != NULL) {
      struct Gamepad_axisEvent * axisEvent = event.eventData;
      Gamepad_axisMoveCallback(axisEvent->device, axisEvent->axisID, axisEvent->value, axisEvent->lastValue, axisEvent->timestamp, Gamepad_axisMoveContext);
    }
    break;
  }
}
void Gamepad_processEvents() {
	unsigned int deviceIndex;
	static bool inProcessEvents;
	JOYINFOEX info;
	MMRESULT result;
	struct Gamepad_device * device;
	struct Gamepad_devicePrivate * devicePrivate;
	
	if (!inited || inProcessEvents) {
		return;
	}
	
	inProcessEvents = true;
	for (deviceIndex = 0; deviceIndex < numDevices; deviceIndex++) {
		device = devices[deviceIndex];
		devicePrivate = device->privateData;
		
		info.dwSize = sizeof(info);
		info.dwFlags = JOY_RETURNALL;
		result = joyGetPosEx(devicePrivate->joystickID, &info);
		if (result == JOYERR_UNPLUGGED) {
			if (Gamepad_deviceRemoveCallback != NULL) {
				Gamepad_deviceRemoveCallback(device, Gamepad_deviceRemoveContext);
			}
			
			disposeDevice(device);
			numDevices--;
			for (; deviceIndex < numDevices; deviceIndex++) {
				devices[deviceIndex] = devices[deviceIndex + 1];
			}
			
		} else if (result == JOYERR_NOERROR) {
			if (info.dwXpos != devicePrivate->lastState.dwXpos) {
				handleAxisChange(device, devicePrivate->xAxisIndex, info.dwXpos);
			}
			if (info.dwYpos != devicePrivate->lastState.dwYpos) {
				handleAxisChange(device, devicePrivate->yAxisIndex, info.dwYpos);
			}
			if (info.dwZpos != devicePrivate->lastState.dwZpos) {
				handleAxisChange(device, devicePrivate->zAxisIndex, info.dwZpos);
			}
			if (info.dwRpos != devicePrivate->lastState.dwRpos) {
				handleAxisChange(device, devicePrivate->rAxisIndex, info.dwRpos);
			}
			if (info.dwUpos != devicePrivate->lastState.dwUpos) {
				handleAxisChange(device, devicePrivate->uAxisIndex, info.dwUpos);
			}
			if (info.dwVpos != devicePrivate->lastState.dwVpos) {
				handleAxisChange(device, devicePrivate->vAxisIndex, info.dwVpos);
			}
			if (info.dwPOV != devicePrivate->lastState.dwPOV) {
				handlePOVChange(device, devicePrivate->lastState.dwPOV, info.dwPOV);
			}
			if (info.dwButtons != devicePrivate->lastState.dwButtons) {
				handleButtonChange(device, devicePrivate->lastState.dwButtons, info.dwButtons);
			}
			devicePrivate->lastState = info;
		}
	}
	inProcessEvents = false;
}