diff options
author | 2011-01-15 18:14:15 -0800 | |
---|---|---|
committer | 2011-01-17 13:51:00 -0800 | |
commit | cb1404e45639d20439d7700b06d57ca1a1aad1fa (patch) | |
tree | 1d9bb7c58ea0a9ae36c242602fca59d343e8a0cd | |
parent | e0dfee2b7075c1eac0db08d79d47381693d2f125 (diff) |
Add joystick support to framework.
Change-Id: I95374436708752e1a9cff3f85c5b9bc3e0987961
-rw-r--r-- | api/current.xml | 313 | ||||
-rw-r--r-- | core/java/android/app/Activity.java | 52 | ||||
-rw-r--r-- | core/java/android/app/Dialog.java | 51 | ||||
-rwxr-xr-x | core/java/android/view/InputDevice.java | 31 | ||||
-rwxr-xr-x | core/java/android/view/KeyEvent.java | 52 | ||||
-rw-r--r-- | core/java/android/view/MotionEvent.java | 12 | ||||
-rw-r--r-- | core/java/android/view/View.java | 41 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 13 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 120 | ||||
-rw-r--r-- | core/java/android/view/Window.java | 20 | ||||
-rwxr-xr-x | core/res/res/values/attrs.xml | 16 | ||||
-rw-r--r-- | data/keyboards/Generic.kl | 18 | ||||
-rwxr-xr-x | include/ui/KeycodeLabels.h | 16 | ||||
-rw-r--r-- | native/include/android/input.h | 3 | ||||
-rw-r--r-- | native/include/android/keycodes.h | 16 | ||||
-rw-r--r-- | policy/src/com/android/internal/policy/impl/PhoneWindow.java | 16 | ||||
-rw-r--r-- | services/input/EventHub.cpp | 22 | ||||
-rw-r--r-- | services/input/EventHub.h | 7 | ||||
-rw-r--r-- | services/input/InputReader.cpp | 240 | ||||
-rw-r--r-- | services/input/InputReader.h | 136 |
20 files changed, 1157 insertions, 38 deletions
diff --git a/api/current.xml b/api/current.xml index b733d891025e..7b19677bd6f3 100644 --- a/api/current.xml +++ b/api/current.xml @@ -22816,6 +22816,19 @@ <parameter name="id" type="int"> </parameter> </method> +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="ev" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="false" @@ -23646,6 +23659,19 @@ visibility="public" > </method> +<method name="onGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="onKeyDown" return="boolean" abstract="false" @@ -27595,6 +27621,19 @@ visibility="public" > </method> +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="ev" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="false" @@ -27950,6 +27989,19 @@ visibility="public" > </method> +<method name="onGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="onKeyDown" return="boolean" abstract="false" @@ -207180,6 +207232,17 @@ visibility="public" > </field> +<field name="SOURCE_CLASS_JOYSTICK" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SOURCE_CLASS_MASK" type="int" transient="false" @@ -207235,6 +207298,28 @@ visibility="public" > </field> +<field name="SOURCE_GAMEPAD" + type="int" + transient="false" + volatile="false" + value="1025" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="SOURCE_JOYSTICK" + type="int" + transient="false" + volatile="false" + value="16777232" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SOURCE_KEYBOARD" type="int" transient="false" @@ -209077,6 +209162,182 @@ visibility="public" > </field> +<field name="KEYCODE_BUTTON_1" + type="int" + transient="false" + volatile="false" + value="188" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_10" + type="int" + transient="false" + volatile="false" + value="197" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_11" + type="int" + transient="false" + volatile="false" + value="198" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_12" + type="int" + transient="false" + volatile="false" + value="199" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_13" + type="int" + transient="false" + volatile="false" + value="200" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_14" + type="int" + transient="false" + volatile="false" + value="201" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_15" + type="int" + transient="false" + volatile="false" + value="202" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_16" + type="int" + transient="false" + volatile="false" + value="203" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_2" + type="int" + transient="false" + volatile="false" + value="189" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_3" + type="int" + transient="false" + volatile="false" + value="190" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_4" + type="int" + transient="false" + volatile="false" + value="191" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_5" + type="int" + transient="false" + volatile="false" + value="192" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_6" + type="int" + transient="false" + volatile="false" + value="193" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_7" + type="int" + transient="false" + volatile="false" + value="194" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_8" + type="int" + transient="false" + volatile="false" + value="195" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="KEYCODE_BUTTON_9" + type="int" + transient="false" + volatile="false" + value="196" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KEYCODE_BUTTON_A" type="int" transient="false" @@ -216194,6 +216455,19 @@ <parameter name="canvas" type="android.graphics.Canvas"> </parameter> </method> +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="false" @@ -218336,6 +218610,19 @@ <parameter name="previouslyFocusedRect" type="android.graphics.Rect"> </parameter> </method> +<method name="onGenericMotionEvent" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="onKeyDown" return="boolean" abstract="false" @@ -225273,6 +225560,19 @@ <parameter name="hardwareAccelerated" type="boolean"> </parameter> </method> +<method name="superDispatchGenericMotionEvent" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="superDispatchKeyEvent" return="boolean" abstract="true" @@ -225618,6 +225918,19 @@ deprecated="not deprecated" visibility="public" > +<method name="dispatchGenericMotionEvent" + return="boolean" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="event" type="android.view.MotionEvent"> +</parameter> +</method> <method name="dispatchKeyEvent" return="boolean" abstract="true" diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 6f0cb45dd466..3eef7855e27a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2081,7 +2081,39 @@ public class Activity extends ContextThemeWrapper public boolean onTrackballEvent(MotionEvent event) { return false; } - + + /** + * Called when a generic motion event was not handled by any of the + * views inside of the activity. + * <p> + * Generic motion events are dispatched to the focused view to describe + * the motions of input devices such as joysticks. The + * {@link MotionEvent#getSource() source} of the motion event specifies + * the class of input that was received. Implementations of this method + * must examine the bits in the source before processing the event. + * The following code example shows how this is done. + * </p> + * <code> + * public boolean onGenericMotionEvent(MotionEvent event) { + * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + * float x = event.getX(); + * float y = event.getY(); + * // process the joystick motion + * return true; + * } + * return super.onGenericMotionEvent(event); + * } + * </code> + * + * @param event The generic motion event being processed. + * + * @return Return true if you have consumed the event, false if you haven't. + * The default implementation always returns false. + */ + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } + /** * Called whenever a key, touch, or trackball event is dispatched to the * activity. Implement this method if you wish to know that the user has @@ -2264,6 +2296,24 @@ public class Activity extends ContextThemeWrapper return onTrackballEvent(ev); } + /** + * Called to process generic motion events. You can override this to + * intercept all generic motion events before they are dispatched to the + * window. Be sure to call this implementation for generic motion events + * that should be handled normally. + * + * @param ev The generic motion event. + * + * @return boolean Return true if this event was consumed. + */ + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + onUserInteraction(); + if (getWindow().superDispatchGenericMotionEvent(ev)) { + return true; + } + return onGenericMotionEvent(ev); + } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { event.setClassName(getClass().getName()); event.setPackageName(getPackageName()); diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 6791400a0055..b0929dd0aa6c 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -633,7 +633,39 @@ public class Dialog implements DialogInterface, Window.Callback, public boolean onTrackballEvent(MotionEvent event) { return false; } - + + /** + * Called when a generic motion event was not handled by any of the + * views inside of the dialog. + * <p> + * Generic motion events are dispatched to the focused view to describe + * the motions of input devices such as joysticks. The + * {@link MotionEvent#getSource() source} of the motion event specifies + * the class of input that was received. Implementations of this method + * must examine the bits in the source before processing the event. + * The following code example shows how this is done. + * </p> + * <code> + * public boolean onGenericMotionEvent(MotionEvent event) { + * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + * float x = event.getX(); + * float y = event.getY(); + * // process the joystick motion + * return true; + * } + * return super.onGenericMotionEvent(event); + * } + * </code> + * + * @param event The generic motion event being processed. + * + * @return Return true if you have consumed the event, false if you haven't. + * The default implementation always returns false. + */ + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { if (mDecor != null) { mWindowManager.updateViewLayout(mDecor, params); @@ -722,6 +754,23 @@ public class Dialog implements DialogInterface, Window.Callback, return onTrackballEvent(ev); } + /** + * Called to process generic motion events. You can override this to + * intercept all generic motion events before they are dispatched to the + * window. Be sure to call this implementation for generic motion events + * that should be handled normally. + * + * @param ev The generic motion event. + * + * @return boolean Return true if this event was consumed. + */ + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + if (mWindow.superDispatchGenericMotionEvent(ev)) { + return true; + } + return onGenericMotionEvent(ev); + } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { event.setClassName(getClass().getName()); event.setPackageName(mContext.getPackageName()); diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index dd04975c11d6..e799f7696c65 100755 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -98,7 +98,16 @@ public final class InputDevice implements Parcelable { * Use {@link #getMotionRange} to query the range of positions. */ public static final int SOURCE_CLASS_POSITION = 0x00000008; - + + /** + * The input source is a joystick. + * + * A {@link MotionEvent} should be interpreted as absolute joystick movements. + * + * Use {@link #getMotionRange} to query the range of positions. + */ + public static final int SOURCE_CLASS_JOYSTICK = 0x00000010; + /** * The input source is unknown. */ @@ -117,7 +126,15 @@ public final class InputDevice implements Parcelable { * @see #SOURCE_CLASS_BUTTON */ public static final int SOURCE_DPAD = 0x00000200 | SOURCE_CLASS_BUTTON; - + + /** + * The input source is a game pad. + * (It may also be a {@link #SOURCE_JOYSTICK}). + * + * @see #SOURCE_CLASS_BUTTON + */ + public static final int SOURCE_GAMEPAD = 0x00000400 | SOURCE_CLASS_BUTTON; + /** * The input source is a touch screen pointing device. * @@ -148,7 +165,15 @@ public final class InputDevice implements Parcelable { * @see #SOURCE_CLASS_POSITION */ public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION; - + + /** + * The input source is a joystick. + * (It may also be a {@link #SOURCE_GAMEPAD}). + * + * @see #SOURCE_CLASS_JOYSTICK + */ + public static final int SOURCE_JOYSTICK = 0x01000000 | SOURCE_CLASS_JOYSTICK; + /** * A special input source constant that is used when filtering input devices * to match devices that provide any type of input source. diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index ecf1aefc935e..695d16a2de8c 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -533,8 +533,40 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: App switch key. * Should bring up the application switcher dialog. */ public static final int KEYCODE_APP_SWITCH = 187; - - private static final int LAST_KEYCODE = KEYCODE_APP_SWITCH; + /** Key code constant: Generic Game Pad Button #1.*/ + public static final int KEYCODE_BUTTON_1 = 188; + /** Key code constant: Generic Game Pad Button #2.*/ + public static final int KEYCODE_BUTTON_2 = 189; + /** Key code constant: Generic Game Pad Button #3.*/ + public static final int KEYCODE_BUTTON_3 = 190; + /** Key code constant: Generic Game Pad Button #4.*/ + public static final int KEYCODE_BUTTON_4 = 191; + /** Key code constant: Generic Game Pad Button #5.*/ + public static final int KEYCODE_BUTTON_5 = 192; + /** Key code constant: Generic Game Pad Button #6.*/ + public static final int KEYCODE_BUTTON_6 = 193; + /** Key code constant: Generic Game Pad Button #7.*/ + public static final int KEYCODE_BUTTON_7 = 194; + /** Key code constant: Generic Game Pad Button #8.*/ + public static final int KEYCODE_BUTTON_8 = 195; + /** Key code constant: Generic Game Pad Button #9.*/ + public static final int KEYCODE_BUTTON_9 = 196; + /** Key code constant: Generic Game Pad Button #10.*/ + public static final int KEYCODE_BUTTON_10 = 197; + /** Key code constant: Generic Game Pad Button #11.*/ + public static final int KEYCODE_BUTTON_11 = 198; + /** Key code constant: Generic Game Pad Button #12.*/ + public static final int KEYCODE_BUTTON_12 = 199; + /** Key code constant: Generic Game Pad Button #13.*/ + public static final int KEYCODE_BUTTON_13 = 200; + /** Key code constant: Generic Game Pad Button #14.*/ + public static final int KEYCODE_BUTTON_14 = 201; + /** Key code constant: Generic Game Pad Button #15.*/ + public static final int KEYCODE_BUTTON_15 = 202; + /** Key code constant: Generic Game Pad Button #16.*/ + public static final int KEYCODE_BUTTON_16 = 203; + + private static final int LAST_KEYCODE = KEYCODE_BUTTON_16; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -741,6 +773,22 @@ public class KeyEvent extends InputEvent implements Parcelable { "KEYCODE_PROG_YELLOW", "KEYCODE_PROG_BLUE", "KEYCODE_APP_SWITCH", + "KEYCODE_BUTTON_1", + "KEYCODE_BUTTON_2", + "KEYCODE_BUTTON_3", + "KEYCODE_BUTTON_4", + "KEYCODE_BUTTON_5", + "KEYCODE_BUTTON_6", + "KEYCODE_BUTTON_7", + "KEYCODE_BUTTON_8", + "KEYCODE_BUTTON_9", + "KEYCODE_BUTTON_10", + "KEYCODE_BUTTON_11", + "KEYCODE_BUTTON_12", + "KEYCODE_BUTTON_13", + "KEYCODE_BUTTON_14", + "KEYCODE_BUTTON_15", + "KEYCODE_BUTTON_16", }; // Symbolic names of all metakeys in bit order from least significant to most significant. diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index db2cd5056252..0a5c5c64f770 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -26,7 +26,8 @@ import android.os.SystemClock; * class may hold either absolute or relative movements, depending on what * it is being used for. * <p> - * On pointing devices such as touch screens, pointer coordinates specify absolute + * On pointing devices with source class {@link InputDevice#SOURCE_CLASS_POINTER} + * such as touch screens, the pointer coordinates specify absolute * positions such as view X/Y coordinates. Each complete gesture is represented * by a sequence of motion events with actions that describe pointer state transitions * and movements. A gesture starts with a motion event with {@link #ACTION_DOWN} @@ -38,11 +39,18 @@ import android.os.SystemClock; * by a motion event with {@link #ACTION_UP} or when gesture is canceled * with {@link #ACTION_CANCEL}. * </p><p> - * On trackballs, the pointer coordinates specify relative movements as X/Y deltas. + * On trackball devices with source class {@link InputDevice#SOURCE_CLASS_TRACKBALL}, + * the pointer coordinates specify relative movements as X/Y deltas. * A trackball gesture consists of a sequence of movements described by motion * events with {@link #ACTION_MOVE} interspersed with occasional {@link #ACTION_DOWN} * or {@link #ACTION_UP} motion events when the trackball button is pressed or released. * </p><p> + * On joystick devices with source class {@link InputDevice#SOURCE_CLASS_JOYSTICK}, + * the pointer coordinates specify the absolute position of the joystick axes. + * The joystick axis values are normalized to a range of -1.0 to 1.0 where 0.0 corresponds + * to the center position. More information about the set of available axes and the + * range of motion can be obtained using {@link InputDevice#getMotionRange}. + * </p><p> * Motion events always report movements for all pointers at once. The number * of pointers only ever changes by one as individual pointers go up and down, * except when the gesture is canceled. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f05ef8c588cd..57520d1e5bfb 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4493,6 +4493,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Pass a generic motion event down to the focused view. + * + * @param event The motion event to be dispatched. + * @return True if the event was handled by the view, false otherwise. + */ + public boolean dispatchGenericMotionEvent(MotionEvent event) { + return onGenericMotionEvent(event); + } + + /** * Called when the window containing this view gains or loses window focus. * ViewGroups should override to route to their children. * @@ -4994,6 +5004,37 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Implement this method to handle generic motion events. + * <p> + * Generic motion events are dispatched to the focused view to describe + * the motions of input devices such as joysticks. The + * {@link MotionEvent#getSource() source} of the motion event specifies + * the class of input that was received. Implementations of this method + * must examine the bits in the source before processing the event. + * The following code example shows how this is done. + * </p> + * <code> + * public boolean onGenericMotionEvent(MotionEvent event) { + * if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) { + * float x = event.getX(); + * float y = event.getY(); + * // process the joystick motion + * return true; + * } + * return super.onGenericMotionEvent(event); + * } + * </code> + * + * @param event The generic motion event being processed. + * + * @return Return true if you have consumed the event, false if you haven't. + * The default implementation always returns false. + */ + public boolean onGenericMotionEvent(MotionEvent event) { + return false; + } + + /** * Implement this method to handle touch screen motion events. * * @param event The motion event. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index f6b6778ea555..dbc4779167b3 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1124,6 +1124,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) { + return super.dispatchGenericMotionEvent(event); + } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) { + return mFocused.dispatchGenericMotionEvent(event); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!onFilterTouchEventForSecurity(ev)) { return false; diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index ad9e68633958..4ed97422cbde 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -128,6 +128,11 @@ public final class ViewRoot extends Handler implements ViewParent, final TrackballAxis mTrackballAxisX = new TrackballAxis(); final TrackballAxis mTrackballAxisY = new TrackballAxis(); + int mLastJoystickXDirection; + int mLastJoystickYDirection; + int mLastJoystickXKeyCode; + int mLastJoystickYKeyCode; + final int[] mTmpLocation = new int[2]; final TypedValue mTmpValue = new TypedValue(); @@ -1878,6 +1883,7 @@ public final class ViewRoot extends Handler implements ViewParent, public final static int CLOSE_SYSTEM_DIALOGS = 1014; public final static int DISPATCH_DRAG_EVENT = 1015; public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016; + public final static int DISPATCH_GENERIC_MOTION = 1017; @Override public void handleMessage(Message msg) { @@ -1914,6 +1920,9 @@ public final class ViewRoot extends Handler implements ViewParent, case DISPATCH_TRACKBALL: deliverTrackballEvent((MotionEvent) msg.obj, msg.arg1 != 0); break; + case DISPATCH_GENERIC_MOTION: + deliverGenericMotionEvent((MotionEvent) msg.obj, msg.arg1 != 0); + break; case DISPATCH_APP_VISIBILITY: handleAppVisibility(msg.arg1 != 0); break; @@ -2420,6 +2429,102 @@ public final class ViewRoot extends Handler implements ViewParent, } } + private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) { + final int source = event.getSource(); + final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0; + + // If there is no view, then the event will not be handled. + if (mView == null || !mAdded) { + if (isJoystick) { + updateJoystickDirection(event, false); + } + finishGenericMotionEvent(event, sendDone, false); + return; + } + + // Deliver the event to the view. + if (mView.dispatchGenericMotionEvent(event)) { + ensureTouchMode(false); + if (isJoystick) { + updateJoystickDirection(event, false); + } + finishGenericMotionEvent(event, sendDone, true); + return; + } + + if (isJoystick) { + // Translate the joystick event into DPAD keys and try to deliver those. + updateJoystickDirection(event, true); + finishGenericMotionEvent(event, sendDone, true); + } else { + finishGenericMotionEvent(event, sendDone, false); + } + } + + private void finishGenericMotionEvent(MotionEvent event, boolean sendDone, boolean handled) { + event.recycle(); + if (sendDone) { + finishInputEvent(handled); + } + } + + private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) { + final long time = event.getEventTime(); + final int metaState = event.getMetaState(); + final int deviceId = event.getDeviceId(); + final int source = event.getSource(); + final int xDirection = joystickAxisValueToDirection(event.getX()); + final int yDirection = joystickAxisValueToDirection(event.getY()); + + if (xDirection != mLastJoystickXDirection) { + if (mLastJoystickXKeyCode != 0) { + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + mLastJoystickXKeyCode = 0; + } + + mLastJoystickXDirection = xDirection; + + if (xDirection != 0 && synthesizeNewKeys) { + mLastJoystickXKeyCode = xDirection > 0 + ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT; + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + } + } + + if (yDirection != mLastJoystickYDirection) { + if (mLastJoystickYKeyCode != 0) { + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + mLastJoystickYKeyCode = 0; + } + + mLastJoystickYDirection = yDirection; + + if (yDirection != 0 && synthesizeNewKeys) { + mLastJoystickYKeyCode = yDirection > 0 + ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP; + deliverKeyEvent(new KeyEvent(time, time, + KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState, + deviceId, 0, KeyEvent.FLAG_FALLBACK, source), false); + } + } + } + + private static int joystickAxisValueToDirection(float value) { + if (value >= 0.5f) { + return 1; + } else if (value <= -0.5f) { + return -1; + } else { + return 0; + } + } + /** * Returns true if the key is used for keyboard navigation. * @param keyEvent The key event. @@ -3053,11 +3158,7 @@ public final class ViewRoot extends Handler implements ViewParent, } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { dispatchTrackball(event, sendDone); } else { - // TODO - Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event); - if (sendDone) { - finishInputEvent(false); - } + dispatchGenericMotion(event, sendDone); } } @@ -3082,7 +3183,14 @@ public final class ViewRoot extends Handler implements ViewParent, msg.arg1 = sendDone ? 1 : 0; sendMessageAtTime(msg, event.getEventTime()); } - + + private void dispatchGenericMotion(MotionEvent event, boolean sendDone) { + Message msg = obtainMessage(DISPATCH_GENERIC_MOTION); + msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; + sendMessageAtTime(msg, event.getEventTime()); + } + public void dispatchAppVisibility(boolean visible) { Message msg = obtainMessage(DISPATCH_APP_VISIBILITY); msg.arg1 = visible ? 1 : 0; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 2f27935dd331..5cbaf2a69e9d 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -185,6 +185,18 @@ public abstract class Window { public boolean dispatchTrackballEvent(MotionEvent event); /** + * Called to process generic motion events. At the very least your + * implementation must call + * {@link android.view.Window#superDispatchGenericMotionEvent} to do the + * standard processing. + * + * @param event The generic motion event. + * + * @return boolean Return true if this event was consumed. + */ + public boolean dispatchGenericMotionEvent(MotionEvent event); + + /** * Called to process population of {@link AccessibilityEvent}s. * * @param event The event. @@ -1063,6 +1075,14 @@ public abstract class Window { public abstract boolean superDispatchTrackballEvent(MotionEvent event); /** + * Used by custom windows, such as Dialog, to pass the generic motion event + * further down the view hierarchy. Application developers should + * not need to implement or call this. + * + */ + public abstract boolean superDispatchGenericMotionEvent(MotionEvent event); + + /** * Retrieve the top-level window decor view (containing the standard * window frame/decorations and the client's content inside of that), which * can be added as a window to the window manager. diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 19e2b8d9c937..3f02b0ba6c98 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1310,6 +1310,22 @@ <enum name="KEYCODE_PROG_YELLOW" value="185" /> <enum name="KEYCODE_PROG_BLUE" value="186" /> <enum name="KEYCODE_APP_SWITCH" value="187" /> + <enum name="KEYCODE_BUTTON_1" value="188" /> + <enum name="KEYCODE_BUTTON_2" value="189" /> + <enum name="KEYCODE_BUTTON_3" value="190" /> + <enum name="KEYCODE_BUTTON_4" value="191" /> + <enum name="KEYCODE_BUTTON_5" value="192" /> + <enum name="KEYCODE_BUTTON_6" value="193" /> + <enum name="KEYCODE_BUTTON_7" value="194" /> + <enum name="KEYCODE_BUTTON_8" value="195" /> + <enum name="KEYCODE_BUTTON_9" value="196" /> + <enum name="KEYCODE_BUTTON_10" value="197" /> + <enum name="KEYCODE_BUTTON_11" value="198" /> + <enum name="KEYCODE_BUTTON_12" value="199" /> + <enum name="KEYCODE_BUTTON_13" value="200" /> + <enum name="KEYCODE_BUTTON_14" value="201" /> + <enum name="KEYCODE_BUTTON_15" value="202" /> + <enum name="KEYCODE_BUTTON_16" value="203" /> </attr> <!-- ***************************************************************** --> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 1ee121e977d9..0aefc3114679 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -262,6 +262,24 @@ key 233 HEADSETHOOK # key 240 "KEY_UNKNOWN" +key 288 BUTTON_1 +key 289 BUTTON_2 +key 290 BUTTON_3 +key 291 BUTTON_4 +key 292 BUTTON_5 +key 293 BUTTON_6 +key 294 BUTTON_7 +key 295 BUTTON_8 +key 296 BUTTON_9 +key 297 BUTTON_10 +key 298 BUTTON_11 +key 299 BUTTON_12 +key 300 BUTTON_13 +key 301 BUTTON_14 +key 302 BUTTON_15 +key 303 BUTTON_16 + + key 304 BUTTON_A key 305 BUTTON_B key 306 BUTTON_C diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index 9b1a8975e457..dbccf29b110e 100755 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -212,6 +212,22 @@ static const KeycodeLabel KEYCODES[] = { { "PROG_YELLOW", 185 }, { "PROG_BLUE", 186 }, { "APP_SWITCH", 187 }, + { "BUTTON_1", 188 }, + { "BUTTON_2", 189 }, + { "BUTTON_3", 190 }, + { "BUTTON_4", 191 }, + { "BUTTON_5", 192 }, + { "BUTTON_6", 193 }, + { "BUTTON_7", 194 }, + { "BUTTON_8", 195 }, + { "BUTTON_9", 196 }, + { "BUTTON_10", 197 }, + { "BUTTON_11", 198 }, + { "BUTTON_12", 199 }, + { "BUTTON_13", 200 }, + { "BUTTON_14", 201 }, + { "BUTTON_15", 202 }, + { "BUTTON_16", 203 }, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/native/include/android/input.h b/native/include/android/input.h index e196686ea37d..bad363d21471 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -333,6 +333,7 @@ enum { AINPUT_SOURCE_CLASS_POINTER = 0x00000002, AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004, AINPUT_SOURCE_CLASS_POSITION = 0x00000008, + AINPUT_SOURCE_CLASS_JOYSTICK = 0x00000010, }; enum { @@ -340,10 +341,12 @@ enum { AINPUT_SOURCE_KEYBOARD = 0x00000100 | AINPUT_SOURCE_CLASS_BUTTON, AINPUT_SOURCE_DPAD = 0x00000200 | AINPUT_SOURCE_CLASS_BUTTON, + AINPUT_SOURCE_GAMEPAD = 0x00000400 | AINPUT_SOURCE_CLASS_BUTTON, AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION, AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION, + AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, AINPUT_SOURCE_ANY = 0xffffff00, }; diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h index b026a0c9421b..c4a7eff947b1 100644 --- a/native/include/android/keycodes.h +++ b/native/include/android/keycodes.h @@ -231,6 +231,22 @@ enum { AKEYCODE_PROG_YELLOW = 185, AKEYCODE_PROG_BLUE = 186, AKEYCODE_APP_SWITCH = 187, + AKEYCODE_BUTTON_1 = 188, + AKEYCODE_BUTTON_2 = 189, + AKEYCODE_BUTTON_3 = 190, + AKEYCODE_BUTTON_4 = 191, + AKEYCODE_BUTTON_5 = 192, + AKEYCODE_BUTTON_6 = 193, + AKEYCODE_BUTTON_7 = 194, + AKEYCODE_BUTTON_8 = 195, + AKEYCODE_BUTTON_9 = 196, + AKEYCODE_BUTTON_10 = 197, + AKEYCODE_BUTTON_11 = 198, + AKEYCODE_BUTTON_12 = 199, + AKEYCODE_BUTTON_13 = 200, + AKEYCODE_BUTTON_14 = 201, + AKEYCODE_BUTTON_15 = 202, + AKEYCODE_BUTTON_16 = 203, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index d6b7366ea168..8e670b8357fb 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -1278,6 +1278,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return mDecor.superDispatchTrackballEvent(event); } + @Override + public boolean superDispatchGenericMotionEvent(MotionEvent event) { + return mDecor.superDispatchGenericMotionEvent(event); + } + /** * A key was pressed down and not handled by anything else in the window. * @@ -1691,6 +1696,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { .dispatchTrackballEvent(ev); } + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) { + final Callback cb = getCallback(); + return cb != null && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev) : super + .dispatchGenericMotionEvent(ev); + } + public boolean superDispatchKeyEvent(KeyEvent event) { return super.dispatchKeyEvent(event); } @@ -1707,6 +1719,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return super.dispatchTrackballEvent(event); } + public boolean superDispatchGenericMotionEvent(MotionEvent event) { + return super.dispatchGenericMotionEvent(event); + } + @Override public boolean onTouchEvent(MotionEvent event) { return onInterceptTouchEvent(event); diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 487e73fbbdce..efdb1775de76 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -624,7 +624,11 @@ static const int32_t GAMEPAD_KEYCODES[] = { AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, - AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE + AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, + AKEYCODE_BUTTON_1, AKEYCODE_BUTTON_2, AKEYCODE_BUTTON_3, AKEYCODE_BUTTON_4, + AKEYCODE_BUTTON_5, AKEYCODE_BUTTON_6, AKEYCODE_BUTTON_7, AKEYCODE_BUTTON_8, + AKEYCODE_BUTTON_9, AKEYCODE_BUTTON_10, AKEYCODE_BUTTON_11, AKEYCODE_BUTTON_12, + AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16, }; int EventHub::openDevice(const char *devicePath) { @@ -739,9 +743,9 @@ int EventHub::openDevice(const char *devicePath) { //} // See if this is a keyboard. Ignore everything in the button range except for - // gamepads which are also considered keyboards. + // joystick and gamepad buttons which are also considered keyboards. if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD), + || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK), sizeof_bit_array(BTN_DIGI)) || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX + 1))) { @@ -856,6 +860,18 @@ int EventHub::openDevice(const char *devicePath) { } } + // See if this device is a joystick. + // Ignore touchscreens because they use the same absolute axes for other purposes. + if (device->classes & INPUT_DEVICE_CLASS_GAMEPAD + && !(device->classes & INPUT_DEVICE_CLASS_TOUCHSCREEN)) { + if (test_bit(ABS_X, abs_bitmask) + || test_bit(ABS_Y, abs_bitmask) + || test_bit(ABS_HAT0X, abs_bitmask) + || test_bit(ABS_HAT0Y, abs_bitmask)) { + device->classes |= INPUT_DEVICE_CLASS_JOYSTICK; + } + } + // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == 0) { LOGV("Dropping device: id=%d, path='%s', name='%s'", diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 74b7ec5e9ebc..3b5a1ea05168 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -85,7 +85,7 @@ struct RawAbsoluteAxisInfo { int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise - inline int32_t getRange() { return maxValue - minValue; } + inline int32_t getRange() const { return maxValue - minValue; } inline void clear() { valid = false; @@ -100,7 +100,7 @@ struct RawAbsoluteAxisInfo { * Input device classes. */ enum { - /* The input device is a keyboard. */ + /* The input device is a keyboard or has buttons. */ INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, /* The input device is an alpha-numeric keyboard (not just a dial pad). */ @@ -123,6 +123,9 @@ enum { /* The input device has switches. */ INPUT_DEVICE_CLASS_SWITCH = 0x00000080, + + /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ + INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, }; /* diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 6b6679117fab..77b745ff5708 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -250,6 +250,9 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui if (classes & INPUT_DEVICE_CLASS_DPAD) { keyboardSources |= AINPUT_SOURCE_DPAD; } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSources |= AINPUT_SOURCE_GAMEPAD; + } if (keyboardSources != 0) { device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType)); @@ -267,6 +270,11 @@ InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, ui device->addMapper(new SingleTouchInputMapper(device)); } + // Joystick-like devices. + if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { + device->addMapper(new JoystickInputMapper(device)); + } + return device; } @@ -715,6 +723,16 @@ int32_t InputMapper::getMetaState() { return 0; } +void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, + const RawAbsoluteAxisInfo& axis, const char* name) { + if (axis.valid) { + dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", + name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); + } else { + dump.appendFormat(INDENT4 "%s: unknown range\n", name); + } +} + // --- SwitchInputMapper --- @@ -857,7 +875,7 @@ void KeyboardInputMapper::process(const RawEvent* rawEvent) { bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { return scanCode < BTN_MOUSE || scanCode >= KEY_OK - || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); + || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); } void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, @@ -1486,25 +1504,16 @@ void TouchInputMapper::configureRawAxes() { mRawAxes.orientation.clear(); } -static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) { - if (axis.valid) { - dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); - } else { - dump.appendFormat(INDENT4 "%s: unknown range\n", name); - } -} - void TouchInputMapper::dumpRawAxes(String8& dump) { dump.append(INDENT3 "Raw Axes:\n"); - dumpAxisInfo(dump, mRawAxes.x, "X"); - dumpAxisInfo(dump, mRawAxes.y, "Y"); - dumpAxisInfo(dump, mRawAxes.pressure, "Pressure"); - dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); - dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); - dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); - dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); - dumpAxisInfo(dump, mRawAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.pressure, "Pressure"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation"); } bool TouchInputMapper::configureSurfaceLocked() { @@ -3646,4 +3655,199 @@ void MultiTouchInputMapper::configureRawAxes() { } +// --- JoystickInputMapper --- + +JoystickInputMapper::JoystickInputMapper(InputDevice* device) : + InputMapper(device) { + initialize(); +} + +JoystickInputMapper::~JoystickInputMapper() { +} + +uint32_t JoystickInputMapper::getSources() { + return AINPUT_SOURCE_JOYSTICK; +} + +void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mAxes.x.valid) { + info->addMotionRange(AINPUT_MOTION_RANGE_X, + mAxes.x.min, mAxes.x.max, mAxes.x.flat, mAxes.x.fuzz); + } + if (mAxes.y.valid) { + info->addMotionRange(AINPUT_MOTION_RANGE_Y, + mAxes.y.min, mAxes.y.max, mAxes.y.flat, mAxes.y.fuzz); + } +} + +void JoystickInputMapper::dump(String8& dump) { + dump.append(INDENT2 "Joystick Input Mapper:\n"); + + dump.append(INDENT3 "Raw Axes:\n"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawAxes.y, "Y"); + + dump.append(INDENT3 "Normalized Axes:\n"); + dumpNormalizedAxis(dump, mAxes.x, "X"); + dumpNormalizedAxis(dump, mAxes.y, "Y"); + dumpNormalizedAxis(dump, mAxes.hat0X, "Hat0X"); + dumpNormalizedAxis(dump, mAxes.hat0Y, "Hat0Y"); +} + +void JoystickInputMapper::dumpNormalizedAxis(String8& dump, + const NormalizedAxis& axis, const char* name) { + if (axis.valid) { + dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, " + "scale=%0.3f, center=%0.3f, precision=%0.3f, value=%0.3f\n", + name, axis.min, axis.max, axis.flat, axis.fuzz, + axis.scale, axis.center, axis.precision, axis.value); + } else { + dump.appendFormat(INDENT4 "%s: unknown range\n", name); + } +} + +void JoystickInputMapper::configure() { + InputMapper::configure(); + + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0X, & mRawAxes.hat0X); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_HAT0Y, & mRawAxes.hat0Y); + + mAxes.x.configure(mRawAxes.x); + mAxes.y.configure(mRawAxes.y); + mAxes.hat0X.configure(mRawAxes.hat0X); + mAxes.hat0Y.configure(mRawAxes.hat0Y); +} + +void JoystickInputMapper::initialize() { + mAccumulator.clear(); + + mAxes.x.resetState(); + mAxes.y.resetState(); + mAxes.hat0X.resetState(); + mAxes.hat0Y.resetState(); +} + +void JoystickInputMapper::reset() { + // Recenter all axes. + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + mAccumulator.clear(); + mAccumulator.fields = Accumulator::FIELD_ALL; + sync(when); + + // Reinitialize state. + initialize(); + + InputMapper::reset(); +} + +void JoystickInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: + switch (rawEvent->scanCode) { + case ABS_X: + mAccumulator.fields |= Accumulator::FIELD_ABS_X; + mAccumulator.absX = rawEvent->value; + break; + case ABS_Y: + mAccumulator.fields |= Accumulator::FIELD_ABS_Y; + mAccumulator.absY = rawEvent->value; + break; + case ABS_HAT0X: + mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0X; + mAccumulator.absHat0X = rawEvent->value; + break; + case ABS_HAT0Y: + mAccumulator.fields |= Accumulator::FIELD_ABS_HAT0Y; + mAccumulator.absHat0Y = rawEvent->value; + break; + } + break; + + case EV_SYN: + switch (rawEvent->scanCode) { + case SYN_REPORT: + sync(rawEvent->when); + break; + } + break; + } +} + +void JoystickInputMapper::sync(nsecs_t when) { + uint32_t fields = mAccumulator.fields; + if (fields == 0) { + return; // no new state changes, so nothing to do + } + + int32_t metaState = mContext->getGlobalMetaState(); + + bool motionAxisChanged = false; + if (fields & Accumulator::FIELD_ABS_X) { + if (mAxes.x.updateValue(mAccumulator.absX)) { + motionAxisChanged = true; + } + } + + if (fields & Accumulator::FIELD_ABS_Y) { + if (mAxes.y.updateValue(mAccumulator.absY)) { + motionAxisChanged = true; + } + } + + if (motionAxisChanged) { + PointerCoords pointerCoords; + pointerCoords.x = mAxes.x.value; + pointerCoords.y = mAxes.y.value; + pointerCoords.touchMajor = 0; + pointerCoords.touchMinor = 0; + pointerCoords.toolMajor = 0; + pointerCoords.toolMinor = 0; + pointerCoords.pressure = 0; + pointerCoords.size = 0; + pointerCoords.orientation = 0; + + int32_t pointerId = 0; + getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, + AMOTION_EVENT_ACTION_MOVE, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerId, &pointerCoords, mAxes.x.precision, mAxes.y.precision, 0); + } + + if (fields & Accumulator::FIELD_ABS_HAT0X) { + if (mAxes.hat0X.updateValueAndDirection(mAccumulator.absHat0X)) { + notifyDirectionalAxis(mAxes.hat0X, when, metaState, + AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT); + } + } + + if (fields & Accumulator::FIELD_ABS_HAT0Y) { + if (mAxes.hat0Y.updateValueAndDirection(mAccumulator.absHat0Y)) { + notifyDirectionalAxis(mAxes.hat0Y, when, metaState, + AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN); + } + } + + mAccumulator.clear(); +} + +void JoystickInputMapper::notifyDirectionalAxis(DirectionalAxis& axis, + nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode) { + if (axis.lastKeyCode) { + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, + AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, + axis.lastKeyCode, 0, metaState, when); + axis.lastKeyCode = 0; + } + if (axis.direction) { + axis.lastKeyCode = axis.direction > 0 ? highKeyCode : lowKeyCode; + getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, 0, + AKEY_EVENT_ACTION_DOWN, AKEY_EVENT_FLAG_FROM_SYSTEM, + axis.lastKeyCode, 0, metaState, when); + } +} + + } // namespace android diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 8b2d40a096d2..613ed18bdc24 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -339,6 +339,9 @@ public: protected: InputDevice* mDevice; InputReaderContext* mContext; + + static void dumpRawAbsoluteAxisInfo(String8& dump, + const RawAbsoluteAxisInfo& axis, const char* name); }; @@ -951,6 +954,139 @@ private: void sync(nsecs_t when); }; + +class JoystickInputMapper : public InputMapper { +public: + JoystickInputMapper(InputDevice* device); + virtual ~JoystickInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(String8& dump); + virtual void configure(); + virtual void reset(); + virtual void process(const RawEvent* rawEvent); + +private: + struct RawAxes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo hat0X; + RawAbsoluteAxisInfo hat0Y; + } mRawAxes; + + struct NormalizedAxis { + bool valid; + + static const float min = -1.0f; + static const float max = -1.0f; + + float scale; // scale factor + float center; // center offset after scaling + float precision; // precision + float flat; // size of flat region + float fuzz; // error tolerance + + float value; // most recent value + + NormalizedAxis() : valid(false), scale(0), center(0), precision(0), + flat(0), fuzz(0), value(0) { + } + + void configure(const RawAbsoluteAxisInfo& rawAxis) { + if (rawAxis.valid && rawAxis.getRange() != 0) { + valid = true; + scale = 2.0f / rawAxis.getRange(); + precision = rawAxis.getRange(); + flat = rawAxis.flat * scale; + fuzz = rawAxis.fuzz * scale; + center = float(rawAxis.minValue + rawAxis.maxValue) / rawAxis.getRange(); + } + } + + void resetState() { + value = 0; + } + + bool updateValue(int32_t rawValue) { + float newValue = rawValue * scale - center; + if (value == newValue) { + return false; + } + value = newValue; + return true; + } + }; + + struct DirectionalAxis : NormalizedAxis { + int32_t direction; // most recent direction vector: value is one of -1, 0, 1. + + int32_t lastKeyCode; // most recent key code produced + + DirectionalAxis() : lastKeyCode(0) { + } + + void resetState() { + NormalizedAxis::resetState(); + direction = 0; + lastKeyCode = 0; + } + + bool updateValueAndDirection(int32_t rawValue) { + if (!updateValue(rawValue)) { + return false; + } + if (value > flat) { + direction = 1; + } else if (value < -flat) { + direction = -1; + } else { + direction = 0; + } + return true; + } + }; + + struct Axes { + NormalizedAxis x; + NormalizedAxis y; + DirectionalAxis hat0X; + DirectionalAxis hat0Y; + } mAxes; + + struct Accumulator { + enum { + FIELD_ABS_X = 1, + FIELD_ABS_Y = 2, + FIELD_ABS_HAT0X = 4, + FIELD_ABS_HAT0Y = 8, + + FIELD_ALL = FIELD_ABS_X | FIELD_ABS_Y | FIELD_ABS_HAT0X | FIELD_ABS_HAT0Y, + }; + + uint32_t fields; + + int32_t absX; + int32_t absY; + int32_t absHat0X; + int32_t absHat0Y; + + inline void clear() { + fields = 0; + } + } mAccumulator; + + void initialize(); + + void sync(nsecs_t when); + + void notifyDirectionalAxis(DirectionalAxis& axis, + nsecs_t when, int32_t metaState, int32_t lowKeyCode, int32_t highKeyCode); + + static void dumpNormalizedAxis(String8& dump, + const NormalizedAxis& axis, const char* name); +}; + } // namespace android #endif // _UI_INPUT_READER_H |