diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-08-30 18:29:12 -0700 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-08-30 18:29:12 -0700 |
commit | 22f5ee93746f37823265b8cb9b1e94e572607514 (patch) | |
tree | 269e105935875e8453c0bd33792fdeedd4c928ee | |
parent | 02f4f0eb4919453e8dbf40549d4ae9c05f05b8dd (diff) | |
parent | 36f0cb26cbb4ef62995ff2e5a540cf8814e7f030 (diff) | |
download | frameworks_base-22f5ee93746f37823265b8cb9b1e94e572607514.zip frameworks_base-22f5ee93746f37823265b8cb9b1e94e572607514.tar.gz frameworks_base-22f5ee93746f37823265b8cb9b1e94e572607514.tar.bz2 |
am 36f0cb26: am 8d60866e: Input device calibration and capabilities.
Merge commit '36f0cb26cbb4ef62995ff2e5a540cf8814e7f030'
* commit '36f0cb26cbb4ef62995ff2e5a540cf8814e7f030':
Input device calibration and capabilities.
-rw-r--r-- | api/current.xml | 61 | ||||
-rw-r--r-- | core/java/android/view/IWindowManager.aidl | 5 | ||||
-rw-r--r-- | core/java/android/view/InputDevice.aidl | 20 | ||||
-rwxr-xr-x | core/java/android/view/InputDevice.java | 219 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/PointerLocationView.java | 406 | ||||
-rw-r--r-- | include/ui/EventHub.h | 8 | ||||
-rw-r--r-- | include/ui/Input.h | 4 | ||||
-rw-r--r-- | include/ui/InputReader.h | 157 | ||||
-rw-r--r-- | libs/ui/EventHub.cpp | 9 | ||||
-rw-r--r-- | libs/ui/InputDispatcher.cpp | 22 | ||||
-rw-r--r-- | libs/ui/InputReader.cpp | 1046 | ||||
-rw-r--r-- | native/include/android/input.h | 2 | ||||
-rw-r--r-- | services/java/com/android/server/InputManager.java | 64 | ||||
-rw-r--r-- | services/java/com/android/server/WindowManagerService.java | 8 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 179 |
15 files changed, 1777 insertions, 433 deletions
diff --git a/api/current.xml b/api/current.xml index fc0a8cf..8549080 100644 --- a/api/current.xml +++ b/api/current.xml @@ -186729,14 +186729,19 @@ deprecated="not deprecated" visibility="public" > -<constructor name="InputDevice" - type="android.view.InputDevice" +<implements name="android.os.Parcelable"> +</implements> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" static="false" final="false" deprecated="not deprecated" visibility="public" > -</constructor> +</method> <method name="getDevice" return="android.view.InputDevice" abstract="false" @@ -186750,6 +186755,28 @@ <parameter name="id" type="int"> </parameter> </method> +<method name="getDeviceIds" + return="int[]" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="getId" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getKeyCharacterMap" return="android.view.KeyCharacterMap" abstract="false" @@ -186782,7 +186809,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="range" type="int"> +<parameter name="rangeType" type="int"> </parameter> </method> <method name="getName" @@ -186807,8 +186834,8 @@ visibility="public" > </method> -<method name="hasKey" - return="boolean" +<method name="writeToParcel" + return="void" abstract="false" native="false" synchronized="false" @@ -186817,9 +186844,21 @@ deprecated="not deprecated" visibility="public" > -<parameter name="keyCode" type="int"> +<parameter name="out" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> </parameter> </method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="KEYBOARD_TYPE_ALPHABETIC" type="int" transient="false" @@ -187148,14 +187187,6 @@ deprecated="not deprecated" visibility="public" > -<constructor name="InputDevice.MotionRange" - type="android.view.InputDevice.MotionRange" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</constructor> <method name="getFlat" return="float" abstract="false" diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index e86e3bf..d4dd05c 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -29,6 +29,7 @@ import android.view.KeyEvent; import android.view.InputEvent; import android.view.MotionEvent; import android.view.InputChannel; +import android.view.InputDevice; /** * System private interface to the window manager. @@ -125,6 +126,10 @@ interface IWindowManager // Report whether the hardware supports the given keys; returns true if successful boolean hasKeys(in int[] keycodes, inout boolean[] keyExists); + // Get input device information. + InputDevice getInputDevice(int deviceId); + int[] getInputDeviceIds(); + // For testing void setInTouchMode(boolean showFocus); diff --git a/core/java/android/view/InputDevice.aidl b/core/java/android/view/InputDevice.aidl new file mode 100644 index 0000000..dbc40c1 --- /dev/null +++ b/core/java/android/view/InputDevice.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android.view.InputDevice.aidl +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.view; + +parcelable InputDevice; diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index d5b2121..fb47b9c 100755 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -16,6 +16,12 @@ package android.view; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + /** * Describes the capabilities of a particular input device. * <p> @@ -32,12 +38,14 @@ package android.view; * the appropriate interpretation. * </p> */ -public final class InputDevice { +public final class InputDevice implements Parcelable { private int mId; private String mName; private int mSources; private int mKeyboardType; + private MotionRange[] mMotionRanges; + /** * A mask for input source classes. * @@ -246,6 +254,8 @@ public final class InputDevice { */ public static final int MOTION_RANGE_ORIENTATION = 8; + private static final int MOTION_RANGE_LAST = MOTION_RANGE_ORIENTATION; + /** * There is no keyboard. */ @@ -261,6 +271,11 @@ public final class InputDevice { * The keyboard supports a complement of alphabetic keys. */ public static final int KEYBOARD_TYPE_ALPHABETIC = 2; + + // Called by native code. + private InputDevice() { + mMotionRanges = new MotionRange[MOTION_RANGE_LAST + 1]; + } /** * Gets information about the input device with the specified id. @@ -268,8 +283,35 @@ public final class InputDevice { * @return The input device or null if not found. */ public static InputDevice getDevice(int id) { - // TODO - return null; + IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + try { + return wm.getInputDevice(id); + } catch (RemoteException ex) { + throw new RuntimeException( + "Could not get input device information from Window Manager.", ex); + } + } + + /** + * Gets the ids of all input devices in the system. + * @return The input device ids. + */ + public static int[] getDeviceIds() { + IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + try { + return wm.getInputDeviceIds(); + } catch (RemoteException ex) { + throw new RuntimeException( + "Could not get input device ids from Window Manager.", ex); + } + } + + /** + * Gets the input device id. + * @return The input device id. + */ + public int getId() { + return mId; } /** @@ -307,23 +349,23 @@ public final class InputDevice { /** * Gets information about the range of values for a particular {@link MotionEvent} * coordinate. - * @param range The motion range constant. + * @param rangeType The motion range constant. * @return The range of values, or null if the requested coordinate is not * supported by the device. */ - public MotionRange getMotionRange(int range) { - // TODO - return null; + public MotionRange getMotionRange(int rangeType) { + if (rangeType < 0 || rangeType > MOTION_RANGE_LAST) { + throw new IllegalArgumentException("Requested range is out of bounds."); + } + + return mMotionRanges[rangeType]; } - /** - * Returns true if the device supports a particular button or key. - * @param keyCode The key code. - * @return True if the device supports the key. - */ - public boolean hasKey(int keyCode) { - // TODO - return false; + private void addMotionRange(int rangeType, float min, float max, float flat, float fuzz) { + if (rangeType >= 0 && rangeType <= MOTION_RANGE_LAST) { + MotionRange range = new MotionRange(min, max, flat, fuzz); + mMotionRanges[rangeType] = range; + } } /** @@ -331,13 +373,24 @@ public final class InputDevice { * coordinate. */ public static final class MotionRange { + private float mMin; + private float mMax; + private float mFlat; + private float mFuzz; + + private MotionRange(float min, float max, float flat, float fuzz) { + mMin = min; + mMax = max; + mFlat = flat; + mFuzz = fuzz; + } + /** * Gets the minimum value for the coordinate. * @return The minimum value. */ public float getMin() { - // TODO - return 0; + return mMin; } /** @@ -345,8 +398,7 @@ public final class InputDevice { * @return The minimum value. */ public float getMax() { - // TODO - return 0; + return mMax; } /** @@ -354,8 +406,7 @@ public final class InputDevice { * @return The range of values. */ public float getRange() { - // TODO - return 0; + return mMax - mMin; } /** @@ -365,8 +416,7 @@ public final class InputDevice { * @return The extent of the center flat position. */ public float getFlat() { - // TODO - return 0; + return mFlat; } /** @@ -376,8 +426,127 @@ public final class InputDevice { * @return The error tolerance. */ public float getFuzz() { - // TODO - return 0; + return mFuzz; + } + } + + public static final Parcelable.Creator<InputDevice> CREATOR + = new Parcelable.Creator<InputDevice>() { + public InputDevice createFromParcel(Parcel in) { + InputDevice result = new InputDevice(); + result.readFromParcel(in); + return result; + } + + public InputDevice[] newArray(int size) { + return new InputDevice[size]; + } + }; + + private void readFromParcel(Parcel in) { + mId = in.readInt(); + mName = in.readString(); + mSources = in.readInt(); + mKeyboardType = in.readInt(); + + for (;;) { + int rangeType = in.readInt(); + if (rangeType < 0) { + break; + } + + addMotionRange(rangeType, + in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat()); + } + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mId); + out.writeString(mName); + out.writeInt(mSources); + out.writeInt(mKeyboardType); + + for (int i = 0; i <= MOTION_RANGE_LAST; i++) { + MotionRange range = mMotionRanges[i]; + if (range != null) { + out.writeInt(i); + out.writeFloat(range.mMin); + out.writeFloat(range.mMax); + out.writeFloat(range.mFlat); + out.writeFloat(range.mFuzz); + } + } + out.writeInt(-1); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + StringBuilder description = new StringBuilder(); + description.append("Input Device ").append(mId).append(": ").append(mName).append("\n"); + + description.append(" Keyboard Type: "); + switch (mKeyboardType) { + case KEYBOARD_TYPE_NONE: + description.append("none"); + break; + case KEYBOARD_TYPE_NON_ALPHABETIC: + description.append("non-alphabetic"); + break; + case KEYBOARD_TYPE_ALPHABETIC: + description.append("alphabetic"); + break; + } + description.append("\n"); + + description.append(" Sources:"); + appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard"); + appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad"); + appendSourceDescriptionIfApplicable(description, SOURCE_GAMEPAD, "gamepad"); + appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHSCREEN, "touchscreen"); + appendSourceDescriptionIfApplicable(description, SOURCE_MOUSE, "mouse"); + appendSourceDescriptionIfApplicable(description, SOURCE_TRACKBALL, "trackball"); + appendSourceDescriptionIfApplicable(description, SOURCE_TOUCHPAD, "touchpad"); + appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK_LEFT, "joystick_left"); + appendSourceDescriptionIfApplicable(description, SOURCE_JOYSTICK_RIGHT, "joystick_right"); + description.append("\n"); + + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_X, "x"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_Y, "y"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_PRESSURE, "pressure"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_SIZE, "size"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOUCH_MAJOR, "touchMajor"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOUCH_MINOR, "touchMinor"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOOL_MAJOR, "toolMajor"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_TOOL_MINOR, "toolMinor"); + appendRangeDescriptionIfApplicable(description, MOTION_RANGE_ORIENTATION, "orientation"); + + return description.toString(); + } + + private void appendSourceDescriptionIfApplicable(StringBuilder description, int source, + String sourceName) { + if ((mSources & source) == source) { + description.append(" "); + description.append(sourceName); + } + } + + private void appendRangeDescriptionIfApplicable(StringBuilder description, + int rangeType, String rangeName) { + MotionRange range = mMotionRanges[rangeType]; + if (range != null) { + description.append(" Range[").append(rangeName); + description.append("]: min=").append(range.mMin); + description.append(" max=").append(range.mMax); + description.append(" flat=").append(range.mFlat); + description.append(" fuzz=").append(range.mFuzz); + description.append("\n"); } } } diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index d5a9979..939f118 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -19,8 +19,10 @@ package com.android.internal.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.RectF; import android.graphics.Paint.FontMetricsInt; import android.util.Log; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; @@ -29,17 +31,45 @@ import android.view.ViewConfiguration; import java.util.ArrayList; public class PointerLocationView extends View { + private static final String TAG = "Pointer"; + public static class PointerState { - private final ArrayList<Float> mXs = new ArrayList<Float>(); - private final ArrayList<Float> mYs = new ArrayList<Float>(); + // Trace of previous points. + private float[] mTraceX = new float[32]; + private float[] mTraceY = new float[32]; + private int mTraceCount; + + // True if the pointer is down. private boolean mCurDown; - private int mCurX; - private int mCurY; - private float mCurPressure; - private float mCurSize; - private int mCurWidth; + + // Most recent coordinates. + private MotionEvent.PointerCoords mCoords = new MotionEvent.PointerCoords(); + + // Most recent velocity. private float mXVelocity; private float mYVelocity; + + public void clearTrace() { + mTraceCount = 0; + } + + public void addTrace(float x, float y) { + int traceCapacity = mTraceX.length; + if (mTraceCount == traceCapacity) { + traceCapacity *= 2; + float[] newTraceX = new float[traceCapacity]; + System.arraycopy(mTraceX, 0, newTraceX, 0, mTraceCount); + mTraceX = newTraceX; + + float[] newTraceY = new float[traceCapacity]; + System.arraycopy(mTraceY, 0, newTraceY, 0, mTraceCount); + mTraceY = newTraceY; + } + + mTraceX[mTraceCount] = x; + mTraceY[mTraceCount] = y; + mTraceCount += 1; + } } private final ViewConfiguration mVC; @@ -54,11 +84,12 @@ public class PointerLocationView extends View { private boolean mCurDown; private int mCurNumPointers; private int mMaxNumPointers; - private final ArrayList<PointerState> mPointers - = new ArrayList<PointerState>(); + private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>(); private final VelocityTracker mVelocity; + private final FasterStringBuilder mText = new FasterStringBuilder(); + private boolean mPrintCoords = true; public PointerLocationView(Context c) { @@ -94,6 +125,18 @@ public class PointerLocationView extends View { mPointers.add(ps); mVelocity = VelocityTracker.obtain(); + + logInputDeviceCapabilities(); + } + + private void logInputDeviceCapabilities() { + int[] deviceIds = InputDevice.getDeviceIds(); + for (int i = 0; i < deviceIds.length; i++) { + InputDevice device = InputDevice.getDevice(deviceIds[i]); + if (device != null) { + Log.i(TAG, device.toString()); + } + } } public void setPrintCoords(boolean state) { @@ -113,6 +156,21 @@ public class PointerLocationView extends View { + " bottom=" + mTextMetrics.bottom); } } + + // Draw an oval. When angle is 0 radians, orients the major axis vertically, + // angles less than or greater than 0 radians rotate the major axis left or right. + private RectF mReusableOvalRect = new RectF(); + private void drawOval(Canvas canvas, float x, float y, float major, float minor, + float angle, Paint paint) { + canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.rotate((float) (angle * 180 / Math.PI), x, y); + mReusableOvalRect.left = x - minor / 2; + mReusableOvalRect.right = x + minor / 2; + mReusableOvalRect.top = y - major / 2; + mReusableOvalRect.bottom = y + major / 2; + canvas.drawOval(mReusableOvalRect, paint); + canvas.restore(); + } @Override protected void onDraw(Canvas canvas) { @@ -124,76 +182,80 @@ public class PointerLocationView extends View { final int NP = mPointers.size(); + // Labels if (NP > 0) { final PointerState ps = mPointers.get(0); canvas.drawRect(0, 0, itemW-1, bottom,mTextBackgroundPaint); - canvas.drawText("P: " + mCurNumPointers + " / " + mMaxNumPointers, - 1, base, mTextPaint); + canvas.drawText(mText.clear() + .append("P: ").append(mCurNumPointers) + .append(" / ").append(mMaxNumPointers) + .toString(), 1, base, mTextPaint); - final int N = ps.mXs.size(); + final int N = ps.mTraceCount; if ((mCurDown && ps.mCurDown) || N == 0) { canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, mTextBackgroundPaint); - canvas.drawText("X: " + ps.mCurX, 1 + itemW, base, mTextPaint); + canvas.drawText(mText.clear() + .append("X: ").append(ps.mCoords.x, 1) + .toString(), 1 + itemW, base, mTextPaint); canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, mTextBackgroundPaint); - canvas.drawText("Y: " + ps.mCurY, 1 + itemW * 2, base, mTextPaint); + canvas.drawText(mText.clear() + .append("Y: ").append(ps.mCoords.y, 1) + .toString(), 1 + itemW * 2, base, mTextPaint); } else { - float dx = ps.mXs.get(N-1) - ps.mXs.get(0); - float dy = ps.mYs.get(N-1) - ps.mYs.get(0); + float dx = ps.mTraceX[N - 1] - ps.mTraceX[0]; + float dy = ps.mTraceY[N - 1] - ps.mTraceY[0]; canvas.drawRect(itemW, 0, (itemW * 2) - 1, bottom, Math.abs(dx) < mVC.getScaledTouchSlop() ? mTextBackgroundPaint : mTextLevelPaint); - canvas.drawText("dX: " + String.format("%.1f", dx), 1 + itemW, base, mTextPaint); + canvas.drawText(mText.clear() + .append("dX: ").append(dx, 1) + .toString(), 1 + itemW, base, mTextPaint); canvas.drawRect(itemW * 2, 0, (itemW * 3) - 1, bottom, Math.abs(dy) < mVC.getScaledTouchSlop() ? mTextBackgroundPaint : mTextLevelPaint); - canvas.drawText("dY: " + String.format("%.1f", dy), 1 + itemW * 2, base, mTextPaint); + canvas.drawText(mText.clear() + .append("dY: ").append(dy, 1) + .toString(), 1 + itemW * 2, base, mTextPaint); } canvas.drawRect(itemW * 3, 0, (itemW * 4) - 1, bottom, mTextBackgroundPaint); - int velocity = (int) (ps.mXVelocity * 1000); - canvas.drawText("Xv: " + velocity, 1 + itemW * 3, base, mTextPaint); + canvas.drawText(mText.clear() + .append("Xv: ").append(ps.mXVelocity, 3) + .toString(), 1 + itemW * 3, base, mTextPaint); canvas.drawRect(itemW * 4, 0, (itemW * 5) - 1, bottom, mTextBackgroundPaint); - velocity = (int) (ps.mYVelocity * 1000); - canvas.drawText("Yv: " + velocity, 1 + itemW * 4, base, mTextPaint); + canvas.drawText(mText.clear() + .append("Yv: ").append(ps.mYVelocity, 3) + .toString(), 1 + itemW * 4, base, mTextPaint); canvas.drawRect(itemW * 5, 0, (itemW * 6) - 1, bottom, mTextBackgroundPaint); - canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCurPressure * itemW) - 1, + canvas.drawRect(itemW * 5, 0, (itemW * 5) + (ps.mCoords.pressure * itemW) - 1, bottom, mTextLevelPaint); - canvas.drawText("Prs: " + String.format("%.2f", ps.mCurPressure), 1 + itemW * 5, - base, mTextPaint); + canvas.drawText(mText.clear() + .append("Prs: ").append(ps.mCoords.pressure, 2) + .toString(), 1 + itemW * 5, base, mTextPaint); canvas.drawRect(itemW * 6, 0, w, bottom, mTextBackgroundPaint); - canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCurSize * itemW) - 1, + canvas.drawRect(itemW * 6, 0, (itemW * 6) + (ps.mCoords.size * itemW) - 1, bottom, mTextLevelPaint); - canvas.drawText("Size: " + String.format("%.2f", ps.mCurSize), 1 + itemW * 6, - base, mTextPaint); + canvas.drawText(mText.clear() + .append("Size: ").append(ps.mCoords.size, 2) + .toString(), 1 + itemW * 6, base, mTextPaint); } - for (int p=0; p<NP; p++) { + // Pointer trace. + for (int p = 0; p < NP; p++) { final PointerState ps = mPointers.get(p); - if (mCurDown && ps.mCurDown) { - canvas.drawLine(0, (int)ps.mCurY, getWidth(), (int)ps.mCurY, mTargetPaint); - canvas.drawLine((int)ps.mCurX, 0, (int)ps.mCurX, getHeight(), mTargetPaint); - int pressureLevel = (int)(ps.mCurPressure*255); - mPaint.setARGB(255, pressureLevel, 128, 255-pressureLevel); - canvas.drawPoint(ps.mCurX, ps.mCurY, mPaint); - canvas.drawCircle(ps.mCurX, ps.mCurY, ps.mCurWidth, mPaint); - } - } - - for (int p=0; p<NP; p++) { - final PointerState ps = mPointers.get(p); - - final int N = ps.mXs.size(); - float lastX=0, lastY=0; + // Draw path. + final int N = ps.mTraceCount; + float lastX = 0, lastY = 0; boolean haveLast = false; boolean drawn = false; mPaint.setARGB(255, 128, 255, 255); - for (int i=0; i<N; i++) { - float x = ps.mXs.get(i); - float y = ps.mYs.get(i); + for (int i=0; i < N; i++) { + float x = ps.mTraceX[i]; + float y = ps.mTraceY[i]; if (Float.isNaN(x)) { haveLast = false; continue; @@ -208,21 +270,57 @@ public class PointerLocationView extends View { haveLast = true; } + // Draw velocity vector. if (drawn) { mPaint.setARGB(255, 255, 64, 128); - float xVel = ps.mXVelocity * (1000/60); - float yVel = ps.mYVelocity * (1000/60); - canvas.drawLine(lastX, lastY, lastX+xVel, lastY+yVel, mPaint); + float xVel = ps.mXVelocity * (1000 / 60); + float yVel = ps.mYVelocity * (1000 / 60); + canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint); + } + + if (mCurDown && ps.mCurDown) { + // Draw crosshairs. + canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint); + canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint); + + // Draw current point. + int pressureLevel = (int)(ps.mCoords.pressure * 255); + mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel); + canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint); + + // Draw current touch ellipse. + mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128); + drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor, + ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint); + + // Draw current tool ellipse. + mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel); + drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor, + ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint); } } } } + + private void logPointerCoords(MotionEvent.PointerCoords coords, int id) { + Log.i(TAG, mText.clear() + .append("Pointer ").append(id + 1) + .append(": (").append(coords.x, 3).append(", ").append(coords.y, 3) + .append(") Pressure=").append(coords.pressure, 3) + .append(" Size=").append(coords.size, 3) + .append(" TouchMajor=").append(coords.touchMajor, 3) + .append(" TouchMinor=").append(coords.touchMinor, 3) + .append(" ToolMajor=").append(coords.toolMajor, 3) + .append(" ToolMinor=").append(coords.toolMinor, 3) + .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1) + .append("deg").toString()); + } public void addTouchEvent(MotionEvent event) { synchronized (mPointers) { int action = event.getAction(); - //Log.i("Pointer", "Motion: action=0x" + Integer.toHexString(action) + //Log.i(TAG, "Motion: action=0x" + Integer.toHexString(action) // + " pointers=" + event.getPointerCount()); int NP = mPointers.size(); @@ -235,35 +333,33 @@ public class PointerLocationView extends View { //} else { // mRect.setEmpty(); //} - if (action == MotionEvent.ACTION_DOWN) { - mVelocity.clear(); - - for (int p=0; p<NP; p++) { - final PointerState ps = mPointers.get(p); - ps.mXs.clear(); - ps.mYs.clear(); - ps.mCurDown = false; - } - mPointers.get(0).mCurDown = true; - mMaxNumPointers = 0; - if (mPrintCoords) { - Log.i("Pointer", "Pointer 1: DOWN"); + if (action == MotionEvent.ACTION_DOWN + || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) { + final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down + if (action == MotionEvent.ACTION_DOWN) { + for (int p=0; p<NP; p++) { + final PointerState ps = mPointers.get(p); + ps.clearTrace(); + ps.mCurDown = false; + } + mCurDown = true; + mMaxNumPointers = 0; + mVelocity.clear(); } - } - - if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) { - final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int id = event.getPointerId(index); while (NP <= id) { PointerState ps = new PointerState(); mPointers.add(ps); NP++; } + final PointerState ps = mPointers.get(id); ps.mCurDown = true; if (mPrintCoords) { - Log.i("Pointer", "Pointer " + (id+1) + ": DOWN"); + Log.i(TAG, mText.clear().append("Pointer ") + .append(id + 1).append(": DOWN").toString()); } } @@ -284,58 +380,38 @@ public class PointerLocationView extends View { final PointerState ps = mPointers.get(id); final int N = event.getHistorySize(); for (int j=0; j<N; j++) { + event.getHistoricalPointerCoords(i, j, ps.mCoords); if (mPrintCoords) { - Log.i("Pointer", "Pointer " + (id+1) + ": (" - + event.getHistoricalX(i, j) - + ", " + event.getHistoricalY(i, j) + ")" - + " Prs=" + event.getHistoricalPressure(i, j) - + " Size=" + event.getHistoricalSize(i, j)); + logPointerCoords(ps.mCoords, id); } - ps.mXs.add(event.getHistoricalX(i, j)); - ps.mYs.add(event.getHistoricalY(i, j)); + ps.addTrace(event.getHistoricalX(i, j), event.getHistoricalY(i, j)); } + event.getPointerCoords(i, ps.mCoords); if (mPrintCoords) { - Log.i("Pointer", "Pointer " + (id+1) + ": (" - + event.getX(i) + ", " + event.getY(i) + ")" - + " Prs=" + event.getPressure(i) - + " Size=" + event.getSize(i)); + logPointerCoords(ps.mCoords, id); } - ps.mXs.add(event.getX(i)); - ps.mYs.add(event.getY(i)); - ps.mCurX = (int)event.getX(i); - ps.mCurY = (int)event.getY(i); - //Log.i("Pointer", "Pointer #" + p + ": (" + ps.mCurX - // + "," + ps.mCurY + ")"); - ps.mCurPressure = event.getPressure(i); - ps.mCurSize = event.getSize(i); - ps.mCurWidth = (int)(ps.mCurSize*(getWidth()/3)); + ps.addTrace(ps.mCoords.x, ps.mCoords.y); ps.mXVelocity = mVelocity.getXVelocity(id); ps.mYVelocity = mVelocity.getYVelocity(id); } - if ((action&MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) { - final int index = (action&MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + if (action == MotionEvent.ACTION_UP + || (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) { + final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP + final int id = event.getPointerId(index); final PointerState ps = mPointers.get(id); - ps.mXs.add(Float.NaN); - ps.mYs.add(Float.NaN); ps.mCurDown = false; if (mPrintCoords) { - Log.i("Pointer", "Pointer " + (id+1) + ": UP"); + Log.i(TAG, mText.clear().append("Pointer ") + .append(id + 1).append(": UP").toString()); } - } - - if (action == MotionEvent.ACTION_UP) { - for (int i=0; i<NI; i++) { - final int id = event.getPointerId(i); - final PointerState ps = mPointers.get(id); - if (ps.mCurDown) { - ps.mCurDown = false; - if (mPrintCoords) { - Log.i("Pointer", "Pointer " + (id+1) + ": UP"); - } - } + + if (action == MotionEvent.ACTION_UP) { + mCurDown = false; + } else { + ps.addTrace(Float.NaN, Float.NaN); } } @@ -356,8 +432,120 @@ public class PointerLocationView extends View { @Override public boolean onTrackballEvent(MotionEvent event) { - Log.i("Pointer", "Trackball: " + event); + Log.i(TAG, "Trackball: " + event); return super.onTrackballEvent(event); } + // HACK + // A quick and dirty string builder implementation optimized for GC. + // Using the basic StringBuilder implementation causes the application grind to a halt when + // more than a couple of pointers are down due to the number of temporary objects allocated + // while formatting strings for drawing or logging. + private static final class FasterStringBuilder { + private char[] mChars; + private int mLength; + + public FasterStringBuilder() { + mChars = new char[64]; + } + + public FasterStringBuilder clear() { + mLength = 0; + return this; + } + + public FasterStringBuilder append(String value) { + final int valueLength = value.length(); + final int index = reserve(valueLength); + value.getChars(0, valueLength, mChars, index); + mLength += valueLength; + return this; + } + + public FasterStringBuilder append(int value) { + return append(value, 0); + } + + public FasterStringBuilder append(int value, int zeroPadWidth) { + final boolean negative = value < 0; + if (negative) { + value = - value; + if (value < 0) { + append("-2147483648"); + return this; + } + } + + int index = reserve(11); + final char[] chars = mChars; + + if (value == 0) { + chars[index++] = '0'; + mLength += 1; + return this; + } + + if (negative) { + chars[index++] = '-'; + } + + int divisor = 1000000000; + int numberWidth = 10; + while (value < divisor) { + divisor /= 10; + numberWidth -= 1; + if (numberWidth < zeroPadWidth) { + chars[index++] = '0'; + } + } + + do { + int digit = value / divisor; + value -= digit * divisor; + divisor /= 10; + chars[index++] = (char) (digit + '0'); + } while (divisor != 0); + + mLength = index; + return this; + } + + public FasterStringBuilder append(float value, int precision) { + int scale = 1; + for (int i = 0; i < precision; i++) { + scale *= 10; + } + value = (float) (Math.rint(value * scale) / scale); + + append((int) value); + + if (precision != 0) { + append("."); + value = Math.abs(value); + value -= Math.floor(value); + append((int) (value * scale), precision); + } + + return this; + } + + @Override + public String toString() { + return new String(mChars, 0, mLength); + } + + private int reserve(int length) { + final int oldLength = mLength; + final int newLength = mLength + length; + final char[] oldChars = mChars; + final int oldCapacity = oldChars.length; + if (newLength > oldCapacity) { + final int newCapacity = oldCapacity * 2; + final char[] newChars = new char[newCapacity]; + System.arraycopy(oldChars, 0, newChars, 0, oldLength); + mChars = newChars; + } + return oldLength; + } + } } diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h index 3d42856..25d5afb 100644 --- a/include/ui/EventHub.h +++ b/include/ui/EventHub.h @@ -82,6 +82,14 @@ struct RawAbsoluteAxisInfo { int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise inline int32_t getRange() { return maxValue - minValue; } + + inline void clear() { + valid = false; + minValue = 0; + maxValue = 0; + flat = 0; + fuzz = 0; + } }; /* diff --git a/include/ui/Input.h b/include/ui/Input.h index 2385973..49347d3 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -453,6 +453,10 @@ public: inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } + inline const KeyedVector<int32_t, MotionRange> getMotionRanges() const { + return mMotionRanges; + } + private: int32_t mId; String8 mName; diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index 56d2765..7a089a4 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -35,6 +35,34 @@ namespace android { class InputDevice; class InputMapper; +/* Describes a virtual key. */ +struct VirtualKeyDefinition { + int32_t scanCode; + + // configured position data, specified in display coords + int32_t centerX; + int32_t centerY; + int32_t width; + int32_t height; +}; + + +/* Specifies input device calibration settings. */ +class InputDeviceCalibration { +public: + InputDeviceCalibration(); + + void clear(); + void addProperty(const String8& key, const String8& value); + + bool tryGetProperty(const String8& key, String8& outValue) const; + bool tryGetProperty(const String8& key, int32_t& outValue) const; + bool tryGetProperty(const String8& key, float& outValue) const; + +private: + KeyedVector<String8, String8> mProperties; +}; + /* * Input reader policy interface. @@ -73,17 +101,6 @@ public: ACTION_APP_SWITCH_COMING = 0x00000002, }; - /* Describes a virtual key. */ - struct VirtualKeyDefinition { - int32_t scanCode; - - // configured position data, specified in display coords - int32_t centerX; - int32_t centerY; - int32_t width; - int32_t height; - }; - /* Gets information about the display with the specified id. * Returns true if the display info is available, false otherwise. */ @@ -135,6 +152,10 @@ public: virtual void getVirtualKeyDefinitions(const String8& deviceName, Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) = 0; + /* Gets the calibration for an input device. */ + virtual void getInputDeviceCalibration(const String8& deviceName, + InputDeviceCalibration& outCalibration) = 0; + /* Gets the excluded device names for the platform. */ virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) = 0; }; @@ -327,6 +348,10 @@ public: int32_t getMetaState(); + inline const InputDeviceCalibration& getCalibration() { + return mCalibration; + } + private: InputReaderContext* mContext; int32_t mId; @@ -338,6 +363,8 @@ private: typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); + + InputDeviceCalibration mCalibration; }; @@ -538,12 +565,12 @@ protected: } }; + // Raw data for a single pointer. struct PointerData { uint32_t id; int32_t x; int32_t y; int32_t pressure; - int32_t size; int32_t touchMajor; int32_t touchMinor; int32_t toolMajor; @@ -551,6 +578,7 @@ protected: int32_t orientation; }; + // Raw data for a collection of pointers including a pointer id mapping table. struct TouchData { uint32_t pointerCount; PointerData pointers[MAX_POINTERS]; @@ -584,18 +612,82 @@ protected: bool useAveragingTouchFilter; } mParameters; - // Raw axis information. - struct Axes { + // Immutable calibration parameters in parsed form. + struct Calibration { + // Touch Area + enum TouchAreaCalibration { + TOUCH_AREA_CALIBRATION_DEFAULT, + TOUCH_AREA_CALIBRATION_NONE, + TOUCH_AREA_CALIBRATION_GEOMETRIC, + TOUCH_AREA_CALIBRATION_PRESSURE, + }; + + TouchAreaCalibration touchAreaCalibration; + + // Tool Area + enum ToolAreaCalibration { + TOOL_AREA_CALIBRATION_DEFAULT, + TOOL_AREA_CALIBRATION_NONE, + TOOL_AREA_CALIBRATION_GEOMETRIC, + TOOL_AREA_CALIBRATION_LINEAR, + }; + + ToolAreaCalibration toolAreaCalibration; + bool haveToolAreaLinearScale; + float toolAreaLinearScale; + bool haveToolAreaLinearBias; + float toolAreaLinearBias; + bool haveToolAreaIsSummed; + int32_t toolAreaIsSummed; + + // Pressure + enum PressureCalibration { + PRESSURE_CALIBRATION_DEFAULT, + PRESSURE_CALIBRATION_NONE, + PRESSURE_CALIBRATION_PHYSICAL, + PRESSURE_CALIBRATION_AMPLITUDE, + }; + enum PressureSource { + PRESSURE_SOURCE_DEFAULT, + PRESSURE_SOURCE_PRESSURE, + PRESSURE_SOURCE_TOUCH, + }; + + PressureCalibration pressureCalibration; + PressureSource pressureSource; + bool havePressureScale; + float pressureScale; + + // Size + enum SizeCalibration { + SIZE_CALIBRATION_DEFAULT, + SIZE_CALIBRATION_NONE, + SIZE_CALIBRATION_NORMALIZED, + }; + + SizeCalibration sizeCalibration; + + // Orientation + enum OrientationCalibration { + ORIENTATION_CALIBRATION_DEFAULT, + ORIENTATION_CALIBRATION_NONE, + ORIENTATION_CALIBRATION_INTERPOLATED, + }; + + OrientationCalibration orientationCalibration; + } mCalibration; + + // Raw axis information from the driver. + struct RawAxes { RawAbsoluteAxisInfo x; RawAbsoluteAxisInfo y; RawAbsoluteAxisInfo pressure; - RawAbsoluteAxisInfo size; RawAbsoluteAxisInfo touchMajor; RawAbsoluteAxisInfo touchMinor; RawAbsoluteAxisInfo toolMajor; RawAbsoluteAxisInfo toolMinor; RawAbsoluteAxisInfo orientation; - } mAxes; + } mRawAxes; // Current and previous touch sample data. TouchData mCurrentTouch; @@ -620,10 +712,13 @@ protected: float yScale; float yPrecision; - int32_t pressureOrigin; + float geometricScale; + + float toolAreaLinearScale; + float toolAreaLinearBias; + float pressureScale; - int32_t sizeOrigin; float sizeScale; float orientationScale; @@ -632,12 +727,22 @@ protected: struct OrientedRanges { InputDeviceInfo::MotionRange x; InputDeviceInfo::MotionRange y; + + bool havePressure; InputDeviceInfo::MotionRange pressure; + + bool haveSize; InputDeviceInfo::MotionRange size; + + bool haveTouchArea; InputDeviceInfo::MotionRange touchMajor; InputDeviceInfo::MotionRange touchMinor; + + bool haveToolArea; InputDeviceInfo::MotionRange toolMajor; InputDeviceInfo::MotionRange toolMinor; + + bool haveOrientation; InputDeviceInfo::MotionRange orientation; } orientedRanges; @@ -653,9 +758,14 @@ protected: } currentVirtualKey; } mLocked; - virtual void configureAxes(); + virtual void configureParameters(); + virtual void configureRawAxes(); + virtual void logRawAxes(); virtual bool configureSurfaceLocked(); virtual void configureVirtualKeysLocked(); + virtual void parseCalibration(); + virtual void resolveCalibration(); + virtual void logCalibration(); enum TouchResult { // Dispatch the touch normally. @@ -713,7 +823,8 @@ private: TouchResult consumeOffScreenTouches(nsecs_t when, uint32_t policyFlags); void dispatchTouches(nsecs_t when, uint32_t policyFlags); void dispatchTouch(nsecs_t when, uint32_t policyFlags, TouchData* touch, - BitSet32 idBits, uint32_t changedId, int32_t motionEventAction); + BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, + int32_t motionEventAction); void applyPolicyAndDispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction, int32_t keyEventFlags, @@ -738,7 +849,7 @@ public: virtual void process(const RawEvent* rawEvent); protected: - virtual void configureAxes(); + virtual void configureRawAxes(); private: struct Accumulator { @@ -767,7 +878,7 @@ private: int32_t mX; int32_t mY; int32_t mPressure; - int32_t mSize; + int32_t mToolWidth; void initialize(); @@ -784,7 +895,7 @@ public: virtual void process(const RawEvent* rawEvent); protected: - virtual void configureAxes(); + virtual void configureRawAxes(); private: struct Accumulator { diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 891661d..1d38b4b 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -139,11 +139,7 @@ uint32_t EventHub::getDeviceClasses(int32_t deviceId) const status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const { - outAxisInfo->valid = false; - outAxisInfo->minValue = 0; - outAxisInfo->maxValue = 0; - outAxisInfo->flat = 0; - outAxisInfo->fuzz = 0; + outAxisInfo->clear(); AutoMutex _l(mLock); device_t* device = getDevice(deviceId); @@ -709,8 +705,7 @@ int EventHub::open_device(const char *deviceName) LOGV("Getting absolute controllers..."); if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) { // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask) - && test_bit(ABS_MT_POSITION_X, abs_bitmask) + if (test_bit(ABS_MT_POSITION_X, abs_bitmask) && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index e35050c..886c785 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -405,12 +405,15 @@ void InputDispatcher::processMotionLockedInterruptible( sampleCount += 1; } for (uint32_t i = 0; i < entry->pointerCount; i++) { - LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f", + LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%d, toolMajor=%f, toolMinor=%f, " + "orientation=%f", i, entry->pointerIds[i], - sample->pointerCoords[i].x, - sample->pointerCoords[i].y, - sample->pointerCoords[i].pressure, - sample->pointerCoords[i].size); + sample->pointerCoords[i].x, sample->pointerCoords[i].y, + sample->pointerCoords[i].pressure, sample->pointerCoords[i].size, + sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor, + sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor, + sample->pointerCoords[i].orientation); } // Keep in mind that due to batching, it is possible for the number of samples actually @@ -1080,9 +1083,14 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t eventTime, deviceId, source, policyFlags, action, metaState, edgeFlags, xPrecision, yPrecision, downTime); for (uint32_t i = 0; i < pointerCount; i++) { - LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f", + LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%d, toolMajor=%f, toolMinor=%f, " + "orientation=%f", i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y, - pointerCoords[i].pressure, pointerCoords[i].size); + pointerCoords[i].pressure, pointerCoords[i].size, + pointerCoords[i].touchMajor, pointerCoords[i].touchMinor, + pointerCoords[i].toolMajor, pointerCoords[i].toolMinor, + pointerCoords[i].orientation); } #endif diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 6f042ec..8ffb48d 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -26,11 +26,14 @@ #include <ui/InputReader.h> #include <stddef.h> +#include <stdlib.h> #include <unistd.h> #include <errno.h> #include <limits.h> #include <math.h> +#define INDENT " " + namespace android { // --- Static Functions --- @@ -52,6 +55,14 @@ inline static void swap(T& a, T& b) { b = temp; } +inline static float avg(float x, float y) { + return (x + y) / 2; +} + +inline static float pythag(float x, float y) { + return sqrtf(x * x + y * y); +} + int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { int32_t mask; @@ -116,6 +127,64 @@ static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { } +// --- InputDeviceCalibration --- + +InputDeviceCalibration::InputDeviceCalibration() { +} + +void InputDeviceCalibration::clear() { + mProperties.clear(); +} + +void InputDeviceCalibration::addProperty(const String8& key, const String8& value) { + mProperties.add(key, value); +} + +bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const { + ssize_t index = mProperties.indexOfKey(key); + if (index < 0) { + return false; + } + + outValue = mProperties.valueAt(index); + return true; +} + +bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + int value = strtol(stringValue.string(), & end, 10); + if (*end != '\0') { + LOGW("Input device calibration key '%s' has invalid value '%s'. Expected an integer.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + float value = strtof(stringValue.string(), & end); + if (*end != '\0') { + LOGW("Input device calibration key '%s' has invalid value '%s'. Expected a float.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + + // --- InputReader --- InputReader::InputReader(const sp<EventHubInterface>& eventHub, @@ -167,9 +236,18 @@ void InputReader::addDevice(nsecs_t when, int32_t deviceId) { String8 name = mEventHub->getDeviceName(deviceId); uint32_t classes = mEventHub->getDeviceClasses(deviceId); + // Write a log message about the added device as a heading for subsequent log messages. + LOGI("Device added: id=0x%x, name=%s", deviceId, name.string()); + InputDevice* device = createDevice(deviceId, name, classes); device->configure(); + if (device->isIgnored()) { + LOGI(INDENT "Sources: none (device is ignored)"); + } else { + LOGI(INDENT "Sources: 0x%08x", device->getSources()); + } + bool added = false; { // acquire device registry writer lock RWLock::AutoWLock _wl(mDeviceRegistryLock); @@ -187,14 +265,6 @@ void InputReader::addDevice(nsecs_t when, int32_t deviceId) { return; } - if (device->isIgnored()) { - LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", - deviceId, name.string()); - } else { - LOGI("Device added: id=0x%x, name=%s, sources=%08x", - deviceId, name.string(), device->getSources()); - } - handleConfigurationChanged(when); } @@ -217,8 +287,7 @@ void InputReader::removeDevice(nsecs_t when, int32_t deviceId) { return; } - device->reset(); - + // Write a log message about the removed device as a heading for subsequent log messages. if (device->isIgnored()) { LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", device->getId(), device->getName().string()); @@ -227,6 +296,8 @@ void InputReader::removeDevice(nsecs_t when, int32_t deviceId) { device->getId(), device->getName().string(), device->getSources()); } + device->reset(); + delete device; handleConfigurationChanged(when); @@ -537,6 +608,10 @@ void InputDevice::addMapper(InputMapper* mapper) { } void InputDevice::configure() { + if (! isIgnored()) { + mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration); + } + mSources = 0; size_t numMappers = mMappers.size(); @@ -1121,13 +1196,35 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x); info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y); - info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, mLocked.orientedRanges.pressure); - info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, mLocked.orientedRanges.size); - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, mLocked.orientedRanges.touchMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, mLocked.orientedRanges.touchMinor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, mLocked.orientedRanges.toolMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, mLocked.orientedRanges.toolMinor); - info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, mLocked.orientedRanges.orientation); + + if (mLocked.orientedRanges.havePressure) { + info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, + mLocked.orientedRanges.pressure); + } + + if (mLocked.orientedRanges.haveSize) { + info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, + mLocked.orientedRanges.size); + } + + if (mLocked.orientedRanges.haveTouchArea) { + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, + mLocked.orientedRanges.touchMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, + mLocked.orientedRanges.touchMinor); + } + + if (mLocked.orientedRanges.haveToolArea) { + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, + mLocked.orientedRanges.toolMajor); + info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, + mLocked.orientedRanges.toolMinor); + } + + if (mLocked.orientedRanges.haveOrientation) { + info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, + mLocked.orientedRanges.orientation); + } } // release lock } @@ -1144,77 +1241,72 @@ void TouchInputMapper::initializeLocked() { mJumpyTouchFilter.jumpyPointsDropped = 0; mLocked.currentVirtualKey.down = false; + + mLocked.orientedRanges.havePressure = false; + mLocked.orientedRanges.haveSize = false; + mLocked.orientedRanges.haveTouchArea = false; + mLocked.orientedRanges.haveToolArea = false; + mLocked.orientedRanges.haveOrientation = false; +} + +static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) { + if (axis.valid) { + LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d", + name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); + } else { + LOGI(INDENT "Raw %s axis: unknown range", name); + } } void TouchInputMapper::configure() { InputMapper::configure(); // Configure basic parameters. - mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents(); - mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents(); - mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); + configureParameters(); // Configure absolute axis information. - configureAxes(); + configureRawAxes(); + logRawAxes(); + + // Prepare input device calibration. + parseCalibration(); + resolveCalibration(); + logCalibration(); { // acquire lock AutoMutex _l(mLock); - // Configure pressure factors. - if (mAxes.pressure.valid) { - mLocked.pressureOrigin = mAxes.pressure.minValue; - mLocked.pressureScale = 1.0f / mAxes.pressure.getRange(); - } else { - mLocked.pressureOrigin = 0; - mLocked.pressureScale = 1.0f; - } - - mLocked.orientedRanges.pressure.min = 0.0f; - mLocked.orientedRanges.pressure.max = 1.0f; - mLocked.orientedRanges.pressure.flat = 0.0f; - mLocked.orientedRanges.pressure.fuzz = mLocked.pressureScale; - - // Configure size factors. - if (mAxes.size.valid) { - mLocked.sizeOrigin = mAxes.size.minValue; - mLocked.sizeScale = 1.0f / mAxes.size.getRange(); - } else { - mLocked.sizeOrigin = 0; - mLocked.sizeScale = 1.0f; - } - - mLocked.orientedRanges.size.min = 0.0f; - mLocked.orientedRanges.size.max = 1.0f; - mLocked.orientedRanges.size.flat = 0.0f; - mLocked.orientedRanges.size.fuzz = mLocked.sizeScale; - - // Configure orientation factors. - if (mAxes.orientation.valid && mAxes.orientation.maxValue > 0) { - mLocked.orientationScale = float(M_PI_2) / mAxes.orientation.maxValue; - } else { - mLocked.orientationScale = 0.0f; - } - - mLocked.orientedRanges.orientation.min = - M_PI_2; - mLocked.orientedRanges.orientation.max = M_PI_2; - mLocked.orientedRanges.orientation.flat = 0; - mLocked.orientedRanges.orientation.fuzz = mLocked.orientationScale; - - // Configure surface dimensions and orientation. + // Configure surface dimensions and orientation. configureSurfaceLocked(); } // release lock } -void TouchInputMapper::configureAxes() { - mAxes.x.valid = false; - mAxes.y.valid = false; - mAxes.pressure.valid = false; - mAxes.size.valid = false; - mAxes.touchMajor.valid = false; - mAxes.touchMinor.valid = false; - mAxes.toolMajor.valid = false; - mAxes.toolMinor.valid = false; - mAxes.orientation.valid = false; +void TouchInputMapper::configureParameters() { + mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents(); + mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); +} + +void TouchInputMapper::configureRawAxes() { + mRawAxes.x.clear(); + mRawAxes.y.clear(); + mRawAxes.pressure.clear(); + mRawAxes.touchMajor.clear(); + mRawAxes.touchMinor.clear(); + mRawAxes.toolMajor.clear(); + mRawAxes.toolMinor.clear(); + mRawAxes.orientation.clear(); +} + +void TouchInputMapper::logRawAxes() { + logAxisInfo(mRawAxes.x, "x"); + logAxisInfo(mRawAxes.y, "y"); + logAxisInfo(mRawAxes.pressure, "pressure"); + logAxisInfo(mRawAxes.touchMajor, "touchMajor"); + logAxisInfo(mRawAxes.touchMinor, "touchMinor"); + logAxisInfo(mRawAxes.toolMajor, "toolMajor"); + logAxisInfo(mRawAxes.toolMinor, "toolMinor"); + logAxisInfo(mRawAxes.orientation, "orientation"); } bool TouchInputMapper::configureSurfaceLocked() { @@ -1228,8 +1320,8 @@ bool TouchInputMapper::configureSurfaceLocked() { } } else { orientation = InputReaderPolicyInterface::ROTATION_0; - width = mAxes.x.getRange(); - height = mAxes.y.getRange(); + width = mRawAxes.x.getRange(); + height = mRawAxes.y.getRange(); } bool orientationChanged = mLocked.surfaceOrientation != orientation; @@ -1239,24 +1331,24 @@ bool TouchInputMapper::configureSurfaceLocked() { bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height; if (sizeChanged) { + LOGI("Device configured: id=0x%x, name=%s (display size was changed)", + getDeviceId(), getDeviceName().string()); + mLocked.surfaceWidth = width; mLocked.surfaceHeight = height; - // Compute size-dependent translation and scaling factors and place virtual keys. - if (mAxes.x.valid && mAxes.y.valid) { - mLocked.xOrigin = mAxes.x.minValue; - mLocked.yOrigin = mAxes.y.minValue; - - LOGI("Device configured: id=0x%x, name=%s (display size was changed)", - getDeviceId(), getDeviceName().string()); - - mLocked.xScale = float(width) / mAxes.x.getRange(); - mLocked.yScale = float(height) / mAxes.y.getRange(); + // Configure X and Y factors. + if (mRawAxes.x.valid && mRawAxes.y.valid) { + mLocked.xOrigin = mRawAxes.x.minValue; + mLocked.yOrigin = mRawAxes.y.minValue; + mLocked.xScale = float(width) / mRawAxes.x.getRange(); + mLocked.yScale = float(height) / mRawAxes.y.getRange(); mLocked.xPrecision = 1.0f / mLocked.xScale; mLocked.yPrecision = 1.0f / mLocked.yScale; configureVirtualKeysLocked(); } else { + LOGW(INDENT "Touch device did not report support for X or Y axis!"); mLocked.xOrigin = 0; mLocked.yOrigin = 0; mLocked.xScale = 1.0f; @@ -1265,22 +1357,112 @@ bool TouchInputMapper::configureSurfaceLocked() { mLocked.yPrecision = 1.0f; } - // Configure touch and tool area ranges. - float diagonal = sqrt(float(width * width + height * height)); - float diagonalFuzz = sqrt(mLocked.xScale * mLocked.xScale - + mLocked.yScale * mLocked.yScale); + // Scale factor for terms that are not oriented in a particular axis. + // If the pixels are square then xScale == yScale otherwise we fake it + // by choosing an average. + mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale); + + // Size of diagonal axis. + float diagonalSize = pythag(width, height); + + // TouchMajor and TouchMinor factors. + if (mCalibration.touchAreaCalibration != Calibration::TOUCH_AREA_CALIBRATION_NONE) { + mLocked.orientedRanges.haveTouchArea = true; + mLocked.orientedRanges.touchMajor.min = 0; + mLocked.orientedRanges.touchMajor.max = diagonalSize; + mLocked.orientedRanges.touchMajor.flat = 0; + mLocked.orientedRanges.touchMajor.fuzz = 0; + mLocked.orientedRanges.touchMinor = mLocked.orientedRanges.touchMajor; + } + + // ToolMajor and ToolMinor factors. + if (mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) { + mLocked.toolAreaLinearScale = 0; + mLocked.toolAreaLinearBias = 0; + if (mCalibration.toolAreaCalibration == Calibration::TOOL_AREA_CALIBRATION_LINEAR) { + if (mCalibration.haveToolAreaLinearScale) { + mLocked.toolAreaLinearScale = mCalibration.toolAreaLinearScale; + } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) { + mLocked.toolAreaLinearScale = float(min(width, height)) + / mRawAxes.toolMajor.maxValue; + } + + if (mCalibration.haveToolAreaLinearBias) { + mLocked.toolAreaLinearBias = mCalibration.toolAreaLinearBias; + } + } + + mLocked.orientedRanges.haveToolArea = true; + mLocked.orientedRanges.toolMajor.min = 0; + mLocked.orientedRanges.toolMajor.max = diagonalSize; + mLocked.orientedRanges.toolMajor.flat = 0; + mLocked.orientedRanges.toolMajor.fuzz = 0; + mLocked.orientedRanges.toolMinor = mLocked.orientedRanges.toolMajor; + } + + // Pressure factors. + if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE) { + RawAbsoluteAxisInfo rawPressureAxis; + switch (mCalibration.pressureSource) { + case Calibration::PRESSURE_SOURCE_PRESSURE: + rawPressureAxis = mRawAxes.pressure; + break; + case Calibration::PRESSURE_SOURCE_TOUCH: + rawPressureAxis = mRawAxes.touchMajor; + break; + default: + rawPressureAxis.clear(); + } + + mLocked.pressureScale = 0; + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL + || mCalibration.pressureCalibration + == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { + if (mCalibration.havePressureScale) { + mLocked.pressureScale = mCalibration.pressureScale; + } else if (rawPressureAxis.valid && rawPressureAxis.maxValue != 0) { + mLocked.pressureScale = 1.0f / rawPressureAxis.maxValue; + } + } + + mLocked.orientedRanges.havePressure = true; + mLocked.orientedRanges.pressure.min = 0; + mLocked.orientedRanges.pressure.max = 1.0; + mLocked.orientedRanges.pressure.flat = 0; + mLocked.orientedRanges.pressure.fuzz = 0; + } + + // Size factors. + if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { + mLocked.sizeScale = 0; + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_NORMALIZED) { + if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) { + mLocked.sizeScale = 1.0f / mRawAxes.toolMajor.maxValue; + } + } - InputDeviceInfo::MotionRange area; - area.min = 0.0f; - area.max = diagonal; - area.flat = 0.0f; - area.fuzz = diagonalFuzz; + mLocked.orientedRanges.haveSize = true; + mLocked.orientedRanges.size.min = 0; + mLocked.orientedRanges.size.max = 1.0; + mLocked.orientedRanges.size.flat = 0; + mLocked.orientedRanges.size.fuzz = 0; + } - mLocked.orientedRanges.touchMajor = area; - mLocked.orientedRanges.touchMinor = area; + // Orientation + if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) { + mLocked.orientationScale = 0; + if (mCalibration.orientationCalibration + == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { + if (mRawAxes.orientation.valid && mRawAxes.orientation.maxValue != 0) { + mLocked.orientationScale = float(M_PI_2) / mRawAxes.orientation.maxValue; + } + } - mLocked.orientedRanges.toolMajor = area; - mLocked.orientedRanges.toolMinor = area; + mLocked.orientedRanges.orientation.min = - M_PI_2; + mLocked.orientedRanges.orientation.max = M_PI_2; + mLocked.orientedRanges.orientation.flat = 0; + mLocked.orientedRanges.orientation.fuzz = 0; + } } if (orientationChanged || sizeChanged) { @@ -1322,10 +1504,10 @@ bool TouchInputMapper::configureSurfaceLocked() { } void TouchInputMapper::configureVirtualKeysLocked() { - assert(mAxes.x.valid && mAxes.y.valid); + assert(mRawAxes.x.valid && mRawAxes.y.valid); // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock. - Vector<InputReaderPolicyInterface::VirtualKeyDefinition> virtualKeyDefinitions; + Vector<VirtualKeyDefinition> virtualKeyDefinitions; getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions); mLocked.virtualKeys.clear(); @@ -1336,13 +1518,13 @@ void TouchInputMapper::configureVirtualKeysLocked() { mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size()); - int32_t touchScreenLeft = mAxes.x.minValue; - int32_t touchScreenTop = mAxes.y.minValue; - int32_t touchScreenWidth = mAxes.x.getRange(); - int32_t touchScreenHeight = mAxes.y.getRange(); + int32_t touchScreenLeft = mRawAxes.x.minValue; + int32_t touchScreenTop = mRawAxes.y.minValue; + int32_t touchScreenWidth = mRawAxes.x.getRange(); + int32_t touchScreenHeight = mRawAxes.y.getRange(); for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { - const InputReaderPolicyInterface::VirtualKeyDefinition& virtualKeyDefinition = + const VirtualKeyDefinition& virtualKeyDefinition = virtualKeyDefinitions[i]; mLocked.virtualKeys.add(); @@ -1353,7 +1535,8 @@ void TouchInputMapper::configureVirtualKeysLocked() { uint32_t flags; if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, & keyCode, & flags)) { - LOGW(" VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", + virtualKey.scanCode); mLocked.virtualKeys.pop(); // drop the key continue; } @@ -1374,12 +1557,316 @@ void TouchInputMapper::configureVirtualKeysLocked() { virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop; - LOGI(" VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", + LOGI(INDENT "VirtualKey %d: keyCode=%d hitLeft=%d hitRight=%d hitTop=%d hitBottom=%d", virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft, virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); } } +void TouchInputMapper::parseCalibration() { + const InputDeviceCalibration& in = getDevice()->getCalibration(); + Calibration& out = mCalibration; + + // Touch Area + out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_DEFAULT; + String8 touchAreaCalibrationString; + if (in.tryGetProperty(String8("touch.touchArea.calibration"), touchAreaCalibrationString)) { + if (touchAreaCalibrationString == "none") { + out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE; + } else if (touchAreaCalibrationString == "geometric") { + out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC; + } else if (touchAreaCalibrationString == "pressure") { + out.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE; + } else if (touchAreaCalibrationString != "default") { + LOGW("Invalid value for touch.touchArea.calibration: '%s'", + touchAreaCalibrationString.string()); + } + } + + // Tool Area + out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_DEFAULT; + String8 toolAreaCalibrationString; + if (in.tryGetProperty(String8("tool.toolArea.calibration"), toolAreaCalibrationString)) { + if (toolAreaCalibrationString == "none") { + out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE; + } else if (toolAreaCalibrationString == "geometric") { + out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC; + } else if (toolAreaCalibrationString == "linear") { + out.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR; + } else if (toolAreaCalibrationString != "default") { + LOGW("Invalid value for tool.toolArea.calibration: '%s'", + toolAreaCalibrationString.string()); + } + } + + out.haveToolAreaLinearScale = in.tryGetProperty(String8("touch.toolArea.linearScale"), + out.toolAreaLinearScale); + out.haveToolAreaLinearBias = in.tryGetProperty(String8("touch.toolArea.linearBias"), + out.toolAreaLinearBias); + out.haveToolAreaIsSummed = in.tryGetProperty(String8("touch.toolArea.isSummed"), + out.toolAreaIsSummed); + + // Pressure + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; + String8 pressureCalibrationString; + if (in.tryGetProperty(String8("tool.pressure.calibration"), pressureCalibrationString)) { + if (pressureCalibrationString == "none") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } else if (pressureCalibrationString == "physical") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } else if (pressureCalibrationString == "amplitude") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; + } else if (pressureCalibrationString != "default") { + LOGW("Invalid value for tool.pressure.calibration: '%s'", + pressureCalibrationString.string()); + } + } + + out.pressureSource = Calibration::PRESSURE_SOURCE_DEFAULT; + String8 pressureSourceString; + if (in.tryGetProperty(String8("touch.pressure.source"), pressureSourceString)) { + if (pressureSourceString == "pressure") { + out.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE; + } else if (pressureSourceString == "touch") { + out.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH; + } else if (pressureSourceString != "default") { + LOGW("Invalid value for touch.pressure.source: '%s'", + pressureSourceString.string()); + } + } + + out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), + out.pressureScale); + + // Size + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; + String8 sizeCalibrationString; + if (in.tryGetProperty(String8("tool.size.calibration"), sizeCalibrationString)) { + if (sizeCalibrationString == "none") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } else if (sizeCalibrationString == "normalized") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED; + } else if (sizeCalibrationString != "default") { + LOGW("Invalid value for tool.size.calibration: '%s'", + sizeCalibrationString.string()); + } + } + + // Orientation + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; + String8 orientationCalibrationString; + if (in.tryGetProperty(String8("tool.orientation.calibration"), orientationCalibrationString)) { + if (orientationCalibrationString == "none") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } else if (orientationCalibrationString == "interpolated") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } else if (orientationCalibrationString != "default") { + LOGW("Invalid value for tool.orientation.calibration: '%s'", + orientationCalibrationString.string()); + } + } +} + +void TouchInputMapper::resolveCalibration() { + // Pressure + switch (mCalibration.pressureSource) { + case Calibration::PRESSURE_SOURCE_DEFAULT: + if (mRawAxes.pressure.valid) { + mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE; + } else if (mRawAxes.touchMajor.valid) { + mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH; + } + break; + + case Calibration::PRESSURE_SOURCE_PRESSURE: + if (! mRawAxes.pressure.valid) { + LOGW("Calibration property touch.pressure.source is 'pressure' but " + "the pressure axis is not available."); + } + break; + + case Calibration::PRESSURE_SOURCE_TOUCH: + if (! mRawAxes.touchMajor.valid) { + LOGW("Calibration property touch.pressure.source is 'touch' but " + "the touchMajor axis is not available."); + } + break; + + default: + break; + } + + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_DEFAULT: + if (mCalibration.pressureSource != Calibration::PRESSURE_SOURCE_DEFAULT) { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; + } else { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } + break; + + default: + break; + } + + // Tool Area + switch (mCalibration.toolAreaCalibration) { + case Calibration::TOOL_AREA_CALIBRATION_DEFAULT: + if (mRawAxes.toolMajor.valid) { + mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_LINEAR; + } else { + mCalibration.toolAreaCalibration = Calibration::TOOL_AREA_CALIBRATION_NONE; + } + break; + + default: + break; + } + + // Touch Area + switch (mCalibration.touchAreaCalibration) { + case Calibration::TOUCH_AREA_CALIBRATION_DEFAULT: + if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE + && mCalibration.toolAreaCalibration != Calibration::TOOL_AREA_CALIBRATION_NONE) { + mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_PRESSURE; + } else { + mCalibration.touchAreaCalibration = Calibration::TOUCH_AREA_CALIBRATION_NONE; + } + break; + + default: + break; + } + + // Size + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_DEFAULT: + if (mRawAxes.toolMajor.valid) { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED; + } else { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } + break; + + default: + break; + } + + // Orientation + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_DEFAULT: + if (mRawAxes.orientation.valid) { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } else { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } + break; + + default: + break; + } +} + +void TouchInputMapper::logCalibration() { + // Touch Area + switch (mCalibration.touchAreaCalibration) { + case Calibration::TOUCH_AREA_CALIBRATION_NONE: + LOGI(INDENT " touch.touchArea.calibration: none"); + break; + case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC: + LOGI(INDENT " touch.touchArea.calibration: geometric"); + break; + case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE: + LOGI(INDENT " touch.touchArea.calibration: pressure"); + break; + default: + assert(false); + } + + // Tool Area + switch (mCalibration.toolAreaCalibration) { + case Calibration::TOOL_AREA_CALIBRATION_NONE: + LOGI(INDENT " touch.toolArea.calibration: none"); + break; + case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC: + LOGI(INDENT " touch.toolArea.calibration: geometric"); + break; + case Calibration::TOOL_AREA_CALIBRATION_LINEAR: + LOGI(INDENT " touch.toolArea.calibration: linear"); + break; + default: + assert(false); + } + + if (mCalibration.haveToolAreaLinearScale) { + LOGI(INDENT " touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale); + } + + if (mCalibration.haveToolAreaLinearBias) { + LOGI(INDENT " touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias); + } + + if (mCalibration.haveToolAreaIsSummed) { + LOGI(INDENT " touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed); + } + + // Pressure + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_NONE: + LOGI(INDENT " touch.pressure.calibration: none"); + break; + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + LOGI(INDENT " touch.pressure.calibration: physical"); + break; + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + LOGI(INDENT " touch.pressure.calibration: amplitude"); + break; + default: + assert(false); + } + + switch (mCalibration.pressureSource) { + case Calibration::PRESSURE_SOURCE_PRESSURE: + LOGI(INDENT " touch.pressure.source: pressure"); + break; + case Calibration::PRESSURE_SOURCE_TOUCH: + LOGI(INDENT " touch.pressure.source: touch"); + break; + case Calibration::PRESSURE_SOURCE_DEFAULT: + break; + default: + assert(false); + } + + if (mCalibration.havePressureScale) { + LOGI(INDENT " touch.pressure.scale: %f", mCalibration.pressureScale); + } + + // Size + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NONE: + LOGI(INDENT " touch.size.calibration: none"); + break; + case Calibration::SIZE_CALIBRATION_NORMALIZED: + LOGI(INDENT " touch.size.calibration: normalized"); + break; + default: + assert(false); + } + + // Orientation + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_NONE: + LOGI(INDENT " touch.orientation.calibration: none"); + break; + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + LOGI(INDENT " touch.orientation.calibration: interpolated"); + break; + default: + assert(false); + } +} + void TouchInputMapper::reset() { // Synthesize touch up event if touch is currently down. // This will also take care of finishing virtual key processing if needed. @@ -1584,13 +2071,14 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { // The dispatcher takes care of batching moves so we don't have to deal with that here. int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE; dispatchTouch(when, policyFlags, & mCurrentTouch, - currentIdBits, -1, motionEventAction); + currentIdBits, -1, currentPointerCount, motionEventAction); } else { // There may be pointers going up and pointers going down at the same time when pointer // ids are reported by the device driver. BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value); BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value); BitSet32 activeIdBits(lastIdBits.value); + uint32_t pointerCount = lastPointerCount; while (! upIdBits.isEmpty()) { uint32_t upId = upIdBits.firstMarkedBit(); @@ -1606,7 +2094,8 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { } dispatchTouch(when, policyFlags, & mLastTouch, - oldActiveIdBits, upId, motionEventAction); + oldActiveIdBits, upId, pointerCount, motionEventAction); + pointerCount -= 1; } while (! downIdBits.isEmpty()) { @@ -1623,16 +2112,16 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN; } + pointerCount += 1; dispatchTouch(when, policyFlags, & mCurrentTouch, - activeIdBits, downId, motionEventAction); + activeIdBits, downId, pointerCount, motionEventAction); } } } void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, - TouchData* touch, BitSet32 idBits, uint32_t changedId, + TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, int32_t motionEventAction) { - uint32_t pointerCount = 0; int32_t pointerIds[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; int32_t motionEventEdgeFlags = 0; @@ -1643,36 +2132,130 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, // Walk through the the active pointers and map touch screen coordinates (TouchData) into // display coordinates (PointerCoords) and adjust for display orientation. - while (! idBits.isEmpty()) { + for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); - uint32_t index = touch->idToIndex[id]; - - float x = float(touch->pointers[index].x - mLocked.xOrigin) * mLocked.xScale; - float y = float(touch->pointers[index].y - mLocked.yOrigin) * mLocked.yScale; - float pressure = float(touch->pointers[index].pressure - mLocked.pressureOrigin) - * mLocked.pressureScale; - float size = float(touch->pointers[index].size - mLocked.sizeOrigin) - * mLocked.sizeScale; - - float orientation = float(touch->pointers[index].orientation) - * mLocked.orientationScale; - - float touchMajor, touchMinor, toolMajor, toolMinor; - if (abs(orientation) <= M_PI_4) { - // Nominally vertical orientation: scale major axis by Y, and scale minor axis by X. - touchMajor = float(touch->pointers[index].touchMajor) * mLocked.yScale; - touchMinor = float(touch->pointers[index].touchMinor) * mLocked.xScale; - toolMajor = float(touch->pointers[index].toolMajor) * mLocked.yScale; - toolMinor = float(touch->pointers[index].toolMinor) * mLocked.xScale; - } else { - // Nominally horizontal orientation: scale major axis by X, and scale minor axis by Y. - touchMajor = float(touch->pointers[index].touchMajor) * mLocked.xScale; - touchMinor = float(touch->pointers[index].touchMinor) * mLocked.yScale; - toolMajor = float(touch->pointers[index].toolMajor) * mLocked.xScale; - toolMinor = float(touch->pointers[index].toolMinor) * mLocked.yScale; + uint32_t inIndex = touch->idToIndex[id]; + + const PointerData& in = touch->pointers[inIndex]; + + // X and Y + float x = float(in.x - mLocked.xOrigin) * mLocked.xScale; + float y = float(in.y - mLocked.yOrigin) * mLocked.yScale; + + // ToolMajor and ToolMinor + float toolMajor, toolMinor; + switch (mCalibration.toolAreaCalibration) { + case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC: + toolMajor = in.toolMajor * mLocked.geometricScale; + if (mRawAxes.toolMinor.valid) { + toolMinor = in.toolMinor * mLocked.geometricScale; + } else { + toolMinor = toolMajor; + } + break; + case Calibration::TOOL_AREA_CALIBRATION_LINEAR: + toolMajor = in.toolMajor != 0 + ? in.toolMajor * mLocked.toolAreaLinearScale + mLocked.toolAreaLinearBias + : 0; + if (mRawAxes.toolMinor.valid) { + toolMinor = in.toolMinor != 0 + ? in.toolMinor * mLocked.toolAreaLinearScale + + mLocked.toolAreaLinearBias + : 0; + } else { + toolMinor = toolMajor; + } + break; + default: + toolMajor = 0; + toolMinor = 0; + break; + } + + if (mCalibration.haveToolAreaIsSummed && mCalibration.toolAreaIsSummed) { + toolMajor /= pointerCount; + toolMinor /= pointerCount; + } + + // Pressure + float rawPressure; + switch (mCalibration.pressureSource) { + case Calibration::PRESSURE_SOURCE_PRESSURE: + rawPressure = in.pressure; + break; + case Calibration::PRESSURE_SOURCE_TOUCH: + rawPressure = in.touchMajor; + break; + default: + rawPressure = 0; + } + + float pressure; + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + pressure = rawPressure * mLocked.pressureScale; + break; + default: + pressure = 1; + break; + } + + // TouchMajor and TouchMinor + float touchMajor, touchMinor; + switch (mCalibration.touchAreaCalibration) { + case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC: + touchMajor = in.touchMajor * mLocked.geometricScale; + if (mRawAxes.touchMinor.valid) { + touchMinor = in.touchMinor * mLocked.geometricScale; + } else { + touchMinor = touchMajor; + } + break; + case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE: + touchMajor = toolMajor * pressure; + touchMinor = toolMinor * pressure; + break; + default: + touchMajor = 0; + touchMinor = 0; + break; + } + + if (touchMajor > toolMajor) { + touchMajor = toolMajor; + } + if (touchMinor > toolMinor) { + touchMinor = toolMinor; + } + + // Size + float size; + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NORMALIZED: { + float rawSize = mRawAxes.toolMinor.valid + ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; + size = rawSize * mLocked.sizeScale; + break; + } + default: + size = 0; + break; } + // Orientation + float orientation; + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + orientation = in.orientation * mLocked.orientationScale; + break; + default: + orientation = 0; + } + + // Adjust coords for orientation. switch (mLocked.surfaceOrientation) { case InputReaderPolicyInterface::ROTATION_90: { float xTemp = x; @@ -1702,23 +2285,23 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, } } - pointerIds[pointerCount] = int32_t(id); + // Write output coords. + PointerCoords& out = pointerCoords[outIndex]; + out.x = x; + out.y = y; + out.pressure = pressure; + out.size = size; + out.touchMajor = touchMajor; + out.touchMinor = touchMinor; + out.toolMajor = toolMajor; + out.toolMinor = toolMinor; + out.orientation = orientation; - pointerCoords[pointerCount].x = x; - pointerCoords[pointerCount].y = y; - pointerCoords[pointerCount].pressure = pressure; - pointerCoords[pointerCount].size = size; - pointerCoords[pointerCount].touchMajor = touchMajor; - pointerCoords[pointerCount].touchMinor = touchMinor; - pointerCoords[pointerCount].toolMajor = toolMajor; - pointerCoords[pointerCount].toolMinor = toolMinor; - pointerCoords[pointerCount].orientation = orientation; + pointerIds[outIndex] = int32_t(id); if (id == changedId) { - motionEventAction |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } - - pointerCount += 1; } // Check edge flags by looking only at the first pointer since the flags are @@ -1747,9 +2330,9 @@ void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, } bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) { - if (mAxes.x.valid && mAxes.y.valid) { - return x >= mAxes.x.minValue && x <= mAxes.x.maxValue - && y >= mAxes.y.minValue && y <= mAxes.y.maxValue; + if (mRawAxes.x.valid && mRawAxes.y.valid) { + return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue + && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue; } return true; } @@ -1960,7 +2543,7 @@ void TouchInputMapper::calculatePointerIds() { * then drop it. */ bool TouchInputMapper::applyBadTouchFilter() { // This hack requires valid axis parameters. - if (! mAxes.y.valid) { + if (! mRawAxes.y.valid) { return false; } @@ -1982,7 +2565,7 @@ bool TouchInputMapper::applyBadTouchFilter() { // the long size of the screen to be bad. This was a magic value // determined by looking at the maximum distance it is feasible // to actually move in one sample. - int32_t maxDeltaY = mAxes.y.getRange() * 7 / 16; + int32_t maxDeltaY = mRawAxes.y.getRange() * 7 / 16; // XXX The original code in InputDevice.java included commented out // code for testing the X axis. Note that when we drop a point @@ -2044,7 +2627,7 @@ bool TouchInputMapper::applyBadTouchFilter() { */ bool TouchInputMapper::applyJumpyTouchFilter() { // This hack requires valid axis parameters. - if (! mAxes.y.valid) { + if (! mRawAxes.y.valid) { return false; } @@ -2104,7 +2687,7 @@ bool TouchInputMapper::applyJumpyTouchFilter() { } if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) { - int jumpyEpsilon = mAxes.y.getRange() / JUMPY_EPSILON_DIVISOR; + int jumpyEpsilon = mRawAxes.y.getRange() / JUMPY_EPSILON_DIVISOR; // We only replace the single worst jumpy point as characterized by pointer distance // in a single axis. @@ -2209,7 +2792,18 @@ void TouchInputMapper::applyAveragingTouchFilter() { uint32_t id = mCurrentTouch.pointers[currentIndex].id; int32_t x = mCurrentTouch.pointers[currentIndex].x; int32_t y = mCurrentTouch.pointers[currentIndex].y; - int32_t pressure = mCurrentTouch.pointers[currentIndex].pressure; + int32_t pressure; + switch (mCalibration.pressureSource) { + case Calibration::PRESSURE_SOURCE_PRESSURE: + pressure = mCurrentTouch.pointers[currentIndex].pressure; + break; + case Calibration::PRESSURE_SOURCE_TOUCH: + pressure = mCurrentTouch.pointers[currentIndex].touchMajor; + break; + default: + pressure = 1; + break; + } if (mLastTouch.idBits.hasBit(id)) { // Pointer was down before and is still down now. @@ -2274,17 +2868,19 @@ void TouchInputMapper::applyAveragingTouchFilter() { } } - averagedX /= totalPressure; - averagedY /= totalPressure; + if (totalPressure != 0) { + averagedX /= totalPressure; + averagedY /= totalPressure; #if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - " - "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, - averagedX, averagedY); + LOGD("AveragingTouchFilter: Pointer id %d - " + "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, + averagedX, averagedY); #endif - mCurrentTouch.pointers[currentIndex].x = averagedX; - mCurrentTouch.pointers[currentIndex].y = averagedY; + mCurrentTouch.pointers[currentIndex].x = averagedX; + mCurrentTouch.pointers[currentIndex].y = averagedY; + } } else { #if DEBUG_HACKS LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id); @@ -2382,8 +2978,8 @@ void SingleTouchInputMapper::initialize() { mDown = false; mX = 0; mY = 0; - mPressure = 1; // default to 1 for devices that don't report pressure - mSize = 0; // default to 0 for devices that don't report size + mPressure = 0; // default to 0 for devices that don't report pressure + mToolWidth = 0; // default to 0 for devices that don't report tool width } void SingleTouchInputMapper::reset() { @@ -2460,7 +3056,7 @@ void SingleTouchInputMapper::sync(nsecs_t when) { } if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) { - mSize = mAccumulator.absToolWidth; + mToolWidth = mAccumulator.absToolWidth; } mCurrentTouch.clear(); @@ -2471,11 +3067,10 @@ void SingleTouchInputMapper::sync(nsecs_t when) { mCurrentTouch.pointers[0].x = mX; mCurrentTouch.pointers[0].y = mY; mCurrentTouch.pointers[0].pressure = mPressure; - mCurrentTouch.pointers[0].size = mSize; - mCurrentTouch.pointers[0].touchMajor = mSize; - mCurrentTouch.pointers[0].touchMinor = mSize; - mCurrentTouch.pointers[0].toolMajor = mSize; - mCurrentTouch.pointers[0].toolMinor = mSize; + mCurrentTouch.pointers[0].touchMajor = 0; + mCurrentTouch.pointers[0].touchMinor = 0; + mCurrentTouch.pointers[0].toolMajor = mToolWidth; + mCurrentTouch.pointers[0].toolMinor = mToolWidth; mCurrentTouch.pointers[0].orientation = 0; mCurrentTouch.idToIndex[0] = 0; mCurrentTouch.idBits.markBit(0); @@ -2486,20 +3081,13 @@ void SingleTouchInputMapper::sync(nsecs_t when) { mAccumulator.clear(); } -void SingleTouchInputMapper::configureAxes() { - TouchInputMapper::configureAxes(); - - // The axes are aliased to take into account the manner in which they are presented - // as part of the TouchData during the sync. - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mAxes.x); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mAxes.y); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size); +void SingleTouchInputMapper::configureRawAxes() { + TouchInputMapper::configureRawAxes(); - mAxes.touchMajor = mAxes.size; - mAxes.touchMinor = mAxes.size; - mAxes.toolMajor = mAxes.size; - mAxes.toolMinor = mAxes.size; + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mRawAxes.pressure); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mRawAxes.toolMajor); } @@ -2562,6 +3150,10 @@ void MultiTouchInputMapper::process(const RawEvent* rawEvent) { pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; pointer->absMTTrackingId = rawEvent->value; break; + case ABS_MT_PRESSURE: + pointer->fields |= Accumulator::FIELD_ABS_MT_PRESSURE; + pointer->absMTPressure = rawEvent->value; + break; } break; } @@ -2596,8 +3188,7 @@ void MultiTouchInputMapper::process(const RawEvent* rawEvent) { void MultiTouchInputMapper::sync(nsecs_t when) { static const uint32_t REQUIRED_FIELDS = - Accumulator::FIELD_ABS_MT_POSITION_X - | Accumulator::FIELD_ABS_MT_POSITION_Y; + Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y; uint32_t inCount = mAccumulator.pointerCount; uint32_t outCount = 0; @@ -2619,60 +3210,59 @@ void MultiTouchInputMapper::sync(nsecs_t when) { outPointer.x = inPointer.absMTPositionX; outPointer.y = inPointer.absMTPositionY; + if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) { + if (inPointer.absMTPressure <= 0) { + // Some devices send sync packets with X / Y but with a 0 presure to indicate + // a pointer up. Drop this finger. + continue; + } + outPointer.pressure = inPointer.absMTPressure; + } else { + // Default pressure to 0 if absent. + outPointer.pressure = 0; + } + if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) { - int32_t value = inPointer.absMTTouchMajor; - if (value <= 0) { + if (inPointer.absMTTouchMajor <= 0) { // Some devices send sync packets with X / Y but with a 0 touch major to indicate - // a pointer up. Drop this finger. + // a pointer going up. Drop this finger. continue; } outPointer.touchMajor = inPointer.absMTTouchMajor; } else { + // Default touch area to 0 if absent. outPointer.touchMajor = 0; } if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) { outPointer.touchMinor = inPointer.absMTTouchMinor; } else { + // Assume touch area is circular. outPointer.touchMinor = outPointer.touchMajor; } if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) { outPointer.toolMajor = inPointer.absMTWidthMajor; } else { - outPointer.toolMajor = outPointer.touchMajor; + // Default tool area to 0 if absent. + outPointer.toolMajor = 0; } if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) { outPointer.toolMinor = inPointer.absMTWidthMinor; } else { + // Assume tool area is circular. outPointer.toolMinor = outPointer.toolMajor; } if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) { outPointer.orientation = inPointer.absMTOrientation; } else { + // Default orientation to vertical if absent. outPointer.orientation = 0; } - if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) { - outPointer.pressure = inPointer.absMTPressure; - } else { - // Derive an approximation of pressure. - // FIXME Traditionally we have just passed a normalized value based on - // ABS_MT_TOUCH_MAJOR as an estimate of pressure but the result is not - // very meaningful, particularly on large displays. We should probably let - // pressure = touch_major / tool_major but it is unclear whether that will - // break applications. - outPointer.pressure = outPointer.touchMajor; - } - - // Size is an alias for a normalized tool width. - // FIXME Normalized tool width doesn't actually make much sense since it literally - // means the approaching contact major axis is divided by its full range as - // reported by the driver. On a large display this could produce very small values. - outPointer.size = outPointer.toolMajor; - + // Assign pointer id using tracking id if available. if (havePointerIds) { if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) { uint32_t id = uint32_t(inPointer.absMTTrackingId); @@ -2705,33 +3295,17 @@ void MultiTouchInputMapper::sync(nsecs_t when) { mAccumulator.clear(); } -void MultiTouchInputMapper::configureAxes() { - TouchInputMapper::configureAxes(); - - // The axes are aliased to take into account the manner in which they are presented - // as part of the TouchData during the sync. - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mAxes.x); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mAxes.y); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mAxes.touchMajor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mAxes.touchMinor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mAxes.pressure); - - if (! mAxes.touchMinor.valid) { - mAxes.touchMinor = mAxes.touchMajor; - } - - if (! mAxes.toolMinor.valid) { - mAxes.toolMinor = mAxes.toolMajor; - } - - if (! mAxes.pressure.valid) { - mAxes.pressure = mAxes.touchMajor; - } +void MultiTouchInputMapper::configureRawAxes() { + TouchInputMapper::configureRawAxes(); - mAxes.size = mAxes.toolMajor; + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mRawAxes.x); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mRawAxes.y); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mRawAxes.touchMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mRawAxes.touchMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mRawAxes.toolMajor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mRawAxes.toolMinor); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mRawAxes.orientation); + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mRawAxes.pressure); } diff --git a/native/include/android/input.h b/native/include/android/input.h index 243c33c..d9486e5 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -718,8 +718,6 @@ int32_t AInputDevice_getKeyboardType(AInputDevice* device); int32_t AInputDevice_getMotionRange(AInputDevice* device, int32_t rangeType, float* outMin, float* outMax, float* outFlat, float* outFuzz); -//TODO hasKey, keymap stuff, etc... - #ifdef __cplusplus } #endif diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index f330d40..314dd8a 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -30,6 +30,7 @@ import android.os.SystemProperties; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; +import android.view.InputDevice; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; @@ -45,6 +46,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Properties; /* * Wraps the C++ InputManager and provides its callbacks. @@ -82,6 +84,8 @@ public class InputManager { private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen); private static native void nativeSetFocusedApplication(InputApplication application); private static native void nativePreemptInputDispatch(); + private static native InputDevice nativeGetInputDevice(int deviceId); + private static native int[] nativeGetInputDeviceIds(); private static native String nativeDump(); // Input event injection constants defined in InputDispatcher.h. @@ -302,6 +306,23 @@ public class InputManager { return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); } + /** + * Gets information about the input device with the specified id. + * @param id The device id. + * @return The input device or null if not found. + */ + public InputDevice getInputDevice(int deviceId) { + return nativeGetInputDevice(deviceId); + } + + /** + * Gets the ids of all input devices in the system. + * @return The input device ids. + */ + public int[] getInputDeviceIds() { + return nativeGetInputDeviceIds(); + } + public void setInputWindows(InputWindow[] windows) { nativeSetInputWindows(windows); } @@ -335,6 +356,11 @@ public class InputManager { public int height; } + private static final class InputDeviceCalibration { + public String[] keys; + public String[] values; + } + /* * Callbacks from native. */ @@ -343,6 +369,7 @@ public class InputManager { private static final boolean DEBUG_VIRTUAL_KEYS = false; private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; + private static final String CALIBRATION_DIR_PATH = "usr/idc/"; @SuppressWarnings("unused") public void virtualKeyDownFeedback() { @@ -438,8 +465,8 @@ public class InputManager { final int N = it.length-6; for (int i=0; i<=N; i+=6) { if (!"0x01".equals(it[i])) { - Slog.w(TAG, "Unknown virtual key type at elem #" + i - + ": " + it[i]); + Slog.w(TAG, "Unknown virtual key type at elem #" + + i + ": " + it[i] + " for device " + deviceName); continue; } try { @@ -455,22 +482,47 @@ public class InputManager { + key.height); keys.add(key); } catch (NumberFormatException e) { - Slog.w(TAG, "Bad number at region " + i + " in: " - + str, e); + Slog.w(TAG, "Bad number in virtual key definition at region " + + i + " in: " + str + " for device " + deviceName, e); } } } br.close(); } catch (FileNotFoundException e) { - Slog.i(TAG, "No virtual keys found"); + Slog.i(TAG, "No virtual keys found for device " + deviceName + "."); } catch (IOException e) { - Slog.w(TAG, "Error reading virtual keys", e); + Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e); } return keys.toArray(new VirtualKeyDefinition[keys.size()]); } @SuppressWarnings("unused") + public InputDeviceCalibration getInputDeviceCalibration(String deviceName) { + // Calibration is specified as a sequence of colon-delimited key value pairs. + Properties properties = new Properties(); + File calibrationFile = new File(Environment.getRootDirectory(), + CALIBRATION_DIR_PATH + deviceName + ".idc"); + if (calibrationFile.exists()) { + try { + properties.load(new FileInputStream(calibrationFile)); + } catch (IOException ex) { + Slog.w(TAG, "Error reading input device calibration properties for device " + + deviceName + " from " + calibrationFile + ".", ex); + } + } else { + Slog.i(TAG, "No input device calibration properties found for device " + + deviceName + "."); + return null; + } + + InputDeviceCalibration calibration = new InputDeviceCalibration(); + calibration.keys = properties.keySet().toArray(new String[properties.size()]); + calibration.values = properties.values().toArray(new String[properties.size()]); + return calibration; + } + + @SuppressWarnings("unused") public String[] getExcludedDeviceNames() { ArrayList<String> names = new ArrayList<String>(); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 4407e96..79bde7c 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -4383,6 +4383,14 @@ public class WindowManagerService extends IWindowManager.Stub return mInputManager.monitorInput(inputChannelName); } + public InputDevice getInputDevice(int deviceId) { + return mInputManager.getInputDevice(deviceId); + } + + public int[] getInputDeviceIds() { + return mInputManager.getInputDeviceIds(); + } + public void enableScreenAfterBoot() { synchronized(mWindowMap) { if (mSystemBooted) { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 3addc0d..a237ee9 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -138,6 +138,7 @@ static struct { jmethodID filterTouchEvents; jmethodID filterJumpyTouchEvents; jmethodID getVirtualKeyDefinitions; + jmethodID getInputDeviceCalibration; jmethodID getExcludedDeviceNames; jmethodID getMaxEventsPerSecond; } gCallbacksClassInfo; @@ -155,6 +156,13 @@ static struct { static struct { jclass clazz; + jfieldID keys; + jfieldID values; +} gInputDeviceCalibrationClassInfo; + +static struct { + jclass clazz; + jfieldID inputChannel; jfieldID layoutParamsFlags; jfieldID layoutParamsType; @@ -189,6 +197,19 @@ static struct { jclass clazz; } gMotionEventClassInfo; +static struct { + jclass clazz; + + jmethodID ctor; + jmethodID addMotionRange; + + jfieldID mId; + jfieldID mName; + jfieldID mSources; + jfieldID mKeyboardType; + jfieldID mMotionRanges; +} gInputDeviceClassInfo; + // ---------------------------------------------------------------------------- static inline nsecs_t now() { @@ -235,7 +256,9 @@ public: virtual bool filterTouchEvents(); virtual bool filterJumpyTouchEvents(); virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions); + Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions); + virtual void getInputDeviceCalibration(const String8& deviceName, + InputDeviceCalibration& outCalibration); virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -761,7 +784,9 @@ bool NativeInputManager::filterJumpyTouchEvents() { } void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, - Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions) { + Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) { + outVirtualKeyDefinitions.clear(); + JNIEnv* env = jniEnv(); jstring deviceNameStr = env->NewStringUTF(deviceName.string()); @@ -793,7 +818,51 @@ void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, } } +void NativeInputManager::getInputDeviceCalibration(const String8& deviceName, + InputDeviceCalibration& outCalibration) { + outCalibration.clear(); + + JNIEnv* env = jniEnv(); + + jstring deviceNameStr = env->NewStringUTF(deviceName.string()); + if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration")) { + jobject result = env->CallObjectMethod(mCallbacksObj, + gCallbacksClassInfo.getInputDeviceCalibration, deviceNameStr); + if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration") && result) { + jobjectArray keys = jobjectArray(env->GetObjectField(result, + gInputDeviceCalibrationClassInfo.keys)); + jobjectArray values = jobjectArray(env->GetObjectField(result, + gInputDeviceCalibrationClassInfo.values)); + + jsize length = env->GetArrayLength(keys); + for (jsize i = 0; i < length; i++) { + jstring keyStr = jstring(env->GetObjectArrayElement(keys, i)); + jstring valueStr = jstring(env->GetObjectArrayElement(values, i)); + + const char* keyChars = env->GetStringUTFChars(keyStr, NULL); + String8 key(keyChars); + env->ReleaseStringUTFChars(keyStr, keyChars); + + const char* valueChars = env->GetStringUTFChars(valueStr, NULL); + String8 value(valueChars); + env->ReleaseStringUTFChars(valueStr, valueChars); + + outCalibration.addProperty(key, value); + + env->DeleteLocalRef(keyStr); + env->DeleteLocalRef(valueStr); + } + env->DeleteLocalRef(keys); + env->DeleteLocalRef(values); + env->DeleteLocalRef(result); + } + env->DeleteLocalRef(deviceNameStr); + } +} + void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) { + outExcludedDeviceNames.clear(); + JNIEnv* env = jniEnv(); jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, @@ -2199,6 +2268,66 @@ static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env, gNativeInputManager->preemptInputDispatch(); } +static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env, + jclass clazz, jint deviceId) { + if (checkInputManagerUnitialized(env)) { + return NULL; + } + + InputDeviceInfo deviceInfo; + status_t status = gNativeInputManager->getInputManager()->getInputDeviceInfo( + deviceId, & deviceInfo); + if (status) { + return NULL; + } + + jobject deviceObj = env->NewObject(gInputDeviceClassInfo.clazz, gInputDeviceClassInfo.ctor); + if (! deviceObj) { + return NULL; + } + + jstring deviceNameObj = env->NewStringUTF(deviceInfo.getName().string()); + if (! deviceNameObj) { + return NULL; + } + + env->SetIntField(deviceObj, gInputDeviceClassInfo.mId, deviceInfo.getId()); + env->SetObjectField(deviceObj, gInputDeviceClassInfo.mName, deviceNameObj); + env->SetIntField(deviceObj, gInputDeviceClassInfo.mSources, deviceInfo.getSources()); + env->SetIntField(deviceObj, gInputDeviceClassInfo.mKeyboardType, deviceInfo.getKeyboardType()); + + const KeyedVector<int, InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); + for (size_t i = 0; i < ranges.size(); i++) { + int rangeType = ranges.keyAt(i); + const InputDeviceInfo::MotionRange& range = ranges.valueAt(i); + env->CallVoidMethod(deviceObj, gInputDeviceClassInfo.addMotionRange, + rangeType, range.min, range.max, range.flat, range.fuzz); + if (env->ExceptionCheck()) { + return NULL; + } + } + + return deviceObj; +} + +static jintArray android_server_InputManager_nativeGetInputDeviceIds(JNIEnv* env, + jclass clazz) { + if (checkInputManagerUnitialized(env)) { + return NULL; + } + + Vector<int> deviceIds; + gNativeInputManager->getInputManager()->getInputDeviceIds(deviceIds); + + jintArray deviceIdsObj = env->NewIntArray(deviceIds.size()); + if (! deviceIdsObj) { + return NULL; + } + + env->SetIntArrayRegion(deviceIdsObj, 0, deviceIds.size(), deviceIds.array()); + return deviceIdsObj; +} + static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) { if (checkInputManagerUnitialized(env)) { return NULL; @@ -2242,6 +2371,10 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeSetInputDispatchMode }, { "nativePreemptInputDispatch", "()V", (void*) android_server_InputManager_nativePreemptInputDispatch }, + { "nativeGetInputDevice", "(I)Landroid/view/InputDevice;", + (void*) android_server_InputManager_nativeGetInputDevice }, + { "nativeGetInputDeviceIds", "()[I", + (void*) android_server_InputManager_nativeGetInputDeviceIds }, { "nativeDump", "()Ljava/lang/String;", (void*) android_server_InputManager_nativeDump }, }; @@ -2311,6 +2444,10 @@ int register_android_server_InputManager(JNIEnv* env) { "getVirtualKeyDefinitions", "(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;"); + GET_METHOD_ID(gCallbacksClassInfo.getInputDeviceCalibration, gCallbacksClassInfo.clazz, + "getInputDeviceCalibration", + "(Ljava/lang/String;)Lcom/android/server/InputManager$InputDeviceCalibration;"); + GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz, "getExcludedDeviceNames", "()[Ljava/lang/String;"); @@ -2337,6 +2474,17 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz, "height", "I"); + // InputDeviceCalibration + + FIND_CLASS(gInputDeviceCalibrationClassInfo.clazz, + "com/android/server/InputManager$InputDeviceCalibration"); + + GET_FIELD_ID(gInputDeviceCalibrationClassInfo.keys, gInputDeviceCalibrationClassInfo.clazz, + "keys", "[Ljava/lang/String;"); + + GET_FIELD_ID(gInputDeviceCalibrationClassInfo.values, gInputDeviceCalibrationClassInfo.clazz, + "values", "[Ljava/lang/String;"); + // InputWindow FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow"); @@ -2407,10 +2555,35 @@ int register_android_server_InputManager(JNIEnv* env) { FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); - // MotionEVent + // MotionEvent FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + // InputDevice + + FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); + + GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz, + "<init>", "()V"); + + GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz, + "addMotionRange", "(IFFFF)V"); + + GET_FIELD_ID(gInputDeviceClassInfo.mId, gInputDeviceClassInfo.clazz, + "mId", "I"); + + GET_FIELD_ID(gInputDeviceClassInfo.mName, gInputDeviceClassInfo.clazz, + "mName", "Ljava/lang/String;"); + + GET_FIELD_ID(gInputDeviceClassInfo.mSources, gInputDeviceClassInfo.clazz, + "mSources", "I"); + + GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz, + "mKeyboardType", "I"); + + GET_FIELD_ID(gInputDeviceClassInfo.mMotionRanges, gInputDeviceClassInfo.clazz, + "mMotionRanges", "[Landroid/view/InputDevice$MotionRange;"); + return 0; } |