blob: d7b8f57098d34f96555de002e15c2152ec436c7a [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 Hackborne3dd8842009-07-14 12:06:54 -070051 static final boolean DEBUG_VIRTUAL_KEYS = false;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -070052 static final boolean DEBUG_POINTERS = false;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070053
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>();
Dianne Hackborna8f60182009-09-01 19:01:50 -070057 final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
Dianne Hackborne3dd8842009-07-14 12:06:54 -070058 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
Dianne Hackbornddca3ee2009-07-23 19:01:31 -070059 final HapticFeedbackCallback mHapticFeedbackCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
61 int mGlobalMetaState = 0;
62 boolean mHaveGlobalMetaState = false;
63
64 final QueuedEvent mFirst;
65 final QueuedEvent mLast;
66 QueuedEvent mCache;
67 int mCacheCount;
68
69 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070070 int mDisplayWidth;
71 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73 int mOrientation = Surface.ROTATION_0;
74 int[] mKeyRotationMap = null;
75
Dianne Hackborne3dd8842009-07-14 12:06:54 -070076 VirtualKey mPressedVirtualKey = null;
77
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 PowerManager.WakeLock mWakeLock;
79
80 static final int[] KEY_90_MAP = new int[] {
81 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
82 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
83 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
84 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
85 };
86
87 static final int[] KEY_180_MAP = new int[] {
88 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
89 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
90 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
91 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
92 };
93
94 static final int[] KEY_270_MAP = new int[] {
95 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
96 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
97 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
98 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
99 };
100
101 public static final int FILTER_REMOVE = 0;
102 public static final int FILTER_KEEP = 1;
103 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -0700104
105 private static final boolean MEASURE_LATENCY = false;
106 private LatencyTimer lt;
107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 public interface FilterCallback {
109 int filterEvent(QueuedEvent ev);
110 }
111
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700112 public interface HapticFeedbackCallback {
113 void virtualKeyFeedback(KeyEvent event);
114 }
115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 static class QueuedEvent {
117 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700118 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 int flags; // From the raw event
120 int classType; // One of the class constants in InputEvent
121 Object event;
122 boolean inQueue;
123
124 void copyFrom(QueuedEvent that) {
125 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700126 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 this.flags = that.flags;
128 this.classType = that.classType;
129 this.event = that.event;
130 }
131
132 @Override
133 public String toString() {
134 return "QueuedEvent{"
135 + Integer.toHexString(System.identityHashCode(this))
136 + " " + event + "}";
137 }
138
139 // not copied
140 QueuedEvent prev;
141 QueuedEvent next;
142 }
143
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700144 /**
145 * A key that exists as a part of the touch-screen, outside of the normal
146 * display area of the screen.
147 */
148 static class VirtualKey {
149 int scancode;
150 int centerx;
151 int centery;
152 int width;
153 int height;
154
155 int hitLeft;
156 int hitTop;
157 int hitRight;
158 int hitBottom;
159
160 InputDevice lastDevice;
161 int lastKeycode;
162
163 boolean checkHit(int x, int y) {
164 return (x >= hitLeft && x <= hitRight
165 && y >= hitTop && y <= hitBottom);
166 }
167
168 void computeHitRect(InputDevice dev, int dw, int dh) {
169 if (dev == lastDevice) {
170 return;
171 }
172
173 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
174 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
175
176 lastDevice = dev;
177
178 int minx = dev.absX.minValue;
179 int maxx = dev.absX.maxValue;
180
181 int halfw = width/2;
182 int left = centerx - halfw;
183 int right = centerx + halfw;
184 hitLeft = minx + ((left*maxx-minx)/dw);
185 hitRight = minx + ((right*maxx-minx)/dw);
186
187 int miny = dev.absY.minValue;
188 int maxy = dev.absY.maxValue;
189
190 int halfh = height/2;
191 int top = centery - halfh;
192 int bottom = centery + halfh;
193 hitTop = miny + ((top*maxy-miny)/dh);
194 hitBottom = miny + ((bottom*maxy-miny)/dh);
195 }
196 }
Michael Chan53071d62009-05-13 17:29:48 -0700197
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700198 private void readVirtualKeys(String deviceName) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700199 try {
200 FileInputStream fis = new FileInputStream(
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700201 "/sys/board_properties/virtualkeys." + deviceName);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700202 InputStreamReader isr = new InputStreamReader(fis);
203 BufferedReader br = new BufferedReader(isr);
204 String str = br.readLine();
205 if (str != null) {
206 String[] it = str.split(":");
207 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
208 final int N = it.length-6;
209 for (int i=0; i<=N; i+=6) {
210 if (!"0x01".equals(it[i])) {
211 Log.w(TAG, "Unknown virtual key type at elem #" + i
212 + ": " + it[i]);
213 continue;
214 }
215 try {
216 VirtualKey sb = new VirtualKey();
217 sb.scancode = Integer.parseInt(it[i+1]);
218 sb.centerx = Integer.parseInt(it[i+2]);
219 sb.centery = Integer.parseInt(it[i+3]);
220 sb.width = Integer.parseInt(it[i+4]);
221 sb.height = Integer.parseInt(it[i+5]);
222 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
223 + sb.scancode + ": center=" + sb.centerx + ","
224 + sb.centery + " size=" + sb.width + "x"
225 + sb.height);
226 mVirtualKeys.add(sb);
227 } catch (NumberFormatException e) {
228 Log.w(TAG, "Bad number at region " + i + " in: "
229 + str, e);
230 }
231 }
232 }
233 br.close();
234 } catch (FileNotFoundException e) {
235 Log.i(TAG, "No virtual keys found");
236 } catch (IOException e) {
237 Log.w(TAG, "Error reading virtual keys", e);
238 }
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400239 }
240
241 private void readExcludedDevices() {
242 // Read partner-provided list of excluded input devices
243 XmlPullParser parser = null;
244 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
245 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
246 FileReader confreader = null;
247 try {
248 confreader = new FileReader(confFile);
249 parser = Xml.newPullParser();
250 parser.setInput(confreader);
251 XmlUtils.beginDocument(parser, "devices");
252
253 while (true) {
254 XmlUtils.nextElement(parser);
255 if (!"device".equals(parser.getName())) {
256 break;
257 }
258 String name = parser.getAttributeValue(null, "name");
259 if (name != null) {
260 Log.d(TAG, "addExcludedDevice " + name);
261 addExcludedDevice(name);
262 }
263 }
264 } catch (FileNotFoundException e) {
265 // It's ok if the file does not exist.
266 } catch (Exception e) {
267 Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
268 } finally {
269 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
270 }
271 }
272
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700273 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400274 if (MEASURE_LATENCY) {
275 lt = new LatencyTimer(100, 1000);
276 }
277
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700278 mHapticFeedbackCallback = hapticFeedbackCallback;
279
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400280 readExcludedDevices();
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700281
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 PowerManager pm = (PowerManager)context.getSystemService(
283 Context.POWER_SERVICE);
284 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
285 "KeyInputQueue");
286 mWakeLock.setReferenceCounted(false);
287
288 mFirst = new QueuedEvent();
289 mLast = new QueuedEvent();
290 mFirst.next = mLast;
291 mLast.prev = mFirst;
292
293 mThread.start();
294 }
295
296 public void setDisplay(Display display) {
297 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700298
299 // We assume at this point that the display dimensions reflect the
300 // natural, unrotated display. We will perform hit tests for soft
301 // buttons based on that display.
302 mDisplayWidth = display.getWidth();
303 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 }
305
306 public void getInputConfiguration(Configuration config) {
307 synchronized (mFirst) {
308 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
309 config.keyboard = Configuration.KEYBOARD_NOKEYS;
310 config.navigation = Configuration.NAVIGATION_NONAV;
311
312 final int N = mDevices.size();
313 for (int i=0; i<N; i++) {
314 InputDevice d = mDevices.valueAt(i);
315 if (d != null) {
316 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
317 config.touchscreen
318 = Configuration.TOUCHSCREEN_FINGER;
319 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
320 }
321 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
322 config.keyboard
323 = Configuration.KEYBOARD_QWERTY;
324 //Log.i("foo", "***** HAVE QWERTY!");
325 }
326 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
327 config.navigation
328 = Configuration.NAVIGATION_TRACKBALL;
329 //Log.i("foo", "***** HAVE TRACKBALL!");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700330 } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
331 config.navigation
332 = Configuration.NAVIGATION_DPAD;
333 //Log.i("foo", "***** HAVE DPAD!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 }
335 }
336 }
337 }
338 }
339
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700340 public int getScancodeState(int code) {
341 synchronized (mFirst) {
342 VirtualKey vk = mPressedVirtualKey;
343 if (vk != null) {
344 if (vk.scancode == code) {
345 return 2;
346 }
347 }
348 return nativeGetScancodeState(code);
349 }
350 }
351
352 public int getScancodeState(int deviceId, int code) {
353 synchronized (mFirst) {
354 VirtualKey vk = mPressedVirtualKey;
355 if (vk != null) {
356 if (vk.scancode == code) {
357 return 2;
358 }
359 }
360 return nativeGetScancodeState(deviceId, code);
361 }
362 }
363
364 public int getKeycodeState(int code) {
365 synchronized (mFirst) {
366 VirtualKey vk = mPressedVirtualKey;
367 if (vk != null) {
368 if (vk.lastKeycode == code) {
369 return 2;
370 }
371 }
372 return nativeGetKeycodeState(code);
373 }
374 }
375
376 public int getKeycodeState(int deviceId, int code) {
377 synchronized (mFirst) {
378 VirtualKey vk = mPressedVirtualKey;
379 if (vk != null) {
380 if (vk.lastKeycode == code) {
381 return 2;
382 }
383 }
384 return nativeGetKeycodeState(deviceId, code);
385 }
386 }
387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 public static native String getDeviceName(int deviceId);
389 public static native int getDeviceClasses(int deviceId);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400390 public static native void addExcludedDevice(String deviceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 public static native boolean getAbsoluteInfo(int deviceId, int axis,
392 InputDevice.AbsoluteInfo outInfo);
393 public static native int getSwitchState(int sw);
394 public static native int getSwitchState(int deviceId, int sw);
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700395 public static native int nativeGetScancodeState(int code);
396 public static native int nativeGetScancodeState(int deviceId, int code);
397 public static native int nativeGetKeycodeState(int code);
398 public static native int nativeGetKeycodeState(int deviceId, int code);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700399 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
401
402 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
403 long eventTime, boolean down, int keycode, int repeatCount,
404 int scancode, int flags) {
405 return new KeyEvent(
406 downTime, eventTime,
407 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
408 keycode, repeatCount,
409 device != null ? device.mMetaKeysState : 0,
410 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700411 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 }
413
414 Thread mThread = new Thread("InputDeviceReader") {
415 public void run() {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400416 Log.d(TAG, "InputDeviceReader.run()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 android.os.Process.setThreadPriority(
418 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
419
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700420 RawInputEvent ev = new RawInputEvent();
421 while (true) {
422 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 InputDevice di;
424
425 // block, doesn't release the monitor
426 readEvent(ev);
427
428 boolean send = false;
429 boolean configChanged = false;
430
431 if (false) {
432 Log.i(TAG, "Input event: dev=0x"
433 + Integer.toHexString(ev.deviceId)
434 + " type=0x" + Integer.toHexString(ev.type)
435 + " scancode=" + ev.scancode
436 + " keycode=" + ev.keycode
437 + " value=" + ev.value);
438 }
439
440 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
441 synchronized (mFirst) {
442 di = newInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700443 if (di.classes != 0) {
444 // If this device is some kind of input class,
445 // we care about it.
446 mDevices.put(ev.deviceId, di);
447 if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
448 readVirtualKeys(di.name);
449 }
450 // The configuration may have changed because
451 // of this device.
452 configChanged = true;
453 } else {
454 // We won't do anything with this device.
455 mIgnoredDevices.put(ev.deviceId, di);
456 Log.i(TAG, "Ignoring non-input device: id=0x"
457 + Integer.toHexString(di.id)
458 + ", name=" + di.name);
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700459 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
461 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
462 synchronized (mFirst) {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700463 if (false) {
464 Log.i(TAG, "Device removed: id=0x"
465 + Integer.toHexString(ev.deviceId));
466 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 di = mDevices.get(ev.deviceId);
468 if (di != null) {
469 mDevices.delete(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700470 // The configuration may have changed because
471 // of this device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 configChanged = true;
Dianne Hackborna8f60182009-09-01 19:01:50 -0700473 } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
474 mIgnoredDevices.remove(ev.deviceId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 } else {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700476 Log.w(TAG, "Removing bad device id: "
477 + Integer.toHexString(ev.deviceId));
478 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 }
480 }
481 } else {
482 di = getInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700483 if (di == null) {
484 // This may be some junk from an ignored device.
485 continue;
486 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487
488 // first crack at it
489 send = preprocessEvent(di, ev);
490
491 if (ev.type == RawInputEvent.EV_KEY) {
492 di.mMetaKeysState = makeMetaState(ev.keycode,
493 ev.value != 0, di.mMetaKeysState);
494 mHaveGlobalMetaState = false;
495 }
496 }
497
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 if (configChanged) {
499 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700500 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
502 null);
503 }
504 }
505
506 if (!send) {
507 continue;
508 }
509
510 synchronized (mFirst) {
511 // NOTE: The event timebase absolutely must be the same
512 // timebase as SystemClock.uptimeMillis().
513 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
514 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700515 final long curTimeNano = System.nanoTime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
517
518 final int classes = di.classes;
519 final int type = ev.type;
520 final int scancode = ev.scancode;
521 send = false;
522
523 // Is it a key event?
524 if (type == RawInputEvent.EV_KEY &&
525 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
526 (scancode < RawInputEvent.BTN_FIRST ||
527 scancode > RawInputEvent.BTN_LAST)) {
528 boolean down;
529 if (ev.value != 0) {
530 down = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700531 di.mKeyDownTime = curTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 } else {
533 down = false;
534 }
535 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700536 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700538 newKeyEvent(di, di.mKeyDownTime, curTime, down,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 keycode, 0, scancode,
540 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
541 ? KeyEvent.FLAG_WOKE_HERE : 0));
542 } else if (ev.type == RawInputEvent.EV_KEY) {
543 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700544 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
545 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
546 == RawInputEvent.CLASS_TOUCHSCREEN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700548 di.mAbs.mDown[0] = ev.value != 0;
549 } else if (ev.scancode == RawInputEvent.BTN_2 &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700550 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
551 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
552 == RawInputEvent.CLASS_TOUCHSCREEN) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700553 di.mAbs.changed = true;
554 di.mAbs.mDown[1] = ev.value != 0;
555 } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
557 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700558 di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 send = true;
560 }
561
562 } else if (ev.type == RawInputEvent.EV_ABS &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700563 (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
564 if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
565 di.mAbs.changed = true;
566 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
567 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
568 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
569 di.mAbs.changed = true;
570 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
571 + MotionEvent.SAMPLE_X] = ev.value;
572 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
573 + di.mAbs.mAddingPointerOffset
574 + " X:" + ev.value);
575 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
576 di.mAbs.changed = true;
577 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
578 + MotionEvent.SAMPLE_Y] = ev.value;
579 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
580 + di.mAbs.mAddingPointerOffset
581 + " Y:" + ev.value);
582 } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
583 di.mAbs.changed = true;
584 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
585 + MotionEvent.SAMPLE_SIZE] = ev.value;
586 }
587
588 } else if (ev.type == RawInputEvent.EV_ABS &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700590 // Finger 1
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 if (ev.scancode == RawInputEvent.ABS_X) {
592 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700593 di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 } else if (ev.scancode == RawInputEvent.ABS_Y) {
595 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700596 di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
598 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700599 di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
600 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700601 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
603 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700604 di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
605 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700606 + MotionEvent.SAMPLE_SIZE] = ev.value;
607
608 // Finger 2
609 } else if (ev.scancode == RawInputEvent.ABS_HAT0X) {
610 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700611 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700612 + MotionEvent.SAMPLE_X] = ev.value;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700613 } else if (ev.scancode == RawInputEvent.ABS_HAT0Y) {
614 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700615 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700616 + MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 }
618
619 } else if (ev.type == RawInputEvent.EV_REL &&
620 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
621 // Add this relative movement into our totals.
622 if (ev.scancode == RawInputEvent.REL_X) {
623 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700624 di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 } else if (ev.scancode == RawInputEvent.REL_Y) {
626 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700627 di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 }
629 }
630
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700631 if (ev.type == RawInputEvent.EV_SYN
632 && ev.scancode == RawInputEvent.SYN_MT_REPORT
633 && di.mAbs != null) {
634 di.mAbs.changed = true;
635 if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
636 // If the value is <= 0, the pointer is not
637 // down, so keep it in the count.
638
639 if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
640 + MotionEvent.SAMPLE_PRESSURE] != 0) {
641 final int num = di.mAbs.mNextNumPointers+1;
642 di.mAbs.mNextNumPointers = num;
643 if (DEBUG_POINTERS) Log.v(TAG,
644 "MT_REPORT: now have " + num + " pointers");
645 final int newOffset = (num <= InputDevice.MAX_POINTERS)
646 ? (num * MotionEvent.NUM_SAMPLE_DATA)
647 : (InputDevice.MAX_POINTERS *
648 MotionEvent.NUM_SAMPLE_DATA);
649 di.mAbs.mAddingPointerOffset = newOffset;
650 di.mAbs.mNextData[newOffset
651 + MotionEvent.SAMPLE_PRESSURE] = 0;
652 } else {
653 if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: no pointer");
654 }
655 }
656 } else if (send || (ev.type == RawInputEvent.EV_SYN
657 && ev.scancode == RawInputEvent.SYN_REPORT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 if (mDisplay != null) {
659 if (!mHaveGlobalMetaState) {
660 computeGlobalMetaStateLocked();
661 }
662
663 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700664
665 InputDevice.MotionState ms = di.mAbs;
666 if (ms.changed) {
667 ms.changed = false;
668
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700669 if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
670 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
671 == RawInputEvent.CLASS_TOUCHSCREEN) {
672 ms.mNextNumPointers = 0;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700673 if (ms.mDown[0]) {
674 System.arraycopy(di.curTouchVals, 0,
675 ms.mNextData, 0,
676 MotionEvent.NUM_SAMPLE_DATA);
677 ms.mNextNumPointers++;
678 }
679 if (ms.mDown[1]) {
680 System.arraycopy(di.curTouchVals,
681 MotionEvent.NUM_SAMPLE_DATA,
682 ms.mNextData,
683 ms.mNextNumPointers
684 * MotionEvent.NUM_SAMPLE_DATA,
685 MotionEvent.NUM_SAMPLE_DATA);
686 ms.mNextNumPointers++;
687 }
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700688 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700689
690 boolean doMotion = !monitorVirtualKey(di,
691 ev, curTime, curTimeNano);
692
693 if (doMotion && ms.mNextNumPointers > 0
694 && ms.mLastNumPointers == 0) {
695 doMotion = !generateVirtualKeyDown(di,
696 ev, curTime, curTimeNano);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700698
699 if (doMotion) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700700 // XXX Need to be able to generate
701 // multiple events here, for example
702 // if two fingers change up/down state
703 // at the same time.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700704 do {
705 me = ms.generateAbsMotion(di, curTime,
706 curTimeNano, mDisplay,
707 mOrientation, mGlobalMetaState);
708 if (false) Log.v(TAG, "Absolute: x="
709 + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
710 + " y="
711 + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
712 + " ev=" + me);
713 if (me != null) {
714 if (WindowManagerPolicy.WATCH_POINTER) {
715 Log.i(TAG, "Enqueueing: " + me);
716 }
717 addLocked(di, curTimeNano, ev.flags,
718 RawInputEvent.CLASS_TOUCHSCREEN, me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700719 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700720 } while (ms.hasMore());
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700721 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700722
723 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700725
726 ms = di.mRel;
727 if (ms.changed) {
728 ms.changed = false;
729
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700730 me = ms.generateRelMotion(di, curTime,
731 curTimeNano,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700732 mOrientation, mGlobalMetaState);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700733 if (false) Log.v(TAG, "Relative: x="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700734 + di.mRel.mNextData[MotionEvent.SAMPLE_X]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700735 + " y="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700736 + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700737 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700738 if (me != null) {
739 addLocked(di, curTimeNano, ev.flags,
740 RawInputEvent.CLASS_TRACKBALL, me);
741 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700742
743 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 }
745 }
746 }
747 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700748
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700749 } catch (RuntimeException exc) {
750 Log.e(TAG, "InputReaderThread uncaught exception", exc);
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 }
753 }
754 };
755
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700756 private boolean isInsideDisplay(InputDevice dev) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700757 final InputDevice.AbsoluteInfo absx = dev.absX;
758 final InputDevice.AbsoluteInfo absy = dev.absY;
759 final InputDevice.MotionState absm = dev.mAbs;
760 if (absx == null || absy == null || absm == null) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700761 return true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700762 }
763
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700764 if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
765 && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
766 && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
767 && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700768 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700769 + absm.mNextData[MotionEvent.SAMPLE_X]
770 + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700771 + ") inside of display");
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700772 return true;
773 }
774
775 return false;
776 }
777
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700778 private VirtualKey findVirtualKey(InputDevice dev) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700779 final int N = mVirtualKeys.size();
780 if (N <= 0) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700781 return null;
782 }
783
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700784 final InputDevice.MotionState absm = dev.mAbs;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700785 for (int i=0; i<N; i++) {
786 VirtualKey sb = mVirtualKeys.get(i);
787 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700788 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700789 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
790 + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700791 + sb.scancode + " - (" + sb.hitLeft
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700792 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
793 + sb.hitBottom + ")");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700794 if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
795 absm.mNextData[MotionEvent.SAMPLE_Y])) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700796 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
797 return sb;
798 }
799 }
800
801 return null;
802 }
803
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700804 private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
805 long curTime, long curTimeNano) {
806 if (isInsideDisplay(di)) {
807 // Didn't consume event.
808 return false;
809 }
810
811
812 VirtualKey vk = findVirtualKey(di);
813 if (vk != null) {
814 final InputDevice.MotionState ms = di.mAbs;
815 mPressedVirtualKey = vk;
816 vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
817 ms.mLastNumPointers = ms.mNextNumPointers;
818 di.mKeyDownTime = curTime;
819 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
820 "Generate key down for: " + vk.scancode
821 + " (keycode=" + vk.lastKeycode + ")");
822 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
823 vk.lastKeycode, 0, vk.scancode,
824 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
825 mHapticFeedbackCallback.virtualKeyFeedback(event);
826 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
827 event);
828 }
829
830 // We always consume the event, even if we didn't
831 // generate a key event. There are two reasons for
832 // this: to avoid spurious touches when holding
833 // the edges of the device near the touchscreen,
834 // and to avoid reporting events if there are virtual
835 // keys on the touchscreen outside of the display
836 // area.
837 // Note that for all of this we are only looking at the
838 // first pointer, since what we are handling here is the
839 // first pointer going down, and this is the coordinate
840 // that will be used to dispatch the event.
841 if (false) {
842 final InputDevice.AbsoluteInfo absx = di.absX;
843 final InputDevice.AbsoluteInfo absy = di.absY;
844 final InputDevice.MotionState absm = di.mAbs;
845 Log.v(TAG, "Rejecting ("
846 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
847 + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
848 + absx.minValue + "," + absy.minValue
849 + ")-(" + absx.maxValue + ","
850 + absx.maxValue + ")");
851 }
852 return true;
853 }
854
855 private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
856 long curTime, long curTimeNano) {
857 VirtualKey vk = mPressedVirtualKey;
858 if (vk == null) {
859 return false;
860 }
861
862 final InputDevice.MotionState ms = di.mAbs;
863 if (ms.mNextNumPointers <= 0) {
864 mPressedVirtualKey = null;
865 ms.mLastNumPointers = 0;
866 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Generate key up for: " + vk.scancode);
867 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
868 vk.lastKeycode, 0, vk.scancode,
869 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
870 mHapticFeedbackCallback.virtualKeyFeedback(event);
871 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
872 event);
873 return true;
874
875 } else if (isInsideDisplay(di)) {
876 // Whoops the pointer has moved into
877 // the display area! Cancel the
878 // virtual key and start a pointer
879 // motion.
880 mPressedVirtualKey = null;
881 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Cancel key up for: " + vk.scancode);
882 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
883 vk.lastKeycode, 0, vk.scancode,
884 KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
885 mHapticFeedbackCallback.virtualKeyFeedback(event);
886 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
887 event);
888 ms.mLastNumPointers = 0;
889 return false;
890 }
891
892 return true;
893 }
894
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 /**
896 * Returns a new meta state for the given keys and old state.
897 */
898 private static final int makeMetaState(int keycode, boolean down, int old) {
899 int mask;
900 switch (keycode) {
901 case KeyEvent.KEYCODE_ALT_LEFT:
902 mask = KeyEvent.META_ALT_LEFT_ON;
903 break;
904 case KeyEvent.KEYCODE_ALT_RIGHT:
905 mask = KeyEvent.META_ALT_RIGHT_ON;
906 break;
907 case KeyEvent.KEYCODE_SHIFT_LEFT:
908 mask = KeyEvent.META_SHIFT_LEFT_ON;
909 break;
910 case KeyEvent.KEYCODE_SHIFT_RIGHT:
911 mask = KeyEvent.META_SHIFT_RIGHT_ON;
912 break;
913 case KeyEvent.KEYCODE_SYM:
914 mask = KeyEvent.META_SYM_ON;
915 break;
916 default:
917 return old;
918 }
919 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
920 & (down ? (old | mask) : (old & ~mask));
921 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
922 result |= KeyEvent.META_ALT_ON;
923 }
924 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
925 result |= KeyEvent.META_SHIFT_ON;
926 }
927 return result;
928 }
929
930 private void computeGlobalMetaStateLocked() {
931 int i = mDevices.size();
932 mGlobalMetaState = 0;
933 while ((--i) >= 0) {
934 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
935 }
936 mHaveGlobalMetaState = true;
937 }
938
939 /*
940 * Return true if you want the event to get passed on to the
941 * rest of the system, and false if you've handled it and want
942 * it dropped.
943 */
944 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
945
946 InputDevice getInputDevice(int deviceId) {
947 synchronized (mFirst) {
948 return getInputDeviceLocked(deviceId);
949 }
950 }
951
952 private InputDevice getInputDeviceLocked(int deviceId) {
953 return mDevices.get(deviceId);
954 }
955
956 public void setOrientation(int orientation) {
957 synchronized(mFirst) {
958 mOrientation = orientation;
959 switch (orientation) {
960 case Surface.ROTATION_90:
961 mKeyRotationMap = KEY_90_MAP;
962 break;
963 case Surface.ROTATION_180:
964 mKeyRotationMap = KEY_180_MAP;
965 break;
966 case Surface.ROTATION_270:
967 mKeyRotationMap = KEY_270_MAP;
968 break;
969 default:
970 mKeyRotationMap = null;
971 break;
972 }
973 }
974 }
975
976 public int rotateKeyCode(int keyCode) {
977 synchronized(mFirst) {
978 return rotateKeyCodeLocked(keyCode);
979 }
980 }
981
982 private int rotateKeyCodeLocked(int keyCode) {
983 int[] map = mKeyRotationMap;
984 if (map != null) {
985 final int N = map.length;
986 for (int i=0; i<N; i+=2) {
987 if (map[i] == keyCode) {
988 return map[i+1];
989 }
990 }
991 }
992 return keyCode;
993 }
994
995 boolean hasEvents() {
996 synchronized (mFirst) {
997 return mFirst.next != mLast;
998 }
999 }
1000
1001 /*
1002 * returns true if we returned an event, and false if we timed out
1003 */
1004 QueuedEvent getEvent(long timeoutMS) {
1005 long begin = SystemClock.uptimeMillis();
1006 final long end = begin+timeoutMS;
1007 long now = begin;
1008 synchronized (mFirst) {
1009 while (mFirst.next == mLast && end > now) {
1010 try {
1011 mWakeLock.release();
1012 mFirst.wait(end-now);
1013 }
1014 catch (InterruptedException e) {
1015 }
1016 now = SystemClock.uptimeMillis();
1017 if (begin > now) {
1018 begin = now;
1019 }
1020 }
1021 if (mFirst.next == mLast) {
1022 return null;
1023 }
1024 QueuedEvent p = mFirst.next;
1025 mFirst.next = p.next;
1026 mFirst.next.prev = mFirst;
1027 p.inQueue = false;
1028 return p;
1029 }
1030 }
1031
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001032 /**
1033 * Return true if the queue has an up event pending that corresponds
1034 * to the same key as the given key event.
1035 */
1036 boolean hasKeyUpEvent(KeyEvent origEvent) {
1037 synchronized (mFirst) {
1038 final int keyCode = origEvent.getKeyCode();
1039 QueuedEvent cur = mLast.prev;
1040 while (cur.prev != null) {
1041 if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
1042 KeyEvent ke = (KeyEvent)cur.event;
1043 if (ke.getAction() == KeyEvent.ACTION_UP
1044 && ke.getKeyCode() == keyCode) {
1045 return true;
1046 }
1047 }
1048 cur = cur.prev;
1049 }
1050 }
1051
1052 return false;
1053 }
1054
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 void recycleEvent(QueuedEvent ev) {
1056 synchronized (mFirst) {
1057 //Log.i(TAG, "Recycle event: " + ev);
1058 if (ev.event == ev.inputDevice.mAbs.currentMove) {
1059 ev.inputDevice.mAbs.currentMove = null;
1060 }
1061 if (ev.event == ev.inputDevice.mRel.currentMove) {
1062 if (false) Log.i(TAG, "Detach rel " + ev.event);
1063 ev.inputDevice.mRel.currentMove = null;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -07001064 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
1065 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 }
1067 recycleLocked(ev);
1068 }
1069 }
1070
1071 void filterQueue(FilterCallback cb) {
1072 synchronized (mFirst) {
1073 QueuedEvent cur = mLast.prev;
1074 while (cur.prev != null) {
1075 switch (cb.filterEvent(cur)) {
1076 case FILTER_REMOVE:
1077 cur.prev.next = cur.next;
1078 cur.next.prev = cur.prev;
1079 break;
1080 case FILTER_ABORT:
1081 return;
1082 }
1083 cur = cur.prev;
1084 }
1085 }
1086 }
1087
Michael Chan53071d62009-05-13 17:29:48 -07001088 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 int flags, int classType, Object event) {
1090 QueuedEvent ev;
1091 if (mCacheCount == 0) {
1092 ev = new QueuedEvent();
1093 } else {
1094 ev = mCache;
1095 ev.inQueue = false;
1096 mCache = ev.next;
1097 mCacheCount--;
1098 }
1099 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -07001100 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 ev.flags = flags;
1102 ev.classType = classType;
1103 ev.event = event;
1104 return ev;
1105 }
1106
1107 private void recycleLocked(QueuedEvent ev) {
1108 if (ev.inQueue) {
1109 throw new RuntimeException("Event already in queue!");
1110 }
1111 if (mCacheCount < 10) {
1112 mCacheCount++;
1113 ev.next = mCache;
1114 mCache = ev;
1115 ev.inQueue = true;
1116 }
1117 }
1118
Michael Chan53071d62009-05-13 17:29:48 -07001119 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 int classType, Object event) {
1121 boolean poke = mFirst.next == mLast;
1122
Michael Chan53071d62009-05-13 17:29:48 -07001123 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -07001125 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 p = p.prev;
1127 }
1128
1129 ev.next = p.next;
1130 ev.prev = p;
1131 p.next = ev;
1132 ev.next.prev = ev;
1133 ev.inQueue = true;
1134
1135 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -07001136 long time;
1137 if (MEASURE_LATENCY) {
1138 time = System.nanoTime();
1139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 mFirst.notify();
1141 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -07001142 if (MEASURE_LATENCY) {
1143 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
1144 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 }
1146 }
1147
1148 private InputDevice newInputDevice(int deviceId) {
1149 int classes = getDeviceClasses(deviceId);
1150 String name = getDeviceName(deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -07001151 InputDevice.AbsoluteInfo absX = null;
1152 InputDevice.AbsoluteInfo absY = null;
1153 InputDevice.AbsoluteInfo absPressure = null;
1154 InputDevice.AbsoluteInfo absSize = null;
1155 if (classes != 0) {
1156 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
1157 + ", name=" + name
1158 + ", classes=" + Integer.toHexString(classes));
1159 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
1160 absX = loadAbsoluteInfo(deviceId,
1161 RawInputEvent.ABS_MT_POSITION_X, "X");
1162 absY = loadAbsoluteInfo(deviceId,
1163 RawInputEvent.ABS_MT_POSITION_Y, "Y");
1164 absPressure = loadAbsoluteInfo(deviceId,
1165 RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
1166 absSize = loadAbsoluteInfo(deviceId,
1167 RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
1168 } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
1169 absX = loadAbsoluteInfo(deviceId,
1170 RawInputEvent.ABS_X, "X");
1171 absY = loadAbsoluteInfo(deviceId,
1172 RawInputEvent.ABS_Y, "Y");
1173 absPressure = loadAbsoluteInfo(deviceId,
1174 RawInputEvent.ABS_PRESSURE, "Pressure");
1175 absSize = loadAbsoluteInfo(deviceId,
1176 RawInputEvent.ABS_TOOL_WIDTH, "Size");
1177 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 }
1179
1180 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
1181 }
1182
1183 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
1184 String name) {
1185 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
1186 if (getAbsoluteInfo(id, channel, info)
1187 && info.minValue != info.maxValue) {
1188 Log.i(TAG, " " + name + ": min=" + info.minValue
1189 + " max=" + info.maxValue
1190 + " flat=" + info.flat
1191 + " fuzz=" + info.fuzz);
1192 info.range = info.maxValue-info.minValue;
1193 return info;
1194 }
1195 Log.i(TAG, " " + name + ": unknown values");
1196 return null;
1197 }
1198 private static native boolean readEvent(RawInputEvent outEvent);
1199}