/* * Copyright (C) 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 com.android.server; import android.util.Log; import android.view.Display; import android.view.MotionEvent; import android.view.Surface; import android.view.WindowManagerPolicy; public class InputDevice { /** Amount that trackball needs to move in order to generate a key event. */ static final int TRACKBALL_MOVEMENT_THRESHOLD = 6; /** Maximum number of pointers we will track and report. */ static final int MAX_POINTERS = 2; final int id; final int classes; final String name; final AbsoluteInfo absX; final AbsoluteInfo absY; final AbsoluteInfo absPressure; final AbsoluteInfo absSize; long mKeyDownTime = 0; int mMetaKeysState = 0; final MotionState mAbs = new MotionState(0, 0); final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD, TRACKBALL_MOVEMENT_THRESHOLD); static class MotionState { int xPrecision; int yPrecision; float xMoveScale; float yMoveScale; MotionEvent currentMove = null; boolean changed = false; boolean mLastAnyDown = false; long mDownTime = 0; final boolean[] mLastDown = new boolean[MAX_POINTERS]; final boolean[] mDown = new boolean[MAX_POINTERS]; final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; final int[] mCurData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS]; MotionState(int mx, int my) { xPrecision = mx; yPrecision = my; xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f; yMoveScale = my != 0 ? (1.0f/my) : 1.0f; } MotionEvent generateAbsMotion(InputDevice device, long curTime, long curTimeNano, Display display, int orientation, int metaState) { final float[] scaled = mReportData; final int[] cur = mCurData; boolean anyDown = false; int firstDownChanged = -1; int numPointers = 0; for (int i=0; i absX.maxValue || cur[MotionEvent.SAMPLE_Y] < absY.minValue || cur[MotionEvent.SAMPLE_Y] > absY.maxValue) { if (false) Log.v("InputDevice", "Rejecting (" + cur[MotionEvent.SAMPLE_X] + "," + cur[MotionEvent.SAMPLE_Y] + "): outside of (" + absX.minValue + "," + absY.minValue + ")-(" + absX.maxValue + "," + absY.maxValue + ")"); return null; } } mLastAnyDown = anyDown; if (anyDown) { action = MotionEvent.ACTION_DOWN; mDownTime = curTime; } else { action = MotionEvent.ACTION_UP; } currentMove = null; } else if (firstDownChanged >= 0) { if (mDown[firstDownChanged]) { action = MotionEvent.ACTION_POINTER_DOWN | (firstDownChanged << MotionEvent.ACTION_POINTER_SHIFT); } else { action = MotionEvent.ACTION_POINTER_UP | (firstDownChanged << MotionEvent.ACTION_POINTER_SHIFT); } currentMove = null; } else { action = MotionEvent.ACTION_MOVE; } final int dispW = display.getWidth()-1; final int dispH = display.getHeight()-1; int w = dispW; int h = dispH; if (orientation == Surface.ROTATION_90 || orientation == Surface.ROTATION_270) { int tmp = w; w = h; h = tmp; } final AbsoluteInfo absX = device.absX; final AbsoluteInfo absY = device.absY; final AbsoluteInfo absPressure = device.absPressure; final AbsoluteInfo absSize = device.absSize; for (int i=0; i= dispW) { edgeFlags |= MotionEvent.EDGE_RIGHT; } if (scaled[MotionEvent.SAMPLE_Y] <= 0) { edgeFlags |= MotionEvent.EDGE_TOP; } else if (scaled[MotionEvent.SAMPLE_Y] >= dispH) { edgeFlags |= MotionEvent.EDGE_BOTTOM; } } if (currentMove != null) { if (false) Log.i("InputDevice", "Adding batch x=" + scaled[MotionEvent.SAMPLE_X] + " y=" + scaled[MotionEvent.SAMPLE_Y] + " to " + currentMove); currentMove.addBatch(curTime, scaled, metaState); if (WindowManagerPolicy.WATCH_POINTER) { Log.i("KeyInputQueue", "Updating: " + currentMove); } return null; } MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime, curTimeNano, action, numPointers, scaled, metaState, xPrecision, yPrecision, device.id, edgeFlags); if (action == MotionEvent.ACTION_MOVE) { currentMove = me; } return me; } MotionEvent generateRelMotion(InputDevice device, long curTime, long curTimeNano, int orientation, int metaState) { final float[] scaled = mReportData; // For now we only support 1 pointer with relative motions. scaled[MotionEvent.SAMPLE_X] = mCurData[MotionEvent.SAMPLE_X]; scaled[MotionEvent.SAMPLE_Y] = mCurData[MotionEvent.SAMPLE_Y]; scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f; scaled[MotionEvent.SAMPLE_SIZE] = 0; int edgeFlags = 0; int action; if (mDown[0] != mLastDown[0]) { mCurData[MotionEvent.SAMPLE_X] = mCurData[MotionEvent.SAMPLE_Y] = 0; mLastDown[0] = mDown[0]; if (mDown[0]) { action = MotionEvent.ACTION_DOWN; mDownTime = curTime; } else { action = MotionEvent.ACTION_UP; } currentMove = null; } else { action = MotionEvent.ACTION_MOVE; } scaled[MotionEvent.SAMPLE_X] *= xMoveScale; scaled[MotionEvent.SAMPLE_Y] *= yMoveScale; switch (orientation) { case Surface.ROTATION_90: { final float temp = scaled[MotionEvent.SAMPLE_X]; scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y]; scaled[MotionEvent.SAMPLE_Y] = -temp; break; } case Surface.ROTATION_180: { scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X]; scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y]; break; } case Surface.ROTATION_270: { final float temp = scaled[MotionEvent.SAMPLE_X]; scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y]; scaled[MotionEvent.SAMPLE_Y] = temp; break; } } if (currentMove != null) { if (false) Log.i("InputDevice", "Adding batch x=" + scaled[MotionEvent.SAMPLE_X] + " y=" + scaled[MotionEvent.SAMPLE_Y] + " to " + currentMove); currentMove.addBatch(curTime, scaled, metaState); if (WindowManagerPolicy.WATCH_POINTER) { Log.i("KeyInputQueue", "Updating: " + currentMove); } return null; } MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime, curTimeNano, action, 1, scaled, metaState, xPrecision, yPrecision, device.id, edgeFlags); if (action == MotionEvent.ACTION_MOVE) { currentMove = me; } return me; } } static class AbsoluteInfo { int minValue; int maxValue; int range; int flat; int fuzz; }; InputDevice(int _id, int _classes, String _name, AbsoluteInfo _absX, AbsoluteInfo _absY, AbsoluteInfo _absPressure, AbsoluteInfo _absSize) { id = _id; classes = _classes; name = _name; absX = _absX; absY = _absY; absPressure = _absPressure; absSize = _absSize; } };