blob: 35ed448603c32bda76df4142e351b78d85f12616 [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;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040038
Dianne Hackborne3dd8842009-07-14 12:06:54 -070039import java.io.BufferedReader;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040040import java.io.File;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070041import java.io.FileInputStream;
42import java.io.FileNotFoundException;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040043import java.io.FileReader;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070044import java.io.IOException;
45import java.io.InputStreamReader;
46import java.util.ArrayList;
47
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048public abstract class KeyInputQueue {
49 static final String TAG = "KeyInputQueue";
50
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -070051 static final boolean DEBUG = false;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070052 static final boolean DEBUG_VIRTUAL_KEYS = false;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -070053 static final boolean DEBUG_POINTERS = false;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070054
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040055 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
56
Dianne Hackborne3dd8842009-07-14 12:06:54 -070057 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
Dianne Hackborna8f60182009-09-01 19:01:50 -070058 final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
Dianne Hackborne3dd8842009-07-14 12:06:54 -070059 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
Dianne Hackbornddca3ee2009-07-23 19:01:31 -070060 final HapticFeedbackCallback mHapticFeedbackCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
62 int mGlobalMetaState = 0;
63 boolean mHaveGlobalMetaState = false;
64
65 final QueuedEvent mFirst;
66 final QueuedEvent mLast;
67 QueuedEvent mCache;
68 int mCacheCount;
69
70 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070071 int mDisplayWidth;
72 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74 int mOrientation = Surface.ROTATION_0;
75 int[] mKeyRotationMap = null;
76
Dianne Hackborne3dd8842009-07-14 12:06:54 -070077 VirtualKey mPressedVirtualKey = null;
78
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 PowerManager.WakeLock mWakeLock;
80
81 static final int[] KEY_90_MAP = new int[] {
82 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
83 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
84 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
85 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
86 };
87
88 static final int[] KEY_180_MAP = new int[] {
89 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
90 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
91 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
92 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
93 };
94
95 static final int[] KEY_270_MAP = new int[] {
96 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
97 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
98 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
99 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
100 };
101
102 public static final int FILTER_REMOVE = 0;
103 public static final int FILTER_KEEP = 1;
104 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -0700105
106 private static final boolean MEASURE_LATENCY = false;
107 private LatencyTimer lt;
108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 public interface FilterCallback {
110 int filterEvent(QueuedEvent ev);
111 }
112
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700113 public interface HapticFeedbackCallback {
114 void virtualKeyFeedback(KeyEvent event);
115 }
116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 static class QueuedEvent {
118 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700119 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 int flags; // From the raw event
121 int classType; // One of the class constants in InputEvent
122 Object event;
123 boolean inQueue;
124
125 void copyFrom(QueuedEvent that) {
126 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700127 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 this.flags = that.flags;
129 this.classType = that.classType;
130 this.event = that.event;
131 }
132
133 @Override
134 public String toString() {
135 return "QueuedEvent{"
136 + Integer.toHexString(System.identityHashCode(this))
137 + " " + event + "}";
138 }
139
140 // not copied
141 QueuedEvent prev;
142 QueuedEvent next;
143 }
144
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700145 /**
146 * A key that exists as a part of the touch-screen, outside of the normal
147 * display area of the screen.
148 */
149 static class VirtualKey {
150 int scancode;
151 int centerx;
152 int centery;
153 int width;
154 int height;
155
156 int hitLeft;
157 int hitTop;
158 int hitRight;
159 int hitBottom;
160
161 InputDevice lastDevice;
162 int lastKeycode;
163
164 boolean checkHit(int x, int y) {
165 return (x >= hitLeft && x <= hitRight
166 && y >= hitTop && y <= hitBottom);
167 }
168
169 void computeHitRect(InputDevice dev, int dw, int dh) {
170 if (dev == lastDevice) {
171 return;
172 }
173
174 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
175 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
176
177 lastDevice = dev;
178
179 int minx = dev.absX.minValue;
180 int maxx = dev.absX.maxValue;
181
182 int halfw = width/2;
183 int left = centerx - halfw;
184 int right = centerx + halfw;
185 hitLeft = minx + ((left*maxx-minx)/dw);
186 hitRight = minx + ((right*maxx-minx)/dw);
187
188 int miny = dev.absY.minValue;
189 int maxy = dev.absY.maxValue;
190
191 int halfh = height/2;
192 int top = centery - halfh;
193 int bottom = centery + halfh;
194 hitTop = miny + ((top*maxy-miny)/dh);
195 hitBottom = miny + ((bottom*maxy-miny)/dh);
196 }
197 }
Michael Chan53071d62009-05-13 17:29:48 -0700198
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700199 private void readVirtualKeys(String deviceName) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700200 try {
201 FileInputStream fis = new FileInputStream(
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700202 "/sys/board_properties/virtualkeys." + deviceName);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700203 InputStreamReader isr = new InputStreamReader(fis);
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700204 BufferedReader br = new BufferedReader(isr, 2048);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700205 String str = br.readLine();
206 if (str != null) {
207 String[] it = str.split(":");
208 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
209 final int N = it.length-6;
210 for (int i=0; i<=N; i+=6) {
211 if (!"0x01".equals(it[i])) {
212 Log.w(TAG, "Unknown virtual key type at elem #" + i
213 + ": " + it[i]);
214 continue;
215 }
216 try {
217 VirtualKey sb = new VirtualKey();
218 sb.scancode = Integer.parseInt(it[i+1]);
219 sb.centerx = Integer.parseInt(it[i+2]);
220 sb.centery = Integer.parseInt(it[i+3]);
221 sb.width = Integer.parseInt(it[i+4]);
222 sb.height = Integer.parseInt(it[i+5]);
223 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
224 + sb.scancode + ": center=" + sb.centerx + ","
225 + sb.centery + " size=" + sb.width + "x"
226 + sb.height);
227 mVirtualKeys.add(sb);
228 } catch (NumberFormatException e) {
229 Log.w(TAG, "Bad number at region " + i + " in: "
230 + str, e);
231 }
232 }
233 }
234 br.close();
235 } catch (FileNotFoundException e) {
236 Log.i(TAG, "No virtual keys found");
237 } catch (IOException e) {
238 Log.w(TAG, "Error reading virtual keys", e);
239 }
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400240 }
241
242 private void readExcludedDevices() {
243 // Read partner-provided list of excluded input devices
244 XmlPullParser parser = null;
245 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
246 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
247 FileReader confreader = null;
248 try {
249 confreader = new FileReader(confFile);
250 parser = Xml.newPullParser();
251 parser.setInput(confreader);
252 XmlUtils.beginDocument(parser, "devices");
253
254 while (true) {
255 XmlUtils.nextElement(parser);
256 if (!"device".equals(parser.getName())) {
257 break;
258 }
259 String name = parser.getAttributeValue(null, "name");
260 if (name != null) {
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700261 if (DEBUG) Log.v(TAG, "addExcludedDevice " + name);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400262 addExcludedDevice(name);
263 }
264 }
265 } catch (FileNotFoundException e) {
266 // It's ok if the file does not exist.
267 } catch (Exception e) {
268 Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
269 } finally {
270 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
271 }
272 }
273
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700274 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400275 if (MEASURE_LATENCY) {
276 lt = new LatencyTimer(100, 1000);
277 }
278
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700279 mHapticFeedbackCallback = hapticFeedbackCallback;
280
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400281 readExcludedDevices();
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700282
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 PowerManager pm = (PowerManager)context.getSystemService(
284 Context.POWER_SERVICE);
285 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
286 "KeyInputQueue");
287 mWakeLock.setReferenceCounted(false);
288
289 mFirst = new QueuedEvent();
290 mLast = new QueuedEvent();
291 mFirst.next = mLast;
292 mLast.prev = mFirst;
293
294 mThread.start();
295 }
296
297 public void setDisplay(Display display) {
298 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700299
300 // We assume at this point that the display dimensions reflect the
301 // natural, unrotated display. We will perform hit tests for soft
302 // buttons based on that display.
303 mDisplayWidth = display.getWidth();
304 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 }
306
307 public void getInputConfiguration(Configuration config) {
308 synchronized (mFirst) {
309 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
310 config.keyboard = Configuration.KEYBOARD_NOKEYS;
311 config.navigation = Configuration.NAVIGATION_NONAV;
312
313 final int N = mDevices.size();
314 for (int i=0; i<N; i++) {
315 InputDevice d = mDevices.valueAt(i);
316 if (d != null) {
317 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
318 config.touchscreen
319 = Configuration.TOUCHSCREEN_FINGER;
320 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
321 }
322 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
323 config.keyboard
324 = Configuration.KEYBOARD_QWERTY;
325 //Log.i("foo", "***** HAVE QWERTY!");
326 }
327 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
328 config.navigation
329 = Configuration.NAVIGATION_TRACKBALL;
330 //Log.i("foo", "***** HAVE TRACKBALL!");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700331 } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
332 config.navigation
333 = Configuration.NAVIGATION_DPAD;
334 //Log.i("foo", "***** HAVE DPAD!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 }
336 }
337 }
338 }
339 }
340
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700341 public int getScancodeState(int code) {
342 synchronized (mFirst) {
343 VirtualKey vk = mPressedVirtualKey;
344 if (vk != null) {
345 if (vk.scancode == code) {
346 return 2;
347 }
348 }
349 return nativeGetScancodeState(code);
350 }
351 }
352
353 public int getScancodeState(int deviceId, int code) {
354 synchronized (mFirst) {
355 VirtualKey vk = mPressedVirtualKey;
356 if (vk != null) {
357 if (vk.scancode == code) {
358 return 2;
359 }
360 }
361 return nativeGetScancodeState(deviceId, code);
362 }
363 }
364
365 public int getKeycodeState(int code) {
366 synchronized (mFirst) {
367 VirtualKey vk = mPressedVirtualKey;
368 if (vk != null) {
369 if (vk.lastKeycode == code) {
370 return 2;
371 }
372 }
373 return nativeGetKeycodeState(code);
374 }
375 }
376
377 public int getKeycodeState(int deviceId, int code) {
378 synchronized (mFirst) {
379 VirtualKey vk = mPressedVirtualKey;
380 if (vk != null) {
381 if (vk.lastKeycode == code) {
382 return 2;
383 }
384 }
385 return nativeGetKeycodeState(deviceId, code);
386 }
387 }
388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 public static native String getDeviceName(int deviceId);
390 public static native int getDeviceClasses(int deviceId);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400391 public static native void addExcludedDevice(String deviceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 public static native boolean getAbsoluteInfo(int deviceId, int axis,
393 InputDevice.AbsoluteInfo outInfo);
394 public static native int getSwitchState(int sw);
395 public static native int getSwitchState(int deviceId, int sw);
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700396 public static native int nativeGetScancodeState(int code);
397 public static native int nativeGetScancodeState(int deviceId, int code);
398 public static native int nativeGetKeycodeState(int code);
399 public static native int nativeGetKeycodeState(int deviceId, int code);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700400 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
402
403 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
404 long eventTime, boolean down, int keycode, int repeatCount,
405 int scancode, int flags) {
406 return new KeyEvent(
407 downTime, eventTime,
408 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
409 keycode, repeatCount,
410 device != null ? device.mMetaKeysState : 0,
411 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700412 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 }
414
415 Thread mThread = new Thread("InputDeviceReader") {
416 public void run() {
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700417 if (DEBUG) Log.v(TAG, "InputDeviceReader.run()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 android.os.Process.setThreadPriority(
419 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
420
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700421 RawInputEvent ev = new RawInputEvent();
422 while (true) {
423 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 InputDevice di;
425
426 // block, doesn't release the monitor
427 readEvent(ev);
428
429 boolean send = false;
430 boolean configChanged = false;
431
432 if (false) {
433 Log.i(TAG, "Input event: dev=0x"
434 + Integer.toHexString(ev.deviceId)
435 + " type=0x" + Integer.toHexString(ev.type)
436 + " scancode=" + ev.scancode
437 + " keycode=" + ev.keycode
438 + " value=" + ev.value);
439 }
440
441 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
442 synchronized (mFirst) {
443 di = newInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700444 if (di.classes != 0) {
445 // If this device is some kind of input class,
446 // we care about it.
447 mDevices.put(ev.deviceId, di);
448 if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
449 readVirtualKeys(di.name);
450 }
451 // The configuration may have changed because
452 // of this device.
453 configChanged = true;
454 } else {
455 // We won't do anything with this device.
456 mIgnoredDevices.put(ev.deviceId, di);
457 Log.i(TAG, "Ignoring non-input device: id=0x"
458 + Integer.toHexString(di.id)
459 + ", name=" + di.name);
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 }
462 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
463 synchronized (mFirst) {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700464 if (false) {
465 Log.i(TAG, "Device removed: id=0x"
466 + Integer.toHexString(ev.deviceId));
467 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 di = mDevices.get(ev.deviceId);
469 if (di != null) {
470 mDevices.delete(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700471 // The configuration may have changed because
472 // of this device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 configChanged = true;
Dianne Hackborna8f60182009-09-01 19:01:50 -0700474 } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
475 mIgnoredDevices.remove(ev.deviceId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 } else {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700477 Log.w(TAG, "Removing bad device id: "
478 + Integer.toHexString(ev.deviceId));
479 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 }
481 }
482 } else {
483 di = getInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700484 if (di == null) {
485 // This may be some junk from an ignored device.
486 continue;
487 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488
489 // first crack at it
490 send = preprocessEvent(di, ev);
491
492 if (ev.type == RawInputEvent.EV_KEY) {
493 di.mMetaKeysState = makeMetaState(ev.keycode,
494 ev.value != 0, di.mMetaKeysState);
495 mHaveGlobalMetaState = false;
496 }
497 }
498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 if (configChanged) {
500 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700501 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
503 null);
504 }
505 }
506
507 if (!send) {
508 continue;
509 }
510
511 synchronized (mFirst) {
512 // NOTE: The event timebase absolutely must be the same
513 // timebase as SystemClock.uptimeMillis().
514 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
515 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700516 final long curTimeNano = System.nanoTime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
518
519 final int classes = di.classes;
520 final int type = ev.type;
521 final int scancode = ev.scancode;
522 send = false;
523
524 // Is it a key event?
525 if (type == RawInputEvent.EV_KEY &&
526 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
527 (scancode < RawInputEvent.BTN_FIRST ||
528 scancode > RawInputEvent.BTN_LAST)) {
529 boolean down;
530 if (ev.value != 0) {
531 down = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700532 di.mKeyDownTime = curTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 } else {
534 down = false;
535 }
536 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700537 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700539 newKeyEvent(di, di.mKeyDownTime, curTime, down,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 keycode, 0, scancode,
541 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
542 ? KeyEvent.FLAG_WOKE_HERE : 0));
543 } else if (ev.type == RawInputEvent.EV_KEY) {
544 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700545 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
546 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
547 == RawInputEvent.CLASS_TOUCHSCREEN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700549 di.mAbs.mDown[0] = ev.value != 0;
550 } else if (ev.scancode == RawInputEvent.BTN_2 &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700551 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
552 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
553 == RawInputEvent.CLASS_TOUCHSCREEN) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700554 di.mAbs.changed = true;
555 di.mAbs.mDown[1] = ev.value != 0;
556 } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
558 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700559 di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 send = true;
561 }
562
563 } else if (ev.type == RawInputEvent.EV_ABS &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700564 (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
565 if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
566 di.mAbs.changed = true;
567 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
568 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
569 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
570 di.mAbs.changed = true;
571 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
572 + MotionEvent.SAMPLE_X] = ev.value;
573 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
574 + di.mAbs.mAddingPointerOffset
575 + " X:" + ev.value);
576 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
577 di.mAbs.changed = true;
578 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
579 + MotionEvent.SAMPLE_Y] = ev.value;
580 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
581 + di.mAbs.mAddingPointerOffset
582 + " Y:" + ev.value);
583 } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
584 di.mAbs.changed = true;
585 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
586 + MotionEvent.SAMPLE_SIZE] = ev.value;
587 }
588
589 } else if (ev.type == RawInputEvent.EV_ABS &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700591 // Finger 1
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 if (ev.scancode == RawInputEvent.ABS_X) {
593 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700594 di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 } else if (ev.scancode == RawInputEvent.ABS_Y) {
596 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700597 di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
599 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700600 di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
601 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700602 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
604 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700605 di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
606 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700607 + MotionEvent.SAMPLE_SIZE] = ev.value;
608
609 // Finger 2
610 } else if (ev.scancode == RawInputEvent.ABS_HAT0X) {
611 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700612 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700613 + MotionEvent.SAMPLE_X] = ev.value;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700614 } else if (ev.scancode == RawInputEvent.ABS_HAT0Y) {
615 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700616 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700617 + MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 }
619
620 } else if (ev.type == RawInputEvent.EV_REL &&
621 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
622 // Add this relative movement into our totals.
623 if (ev.scancode == RawInputEvent.REL_X) {
624 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700625 di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 } else if (ev.scancode == RawInputEvent.REL_Y) {
627 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700628 di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 }
630 }
631
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700632 if (ev.type == RawInputEvent.EV_SYN
633 && ev.scancode == RawInputEvent.SYN_MT_REPORT
634 && di.mAbs != null) {
635 di.mAbs.changed = true;
636 if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
637 // If the value is <= 0, the pointer is not
638 // down, so keep it in the count.
639
640 if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
641 + MotionEvent.SAMPLE_PRESSURE] != 0) {
642 final int num = di.mAbs.mNextNumPointers+1;
643 di.mAbs.mNextNumPointers = num;
644 if (DEBUG_POINTERS) Log.v(TAG,
645 "MT_REPORT: now have " + num + " pointers");
646 final int newOffset = (num <= InputDevice.MAX_POINTERS)
647 ? (num * MotionEvent.NUM_SAMPLE_DATA)
648 : (InputDevice.MAX_POINTERS *
649 MotionEvent.NUM_SAMPLE_DATA);
650 di.mAbs.mAddingPointerOffset = newOffset;
651 di.mAbs.mNextData[newOffset
652 + MotionEvent.SAMPLE_PRESSURE] = 0;
653 } else {
654 if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: no pointer");
655 }
656 }
657 } else if (send || (ev.type == RawInputEvent.EV_SYN
658 && ev.scancode == RawInputEvent.SYN_REPORT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 if (mDisplay != null) {
660 if (!mHaveGlobalMetaState) {
661 computeGlobalMetaStateLocked();
662 }
663
664 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700665
666 InputDevice.MotionState ms = di.mAbs;
667 if (ms.changed) {
668 ms.changed = false;
669
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700670 if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
671 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
672 == RawInputEvent.CLASS_TOUCHSCREEN) {
673 ms.mNextNumPointers = 0;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700674 if (ms.mDown[0]) {
675 System.arraycopy(di.curTouchVals, 0,
676 ms.mNextData, 0,
677 MotionEvent.NUM_SAMPLE_DATA);
678 ms.mNextNumPointers++;
679 }
680 if (ms.mDown[1]) {
681 System.arraycopy(di.curTouchVals,
682 MotionEvent.NUM_SAMPLE_DATA,
683 ms.mNextData,
684 ms.mNextNumPointers
685 * MotionEvent.NUM_SAMPLE_DATA,
686 MotionEvent.NUM_SAMPLE_DATA);
687 ms.mNextNumPointers++;
688 }
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700689 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700690
691 boolean doMotion = !monitorVirtualKey(di,
692 ev, curTime, curTimeNano);
693
694 if (doMotion && ms.mNextNumPointers > 0
695 && ms.mLastNumPointers == 0) {
696 doMotion = !generateVirtualKeyDown(di,
697 ev, curTime, curTimeNano);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700699
700 if (doMotion) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700701 // XXX Need to be able to generate
702 // multiple events here, for example
703 // if two fingers change up/down state
704 // at the same time.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700705 do {
706 me = ms.generateAbsMotion(di, curTime,
707 curTimeNano, mDisplay,
708 mOrientation, mGlobalMetaState);
709 if (false) Log.v(TAG, "Absolute: x="
710 + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
711 + " y="
712 + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
713 + " ev=" + me);
714 if (me != null) {
715 if (WindowManagerPolicy.WATCH_POINTER) {
716 Log.i(TAG, "Enqueueing: " + me);
717 }
718 addLocked(di, curTimeNano, ev.flags,
719 RawInputEvent.CLASS_TOUCHSCREEN, me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700720 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700721 } while (ms.hasMore());
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700722 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700723
724 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700726
727 ms = di.mRel;
728 if (ms.changed) {
729 ms.changed = false;
730
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700731 me = ms.generateRelMotion(di, curTime,
732 curTimeNano,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700733 mOrientation, mGlobalMetaState);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700734 if (false) Log.v(TAG, "Relative: x="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700735 + di.mRel.mNextData[MotionEvent.SAMPLE_X]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700736 + " y="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700737 + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700738 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700739 if (me != null) {
740 addLocked(di, curTimeNano, ev.flags,
741 RawInputEvent.CLASS_TRACKBALL, me);
742 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700743
744 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 }
746 }
747 }
748 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700749
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700750 } catch (RuntimeException exc) {
751 Log.e(TAG, "InputReaderThread uncaught exception", exc);
752 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 }
754 }
755 };
756
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700757 private boolean isInsideDisplay(InputDevice dev) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700758 final InputDevice.AbsoluteInfo absx = dev.absX;
759 final InputDevice.AbsoluteInfo absy = dev.absY;
760 final InputDevice.MotionState absm = dev.mAbs;
761 if (absx == null || absy == null || absm == null) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700762 return true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700763 }
764
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700765 if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
766 && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
767 && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
768 && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700769 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700770 + absm.mNextData[MotionEvent.SAMPLE_X]
771 + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700772 + ") inside of display");
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700773 return true;
774 }
775
776 return false;
777 }
778
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700779 private VirtualKey findVirtualKey(InputDevice dev) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700780 final int N = mVirtualKeys.size();
781 if (N <= 0) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700782 return null;
783 }
784
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700785 final InputDevice.MotionState absm = dev.mAbs;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700786 for (int i=0; i<N; i++) {
787 VirtualKey sb = mVirtualKeys.get(i);
788 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700789 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700790 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
791 + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700792 + sb.scancode + " - (" + sb.hitLeft
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700793 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
794 + sb.hitBottom + ")");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700795 if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
796 absm.mNextData[MotionEvent.SAMPLE_Y])) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700797 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
798 return sb;
799 }
800 }
801
802 return null;
803 }
804
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700805 private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
806 long curTime, long curTimeNano) {
807 if (isInsideDisplay(di)) {
808 // Didn't consume event.
809 return false;
810 }
811
812
813 VirtualKey vk = findVirtualKey(di);
814 if (vk != null) {
815 final InputDevice.MotionState ms = di.mAbs;
816 mPressedVirtualKey = vk;
817 vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
818 ms.mLastNumPointers = ms.mNextNumPointers;
819 di.mKeyDownTime = curTime;
820 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
821 "Generate key down for: " + vk.scancode
822 + " (keycode=" + vk.lastKeycode + ")");
823 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
824 vk.lastKeycode, 0, vk.scancode,
825 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
826 mHapticFeedbackCallback.virtualKeyFeedback(event);
827 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
828 event);
829 }
830
831 // We always consume the event, even if we didn't
832 // generate a key event. There are two reasons for
833 // this: to avoid spurious touches when holding
834 // the edges of the device near the touchscreen,
835 // and to avoid reporting events if there are virtual
836 // keys on the touchscreen outside of the display
837 // area.
838 // Note that for all of this we are only looking at the
839 // first pointer, since what we are handling here is the
840 // first pointer going down, and this is the coordinate
841 // that will be used to dispatch the event.
842 if (false) {
843 final InputDevice.AbsoluteInfo absx = di.absX;
844 final InputDevice.AbsoluteInfo absy = di.absY;
845 final InputDevice.MotionState absm = di.mAbs;
846 Log.v(TAG, "Rejecting ("
847 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
848 + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
849 + absx.minValue + "," + absy.minValue
850 + ")-(" + absx.maxValue + ","
851 + absx.maxValue + ")");
852 }
853 return true;
854 }
855
856 private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
857 long curTime, long curTimeNano) {
858 VirtualKey vk = mPressedVirtualKey;
859 if (vk == null) {
860 return false;
861 }
862
863 final InputDevice.MotionState ms = di.mAbs;
864 if (ms.mNextNumPointers <= 0) {
865 mPressedVirtualKey = null;
866 ms.mLastNumPointers = 0;
867 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Generate key up for: " + vk.scancode);
868 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
869 vk.lastKeycode, 0, vk.scancode,
870 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
871 mHapticFeedbackCallback.virtualKeyFeedback(event);
872 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
873 event);
874 return true;
875
876 } else if (isInsideDisplay(di)) {
877 // Whoops the pointer has moved into
878 // the display area! Cancel the
879 // virtual key and start a pointer
880 // motion.
881 mPressedVirtualKey = null;
882 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Cancel key up for: " + vk.scancode);
883 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
884 vk.lastKeycode, 0, vk.scancode,
885 KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
886 mHapticFeedbackCallback.virtualKeyFeedback(event);
887 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
888 event);
889 ms.mLastNumPointers = 0;
890 return false;
891 }
892
893 return true;
894 }
895
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 /**
897 * Returns a new meta state for the given keys and old state.
898 */
899 private static final int makeMetaState(int keycode, boolean down, int old) {
900 int mask;
901 switch (keycode) {
902 case KeyEvent.KEYCODE_ALT_LEFT:
903 mask = KeyEvent.META_ALT_LEFT_ON;
904 break;
905 case KeyEvent.KEYCODE_ALT_RIGHT:
906 mask = KeyEvent.META_ALT_RIGHT_ON;
907 break;
908 case KeyEvent.KEYCODE_SHIFT_LEFT:
909 mask = KeyEvent.META_SHIFT_LEFT_ON;
910 break;
911 case KeyEvent.KEYCODE_SHIFT_RIGHT:
912 mask = KeyEvent.META_SHIFT_RIGHT_ON;
913 break;
914 case KeyEvent.KEYCODE_SYM:
915 mask = KeyEvent.META_SYM_ON;
916 break;
917 default:
918 return old;
919 }
920 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
921 & (down ? (old | mask) : (old & ~mask));
922 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
923 result |= KeyEvent.META_ALT_ON;
924 }
925 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
926 result |= KeyEvent.META_SHIFT_ON;
927 }
928 return result;
929 }
930
931 private void computeGlobalMetaStateLocked() {
932 int i = mDevices.size();
933 mGlobalMetaState = 0;
934 while ((--i) >= 0) {
935 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
936 }
937 mHaveGlobalMetaState = true;
938 }
939
940 /*
941 * Return true if you want the event to get passed on to the
942 * rest of the system, and false if you've handled it and want
943 * it dropped.
944 */
945 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
946
947 InputDevice getInputDevice(int deviceId) {
948 synchronized (mFirst) {
949 return getInputDeviceLocked(deviceId);
950 }
951 }
952
953 private InputDevice getInputDeviceLocked(int deviceId) {
954 return mDevices.get(deviceId);
955 }
956
957 public void setOrientation(int orientation) {
958 synchronized(mFirst) {
959 mOrientation = orientation;
960 switch (orientation) {
961 case Surface.ROTATION_90:
962 mKeyRotationMap = KEY_90_MAP;
963 break;
964 case Surface.ROTATION_180:
965 mKeyRotationMap = KEY_180_MAP;
966 break;
967 case Surface.ROTATION_270:
968 mKeyRotationMap = KEY_270_MAP;
969 break;
970 default:
971 mKeyRotationMap = null;
972 break;
973 }
974 }
975 }
976
977 public int rotateKeyCode(int keyCode) {
978 synchronized(mFirst) {
979 return rotateKeyCodeLocked(keyCode);
980 }
981 }
982
983 private int rotateKeyCodeLocked(int keyCode) {
984 int[] map = mKeyRotationMap;
985 if (map != null) {
986 final int N = map.length;
987 for (int i=0; i<N; i+=2) {
988 if (map[i] == keyCode) {
989 return map[i+1];
990 }
991 }
992 }
993 return keyCode;
994 }
995
996 boolean hasEvents() {
997 synchronized (mFirst) {
998 return mFirst.next != mLast;
999 }
1000 }
1001
1002 /*
1003 * returns true if we returned an event, and false if we timed out
1004 */
1005 QueuedEvent getEvent(long timeoutMS) {
1006 long begin = SystemClock.uptimeMillis();
1007 final long end = begin+timeoutMS;
1008 long now = begin;
1009 synchronized (mFirst) {
1010 while (mFirst.next == mLast && end > now) {
1011 try {
1012 mWakeLock.release();
1013 mFirst.wait(end-now);
1014 }
1015 catch (InterruptedException e) {
1016 }
1017 now = SystemClock.uptimeMillis();
1018 if (begin > now) {
1019 begin = now;
1020 }
1021 }
1022 if (mFirst.next == mLast) {
1023 return null;
1024 }
1025 QueuedEvent p = mFirst.next;
1026 mFirst.next = p.next;
1027 mFirst.next.prev = mFirst;
1028 p.inQueue = false;
1029 return p;
1030 }
1031 }
1032
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001033 /**
1034 * Return true if the queue has an up event pending that corresponds
1035 * to the same key as the given key event.
1036 */
1037 boolean hasKeyUpEvent(KeyEvent origEvent) {
1038 synchronized (mFirst) {
1039 final int keyCode = origEvent.getKeyCode();
1040 QueuedEvent cur = mLast.prev;
1041 while (cur.prev != null) {
1042 if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
1043 KeyEvent ke = (KeyEvent)cur.event;
1044 if (ke.getAction() == KeyEvent.ACTION_UP
1045 && ke.getKeyCode() == keyCode) {
1046 return true;
1047 }
1048 }
1049 cur = cur.prev;
1050 }
1051 }
1052
1053 return false;
1054 }
1055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 void recycleEvent(QueuedEvent ev) {
1057 synchronized (mFirst) {
1058 //Log.i(TAG, "Recycle event: " + ev);
1059 if (ev.event == ev.inputDevice.mAbs.currentMove) {
1060 ev.inputDevice.mAbs.currentMove = null;
1061 }
1062 if (ev.event == ev.inputDevice.mRel.currentMove) {
1063 if (false) Log.i(TAG, "Detach rel " + ev.event);
1064 ev.inputDevice.mRel.currentMove = null;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -07001065 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
1066 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 }
1068 recycleLocked(ev);
1069 }
1070 }
1071
1072 void filterQueue(FilterCallback cb) {
1073 synchronized (mFirst) {
1074 QueuedEvent cur = mLast.prev;
1075 while (cur.prev != null) {
1076 switch (cb.filterEvent(cur)) {
1077 case FILTER_REMOVE:
1078 cur.prev.next = cur.next;
1079 cur.next.prev = cur.prev;
1080 break;
1081 case FILTER_ABORT:
1082 return;
1083 }
1084 cur = cur.prev;
1085 }
1086 }
1087 }
1088
Michael Chan53071d62009-05-13 17:29:48 -07001089 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 int flags, int classType, Object event) {
1091 QueuedEvent ev;
1092 if (mCacheCount == 0) {
1093 ev = new QueuedEvent();
1094 } else {
1095 ev = mCache;
1096 ev.inQueue = false;
1097 mCache = ev.next;
1098 mCacheCount--;
1099 }
1100 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -07001101 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 ev.flags = flags;
1103 ev.classType = classType;
1104 ev.event = event;
1105 return ev;
1106 }
1107
1108 private void recycleLocked(QueuedEvent ev) {
1109 if (ev.inQueue) {
1110 throw new RuntimeException("Event already in queue!");
1111 }
1112 if (mCacheCount < 10) {
1113 mCacheCount++;
1114 ev.next = mCache;
1115 mCache = ev;
1116 ev.inQueue = true;
1117 }
1118 }
1119
Michael Chan53071d62009-05-13 17:29:48 -07001120 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 int classType, Object event) {
1122 boolean poke = mFirst.next == mLast;
1123
Michael Chan53071d62009-05-13 17:29:48 -07001124 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -07001126 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 p = p.prev;
1128 }
1129
1130 ev.next = p.next;
1131 ev.prev = p;
1132 p.next = ev;
1133 ev.next.prev = ev;
1134 ev.inQueue = true;
1135
1136 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -07001137 long time;
1138 if (MEASURE_LATENCY) {
1139 time = System.nanoTime();
1140 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 mFirst.notify();
1142 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -07001143 if (MEASURE_LATENCY) {
1144 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
1145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 }
1147 }
1148
1149 private InputDevice newInputDevice(int deviceId) {
1150 int classes = getDeviceClasses(deviceId);
1151 String name = getDeviceName(deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -07001152 InputDevice.AbsoluteInfo absX = null;
1153 InputDevice.AbsoluteInfo absY = null;
1154 InputDevice.AbsoluteInfo absPressure = null;
1155 InputDevice.AbsoluteInfo absSize = null;
1156 if (classes != 0) {
1157 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
1158 + ", name=" + name
1159 + ", classes=" + Integer.toHexString(classes));
1160 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
1161 absX = loadAbsoluteInfo(deviceId,
1162 RawInputEvent.ABS_MT_POSITION_X, "X");
1163 absY = loadAbsoluteInfo(deviceId,
1164 RawInputEvent.ABS_MT_POSITION_Y, "Y");
1165 absPressure = loadAbsoluteInfo(deviceId,
1166 RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
1167 absSize = loadAbsoluteInfo(deviceId,
1168 RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
1169 } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
1170 absX = loadAbsoluteInfo(deviceId,
1171 RawInputEvent.ABS_X, "X");
1172 absY = loadAbsoluteInfo(deviceId,
1173 RawInputEvent.ABS_Y, "Y");
1174 absPressure = loadAbsoluteInfo(deviceId,
1175 RawInputEvent.ABS_PRESSURE, "Pressure");
1176 absSize = loadAbsoluteInfo(deviceId,
1177 RawInputEvent.ABS_TOOL_WIDTH, "Size");
1178 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 }
1180
1181 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
1182 }
1183
1184 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
1185 String name) {
1186 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
1187 if (getAbsoluteInfo(id, channel, info)
1188 && info.minValue != info.maxValue) {
1189 Log.i(TAG, " " + name + ": min=" + info.minValue
1190 + " max=" + info.maxValue
1191 + " flat=" + info.flat
1192 + " fuzz=" + info.fuzz);
1193 info.range = info.maxValue-info.minValue;
1194 return info;
1195 }
1196 Log.i(TAG, " " + name + ": unknown values");
1197 return null;
1198 }
1199 private static native boolean readEvent(RawInputEvent outEvent);
1200}