blob: 3e98132cdb737e61a189103d54aca88aac034e56 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.util.Log;
20import android.view.Display;
21import android.view.MotionEvent;
22import android.view.Surface;
23import android.view.WindowManagerPolicy;
24
25public class InputDevice {
26 /** Amount that trackball needs to move in order to generate a key event. */
27 static final int TRACKBALL_MOVEMENT_THRESHOLD = 6;
28
Dianne Hackborn9822d2b2009-07-20 17:33:15 -070029 /** Maximum number of pointers we will track and report. */
30 static final int MAX_POINTERS = 2;
31
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032 final int id;
33 final int classes;
34 final String name;
35 final AbsoluteInfo absX;
36 final AbsoluteInfo absY;
37 final AbsoluteInfo absPressure;
38 final AbsoluteInfo absSize;
39
Dianne Hackborn9822d2b2009-07-20 17:33:15 -070040 long mKeyDownTime = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 int mMetaKeysState = 0;
42
43 final MotionState mAbs = new MotionState(0, 0);
44 final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD,
45 TRACKBALL_MOVEMENT_THRESHOLD);
46
47 static class MotionState {
48 int xPrecision;
49 int yPrecision;
50 float xMoveScale;
51 float yMoveScale;
52 MotionEvent currentMove = null;
53 boolean changed = false;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -070054 boolean mLastAnyDown = false;
55 long mDownTime = 0;
56 final boolean[] mLastDown = new boolean[MAX_POINTERS];
57 final boolean[] mDown = new boolean[MAX_POINTERS];
58 final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
59 final int[] mCurData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
60 final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
62 MotionState(int mx, int my) {
63 xPrecision = mx;
64 yPrecision = my;
65 xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f;
66 yMoveScale = my != 0 ? (1.0f/my) : 1.0f;
67 }
68
Dianne Hackborn9822d2b2009-07-20 17:33:15 -070069 MotionEvent generateAbsMotion(InputDevice device, long curTime,
70 long curTimeNano, Display display, int orientation,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 int metaState) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -070072
73 final float[] scaled = mReportData;
74 final int[] cur = mCurData;
75
76 boolean anyDown = false;
77 int firstDownChanged = -1;
78 int numPointers = 0;
79 for (int i=0; i<MAX_POINTERS; i++) {
80 boolean d = mDown[i];
81 anyDown |= d;
82 if (d != mLastDown[i] && firstDownChanged < 0) {
83 firstDownChanged = i;
84 mLastDown[i] = mDown[i];
85 d = true;
86 }
87
88 if (d) {
89 final int src = i * MotionEvent.NUM_SAMPLE_DATA;
90 final int dest = numPointers * MotionEvent.NUM_SAMPLE_DATA;
91 numPointers++;
92 scaled[dest + MotionEvent.SAMPLE_X] = cur[src + MotionEvent.SAMPLE_X];
93 scaled[dest + MotionEvent.SAMPLE_Y] = cur[src + MotionEvent.SAMPLE_Y];
94 scaled[dest + MotionEvent.SAMPLE_PRESSURE] = cur[src + MotionEvent.SAMPLE_PRESSURE];
95 scaled[dest + MotionEvent.SAMPLE_SIZE] = cur[src + MotionEvent.SAMPLE_SIZE];
96 }
97 }
98
99 if (numPointers <= 0) {
100 return null;
101 }
102
103 int action;
104 int edgeFlags = 0;
105 if (anyDown != mLastAnyDown) {
106 final AbsoluteInfo absX = device.absX;
107 final AbsoluteInfo absY = device.absY;
108 if (anyDown && absX != null && absY != null) {
109 // We don't let downs start unless we are
110 // inside of the screen. There are two reasons for
111 // this: to avoid spurious touches when holding
112 // the edges of the device near the touchscreen,
113 // and to avoid reporting events if there are virtual
114 // keys on the touchscreen outside of the display
115 // area.
116 // Note that we are only looking at the first pointer,
117 // since what we are handling here is the first pointer
118 // going down, and this is the coordinate that will be
119 // used to dispatch the event.
120 if (cur[MotionEvent.SAMPLE_X] < absX.minValue
121 || cur[MotionEvent.SAMPLE_X] > absX.maxValue
122 || cur[MotionEvent.SAMPLE_Y] < absY.minValue
123 || cur[MotionEvent.SAMPLE_Y] > absY.maxValue) {
124 if (false) Log.v("InputDevice", "Rejecting ("
125 + cur[MotionEvent.SAMPLE_X] + ","
126 + cur[MotionEvent.SAMPLE_Y] + "): outside of ("
127 + absX.minValue + "," + absY.minValue
128 + ")-(" + absX.maxValue + ","
129 + absY.maxValue + ")");
130 return null;
131 }
132 }
133 mLastAnyDown = anyDown;
134 if (anyDown) {
135 action = MotionEvent.ACTION_DOWN;
136 mDownTime = curTime;
137 } else {
138 action = MotionEvent.ACTION_UP;
139 }
140 currentMove = null;
141 } else if (firstDownChanged >= 0) {
142 if (mDown[firstDownChanged]) {
143 action = MotionEvent.ACTION_POINTER_DOWN
144 | (firstDownChanged << MotionEvent.ACTION_POINTER_SHIFT);
145 } else {
146 action = MotionEvent.ACTION_POINTER_UP
147 | (firstDownChanged << MotionEvent.ACTION_POINTER_SHIFT);
148 }
149 currentMove = null;
150 } else {
151 action = MotionEvent.ACTION_MOVE;
152 }
153
154 final int dispW = display.getWidth()-1;
155 final int dispH = display.getHeight()-1;
156 int w = dispW;
157 int h = dispH;
158 if (orientation == Surface.ROTATION_90
159 || orientation == Surface.ROTATION_270) {
160 int tmp = w;
161 w = h;
162 h = tmp;
163 }
164
165 final AbsoluteInfo absX = device.absX;
166 final AbsoluteInfo absY = device.absY;
167 final AbsoluteInfo absPressure = device.absPressure;
168 final AbsoluteInfo absSize = device.absSize;
169 for (int i=0; i<numPointers; i++) {
170 final int j = i * MotionEvent.NUM_SAMPLE_DATA;
171
172 if (absX != null) {
173 scaled[j + MotionEvent.SAMPLE_X] =
174 ((scaled[j + MotionEvent.SAMPLE_X]-absX.minValue)
175 / absX.range) * w;
176 }
177 if (absY != null) {
178 scaled[j + MotionEvent.SAMPLE_Y] =
179 ((scaled[j + MotionEvent.SAMPLE_Y]-absY.minValue)
180 / absY.range) * h;
181 }
182 if (absPressure != null) {
183 scaled[j + MotionEvent.SAMPLE_PRESSURE] =
184 ((scaled[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue)
185 / (float)absPressure.range);
186 }
187 if (absSize != null) {
188 scaled[j + MotionEvent.SAMPLE_SIZE] =
189 ((scaled[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue)
190 / (float)absSize.range);
191 }
192
193 switch (orientation) {
194 case Surface.ROTATION_90: {
195 final float temp = scaled[MotionEvent.SAMPLE_X];
196 scaled[j + MotionEvent.SAMPLE_X] = scaled[j + MotionEvent.SAMPLE_Y];
197 scaled[j + MotionEvent.SAMPLE_Y] = w-temp;
198 break;
199 }
200 case Surface.ROTATION_180: {
201 scaled[j + MotionEvent.SAMPLE_X] = w-scaled[j + MotionEvent.SAMPLE_X];
202 scaled[j + MotionEvent.SAMPLE_Y] = h-scaled[j + MotionEvent.SAMPLE_Y];
203 break;
204 }
205 case Surface.ROTATION_270: {
206 final float temp = scaled[i + MotionEvent.SAMPLE_X];
207 scaled[j + MotionEvent.SAMPLE_X] = h-scaled[j + MotionEvent.SAMPLE_Y];
208 scaled[j + MotionEvent.SAMPLE_Y] = temp;
209 break;
210 }
211 }
212 }
213
214 // We only consider the first pointer when computing the edge
215 // flags, since they are global to the event.
216 if (action != MotionEvent.ACTION_DOWN) {
217 if (scaled[MotionEvent.SAMPLE_X] <= 0) {
218 edgeFlags |= MotionEvent.EDGE_LEFT;
219 } else if (scaled[MotionEvent.SAMPLE_X] >= dispW) {
220 edgeFlags |= MotionEvent.EDGE_RIGHT;
221 }
222 if (scaled[MotionEvent.SAMPLE_Y] <= 0) {
223 edgeFlags |= MotionEvent.EDGE_TOP;
224 } else if (scaled[MotionEvent.SAMPLE_Y] >= dispH) {
225 edgeFlags |= MotionEvent.EDGE_BOTTOM;
226 }
227 }
228
229 if (currentMove != null) {
230 if (false) Log.i("InputDevice", "Adding batch x="
231 + scaled[MotionEvent.SAMPLE_X]
232 + " y=" + scaled[MotionEvent.SAMPLE_Y]
233 + " to " + currentMove);
234 currentMove.addBatch(curTime, scaled, metaState);
235 if (WindowManagerPolicy.WATCH_POINTER) {
236 Log.i("KeyInputQueue", "Updating: " + currentMove);
237 }
238 return null;
239 }
240
241 MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
242 curTimeNano, action, numPointers, scaled, metaState,
243 xPrecision, yPrecision, device.id, edgeFlags);
244 if (action == MotionEvent.ACTION_MOVE) {
245 currentMove = me;
246 }
247 return me;
248 }
249
250 MotionEvent generateRelMotion(InputDevice device, long curTime,
251 long curTimeNano, int orientation, int metaState) {
252
253 final float[] scaled = mReportData;
254
255 // For now we only support 1 pointer with relative motions.
256 scaled[MotionEvent.SAMPLE_X] = mCurData[MotionEvent.SAMPLE_X];
257 scaled[MotionEvent.SAMPLE_Y] = mCurData[MotionEvent.SAMPLE_Y];
258 scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f;
259 scaled[MotionEvent.SAMPLE_SIZE] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 int edgeFlags = 0;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700261
262 int action;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700263 if (mDown[0] != mLastDown[0]) {
264 mCurData[MotionEvent.SAMPLE_X] =
265 mCurData[MotionEvent.SAMPLE_Y] = 0;
266 mLastDown[0] = mDown[0];
267 if (mDown[0]) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700268 action = MotionEvent.ACTION_DOWN;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700269 mDownTime = curTime;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700270 } else {
271 action = MotionEvent.ACTION_UP;
272 }
273 currentMove = null;
274 } else {
275 action = MotionEvent.ACTION_MOVE;
276 }
277
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700278 scaled[MotionEvent.SAMPLE_X] *= xMoveScale;
279 scaled[MotionEvent.SAMPLE_Y] *= yMoveScale;
280 switch (orientation) {
281 case Surface.ROTATION_90: {
282 final float temp = scaled[MotionEvent.SAMPLE_X];
283 scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y];
284 scaled[MotionEvent.SAMPLE_Y] = -temp;
285 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 }
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700287 case Surface.ROTATION_180: {
288 scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X];
289 scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y];
290 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 }
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700292 case Surface.ROTATION_270: {
293 final float temp = scaled[MotionEvent.SAMPLE_X];
294 scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y];
295 scaled[MotionEvent.SAMPLE_Y] = temp;
296 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
298 }
299
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700300 if (currentMove != null) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700301 if (false) Log.i("InputDevice", "Adding batch x="
302 + scaled[MotionEvent.SAMPLE_X]
303 + " y=" + scaled[MotionEvent.SAMPLE_Y]
304 + " to " + currentMove);
305 currentMove.addBatch(curTime, scaled, metaState);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700306 if (WindowManagerPolicy.WATCH_POINTER) {
307 Log.i("KeyInputQueue", "Updating: " + currentMove);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700309 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700311
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700312 MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
313 curTimeNano, action, 1, scaled, metaState,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700314 xPrecision, yPrecision, device.id, edgeFlags);
315 if (action == MotionEvent.ACTION_MOVE) {
316 currentMove = me;
317 }
318 return me;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
320 }
321
322 static class AbsoluteInfo {
323 int minValue;
324 int maxValue;
325 int range;
326 int flat;
327 int fuzz;
328 };
329
330 InputDevice(int _id, int _classes, String _name,
331 AbsoluteInfo _absX, AbsoluteInfo _absY,
332 AbsoluteInfo _absPressure, AbsoluteInfo _absSize) {
333 id = _id;
334 classes = _classes;
335 name = _name;
336 absX = _absX;
337 absY = _absY;
338 absPressure = _absPressure;
339 absSize = _absSize;
340 }
341};