SDLControllerManager.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. package org.libsdl.app;
  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. import java.util.Collections;
  5. import java.util.Comparator;
  6. import java.util.List;
  7. import android.app.*;
  8. import android.content.Context;
  9. import android.hardware.*;
  10. import android.os.*;
  11. import android.view.*;
  12. import android.util.Log;
  13. public class SDLControllerManager
  14. {
  15. public static native int nativeSetupJNI();
  16. public static native int nativeAddJoystick(int device_id, String name, String desc,
  17. int is_accelerometer, int nbuttons,
  18. int naxes, int nhats, int nballs);
  19. public static native int nativeRemoveJoystick(int device_id);
  20. public static native int nativeAddHaptic(int device_id, String name);
  21. public static native int nativeRemoveHaptic(int device_id);
  22. public static native int onNativePadDown(int device_id, int keycode);
  23. public static native int onNativePadUp(int device_id, int keycode);
  24. public static native void onNativeJoy(int device_id, int axis,
  25. float value);
  26. public static native void onNativeHat(int device_id, int hat_id,
  27. int x, int y);
  28. protected static SDLJoystickHandler mJoystickHandler;
  29. protected static SDLHapticHandler mHapticHandler;
  30. private static final String TAG = "SDLControllerManager";
  31. public static void initialize() {
  32. mJoystickHandler = null;
  33. mHapticHandler = null;
  34. SDLControllerManager.setup();
  35. }
  36. public static void setup() {
  37. if (Build.VERSION.SDK_INT >= 16) {
  38. mJoystickHandler = new SDLJoystickHandler_API16();
  39. } else if (Build.VERSION.SDK_INT >= 12) {
  40. mJoystickHandler = new SDLJoystickHandler_API12();
  41. } else {
  42. mJoystickHandler = new SDLJoystickHandler();
  43. }
  44. mHapticHandler = new SDLHapticHandler();
  45. }
  46. // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
  47. public static boolean handleJoystickMotionEvent(MotionEvent event) {
  48. return mJoystickHandler.handleMotionEvent(event);
  49. }
  50. /**
  51. * This method is called by SDL using JNI.
  52. */
  53. public static void pollInputDevices() {
  54. mJoystickHandler.pollInputDevices();
  55. }
  56. /**
  57. * This method is called by SDL using JNI.
  58. */
  59. public static void pollHapticDevices() {
  60. mHapticHandler.pollHapticDevices();
  61. }
  62. /**
  63. * This method is called by SDL using JNI.
  64. */
  65. public static void hapticRun(int device_id, int length) {
  66. mHapticHandler.run(device_id, length);
  67. }
  68. // Check if a given device is considered a possible SDL joystick
  69. public static boolean isDeviceSDLJoystick(int deviceId) {
  70. InputDevice device = InputDevice.getDevice(deviceId);
  71. // We cannot use InputDevice.isVirtual before API 16, so let's accept
  72. // only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
  73. if ((device == null) || (deviceId < 0)) {
  74. return false;
  75. }
  76. int sources = device.getSources();
  77. if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) {
  78. Log.v(TAG, "Input device " + device.getName() + " is a joystick.");
  79. }
  80. if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
  81. Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
  82. }
  83. if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
  84. Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
  85. }
  86. return (((sources & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) ||
  87. ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
  88. ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
  89. );
  90. }
  91. }
  92. /* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
  93. class SDLJoystickHandler {
  94. /**
  95. * Handles given MotionEvent.
  96. * @param event the event to be handled.
  97. * @return if given event was processed.
  98. */
  99. public boolean handleMotionEvent(MotionEvent event) {
  100. return false;
  101. }
  102. /**
  103. * Handles adding and removing of input devices.
  104. */
  105. public void pollInputDevices() {
  106. }
  107. }
  108. /* Actual joystick functionality available for API >= 12 devices */
  109. class SDLJoystickHandler_API12 extends SDLJoystickHandler {
  110. static class SDLJoystick {
  111. public int device_id;
  112. public String name;
  113. public String desc;
  114. public ArrayList<InputDevice.MotionRange> axes;
  115. public ArrayList<InputDevice.MotionRange> hats;
  116. }
  117. static class RangeComparator implements Comparator<InputDevice.MotionRange> {
  118. @Override
  119. public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
  120. return arg0.getAxis() - arg1.getAxis();
  121. }
  122. }
  123. private ArrayList<SDLJoystick> mJoysticks;
  124. public SDLJoystickHandler_API12() {
  125. mJoysticks = new ArrayList<SDLJoystick>();
  126. }
  127. @Override
  128. public void pollInputDevices() {
  129. int[] deviceIds = InputDevice.getDeviceIds();
  130. // It helps processing the device ids in reverse order
  131. // For example, in the case of the XBox 360 wireless dongle,
  132. // so the first controller seen by SDL matches what the receiver
  133. // considers to be the first controller
  134. for(int i=deviceIds.length-1; i>-1; i--) {
  135. SDLJoystick joystick = getJoystick(deviceIds[i]);
  136. if (joystick == null) {
  137. joystick = new SDLJoystick();
  138. InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
  139. if (SDLControllerManager.isDeviceSDLJoystick(deviceIds[i])) {
  140. joystick.device_id = deviceIds[i];
  141. joystick.name = joystickDevice.getName();
  142. joystick.desc = getJoystickDescriptor(joystickDevice);
  143. joystick.axes = new ArrayList<InputDevice.MotionRange>();
  144. joystick.hats = new ArrayList<InputDevice.MotionRange>();
  145. List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
  146. Collections.sort(ranges, new RangeComparator());
  147. for (InputDevice.MotionRange range : ranges ) {
  148. if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
  149. if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
  150. range.getAxis() == MotionEvent.AXIS_HAT_Y) {
  151. joystick.hats.add(range);
  152. }
  153. else {
  154. joystick.axes.add(range);
  155. }
  156. }
  157. }
  158. mJoysticks.add(joystick);
  159. SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc, 0, -1,
  160. joystick.axes.size(), joystick.hats.size()/2, 0);
  161. }
  162. }
  163. }
  164. /* Check removed devices */
  165. ArrayList<Integer> removedDevices = new ArrayList<Integer>();
  166. for(int i=0; i < mJoysticks.size(); i++) {
  167. int device_id = mJoysticks.get(i).device_id;
  168. int j;
  169. for (j=0; j < deviceIds.length; j++) {
  170. if (device_id == deviceIds[j]) break;
  171. }
  172. if (j == deviceIds.length) {
  173. removedDevices.add(Integer.valueOf(device_id));
  174. }
  175. }
  176. for(int i=0; i < removedDevices.size(); i++) {
  177. int device_id = removedDevices.get(i).intValue();
  178. SDLControllerManager.nativeRemoveJoystick(device_id);
  179. for (int j=0; j < mJoysticks.size(); j++) {
  180. if (mJoysticks.get(j).device_id == device_id) {
  181. mJoysticks.remove(j);
  182. break;
  183. }
  184. }
  185. }
  186. }
  187. protected SDLJoystick getJoystick(int device_id) {
  188. for(int i=0; i < mJoysticks.size(); i++) {
  189. if (mJoysticks.get(i).device_id == device_id) {
  190. return mJoysticks.get(i);
  191. }
  192. }
  193. return null;
  194. }
  195. @Override
  196. public boolean handleMotionEvent(MotionEvent event) {
  197. if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
  198. int actionPointerIndex = event.getActionIndex();
  199. int action = event.getActionMasked();
  200. switch(action) {
  201. case MotionEvent.ACTION_MOVE:
  202. SDLJoystick joystick = getJoystick(event.getDeviceId());
  203. if ( joystick != null ) {
  204. for (int i = 0; i < joystick.axes.size(); i++) {
  205. InputDevice.MotionRange range = joystick.axes.get(i);
  206. /* Normalize the value to -1...1 */
  207. float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
  208. SDLControllerManager.onNativeJoy(joystick.device_id, i, value );
  209. }
  210. for (int i = 0; i < joystick.hats.size(); i+=2) {
  211. int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
  212. int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
  213. SDLControllerManager.onNativeHat(joystick.device_id, i/2, hatX, hatY );
  214. }
  215. }
  216. break;
  217. default:
  218. break;
  219. }
  220. }
  221. return true;
  222. }
  223. public String getJoystickDescriptor(InputDevice joystickDevice) {
  224. return joystickDevice.getName();
  225. }
  226. }
  227. class SDLJoystickHandler_API16 extends SDLJoystickHandler_API12 {
  228. @Override
  229. public String getJoystickDescriptor(InputDevice joystickDevice) {
  230. String desc = joystickDevice.getDescriptor();
  231. if (desc != null && desc != "") {
  232. return desc;
  233. }
  234. return super.getJoystickDescriptor(joystickDevice);
  235. }
  236. }
  237. class SDLHapticHandler {
  238. class SDLHaptic {
  239. public int device_id;
  240. public String name;
  241. public Vibrator vib;
  242. }
  243. private ArrayList<SDLHaptic> mHaptics;
  244. public SDLHapticHandler() {
  245. mHaptics = new ArrayList<SDLHaptic>();
  246. }
  247. public void run(int device_id, int length) {
  248. SDLHaptic haptic = getHaptic(device_id);
  249. if (haptic != null) {
  250. haptic.vib.vibrate (length);
  251. }
  252. }
  253. public void pollHapticDevices() {
  254. final int deviceId_VIBRATOR_SERVICE = 999999;
  255. boolean hasVibratorService = false;
  256. int[] deviceIds = InputDevice.getDeviceIds();
  257. // It helps processing the device ids in reverse order
  258. // For example, in the case of the XBox 360 wireless dongle,
  259. // so the first controller seen by SDL matches what the receiver
  260. // considers to be the first controller
  261. if (Build.VERSION.SDK_INT >= 16)
  262. {
  263. for (int i = deviceIds.length - 1; i > -1; i--) {
  264. SDLHaptic haptic = getHaptic(deviceIds[i]);
  265. if (haptic == null) {
  266. InputDevice device = InputDevice.getDevice(deviceIds[i]);
  267. Vibrator vib = device.getVibrator();
  268. if (vib.hasVibrator()) {
  269. haptic = new SDLHaptic();
  270. haptic.device_id = deviceIds[i];
  271. haptic.name = device.getName();
  272. haptic.vib = vib;
  273. mHaptics.add(haptic);
  274. SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
  275. }
  276. }
  277. }
  278. }
  279. /* Check VIBRATOR_SERVICE */
  280. Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
  281. if (vib != null) {
  282. if (Build.VERSION.SDK_INT >= 11) {
  283. hasVibratorService = vib.hasVibrator();
  284. } else {
  285. hasVibratorService = true;
  286. }
  287. if (hasVibratorService) {
  288. SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
  289. if (haptic == null) {
  290. haptic = new SDLHaptic();
  291. haptic.device_id = deviceId_VIBRATOR_SERVICE;
  292. haptic.name = "VIBRATOR_SERVICE";
  293. haptic.vib = vib;
  294. mHaptics.add(haptic);
  295. SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
  296. }
  297. }
  298. }
  299. /* Check removed devices */
  300. ArrayList<Integer> removedDevices = new ArrayList<Integer>();
  301. for(int i=0; i < mHaptics.size(); i++) {
  302. int device_id = mHaptics.get(i).device_id;
  303. int j;
  304. for (j=0; j < deviceIds.length; j++) {
  305. if (device_id == deviceIds[j]) break;
  306. }
  307. if (device_id == deviceId_VIBRATOR_SERVICE && hasVibratorService) {
  308. // don't remove the vibrator if it is still present
  309. } else if (j == deviceIds.length) {
  310. removedDevices.add(device_id);
  311. }
  312. }
  313. for(int i=0; i < removedDevices.size(); i++) {
  314. int device_id = removedDevices.get(i);
  315. SDLControllerManager.nativeRemoveHaptic(device_id);
  316. for (int j=0; j < mHaptics.size(); j++) {
  317. if (mHaptics.get(j).device_id == device_id) {
  318. mHaptics.remove(j);
  319. break;
  320. }
  321. }
  322. }
  323. }
  324. protected SDLHaptic getHaptic(int device_id) {
  325. for(int i=0; i < mHaptics.size(); i++) {
  326. if (mHaptics.get(i).device_id == device_id) {
  327. return mHaptics.get(i);
  328. }
  329. }
  330. return null;
  331. }
  332. }
  333. class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
  334. // Generic Motion (mouse hover, joystick...) events go here
  335. @Override
  336. public boolean onGenericMotion(View v, MotionEvent event) {
  337. float x, y;
  338. int action;
  339. switch ( event.getSource() ) {
  340. case InputDevice.SOURCE_JOYSTICK:
  341. case InputDevice.SOURCE_GAMEPAD:
  342. case InputDevice.SOURCE_DPAD:
  343. return SDLControllerManager.handleJoystickMotionEvent(event);
  344. case InputDevice.SOURCE_MOUSE:
  345. if (!SDLActivity.mSeparateMouseAndTouch) {
  346. break;
  347. }
  348. action = event.getActionMasked();
  349. switch (action) {
  350. case MotionEvent.ACTION_SCROLL:
  351. x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
  352. y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
  353. SDLActivity.onNativeMouse(0, action, x, y);
  354. return true;
  355. case MotionEvent.ACTION_HOVER_MOVE:
  356. x = event.getX(0);
  357. y = event.getY(0);
  358. SDLActivity.onNativeMouse(0, action, x, y);
  359. return true;
  360. default:
  361. break;
  362. }
  363. break;
  364. default:
  365. break;
  366. }
  367. // Event was not managed
  368. return false;
  369. }
  370. }