git-subtree-dir: external/gainput git-subtree-split: 2be0a50089eafcc6fccb66142180082e48f27f4c
618 lines
15 KiB
C++
618 lines
15 KiB
C++
|
|
|
|
#include <gainput/gainput.h>
|
|
#include <gainput/GainputInputDeltaState.h>
|
|
|
|
#if defined(GAINPUT_PLATFORM_LINUX)
|
|
#include <time.h>
|
|
#include <X11/Xlib.h>
|
|
#include "keyboard/GainputInputDeviceKeyboardLinux.h"
|
|
#include "mouse/GainputInputDeviceMouseLinux.h"
|
|
#elif defined(GAINPUT_PLATFORM_WIN)
|
|
#include "keyboard/GainputInputDeviceKeyboardWin.h"
|
|
#include "keyboard/GainputInputDeviceKeyboardWinRaw.h"
|
|
#include "mouse/GainputInputDeviceMouseWin.h"
|
|
#include "mouse/GainputInputDeviceMouseWinRaw.h"
|
|
#elif defined(GAINPUT_PLATFORM_ANDROID)
|
|
#include <time.h>
|
|
#include <jni.h>
|
|
#include "keyboard/GainputInputDeviceKeyboardAndroid.h"
|
|
#include "pad/GainputInputDevicePadAndroid.h"
|
|
#include "touch/GainputInputDeviceTouchAndroid.h"
|
|
static gainput::InputManager* gGainputInputManager;
|
|
#elif defined(GAINPUT_PLATFORM_IOS) || defined(GAINPUT_PLATFORM_MAC) || defined(GAINPUT_PLATFORM_TVOS)
|
|
#include <mach/mach.h>
|
|
#include <mach/clock.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "dev/GainputDev.h"
|
|
#include <gainput/GainputHelpers.h>
|
|
|
|
|
|
namespace gainput
|
|
{
|
|
|
|
InputManager::InputManager(bool useSystemTime, Allocator& allocator) :
|
|
allocator_(allocator),
|
|
devices_(allocator_),
|
|
nextDeviceId_(0),
|
|
listeners_(allocator_),
|
|
nextListenerId_(0),
|
|
sortedListeners_(allocator_),
|
|
modifiers_(allocator_),
|
|
nextModifierId_(0),
|
|
deltaState_(allocator_.New<InputDeltaState>(allocator_)),
|
|
currentTime_(0),
|
|
GAINPUT_CONC_CONSTRUCT(concurrentInputs_),
|
|
displayWidth_(-1),
|
|
displayHeight_(-1),
|
|
useSystemTime_(useSystemTime),
|
|
debugRenderingEnabled_(false),
|
|
debugRenderer_(0)
|
|
{
|
|
GAINPUT_DEV_INIT(this);
|
|
#ifdef GAINPUT_PLATFORM_ANDROID
|
|
gGainputInputManager = this;
|
|
#endif
|
|
}
|
|
|
|
InputManager::~InputManager()
|
|
{
|
|
allocator_.Delete(deltaState_);
|
|
|
|
for (DeviceMap::iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
allocator_.Delete(it->second);
|
|
}
|
|
|
|
GAINPUT_DEV_SHUTDOWN(this);
|
|
}
|
|
|
|
void
|
|
InputManager::Update()
|
|
{
|
|
Change change;
|
|
while (GAINPUT_CONC_DEQUEUE(concurrentInputs_, change))
|
|
{
|
|
if (change.type == BT_BOOL)
|
|
{
|
|
HandleButton(*change.device, *change.state, change.delta, change.buttonId, change.b);
|
|
}
|
|
else if (change.type == BT_FLOAT)
|
|
{
|
|
HandleAxis(*change.device, *change.state, change.delta, change.buttonId, change.f);
|
|
}
|
|
}
|
|
|
|
InputDeltaState* ds = listeners_.empty() ? 0 : deltaState_;
|
|
|
|
for (DeviceMap::iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
if (!it->second->IsLateUpdate())
|
|
{
|
|
it->second->Update(ds);
|
|
}
|
|
}
|
|
|
|
GAINPUT_DEV_UPDATE(ds);
|
|
|
|
for (HashMap<ModifierId, DeviceStateModifier*>::iterator it = modifiers_.begin();
|
|
it != modifiers_.end();
|
|
++it)
|
|
{
|
|
it->second->Update(ds);
|
|
}
|
|
|
|
for (DeviceMap::iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
if (it->second->IsLateUpdate())
|
|
{
|
|
it->second->Update(ds);
|
|
}
|
|
}
|
|
|
|
if (ds)
|
|
{
|
|
ds->NotifyListeners(sortedListeners_);
|
|
ds->Clear();
|
|
}
|
|
}
|
|
|
|
void
|
|
InputManager::Update(uint64_t deltaTime)
|
|
{
|
|
GAINPUT_ASSERT(useSystemTime_ == false);
|
|
currentTime_ += deltaTime;
|
|
Update();
|
|
}
|
|
|
|
uint64_t
|
|
InputManager::GetTime() const
|
|
{
|
|
if (useSystemTime_)
|
|
{
|
|
#if defined(GAINPUT_PLATFORM_LINUX) || defined(GAINPUT_PLATFORM_ANDROID)
|
|
struct timespec ts;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
uint64_t t = ts.tv_sec*1000ul + ts.tv_nsec/1000000ul;
|
|
return t;
|
|
#elif defined(GAINPUT_PLATFORM_WIN)
|
|
static LARGE_INTEGER perfFreq = { 0 };
|
|
if (perfFreq.QuadPart == 0)
|
|
{
|
|
QueryPerformanceFrequency(&perfFreq);
|
|
GAINPUT_ASSERT(perfFreq.QuadPart != 0);
|
|
}
|
|
LARGE_INTEGER count;
|
|
QueryPerformanceCounter(&count);
|
|
double t = 1000.0 * double(count.QuadPart) / double(perfFreq.QuadPart);
|
|
return static_cast<uint64_t>(t);
|
|
#elif defined(GAINPUT_PLATFORM_IOS) || defined(GAINPUT_PLATFORM_MAC) || defined(GAINPUT_PLATFORM_TVOS)
|
|
clock_serv_t cclock;
|
|
mach_timespec_t mts;
|
|
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
|
clock_get_time(cclock, &mts);
|
|
mach_port_deallocate(mach_task_self(), cclock);
|
|
uint64_t t = mts.tv_sec*1000ul + mts.tv_nsec/1000000ul;
|
|
return t;
|
|
#else
|
|
#error Gainput: No time support
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
return currentTime_;
|
|
}
|
|
}
|
|
|
|
DeviceId
|
|
InputManager::FindDeviceId(const char* typeName, unsigned index) const
|
|
{
|
|
for (DeviceMap::const_iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
if (strcmp(typeName, it->second->GetTypeName()) == 0
|
|
&& it->second->GetIndex() == index)
|
|
{
|
|
return it->first;
|
|
}
|
|
}
|
|
return InvalidDeviceId;
|
|
}
|
|
|
|
DeviceId
|
|
InputManager::FindDeviceId(InputDevice::DeviceType type, unsigned index) const
|
|
{
|
|
for (DeviceMap::const_iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
if (it->second->GetType() == type
|
|
&& it->second->GetIndex() == index)
|
|
{
|
|
return it->first;
|
|
}
|
|
}
|
|
return InvalidDeviceId;
|
|
}
|
|
|
|
ListenerId
|
|
InputManager::AddListener(InputListener* listener)
|
|
{
|
|
listeners_[nextListenerId_] = listener;
|
|
ReorderListeners();
|
|
return nextListenerId_++;
|
|
}
|
|
|
|
void
|
|
InputManager::RemoveListener(ListenerId listenerId)
|
|
{
|
|
listeners_.erase(listenerId);
|
|
ReorderListeners();
|
|
}
|
|
|
|
namespace {
|
|
static int CompareListeners(const void* a, const void* b)
|
|
{
|
|
const InputListener* listener1 = *reinterpret_cast<const InputListener* const*>(a);
|
|
const InputListener* listener2 = *reinterpret_cast<const InputListener* const*>(b);
|
|
return listener2->GetPriority() - listener1->GetPriority();
|
|
}
|
|
}
|
|
|
|
void
|
|
InputManager::ReorderListeners()
|
|
{
|
|
sortedListeners_.clear();
|
|
for (HashMap<ListenerId, InputListener*>::iterator it = listeners_.begin();
|
|
it != listeners_.end();
|
|
++it)
|
|
{
|
|
sortedListeners_.push_back(it->second);
|
|
}
|
|
|
|
if (sortedListeners_.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
qsort(&sortedListeners_[0],
|
|
sortedListeners_.size(),
|
|
sizeof(InputListener*),
|
|
&CompareListeners);
|
|
}
|
|
|
|
ModifierId
|
|
InputManager::AddDeviceStateModifier(DeviceStateModifier* modifier)
|
|
{
|
|
modifiers_[nextModifierId_] = modifier;
|
|
return nextModifierId_++;
|
|
}
|
|
|
|
void
|
|
InputManager::RemoveDeviceStateModifier(ModifierId modifierId)
|
|
{
|
|
modifiers_.erase(modifierId);
|
|
}
|
|
|
|
size_t
|
|
InputManager::GetAnyButtonDown(DeviceButtonSpec* outButtons, size_t maxButtonCount) const
|
|
{
|
|
size_t buttonsFound = 0;
|
|
for (DeviceMap::const_iterator it = devices_.begin();
|
|
it != devices_.end() && maxButtonCount > buttonsFound;
|
|
++it)
|
|
{
|
|
buttonsFound += it->second->GetAnyButtonDown(outButtons+buttonsFound, maxButtonCount-buttonsFound);
|
|
}
|
|
return buttonsFound;
|
|
}
|
|
|
|
unsigned
|
|
InputManager::GetDeviceCountByType(InputDevice::DeviceType type) const
|
|
{
|
|
unsigned count = 0;
|
|
for (DeviceMap::const_iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
if (it->second->GetType() == type)
|
|
{
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void
|
|
InputManager::DeviceCreated(InputDevice* device)
|
|
{
|
|
GAINPUT_UNUSED(device);
|
|
GAINPUT_DEV_NEW_DEVICE(device);
|
|
}
|
|
|
|
#if defined(GAINPUT_PLATFORM_LINUX)
|
|
void
|
|
InputManager::HandleEvent(XEvent& event)
|
|
{
|
|
for (DeviceMap::const_iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
#if defined(GAINPUT_DEV)
|
|
if (it->second->IsSynced())
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
if (it->second->GetType() == InputDevice::DT_KEYBOARD
|
|
&& it->second->GetVariant() == InputDevice::DV_STANDARD)
|
|
{
|
|
InputDeviceKeyboard* keyboard = static_cast<InputDeviceKeyboard*>(it->second);
|
|
InputDeviceKeyboardImplLinux* keyboardImpl = static_cast<InputDeviceKeyboardImplLinux*>(keyboard->GetPimpl());
|
|
GAINPUT_ASSERT(keyboardImpl);
|
|
keyboardImpl->HandleEvent(event);
|
|
}
|
|
else if (it->second->GetType() == InputDevice::DT_MOUSE
|
|
&& it->second->GetVariant() == InputDevice::DV_STANDARD)
|
|
{
|
|
InputDeviceMouse* mouse = static_cast<InputDeviceMouse*>(it->second);
|
|
InputDeviceMouseImplLinux* mouseImpl = static_cast<InputDeviceMouseImplLinux*>(mouse->GetPimpl());
|
|
GAINPUT_ASSERT(mouseImpl);
|
|
mouseImpl->HandleEvent(event);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(GAINPUT_PLATFORM_WIN)
|
|
void
|
|
InputManager::HandleMessage(const MSG& msg)
|
|
{
|
|
for (DeviceMap::const_iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
#if defined(GAINPUT_DEV)
|
|
if (it->second->IsSynced())
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
if (it->second->GetType() == InputDevice::DT_KEYBOARD)
|
|
{
|
|
InputDeviceKeyboard* keyboard = static_cast<InputDeviceKeyboard*>(it->second);
|
|
if (it->second->GetVariant() == InputDevice::DV_STANDARD)
|
|
{
|
|
InputDeviceKeyboardImplWin* keyboardImpl = static_cast<InputDeviceKeyboardImplWin*>(keyboard->GetPimpl());
|
|
GAINPUT_ASSERT(keyboardImpl);
|
|
keyboardImpl->HandleMessage(msg);
|
|
}
|
|
else if (it->second->GetVariant() == InputDevice::DV_RAW)
|
|
{
|
|
InputDeviceKeyboardImplWinRaw* keyboardImpl = static_cast<InputDeviceKeyboardImplWinRaw*>(keyboard->GetPimpl());
|
|
GAINPUT_ASSERT(keyboardImpl);
|
|
keyboardImpl->HandleMessage(msg);
|
|
}
|
|
}
|
|
else if (it->second->GetType() == InputDevice::DT_MOUSE)
|
|
{
|
|
InputDeviceMouse* mouse = static_cast<InputDeviceMouse*>(it->second);
|
|
if (it->second->GetVariant() == InputDevice::DV_STANDARD)
|
|
{
|
|
InputDeviceMouseImplWin* mouseImpl = static_cast<InputDeviceMouseImplWin*>(mouse->GetPimpl());
|
|
GAINPUT_ASSERT(mouseImpl);
|
|
mouseImpl->HandleMessage(msg);
|
|
}
|
|
else if (it->second->GetVariant() == InputDevice::DV_RAW)
|
|
{
|
|
InputDeviceMouseImplWinRaw* mouseImpl = static_cast<InputDeviceMouseImplWinRaw*>(mouse->GetPimpl());
|
|
GAINPUT_ASSERT(mouseImpl);
|
|
mouseImpl->HandleMessage(msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(GAINPUT_PLATFORM_ANDROID)
|
|
int32_t
|
|
InputManager::HandleInput(AInputEvent* event)
|
|
{
|
|
int handled = 0;
|
|
for (DeviceMap::const_iterator it = devices_.begin();
|
|
it != devices_.end();
|
|
++it)
|
|
{
|
|
#if defined(GAINPUT_DEV)
|
|
if (it->second->IsSynced())
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
if (it->second->GetType() == InputDevice::DT_TOUCH)
|
|
{
|
|
InputDeviceTouch* touch = static_cast<InputDeviceTouch*>(it->second);
|
|
InputDeviceTouchImplAndroid* touchImpl = static_cast<InputDeviceTouchImplAndroid*>(touch->GetPimpl());
|
|
GAINPUT_ASSERT(touchImpl);
|
|
handled |= touchImpl->HandleInput(event);
|
|
}
|
|
else if (it->second->GetType() == InputDevice::DT_KEYBOARD)
|
|
{
|
|
InputDeviceKeyboard* keyboard = static_cast<InputDeviceKeyboard*>(it->second);
|
|
InputDeviceKeyboardImplAndroid* keyboardImpl = static_cast<InputDeviceKeyboardImplAndroid*>(keyboard->GetPimpl());
|
|
GAINPUT_ASSERT(keyboardImpl);
|
|
handled |= keyboardImpl->HandleInput(event);
|
|
}
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
void
|
|
InputManager::HandleDeviceInput(DeviceInput const& input)
|
|
{
|
|
DeviceId devId = FindDeviceId(input.deviceType, input.deviceIndex);
|
|
if (devId == InvalidDeviceId)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InputDevice* device = GetDevice(devId);
|
|
if (!device)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if defined(GAINPUT_DEV)
|
|
if (device->IsSynced())
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
InputState* state = device->GetNextInputState();
|
|
if (!state)
|
|
{
|
|
state = device->GetInputState();
|
|
}
|
|
if (!state)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (input.buttonType == BT_BOOL)
|
|
{
|
|
EnqueueConcurrentChange(*device, *state, deltaState_, input.buttonId, input.value.b);
|
|
}
|
|
else if (input.buttonType == BT_FLOAT)
|
|
{
|
|
EnqueueConcurrentChange(*device, *state, deltaState_, input.buttonId, input.value.f);
|
|
}
|
|
else if (input.buttonType == BT_COUNT && input.deviceType == InputDevice::DT_PAD)
|
|
{
|
|
InputDevicePad* pad = static_cast<InputDevicePad*>(device);
|
|
InputDevicePadImplAndroid* impl = static_cast<InputDevicePadImplAndroid*>(pad->GetPimpl());
|
|
GAINPUT_ASSERT(impl);
|
|
if (input.value.b)
|
|
{
|
|
impl->SetState(InputDevice::DeviceState::DS_OK);
|
|
}
|
|
else
|
|
{
|
|
impl->SetState(InputDevice::DeviceState::DS_UNAVAILABLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void
|
|
InputManager::ConnectForStateSync(const char* ip, unsigned port)
|
|
{
|
|
GAINPUT_UNUSED(ip); GAINPUT_UNUSED(port);
|
|
GAINPUT_DEV_CONNECT(this, ip, port);
|
|
}
|
|
|
|
void
|
|
InputManager::StartDeviceStateSync(DeviceId deviceId)
|
|
{
|
|
GAINPUT_ASSERT(GetDevice(deviceId));
|
|
GAINPUT_ASSERT(GetDevice(deviceId)->GetType() != InputDevice::DT_GESTURE);
|
|
GAINPUT_DEV_START_SYNC(deviceId);
|
|
}
|
|
|
|
void
|
|
InputManager::SetDebugRenderingEnabled(bool enabled)
|
|
{
|
|
debugRenderingEnabled_ = enabled;
|
|
if (enabled)
|
|
{
|
|
GAINPUT_ASSERT(debugRenderer_);
|
|
}
|
|
}
|
|
|
|
void
|
|
InputManager::SetDebugRenderer(DebugRenderer* debugRenderer)
|
|
{
|
|
debugRenderer_ = debugRenderer;
|
|
}
|
|
|
|
void
|
|
InputManager::EnqueueConcurrentChange(InputDevice& device, InputState& state, InputDeltaState* delta, DeviceButtonId buttonId, bool value)
|
|
{
|
|
Change change;
|
|
change.device = &device;
|
|
change.state = &state;
|
|
change.delta = delta;
|
|
change.buttonId = buttonId;
|
|
change.type = BT_BOOL;
|
|
change.b = value;
|
|
GAINPUT_CONC_ENQUEUE(concurrentInputs_, change);
|
|
}
|
|
|
|
void
|
|
InputManager::EnqueueConcurrentChange(InputDevice& device, InputState& state, InputDeltaState* delta, DeviceButtonId buttonId, float value)
|
|
{
|
|
Change change;
|
|
change.device = &device;
|
|
change.state = &state;
|
|
change.delta = delta;
|
|
change.buttonId = buttonId;
|
|
change.type = BT_FLOAT;
|
|
change.f = value;
|
|
GAINPUT_CONC_ENQUEUE(concurrentInputs_, change);
|
|
}
|
|
|
|
}
|
|
|
|
#if defined(GAINPUT_PLATFORM_ANDROID)
|
|
extern "C" {
|
|
JNIEXPORT void JNICALL
|
|
Java_de_johanneskuhlmann_gainput_Gainput_nativeOnInputBool(JNIEnv * /*env*/, jobject /*thiz*/,
|
|
jint deviceType, jint deviceIndex,
|
|
jint buttonId, jboolean value)
|
|
{
|
|
if (!gGainputInputManager)
|
|
{
|
|
return;
|
|
}
|
|
using namespace gainput;
|
|
InputManager::DeviceInput input;
|
|
input.deviceType = static_cast<InputDevice::DeviceType>(deviceType);
|
|
input.deviceIndex = deviceIndex;
|
|
input.buttonType = BT_BOOL;
|
|
if (input.deviceType == InputDevice::DT_KEYBOARD)
|
|
{
|
|
DeviceId deviceId = gGainputInputManager->FindDeviceId(input.deviceType, deviceIndex);
|
|
if (deviceId != InvalidDeviceId)
|
|
{
|
|
InputDevice* device = gGainputInputManager->GetDevice(deviceId);
|
|
if (device)
|
|
{
|
|
InputDeviceKeyboard* keyboard = static_cast<InputDeviceKeyboard*>(device);
|
|
InputDeviceKeyboardImplAndroid* keyboardImpl = static_cast<InputDeviceKeyboardImplAndroid*>(keyboard->GetPimpl());
|
|
GAINPUT_ASSERT(keyboardImpl);
|
|
DeviceButtonId newId = keyboardImpl->Translate(buttonId);
|
|
if (newId != InvalidDeviceButtonId)
|
|
{
|
|
buttonId = newId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
input.buttonId = buttonId;
|
|
input.value.b = value;
|
|
gGainputInputManager->HandleDeviceInput(input);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_de_johanneskuhlmann_gainput_Gainput_nativeOnInputFloat(JNIEnv * /*env*/, jobject /*thiz*/,
|
|
jint deviceType, jint deviceIndex,
|
|
jint buttonId, jfloat value)
|
|
{
|
|
if (!gGainputInputManager)
|
|
{
|
|
return;
|
|
}
|
|
using namespace gainput;
|
|
InputManager::DeviceInput input;
|
|
input.deviceType = static_cast<InputDevice::DeviceType>(deviceType);
|
|
input.deviceIndex = deviceIndex;
|
|
input.buttonType = BT_FLOAT;
|
|
input.buttonId = buttonId;
|
|
input.value.f = value;
|
|
gGainputInputManager->HandleDeviceInput(input);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_de_johanneskuhlmann_gainput_Gainput_nativeOnDeviceChanged(JNIEnv * /*env*/, jobject /*thiz*/,
|
|
jint deviceId, jboolean value)
|
|
{
|
|
if (!gGainputInputManager)
|
|
{
|
|
return;
|
|
}
|
|
using namespace gainput;
|
|
InputManager::DeviceInput input;
|
|
input.deviceType = InputDevice::DT_PAD;
|
|
input.deviceIndex = deviceId;
|
|
input.buttonType = BT_COUNT;
|
|
input.value.b = value;
|
|
gGainputInputManager->HandleDeviceInput(input);
|
|
}
|
|
}
|
|
#endif
|