blob: 17e962540940880ed282bfc5c1a80c99d2a4ca15 [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.content.Context;
20import android.content.res.Configuration;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040021import android.os.Environment;
Michael Chan53071d62009-05-13 17:29:48 -070022import android.os.LatencyTimer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.os.PowerManager;
Michael Chan53071d62009-05-13 17:29:48 -070024import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.util.Log;
26import android.util.SparseArray;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040027import android.util.Xml;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.view.Display;
29import android.view.KeyEvent;
30import android.view.MotionEvent;
31import android.view.RawInputEvent;
32import android.view.Surface;
33import android.view.WindowManagerPolicy;
34
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040035import com.android.internal.util.XmlUtils;
36
37import org.xmlpull.v1.XmlPullParser;
38import org.xmlpull.v1.XmlPullParserException;
39
Dianne Hackborne3dd8842009-07-14 12:06:54 -070040import java.io.BufferedReader;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040041import java.io.File;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070042import java.io.FileInputStream;
43import java.io.FileNotFoundException;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040044import java.io.FileReader;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070045import java.io.IOException;
46import java.io.InputStreamReader;
47import java.util.ArrayList;
48
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049public abstract class KeyInputQueue {
50 static final String TAG = "KeyInputQueue";
51
Dianne Hackborne3dd8842009-07-14 12:06:54 -070052 static final boolean DEBUG_VIRTUAL_KEYS = false;
53
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040054 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
55
Dianne Hackborne3dd8842009-07-14 12:06:54 -070056 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
57 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
59 int mGlobalMetaState = 0;
60 boolean mHaveGlobalMetaState = false;
61
62 final QueuedEvent mFirst;
63 final QueuedEvent mLast;
64 QueuedEvent mCache;
65 int mCacheCount;
66
67 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070068 int mDisplayWidth;
69 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
71 int mOrientation = Surface.ROTATION_0;
72 int[] mKeyRotationMap = null;
73
Dianne Hackborne3dd8842009-07-14 12:06:54 -070074 VirtualKey mPressedVirtualKey = null;
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 PowerManager.WakeLock mWakeLock;
77
78 static final int[] KEY_90_MAP = new int[] {
79 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
80 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
81 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
82 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
83 };
84
85 static final int[] KEY_180_MAP = new int[] {
86 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
87 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
88 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
89 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
90 };
91
92 static final int[] KEY_270_MAP = new int[] {
93 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
94 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
95 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
96 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
97 };
98
99 public static final int FILTER_REMOVE = 0;
100 public static final int FILTER_KEEP = 1;
101 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -0700102
103 private static final boolean MEASURE_LATENCY = false;
104 private LatencyTimer lt;
105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 public interface FilterCallback {
107 int filterEvent(QueuedEvent ev);
108 }
109
110 static class QueuedEvent {
111 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700112 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 int flags; // From the raw event
114 int classType; // One of the class constants in InputEvent
115 Object event;
116 boolean inQueue;
117
118 void copyFrom(QueuedEvent that) {
119 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700120 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 this.flags = that.flags;
122 this.classType = that.classType;
123 this.event = that.event;
124 }
125
126 @Override
127 public String toString() {
128 return "QueuedEvent{"
129 + Integer.toHexString(System.identityHashCode(this))
130 + " " + event + "}";
131 }
132
133 // not copied
134 QueuedEvent prev;
135 QueuedEvent next;
136 }
137
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700138 /**
139 * A key that exists as a part of the touch-screen, outside of the normal
140 * display area of the screen.
141 */
142 static class VirtualKey {
143 int scancode;
144 int centerx;
145 int centery;
146 int width;
147 int height;
148
149 int hitLeft;
150 int hitTop;
151 int hitRight;
152 int hitBottom;
153
154 InputDevice lastDevice;
155 int lastKeycode;
156
157 boolean checkHit(int x, int y) {
158 return (x >= hitLeft && x <= hitRight
159 && y >= hitTop && y <= hitBottom);
160 }
161
162 void computeHitRect(InputDevice dev, int dw, int dh) {
163 if (dev == lastDevice) {
164 return;
165 }
166
167 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
168 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
169
170 lastDevice = dev;
171
172 int minx = dev.absX.minValue;
173 int maxx = dev.absX.maxValue;
174
175 int halfw = width/2;
176 int left = centerx - halfw;
177 int right = centerx + halfw;
178 hitLeft = minx + ((left*maxx-minx)/dw);
179 hitRight = minx + ((right*maxx-minx)/dw);
180
181 int miny = dev.absY.minValue;
182 int maxy = dev.absY.maxValue;
183
184 int halfh = height/2;
185 int top = centery - halfh;
186 int bottom = centery + halfh;
187 hitTop = miny + ((top*maxy-miny)/dh);
188 hitBottom = miny + ((bottom*maxy-miny)/dh);
189 }
190 }
Michael Chan53071d62009-05-13 17:29:48 -0700191
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400192 private void readVirtualKeys() {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700193 try {
194 FileInputStream fis = new FileInputStream(
195 "/sys/board_properties/virtualkeys.synaptics-rmi-touchscreen");
196 InputStreamReader isr = new InputStreamReader(fis);
197 BufferedReader br = new BufferedReader(isr);
198 String str = br.readLine();
199 if (str != null) {
200 String[] it = str.split(":");
201 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
202 final int N = it.length-6;
203 for (int i=0; i<=N; i+=6) {
204 if (!"0x01".equals(it[i])) {
205 Log.w(TAG, "Unknown virtual key type at elem #" + i
206 + ": " + it[i]);
207 continue;
208 }
209 try {
210 VirtualKey sb = new VirtualKey();
211 sb.scancode = Integer.parseInt(it[i+1]);
212 sb.centerx = Integer.parseInt(it[i+2]);
213 sb.centery = Integer.parseInt(it[i+3]);
214 sb.width = Integer.parseInt(it[i+4]);
215 sb.height = Integer.parseInt(it[i+5]);
216 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
217 + sb.scancode + ": center=" + sb.centerx + ","
218 + sb.centery + " size=" + sb.width + "x"
219 + sb.height);
220 mVirtualKeys.add(sb);
221 } catch (NumberFormatException e) {
222 Log.w(TAG, "Bad number at region " + i + " in: "
223 + str, e);
224 }
225 }
226 }
227 br.close();
228 } catch (FileNotFoundException e) {
229 Log.i(TAG, "No virtual keys found");
230 } catch (IOException e) {
231 Log.w(TAG, "Error reading virtual keys", e);
232 }
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400233 }
234
235 private void readExcludedDevices() {
236 // Read partner-provided list of excluded input devices
237 XmlPullParser parser = null;
238 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
239 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
240 FileReader confreader = null;
241 try {
242 confreader = new FileReader(confFile);
243 parser = Xml.newPullParser();
244 parser.setInput(confreader);
245 XmlUtils.beginDocument(parser, "devices");
246
247 while (true) {
248 XmlUtils.nextElement(parser);
249 if (!"device".equals(parser.getName())) {
250 break;
251 }
252 String name = parser.getAttributeValue(null, "name");
253 if (name != null) {
254 Log.d(TAG, "addExcludedDevice " + name);
255 addExcludedDevice(name);
256 }
257 }
258 } catch (FileNotFoundException e) {
259 // It's ok if the file does not exist.
260 } catch (Exception e) {
261 Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
262 } finally {
263 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
264 }
265 }
266
267 KeyInputQueue(Context context) {
268 if (MEASURE_LATENCY) {
269 lt = new LatencyTimer(100, 1000);
270 }
271
272 readVirtualKeys();
273 readExcludedDevices();
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 PowerManager pm = (PowerManager)context.getSystemService(
276 Context.POWER_SERVICE);
277 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
278 "KeyInputQueue");
279 mWakeLock.setReferenceCounted(false);
280
281 mFirst = new QueuedEvent();
282 mLast = new QueuedEvent();
283 mFirst.next = mLast;
284 mLast.prev = mFirst;
285
286 mThread.start();
287 }
288
289 public void setDisplay(Display display) {
290 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700291
292 // We assume at this point that the display dimensions reflect the
293 // natural, unrotated display. We will perform hit tests for soft
294 // buttons based on that display.
295 mDisplayWidth = display.getWidth();
296 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
298
299 public void getInputConfiguration(Configuration config) {
300 synchronized (mFirst) {
301 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
302 config.keyboard = Configuration.KEYBOARD_NOKEYS;
303 config.navigation = Configuration.NAVIGATION_NONAV;
304
305 final int N = mDevices.size();
306 for (int i=0; i<N; i++) {
307 InputDevice d = mDevices.valueAt(i);
308 if (d != null) {
309 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
310 config.touchscreen
311 = Configuration.TOUCHSCREEN_FINGER;
312 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
313 }
314 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
315 config.keyboard
316 = Configuration.KEYBOARD_QWERTY;
317 //Log.i("foo", "***** HAVE QWERTY!");
318 }
319 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
320 config.navigation
321 = Configuration.NAVIGATION_TRACKBALL;
322 //Log.i("foo", "***** HAVE TRACKBALL!");
323 }
324 }
325 }
326 }
327 }
328
329 public static native String getDeviceName(int deviceId);
330 public static native int getDeviceClasses(int deviceId);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400331 public static native void addExcludedDevice(String deviceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 public static native boolean getAbsoluteInfo(int deviceId, int axis,
333 InputDevice.AbsoluteInfo outInfo);
334 public static native int getSwitchState(int sw);
335 public static native int getSwitchState(int deviceId, int sw);
336 public static native int getScancodeState(int sw);
337 public static native int getScancodeState(int deviceId, int sw);
338 public static native int getKeycodeState(int sw);
339 public static native int getKeycodeState(int deviceId, int sw);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700340 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
342
343 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
344 long eventTime, boolean down, int keycode, int repeatCount,
345 int scancode, int flags) {
346 return new KeyEvent(
347 downTime, eventTime,
348 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
349 keycode, repeatCount,
350 device != null ? device.mMetaKeysState : 0,
351 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700352 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
354
355 Thread mThread = new Thread("InputDeviceReader") {
356 public void run() {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400357 Log.d(TAG, "InputDeviceReader.run()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 android.os.Process.setThreadPriority(
359 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
360
361 try {
362 RawInputEvent ev = new RawInputEvent();
363 while (true) {
364 InputDevice di;
365
366 // block, doesn't release the monitor
367 readEvent(ev);
368
369 boolean send = false;
370 boolean configChanged = false;
371
372 if (false) {
373 Log.i(TAG, "Input event: dev=0x"
374 + Integer.toHexString(ev.deviceId)
375 + " type=0x" + Integer.toHexString(ev.type)
376 + " scancode=" + ev.scancode
377 + " keycode=" + ev.keycode
378 + " value=" + ev.value);
379 }
380
381 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
382 synchronized (mFirst) {
383 di = newInputDevice(ev.deviceId);
384 mDevices.put(ev.deviceId, di);
385 configChanged = true;
386 }
387 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
388 synchronized (mFirst) {
389 Log.i(TAG, "Device removed: id=0x"
390 + Integer.toHexString(ev.deviceId));
391 di = mDevices.get(ev.deviceId);
392 if (di != null) {
393 mDevices.delete(ev.deviceId);
394 configChanged = true;
395 } else {
396 Log.w(TAG, "Bad device id: " + ev.deviceId);
397 }
398 }
399 } else {
400 di = getInputDevice(ev.deviceId);
401
402 // first crack at it
403 send = preprocessEvent(di, ev);
404
405 if (ev.type == RawInputEvent.EV_KEY) {
406 di.mMetaKeysState = makeMetaState(ev.keycode,
407 ev.value != 0, di.mMetaKeysState);
408 mHaveGlobalMetaState = false;
409 }
410 }
411
412 if (di == null) {
413 continue;
414 }
415
416 if (configChanged) {
417 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700418 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
420 null);
421 }
422 }
423
424 if (!send) {
425 continue;
426 }
427
428 synchronized (mFirst) {
429 // NOTE: The event timebase absolutely must be the same
430 // timebase as SystemClock.uptimeMillis().
431 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
432 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700433 final long curTimeNano = System.nanoTime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
435
436 final int classes = di.classes;
437 final int type = ev.type;
438 final int scancode = ev.scancode;
439 send = false;
440
441 // Is it a key event?
442 if (type == RawInputEvent.EV_KEY &&
443 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
444 (scancode < RawInputEvent.BTN_FIRST ||
445 scancode > RawInputEvent.BTN_LAST)) {
446 boolean down;
447 if (ev.value != 0) {
448 down = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700449 di.mKeyDownTime = curTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 } else {
451 down = false;
452 }
453 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700454 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700456 newKeyEvent(di, di.mKeyDownTime, curTime, down,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 keycode, 0, scancode,
458 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
459 ? KeyEvent.FLAG_WOKE_HERE : 0));
460 } else if (ev.type == RawInputEvent.EV_KEY) {
461 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
462 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
463 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700464 di.mAbs.mDown[0] = ev.value != 0;
465 } else if (ev.scancode == RawInputEvent.BTN_2 &&
466 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
467 di.mAbs.changed = true;
468 di.mAbs.mDown[1] = ev.value != 0;
469 } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
471 di.mRel.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700472 di.mRel.mDown[0] = ev.value != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 send = true;
474 }
475
476 } else if (ev.type == RawInputEvent.EV_ABS &&
477 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700478 // Finger 1
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 if (ev.scancode == RawInputEvent.ABS_X) {
480 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700481 di.mAbs.mCurData[MotionEvent.SAMPLE_X] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 } else if (ev.scancode == RawInputEvent.ABS_Y) {
483 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700484 di.mAbs.mCurData[MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
486 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700487 di.mAbs.mCurData[MotionEvent.SAMPLE_PRESSURE] = ev.value;
488 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
489 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
491 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700492 di.mAbs.mCurData[MotionEvent.SAMPLE_SIZE] = ev.value;
493 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
494 + MotionEvent.SAMPLE_SIZE] = ev.value;
495
496 // Finger 2
497 } else if (ev.scancode == RawInputEvent.ABS_HAT0X) {
498 di.mAbs.changed = true;
499 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
500 + MotionEvent.SAMPLE_X] = ev.value;
501 } else if (ev.scancode == RawInputEvent.ABS_HAT0Y) {
502 di.mAbs.changed = true;
503 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
504 + MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 }
506
507 } else if (ev.type == RawInputEvent.EV_REL &&
508 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
509 // Add this relative movement into our totals.
510 if (ev.scancode == RawInputEvent.REL_X) {
511 di.mRel.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700512 di.mRel.mCurData[MotionEvent.SAMPLE_X] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 } else if (ev.scancode == RawInputEvent.REL_Y) {
514 di.mRel.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700515 di.mRel.mCurData[MotionEvent.SAMPLE_Y] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
517 }
518
519 if (send || ev.type == RawInputEvent.EV_SYN) {
520 if (mDisplay != null) {
521 if (!mHaveGlobalMetaState) {
522 computeGlobalMetaStateLocked();
523 }
524
525 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700526
527 InputDevice.MotionState ms = di.mAbs;
528 if (ms.changed) {
529 ms.changed = false;
530
531 boolean doMotion = true;
532
533 // Look for virtual buttons.
534 VirtualKey vk = mPressedVirtualKey;
535 if (vk != null) {
536 doMotion = false;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700537 if (!ms.mDown[0]) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700538 mPressedVirtualKey = null;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700539 ms.mLastDown[0] = ms.mDown[0];
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700540 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
541 "Generate key up for: " + vk.scancode);
542 addLocked(di, curTimeNano, ev.flags,
543 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700544 newKeyEvent(di, di.mKeyDownTime,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700545 curTime, false,
546 vk.lastKeycode,
547 0, vk.scancode, 0));
548 }
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700549 } else if (ms.mDown[0] && !ms.mLastDown[0]) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700550 vk = findSoftButton(di);
551 if (vk != null) {
552 doMotion = false;
553 mPressedVirtualKey = vk;
554 vk.lastKeycode = scancodeToKeycode(
555 di.id, vk.scancode);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700556 ms.mLastDown[0] = ms.mDown[0];
557 di.mKeyDownTime = curTime;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700558 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
559 "Generate key down for: " + vk.scancode
560 + " (keycode=" + vk.lastKeycode + ")");
561 addLocked(di, curTimeNano, ev.flags,
562 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700563 newKeyEvent(di, di.mKeyDownTime,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700564 curTime, true,
565 vk.lastKeycode, 0,
566 vk.scancode, 0));
567 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700569
570 if (doMotion) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700571 // XXX Need to be able to generate
572 // multiple events here, for example
573 // if two fingers change up/down state
574 // at the same time.
575 me = ms.generateAbsMotion(di, curTime,
576 curTimeNano, mDisplay,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700577 mOrientation, mGlobalMetaState);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700578 if (false) Log.v(TAG, "Absolute: x="
579 + di.mAbs.mCurData[MotionEvent.SAMPLE_X]
580 + " y="
581 + di.mAbs.mCurData[MotionEvent.SAMPLE_Y]
582 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700583 if (me != null) {
584 if (WindowManagerPolicy.WATCH_POINTER) {
585 Log.i(TAG, "Enqueueing: " + me);
586 }
587 addLocked(di, curTimeNano, ev.flags,
588 RawInputEvent.CLASS_TOUCHSCREEN, me);
589 }
590 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700592
593 ms = di.mRel;
594 if (ms.changed) {
595 ms.changed = false;
596
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700597 me = ms.generateRelMotion(di, curTime,
598 curTimeNano,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700599 mOrientation, mGlobalMetaState);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700600 if (false) Log.v(TAG, "Relative: x="
601 + di.mRel.mCurData[MotionEvent.SAMPLE_X]
602 + " y="
603 + di.mRel.mCurData[MotionEvent.SAMPLE_Y]
604 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700605 if (me != null) {
606 addLocked(di, curTimeNano, ev.flags,
607 RawInputEvent.CLASS_TRACKBALL, me);
608 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 }
610 }
611 }
612 }
613 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700614
615 } catch (RuntimeException exc) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 Log.e(TAG, "InputReaderThread uncaught exception", exc);
617 }
618 }
619 };
620
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700621 private VirtualKey findSoftButton(InputDevice dev) {
622 final int N = mVirtualKeys.size();
623 if (N <= 0) {
624 return null;
625 }
626
627 final InputDevice.AbsoluteInfo absx = dev.absX;
628 final InputDevice.AbsoluteInfo absy = dev.absY;
629 final InputDevice.MotionState absm = dev.mAbs;
630 if (absx == null || absy == null || absm == null) {
631 return null;
632 }
633
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700634 if (absm.mCurData[MotionEvent.SAMPLE_X] >= absx.minValue
635 && absm.mCurData[MotionEvent.SAMPLE_X] <= absx.maxValue
636 && absm.mCurData[MotionEvent.SAMPLE_Y] >= absy.minValue
637 && absm.mCurData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
638 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input ("
639 + absm.mCurData[MotionEvent.SAMPLE_X]
640 + "," + absm.mCurData[MotionEvent.SAMPLE_Y]
641 + ") inside of display");
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700642 return null;
643 }
644
645 for (int i=0; i<N; i++) {
646 VirtualKey sb = mVirtualKeys.get(i);
647 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700648 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test ("
649 + absm.mCurData[MotionEvent.SAMPLE_X] + ","
650 + absm.mCurData[MotionEvent.SAMPLE_Y] + ") in code "
651 + sb.scancode + " - (" + sb.hitLeft
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700652 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
653 + sb.hitBottom + ")");
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700654 if (sb.checkHit(absm.mCurData[MotionEvent.SAMPLE_X],
655 absm.mCurData[MotionEvent.SAMPLE_Y])) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700656 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
657 return sb;
658 }
659 }
660
661 return null;
662 }
663
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 /**
665 * Returns a new meta state for the given keys and old state.
666 */
667 private static final int makeMetaState(int keycode, boolean down, int old) {
668 int mask;
669 switch (keycode) {
670 case KeyEvent.KEYCODE_ALT_LEFT:
671 mask = KeyEvent.META_ALT_LEFT_ON;
672 break;
673 case KeyEvent.KEYCODE_ALT_RIGHT:
674 mask = KeyEvent.META_ALT_RIGHT_ON;
675 break;
676 case KeyEvent.KEYCODE_SHIFT_LEFT:
677 mask = KeyEvent.META_SHIFT_LEFT_ON;
678 break;
679 case KeyEvent.KEYCODE_SHIFT_RIGHT:
680 mask = KeyEvent.META_SHIFT_RIGHT_ON;
681 break;
682 case KeyEvent.KEYCODE_SYM:
683 mask = KeyEvent.META_SYM_ON;
684 break;
685 default:
686 return old;
687 }
688 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
689 & (down ? (old | mask) : (old & ~mask));
690 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
691 result |= KeyEvent.META_ALT_ON;
692 }
693 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
694 result |= KeyEvent.META_SHIFT_ON;
695 }
696 return result;
697 }
698
699 private void computeGlobalMetaStateLocked() {
700 int i = mDevices.size();
701 mGlobalMetaState = 0;
702 while ((--i) >= 0) {
703 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
704 }
705 mHaveGlobalMetaState = true;
706 }
707
708 /*
709 * Return true if you want the event to get passed on to the
710 * rest of the system, and false if you've handled it and want
711 * it dropped.
712 */
713 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
714
715 InputDevice getInputDevice(int deviceId) {
716 synchronized (mFirst) {
717 return getInputDeviceLocked(deviceId);
718 }
719 }
720
721 private InputDevice getInputDeviceLocked(int deviceId) {
722 return mDevices.get(deviceId);
723 }
724
725 public void setOrientation(int orientation) {
726 synchronized(mFirst) {
727 mOrientation = orientation;
728 switch (orientation) {
729 case Surface.ROTATION_90:
730 mKeyRotationMap = KEY_90_MAP;
731 break;
732 case Surface.ROTATION_180:
733 mKeyRotationMap = KEY_180_MAP;
734 break;
735 case Surface.ROTATION_270:
736 mKeyRotationMap = KEY_270_MAP;
737 break;
738 default:
739 mKeyRotationMap = null;
740 break;
741 }
742 }
743 }
744
745 public int rotateKeyCode(int keyCode) {
746 synchronized(mFirst) {
747 return rotateKeyCodeLocked(keyCode);
748 }
749 }
750
751 private int rotateKeyCodeLocked(int keyCode) {
752 int[] map = mKeyRotationMap;
753 if (map != null) {
754 final int N = map.length;
755 for (int i=0; i<N; i+=2) {
756 if (map[i] == keyCode) {
757 return map[i+1];
758 }
759 }
760 }
761 return keyCode;
762 }
763
764 boolean hasEvents() {
765 synchronized (mFirst) {
766 return mFirst.next != mLast;
767 }
768 }
769
770 /*
771 * returns true if we returned an event, and false if we timed out
772 */
773 QueuedEvent getEvent(long timeoutMS) {
774 long begin = SystemClock.uptimeMillis();
775 final long end = begin+timeoutMS;
776 long now = begin;
777 synchronized (mFirst) {
778 while (mFirst.next == mLast && end > now) {
779 try {
780 mWakeLock.release();
781 mFirst.wait(end-now);
782 }
783 catch (InterruptedException e) {
784 }
785 now = SystemClock.uptimeMillis();
786 if (begin > now) {
787 begin = now;
788 }
789 }
790 if (mFirst.next == mLast) {
791 return null;
792 }
793 QueuedEvent p = mFirst.next;
794 mFirst.next = p.next;
795 mFirst.next.prev = mFirst;
796 p.inQueue = false;
797 return p;
798 }
799 }
800
801 void recycleEvent(QueuedEvent ev) {
802 synchronized (mFirst) {
803 //Log.i(TAG, "Recycle event: " + ev);
804 if (ev.event == ev.inputDevice.mAbs.currentMove) {
805 ev.inputDevice.mAbs.currentMove = null;
806 }
807 if (ev.event == ev.inputDevice.mRel.currentMove) {
808 if (false) Log.i(TAG, "Detach rel " + ev.event);
809 ev.inputDevice.mRel.currentMove = null;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700810 ev.inputDevice.mRel.mCurData[MotionEvent.SAMPLE_X] = 0;
811 ev.inputDevice.mRel.mCurData[MotionEvent.SAMPLE_Y] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 }
813 recycleLocked(ev);
814 }
815 }
816
817 void filterQueue(FilterCallback cb) {
818 synchronized (mFirst) {
819 QueuedEvent cur = mLast.prev;
820 while (cur.prev != null) {
821 switch (cb.filterEvent(cur)) {
822 case FILTER_REMOVE:
823 cur.prev.next = cur.next;
824 cur.next.prev = cur.prev;
825 break;
826 case FILTER_ABORT:
827 return;
828 }
829 cur = cur.prev;
830 }
831 }
832 }
833
Michael Chan53071d62009-05-13 17:29:48 -0700834 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 int flags, int classType, Object event) {
836 QueuedEvent ev;
837 if (mCacheCount == 0) {
838 ev = new QueuedEvent();
839 } else {
840 ev = mCache;
841 ev.inQueue = false;
842 mCache = ev.next;
843 mCacheCount--;
844 }
845 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -0700846 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 ev.flags = flags;
848 ev.classType = classType;
849 ev.event = event;
850 return ev;
851 }
852
853 private void recycleLocked(QueuedEvent ev) {
854 if (ev.inQueue) {
855 throw new RuntimeException("Event already in queue!");
856 }
857 if (mCacheCount < 10) {
858 mCacheCount++;
859 ev.next = mCache;
860 mCache = ev;
861 ev.inQueue = true;
862 }
863 }
864
Michael Chan53071d62009-05-13 17:29:48 -0700865 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 int classType, Object event) {
867 boolean poke = mFirst.next == mLast;
868
Michael Chan53071d62009-05-13 17:29:48 -0700869 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -0700871 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 p = p.prev;
873 }
874
875 ev.next = p.next;
876 ev.prev = p;
877 p.next = ev;
878 ev.next.prev = ev;
879 ev.inQueue = true;
880
881 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -0700882 long time;
883 if (MEASURE_LATENCY) {
884 time = System.nanoTime();
885 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 mFirst.notify();
887 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -0700888 if (MEASURE_LATENCY) {
889 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
890 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 }
892 }
893
894 private InputDevice newInputDevice(int deviceId) {
895 int classes = getDeviceClasses(deviceId);
896 String name = getDeviceName(deviceId);
897 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
898 + ", name=" + name
899 + ", classes=" + Integer.toHexString(classes));
900 InputDevice.AbsoluteInfo absX;
901 InputDevice.AbsoluteInfo absY;
902 InputDevice.AbsoluteInfo absPressure;
903 InputDevice.AbsoluteInfo absSize;
904 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
905 absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
906 absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
907 absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
908 absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
909 } else {
910 absX = null;
911 absY = null;
912 absPressure = null;
913 absSize = null;
914 }
915
916 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
917 }
918
919 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
920 String name) {
921 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
922 if (getAbsoluteInfo(id, channel, info)
923 && info.minValue != info.maxValue) {
924 Log.i(TAG, " " + name + ": min=" + info.minValue
925 + " max=" + info.maxValue
926 + " flat=" + info.flat
927 + " fuzz=" + info.fuzz);
928 info.range = info.maxValue-info.minValue;
929 return info;
930 }
931 Log.i(TAG, " " + name + ": unknown values");
932 return null;
933 }
934 private static native boolean readEvent(RawInputEvent outEvent);
935}