10 changed files with 393 additions and 95 deletions
@ -0,0 +1,220 @@
@@ -0,0 +1,220 @@
|
||||
#include "JoystickAndroid.h" |
||||
|
||||
#include "QGCApplication.h" |
||||
|
||||
#include <QQmlEngine> |
||||
|
||||
int JoystickAndroid::_androidBtnListCount; |
||||
int *JoystickAndroid::_androidBtnList; |
||||
int JoystickAndroid::ACTION_DOWN; |
||||
int JoystickAndroid::ACTION_UP; |
||||
QMutex JoystickAndroid::m_mutex; |
||||
|
||||
JoystickAndroid::JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager) |
||||
: Joystick(name,axisCount,buttonCount,multiVehicleManager) |
||||
, deviceId(id) |
||||
{ |
||||
int i; |
||||
|
||||
QAndroidJniEnvironment env; |
||||
QAndroidJniObject inputDevice = QAndroidJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", id); |
||||
|
||||
//set button mapping (number->code)
|
||||
jintArray b = env->NewIntArray(_androidBtnListCount); |
||||
env->SetIntArrayRegion(b,0,_androidBtnListCount,_androidBtnList); |
||||
|
||||
QAndroidJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", b); |
||||
jbooleanArray jSupportedButtons = btns.object<jbooleanArray>(); |
||||
jboolean* supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr); |
||||
//create a mapping table (btnCode) that maps button number with button code
|
||||
btnValue = new bool[_buttonCount]; |
||||
btnCode = new int[_buttonCount]; |
||||
int c = 0; |
||||
for (i=0;i<_androidBtnListCount;i++) |
||||
if (supportedButtons[i]) { |
||||
btnValue[c] = false; |
||||
btnCode[c] = _androidBtnList[i]; |
||||
c++; |
||||
} |
||||
|
||||
env->ReleaseBooleanArrayElements(jSupportedButtons, supportedButtons, 0); |
||||
|
||||
//set axis mapping (number->code)
|
||||
axisValue = new int[_axisCount]; |
||||
axisCode = new int[_axisCount]; |
||||
QAndroidJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;"); |
||||
for (i=0;i<_axisCount;i++) { |
||||
QAndroidJniObject range = rangeListNative.callObjectMethod("get", "(I)Ljava/lang/Object;",i); |
||||
axisCode[i] = range.callMethod<jint>("getAxis"); |
||||
axisValue[i] = 0; |
||||
} |
||||
|
||||
|
||||
qCDebug(JoystickLog) << "axis:" <<_axisCount << "buttons:" <<_buttonCount; |
||||
QtAndroidPrivate::registerGenericMotionEventListener(this); |
||||
QtAndroidPrivate::registerKeyEventListener(this); |
||||
} |
||||
|
||||
JoystickAndroid::~JoystickAndroid() { |
||||
delete btnCode; |
||||
delete axisCode; |
||||
delete btnValue; |
||||
delete axisValue; |
||||
|
||||
QtAndroidPrivate::unregisterGenericMotionEventListener(this); |
||||
QtAndroidPrivate::unregisterKeyEventListener(this); |
||||
} |
||||
|
||||
QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVehicleManager) { |
||||
bool joystickFound = false; |
||||
static QMap<QString, Joystick*> ret; |
||||
|
||||
_initStatic(); //it's enough to run it once, should be in a static constructor
|
||||
|
||||
QMutexLocker lock(&m_mutex); |
||||
|
||||
QAndroidJniEnvironment env; |
||||
QAndroidJniObject o = QAndroidJniObject::callStaticObjectMethod<jintArray>("android/view/InputDevice", "getDeviceIds"); |
||||
jintArray jarr = o.object<jintArray>(); |
||||
int sz = env->GetArrayLength(jarr); |
||||
jint *buff = env->GetIntArrayElements(jarr, nullptr); |
||||
|
||||
int SOURCE_GAMEPAD = QAndroidJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_GAMEPAD"); |
||||
int SOURCE_JOYSTICK = QAndroidJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_JOYSTICK"); |
||||
|
||||
for (int i = 0; i < sz; ++i) { |
||||
QAndroidJniObject inputDevice = QAndroidJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", buff[i]); |
||||
int sources = inputDevice.callMethod<jint>("getSources", "()I"); |
||||
if (((sources & SOURCE_GAMEPAD) != SOURCE_GAMEPAD) //check if the input device is interesting to us
|
||||
&& ((sources & SOURCE_JOYSTICK) != SOURCE_JOYSTICK)) continue; |
||||
|
||||
//get id and name
|
||||
QString id = inputDevice.callObjectMethod("getDescriptor", "()Ljava/lang/String;").toString(); |
||||
QString name = inputDevice.callObjectMethod("getName", "()Ljava/lang/String;").toString(); |
||||
|
||||
|
||||
if (joystickFound) { //skipping {
|
||||
qWarning() << "Skipping joystick:" << name; |
||||
continue; |
||||
} |
||||
|
||||
//get number of axis
|
||||
QAndroidJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;"); |
||||
int axisCount = rangeListNative.callMethod<jint>("size"); |
||||
|
||||
//get number of buttons
|
||||
jintArray a = env->NewIntArray(_androidBtnListCount); |
||||
env->SetIntArrayRegion(a,0,_androidBtnListCount,_androidBtnList); |
||||
QAndroidJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", a); |
||||
jbooleanArray jSupportedButtons = btns.object<jbooleanArray>(); |
||||
jboolean* supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr); |
||||
int buttonCount = 0; |
||||
for (int j=0;j<_androidBtnListCount;j++) |
||||
if (supportedButtons[j]) buttonCount++; |
||||
env->ReleaseBooleanArrayElements(jSupportedButtons, supportedButtons, 0); |
||||
|
||||
qCDebug(JoystickLog) << "\t" << name << "id:" << buff[i] << "axes:" << axisCount << "buttons:" << buttonCount; |
||||
|
||||
ret[name] = new JoystickAndroid(name, axisCount, buttonCount, buff[i], _multiVehicleManager); |
||||
joystickFound = true; |
||||
} |
||||
|
||||
env->ReleaseIntArrayElements(jarr, buff, 0); |
||||
|
||||
|
||||
return ret; |
||||
} |
||||
|
||||
|
||||
bool JoystickAndroid::handleKeyEvent(jobject event) { |
||||
QJNIObjectPrivate ev(event); |
||||
QMutexLocker lock(&m_mutex); |
||||
const int _deviceId = ev.callMethod<jint>("getDeviceId", "()I"); |
||||
if (_deviceId!=deviceId) return false; |
||||
|
||||
const int action = ev.callMethod<jint>("getAction", "()I"); |
||||
const int keyCode = ev.callMethod<jint>("getKeyCode", "()I"); |
||||
|
||||
for (int i=0;i<_buttonCount;i++) { |
||||
if (btnCode[i]==keyCode) { |
||||
if (action==ACTION_DOWN) btnValue[i] = true; |
||||
if (action==ACTION_UP) btnValue[i] = false; |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool JoystickAndroid::handleGenericMotionEvent(jobject event) { |
||||
QJNIObjectPrivate ev(event); |
||||
QMutexLocker lock(&m_mutex); |
||||
const int _deviceId = ev.callMethod<jint>("getDeviceId", "()I"); |
||||
if (_deviceId!=deviceId) return false; |
||||
|
||||
for (int i=0;i<_axisCount;i++) { |
||||
const float v = ev.callMethod<jfloat>("getAxisValue", "(I)F",axisCode[i]); |
||||
axisValue[i] = (int)(v*32767.f); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
|
||||
bool JoystickAndroid::open(void) { |
||||
return true; |
||||
} |
||||
|
||||
void JoystickAndroid::close(void) { |
||||
} |
||||
|
||||
bool JoystickAndroid::update(void) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
bool JoystickAndroid::getButton(int i) { |
||||
return btnValue[ i ]; |
||||
} |
||||
|
||||
int JoystickAndroid::getAxis(int i) { |
||||
return axisValue[ i ]; |
||||
} |
||||
|
||||
|
||||
//helper method
|
||||
void JoystickAndroid::_initStatic() { |
||||
//this gets list of all possible buttons - this is needed to check how many buttons our gamepad supports
|
||||
//instead of the whole logic below we could have just a simple array of hardcoded int values as these 'should' not change
|
||||
|
||||
//int JoystickAndroid::_androidBtnListCount;
|
||||
_androidBtnListCount = 31; |
||||
static int ret[31]; //there are 31 buttons in total accordingy to the API
|
||||
int i; |
||||
//int *JoystickAndroid::
|
||||
_androidBtnList = ret; |
||||
|
||||
for (i=1;i<=16;i++) { |
||||
QString name = "KEYCODE_BUTTON_"+QString::number(i); |
||||
ret[i-1] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", name.toStdString().c_str()); |
||||
} |
||||
i--; |
||||
|
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_A"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_B"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_C"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_L1"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_L2"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_R1"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_R2"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_MODE"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_SELECT"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_START"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_THUMBL"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_THUMBR"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_X"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_Y"); |
||||
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_Z"); |
||||
|
||||
ACTION_DOWN = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_DOWN"); |
||||
ACTION_UP = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_UP"); |
||||
} |
||||
|
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
#ifndef JOYSTICKANDROID_H |
||||
#define JOYSTICKANDROID_H |
||||
|
||||
#include "Joystick.h" |
||||
#include "Vehicle.h" |
||||
#include "MultiVehicleManager.h" |
||||
|
||||
#include <jni.h> |
||||
#include <QtCore/private/qjni_p.h> |
||||
#include <QtCore/private/qjnihelpers_p.h> |
||||
#include <QtAndroidExtras/QtAndroidExtras> |
||||
#include <QtAndroidExtras/QAndroidJniObject> |
||||
|
||||
|
||||
class JoystickAndroid : public Joystick, public QtAndroidPrivate::GenericMotionEventListener, public QtAndroidPrivate::KeyEventListener |
||||
{ |
||||
public: |
||||
JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager); |
||||
~JoystickAndroid(); |
||||
|
||||
static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager);
|
||||
|
||||
private: |
||||
bool handleKeyEvent(jobject event); |
||||
bool handleGenericMotionEvent(jobject event); |
||||
|
||||
virtual bool open(); |
||||
virtual void close(); |
||||
virtual bool update(); |
||||
|
||||
virtual bool getButton(int i); |
||||
virtual int getAxis(int i); |
||||
|
||||
int *btnCode; |
||||
int *axisCode; |
||||
bool *btnValue; |
||||
int *axisValue; |
||||
|
||||
static void _initStatic(); |
||||
static int * _androidBtnList; //list of all possible android buttons
|
||||
static int _androidBtnListCount; |
||||
|
||||
static int ACTION_DOWN, ACTION_UP; |
||||
static QMutex m_mutex; |
||||
|
||||
int deviceId; |
||||
}; |
||||
|
||||
#endif // JOYSTICKANDROID_H
|
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
#include "JoystickSDL.h" |
||||
|
||||
#include "QGCApplication.h" |
||||
|
||||
#include <QQmlEngine> |
||||
|
||||
JoystickSDL::JoystickSDL(const QString& name, int axisCount, int buttonCount, int index, MultiVehicleManager* multiVehicleManager) |
||||
: Joystick(name,axisCount,buttonCount,multiVehicleManager) |
||||
, _index(index) |
||||
{ |
||||
} |
||||
|
||||
QMap<QString, Joystick*> JoystickSDL::discover(MultiVehicleManager* _multiVehicleManager) { |
||||
static QMap<QString, Joystick*> ret; |
||||
|
||||
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE) < 0) { |
||||
qWarning() << "Couldn't initialize SimpleDirectMediaLayer:" << SDL_GetError(); |
||||
return ret; |
||||
} |
||||
|
||||
// Load available joysticks
|
||||
|
||||
qCDebug(JoystickLog) << "Available joysticks"; |
||||
|
||||
for (int i=0; i<SDL_NumJoysticks(); i++) { |
||||
QString name = SDL_JoystickName(i); |
||||
|
||||
if (!ret.contains(name)) { |
||||
int axisCount, buttonCount; |
||||
|
||||
SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i); |
||||
axisCount = SDL_JoystickNumAxes(sdlJoystick); |
||||
buttonCount = SDL_JoystickNumButtons(sdlJoystick); |
||||
SDL_JoystickClose(sdlJoystick); |
||||
|
||||
qCDebug(JoystickLog) << "\t" << name << "axes:" << axisCount << "buttons:" << buttonCount; |
||||
ret[name] = new JoystickSDL(name, axisCount, buttonCount, i, _multiVehicleManager); |
||||
} else { |
||||
qCDebug(JoystickLog) << "\tSkipping duplicate" << name; |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
bool JoystickSDL::open(void) { |
||||
sdlJoystick = SDL_JoystickOpen(_index); |
||||
|
||||
if (!sdlJoystick) { |
||||
qCWarning(JoystickLog) << "SDL_JoystickOpen failed:" << SDL_GetError(); |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void JoystickSDL::close(void) { |
||||
SDL_JoystickClose(sdlJoystick); |
||||
} |
||||
|
||||
bool JoystickSDL::update(void) |
||||
{ |
||||
SDL_JoystickUpdate(); |
||||
return true; |
||||
} |
||||
|
||||
bool JoystickSDL::getButton(int i) { |
||||
return !!SDL_JoystickGetButton(sdlJoystick, i); |
||||
} |
||||
|
||||
int JoystickSDL::getAxis(int i) { |
||||
return SDL_JoystickGetAxis(sdlJoystick, i); |
||||
} |
||||
|
Loading…
Reference in new issue