blob: 09591f44e5e34bbb39e3222f10774d3864e0bd2d [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
Dianne Hackborn1411d1c2009-10-12 23:21:18 -070055 /**
56 * Turn on some hacks we have to improve the touch interaction with a
57 * certain device whose screen currently is not all that good.
58 */
59 static final boolean BAD_TOUCH_HACK = true;
60
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040061 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
62
Dianne Hackborne3dd8842009-07-14 12:06:54 -070063 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
Dianne Hackborna8f60182009-09-01 19:01:50 -070064 final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
Dianne Hackborne3dd8842009-07-14 12:06:54 -070065 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
Dianne Hackbornddca3ee2009-07-23 19:01:31 -070066 final HapticFeedbackCallback mHapticFeedbackCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
68 int mGlobalMetaState = 0;
69 boolean mHaveGlobalMetaState = false;
70
71 final QueuedEvent mFirst;
72 final QueuedEvent mLast;
73 QueuedEvent mCache;
74 int mCacheCount;
75
76 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070077 int mDisplayWidth;
78 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
80 int mOrientation = Surface.ROTATION_0;
81 int[] mKeyRotationMap = null;
82
Dianne Hackborne3dd8842009-07-14 12:06:54 -070083 VirtualKey mPressedVirtualKey = null;
84
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 PowerManager.WakeLock mWakeLock;
86
87 static final int[] KEY_90_MAP = new int[] {
88 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
89 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
90 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
91 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
92 };
93
94 static final int[] KEY_180_MAP = new int[] {
95 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
96 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
97 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
98 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
99 };
100
101 static final int[] KEY_270_MAP = new int[] {
102 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
103 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
104 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
105 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
106 };
107
108 public static final int FILTER_REMOVE = 0;
109 public static final int FILTER_KEEP = 1;
110 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -0700111
112 private static final boolean MEASURE_LATENCY = false;
113 private LatencyTimer lt;
114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 public interface FilterCallback {
116 int filterEvent(QueuedEvent ev);
117 }
118
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700119 public interface HapticFeedbackCallback {
120 void virtualKeyFeedback(KeyEvent event);
121 }
122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 static class QueuedEvent {
124 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700125 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 int flags; // From the raw event
127 int classType; // One of the class constants in InputEvent
128 Object event;
129 boolean inQueue;
130
131 void copyFrom(QueuedEvent that) {
132 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700133 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 this.flags = that.flags;
135 this.classType = that.classType;
136 this.event = that.event;
137 }
138
139 @Override
140 public String toString() {
141 return "QueuedEvent{"
142 + Integer.toHexString(System.identityHashCode(this))
143 + " " + event + "}";
144 }
145
146 // not copied
147 QueuedEvent prev;
148 QueuedEvent next;
149 }
150
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700151 /**
152 * A key that exists as a part of the touch-screen, outside of the normal
153 * display area of the screen.
154 */
155 static class VirtualKey {
156 int scancode;
157 int centerx;
158 int centery;
159 int width;
160 int height;
161
162 int hitLeft;
163 int hitTop;
164 int hitRight;
165 int hitBottom;
166
167 InputDevice lastDevice;
168 int lastKeycode;
169
170 boolean checkHit(int x, int y) {
171 return (x >= hitLeft && x <= hitRight
172 && y >= hitTop && y <= hitBottom);
173 }
174
175 void computeHitRect(InputDevice dev, int dw, int dh) {
176 if (dev == lastDevice) {
177 return;
178 }
179
180 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
181 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
182
183 lastDevice = dev;
184
185 int minx = dev.absX.minValue;
186 int maxx = dev.absX.maxValue;
187
188 int halfw = width/2;
189 int left = centerx - halfw;
190 int right = centerx + halfw;
191 hitLeft = minx + ((left*maxx-minx)/dw);
192 hitRight = minx + ((right*maxx-minx)/dw);
193
194 int miny = dev.absY.minValue;
195 int maxy = dev.absY.maxValue;
196
197 int halfh = height/2;
198 int top = centery - halfh;
199 int bottom = centery + halfh;
200 hitTop = miny + ((top*maxy-miny)/dh);
201 hitBottom = miny + ((bottom*maxy-miny)/dh);
202 }
203 }
Michael Chan53071d62009-05-13 17:29:48 -0700204
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700205 private void readVirtualKeys(String deviceName) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700206 try {
207 FileInputStream fis = new FileInputStream(
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700208 "/sys/board_properties/virtualkeys." + deviceName);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700209 InputStreamReader isr = new InputStreamReader(fis);
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700210 BufferedReader br = new BufferedReader(isr, 2048);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700211 String str = br.readLine();
212 if (str != null) {
213 String[] it = str.split(":");
214 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
215 final int N = it.length-6;
216 for (int i=0; i<=N; i+=6) {
217 if (!"0x01".equals(it[i])) {
218 Log.w(TAG, "Unknown virtual key type at elem #" + i
219 + ": " + it[i]);
220 continue;
221 }
222 try {
223 VirtualKey sb = new VirtualKey();
224 sb.scancode = Integer.parseInt(it[i+1]);
225 sb.centerx = Integer.parseInt(it[i+2]);
226 sb.centery = Integer.parseInt(it[i+3]);
227 sb.width = Integer.parseInt(it[i+4]);
228 sb.height = Integer.parseInt(it[i+5]);
229 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
230 + sb.scancode + ": center=" + sb.centerx + ","
231 + sb.centery + " size=" + sb.width + "x"
232 + sb.height);
233 mVirtualKeys.add(sb);
234 } catch (NumberFormatException e) {
235 Log.w(TAG, "Bad number at region " + i + " in: "
236 + str, e);
237 }
238 }
239 }
240 br.close();
241 } catch (FileNotFoundException e) {
242 Log.i(TAG, "No virtual keys found");
243 } catch (IOException e) {
244 Log.w(TAG, "Error reading virtual keys", e);
245 }
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400246 }
247
248 private void readExcludedDevices() {
249 // Read partner-provided list of excluded input devices
250 XmlPullParser parser = null;
251 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
252 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
253 FileReader confreader = null;
254 try {
255 confreader = new FileReader(confFile);
256 parser = Xml.newPullParser();
257 parser.setInput(confreader);
258 XmlUtils.beginDocument(parser, "devices");
259
260 while (true) {
261 XmlUtils.nextElement(parser);
262 if (!"device".equals(parser.getName())) {
263 break;
264 }
265 String name = parser.getAttributeValue(null, "name");
266 if (name != null) {
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700267 if (DEBUG) Log.v(TAG, "addExcludedDevice " + name);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400268 addExcludedDevice(name);
269 }
270 }
271 } catch (FileNotFoundException e) {
272 // It's ok if the file does not exist.
273 } catch (Exception e) {
274 Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
275 } finally {
276 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
277 }
278 }
279
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700280 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400281 if (MEASURE_LATENCY) {
282 lt = new LatencyTimer(100, 1000);
283 }
284
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700285 mHapticFeedbackCallback = hapticFeedbackCallback;
286
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400287 readExcludedDevices();
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 PowerManager pm = (PowerManager)context.getSystemService(
290 Context.POWER_SERVICE);
291 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
292 "KeyInputQueue");
293 mWakeLock.setReferenceCounted(false);
294
295 mFirst = new QueuedEvent();
296 mLast = new QueuedEvent();
297 mFirst.next = mLast;
298 mLast.prev = mFirst;
299
300 mThread.start();
301 }
302
303 public void setDisplay(Display display) {
304 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700305
306 // We assume at this point that the display dimensions reflect the
307 // natural, unrotated display. We will perform hit tests for soft
308 // buttons based on that display.
309 mDisplayWidth = display.getWidth();
310 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 }
312
313 public void getInputConfiguration(Configuration config) {
314 synchronized (mFirst) {
315 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
316 config.keyboard = Configuration.KEYBOARD_NOKEYS;
317 config.navigation = Configuration.NAVIGATION_NONAV;
318
319 final int N = mDevices.size();
320 for (int i=0; i<N; i++) {
321 InputDevice d = mDevices.valueAt(i);
322 if (d != null) {
323 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
324 config.touchscreen
325 = Configuration.TOUCHSCREEN_FINGER;
326 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
327 }
328 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
329 config.keyboard
330 = Configuration.KEYBOARD_QWERTY;
331 //Log.i("foo", "***** HAVE QWERTY!");
332 }
333 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
334 config.navigation
335 = Configuration.NAVIGATION_TRACKBALL;
336 //Log.i("foo", "***** HAVE TRACKBALL!");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700337 } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
338 config.navigation
339 = Configuration.NAVIGATION_DPAD;
340 //Log.i("foo", "***** HAVE DPAD!");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342 }
343 }
344 }
345 }
346
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700347 public int getScancodeState(int code) {
348 synchronized (mFirst) {
349 VirtualKey vk = mPressedVirtualKey;
350 if (vk != null) {
351 if (vk.scancode == code) {
352 return 2;
353 }
354 }
355 return nativeGetScancodeState(code);
356 }
357 }
358
359 public int getScancodeState(int deviceId, int code) {
360 synchronized (mFirst) {
361 VirtualKey vk = mPressedVirtualKey;
362 if (vk != null) {
363 if (vk.scancode == code) {
364 return 2;
365 }
366 }
367 return nativeGetScancodeState(deviceId, code);
368 }
369 }
370
371 public int getKeycodeState(int code) {
372 synchronized (mFirst) {
373 VirtualKey vk = mPressedVirtualKey;
374 if (vk != null) {
375 if (vk.lastKeycode == code) {
376 return 2;
377 }
378 }
379 return nativeGetKeycodeState(code);
380 }
381 }
382
383 public int getKeycodeState(int deviceId, int code) {
384 synchronized (mFirst) {
385 VirtualKey vk = mPressedVirtualKey;
386 if (vk != null) {
387 if (vk.lastKeycode == code) {
388 return 2;
389 }
390 }
391 return nativeGetKeycodeState(deviceId, code);
392 }
393 }
394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 public static native String getDeviceName(int deviceId);
396 public static native int getDeviceClasses(int deviceId);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400397 public static native void addExcludedDevice(String deviceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 public static native boolean getAbsoluteInfo(int deviceId, int axis,
399 InputDevice.AbsoluteInfo outInfo);
400 public static native int getSwitchState(int sw);
401 public static native int getSwitchState(int deviceId, int sw);
Dianne Hackborn6af0d502009-09-28 13:25:46 -0700402 public static native int nativeGetScancodeState(int code);
403 public static native int nativeGetScancodeState(int deviceId, int code);
404 public static native int nativeGetKeycodeState(int code);
405 public static native int nativeGetKeycodeState(int deviceId, int code);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700406 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
408
409 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
410 long eventTime, boolean down, int keycode, int repeatCount,
411 int scancode, int flags) {
412 return new KeyEvent(
413 downTime, eventTime,
414 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
415 keycode, repeatCount,
416 device != null ? device.mMetaKeysState : 0,
417 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700418 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 }
420
421 Thread mThread = new Thread("InputDeviceReader") {
422 public void run() {
Dianne Hackbornbd0a81f2009-10-04 13:30:50 -0700423 if (DEBUG) Log.v(TAG, "InputDeviceReader.run()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 android.os.Process.setThreadPriority(
425 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
426
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700427 RawInputEvent ev = new RawInputEvent();
428 while (true) {
429 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 InputDevice di;
431
432 // block, doesn't release the monitor
433 readEvent(ev);
434
435 boolean send = false;
436 boolean configChanged = false;
437
438 if (false) {
439 Log.i(TAG, "Input event: dev=0x"
440 + Integer.toHexString(ev.deviceId)
441 + " type=0x" + Integer.toHexString(ev.type)
442 + " scancode=" + ev.scancode
443 + " keycode=" + ev.keycode
444 + " value=" + ev.value);
445 }
446
447 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
448 synchronized (mFirst) {
449 di = newInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700450 if (di.classes != 0) {
451 // If this device is some kind of input class,
452 // we care about it.
453 mDevices.put(ev.deviceId, di);
454 if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
455 readVirtualKeys(di.name);
456 }
457 // The configuration may have changed because
458 // of this device.
459 configChanged = true;
460 } else {
461 // We won't do anything with this device.
462 mIgnoredDevices.put(ev.deviceId, di);
463 Log.i(TAG, "Ignoring non-input device: id=0x"
464 + Integer.toHexString(di.id)
465 + ", name=" + di.name);
Iliyan Malchev75b2aed2009-08-06 14:50:57 -0700466 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 }
468 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
469 synchronized (mFirst) {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700470 if (false) {
471 Log.i(TAG, "Device removed: id=0x"
472 + Integer.toHexString(ev.deviceId));
473 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 di = mDevices.get(ev.deviceId);
475 if (di != null) {
476 mDevices.delete(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700477 // The configuration may have changed because
478 // of this device.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 configChanged = true;
Dianne Hackborna8f60182009-09-01 19:01:50 -0700480 } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
481 mIgnoredDevices.remove(ev.deviceId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 } else {
Dianne Hackborna8f60182009-09-01 19:01:50 -0700483 Log.w(TAG, "Removing bad device id: "
484 + Integer.toHexString(ev.deviceId));
485 continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487 }
488 } else {
489 di = getInputDevice(ev.deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -0700490 if (di == null) {
491 // This may be some junk from an ignored device.
492 continue;
493 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494
495 // first crack at it
496 send = preprocessEvent(di, ev);
497
498 if (ev.type == RawInputEvent.EV_KEY) {
499 di.mMetaKeysState = makeMetaState(ev.keycode,
500 ev.value != 0, di.mMetaKeysState);
501 mHaveGlobalMetaState = false;
502 }
503 }
504
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 if (configChanged) {
506 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700507 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
509 null);
510 }
511 }
512
513 if (!send) {
514 continue;
515 }
516
517 synchronized (mFirst) {
518 // NOTE: The event timebase absolutely must be the same
519 // timebase as SystemClock.uptimeMillis().
520 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
521 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700522 final long curTimeNano = System.nanoTime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
524
525 final int classes = di.classes;
526 final int type = ev.type;
527 final int scancode = ev.scancode;
528 send = false;
529
530 // Is it a key event?
531 if (type == RawInputEvent.EV_KEY &&
532 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
533 (scancode < RawInputEvent.BTN_FIRST ||
534 scancode > RawInputEvent.BTN_LAST)) {
535 boolean down;
536 if (ev.value != 0) {
537 down = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700538 di.mKeyDownTime = curTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 } else {
540 down = false;
541 }
542 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700543 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700545 newKeyEvent(di, di.mKeyDownTime, curTime, down,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 keycode, 0, scancode,
547 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
548 ? KeyEvent.FLAG_WOKE_HERE : 0));
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700549
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 } else if (ev.type == RawInputEvent.EV_KEY) {
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700551 // Single touch protocol: touch going down or up.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700553 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
554 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
555 == RawInputEvent.CLASS_TOUCHSCREEN) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700557 di.mAbs.mDown[0] = ev.value != 0;
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700558
559 // Trackball (mouse) protocol: press down or up.
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700560 } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
562 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700563 di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 send = true;
565 }
566
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700567 // Process position events from multitouch protocol.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 } else if (ev.type == RawInputEvent.EV_ABS &&
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700569 (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
570 if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
571 di.mAbs.changed = true;
572 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
573 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
574 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
575 di.mAbs.changed = true;
576 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
577 + MotionEvent.SAMPLE_X] = ev.value;
578 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
579 + di.mAbs.mAddingPointerOffset
580 + " X:" + ev.value);
581 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
582 di.mAbs.changed = true;
583 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
584 + MotionEvent.SAMPLE_Y] = ev.value;
585 if (DEBUG_POINTERS) Log.v(TAG, "MT @"
586 + di.mAbs.mAddingPointerOffset
587 + " Y:" + ev.value);
588 } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
589 di.mAbs.changed = true;
590 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
591 + MotionEvent.SAMPLE_SIZE] = ev.value;
592 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700593
594 // Process position events from single touch protocol.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700595 } else if (ev.type == RawInputEvent.EV_ABS &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
597 if (ev.scancode == RawInputEvent.ABS_X) {
598 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700599 di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 } else if (ev.scancode == RawInputEvent.ABS_Y) {
601 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700602 di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
604 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700605 di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
606 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700607 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
609 di.mAbs.changed = true;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700610 di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
611 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700612 + MotionEvent.SAMPLE_SIZE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 }
614
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700615 // Process movement events from trackball (mouse) protocol.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 } else if (ev.type == RawInputEvent.EV_REL &&
617 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
618 // Add this relative movement into our totals.
619 if (ev.scancode == RawInputEvent.REL_X) {
620 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700621 di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 } else if (ev.scancode == RawInputEvent.REL_Y) {
623 di.mRel.changed = true;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700624 di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 }
626 }
627
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700628 // Handle multitouch protocol sync: tells us that the
629 // driver has returned all data for -one- of the pointers
630 // that is currently down.
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 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700656
657 // Handle general event sync: all data for the current
658 // event update has been delivered.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700659 } else if (send || (ev.type == RawInputEvent.EV_SYN
660 && ev.scancode == RawInputEvent.SYN_REPORT)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 if (mDisplay != null) {
662 if (!mHaveGlobalMetaState) {
663 computeGlobalMetaStateLocked();
664 }
665
666 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700667
668 InputDevice.MotionState ms = di.mAbs;
669 if (ms.changed) {
670 ms.changed = false;
671
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700672 if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
673 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
674 == RawInputEvent.CLASS_TOUCHSCREEN) {
675 ms.mNextNumPointers = 0;
Dianne Hackborn2a2b34432009-08-12 17:13:55 -0700676 if (ms.mDown[0]) {
677 System.arraycopy(di.curTouchVals, 0,
678 ms.mNextData, 0,
679 MotionEvent.NUM_SAMPLE_DATA);
680 ms.mNextNumPointers++;
681 }
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700682 }
683
684 if (BAD_TOUCH_HACK) {
685 ms.dropBadPoint(di);
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700686 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700687
688 boolean doMotion = !monitorVirtualKey(di,
689 ev, curTime, curTimeNano);
690
691 if (doMotion && ms.mNextNumPointers > 0
692 && ms.mLastNumPointers == 0) {
693 doMotion = !generateVirtualKeyDown(di,
694 ev, curTime, curTimeNano);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700696
697 if (doMotion) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700698 // XXX Need to be able to generate
699 // multiple events here, for example
700 // if two fingers change up/down state
701 // at the same time.
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700702 do {
703 me = ms.generateAbsMotion(di, curTime,
704 curTimeNano, mDisplay,
705 mOrientation, mGlobalMetaState);
706 if (false) Log.v(TAG, "Absolute: x="
707 + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
708 + " y="
709 + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
710 + " ev=" + me);
711 if (me != null) {
712 if (WindowManagerPolicy.WATCH_POINTER) {
713 Log.i(TAG, "Enqueueing: " + me);
714 }
715 addLocked(di, curTimeNano, ev.flags,
716 RawInputEvent.CLASS_TOUCHSCREEN, me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700717 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700718 } while (ms.hasMore());
Dianne Hackborn1411d1c2009-10-12 23:21:18 -0700719 } else {
720 // We are consuming movement in the
721 // virtual key area... but still
722 // propagate this to the previous
723 // data for comparisons.
724 System.arraycopy(ms.mNextData, 0,
725 ms.mLastData, 0,
726 ms.mNextNumPointers
727 * MotionEvent.NUM_SAMPLE_DATA);
728 ms.mLastNumPointers = ms.mNextNumPointers;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700729 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700730
731 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700733
734 ms = di.mRel;
735 if (ms.changed) {
736 ms.changed = false;
737
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700738 me = ms.generateRelMotion(di, curTime,
739 curTimeNano,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700740 mOrientation, mGlobalMetaState);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700741 if (false) Log.v(TAG, "Relative: x="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700742 + di.mRel.mNextData[MotionEvent.SAMPLE_X]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700743 + " y="
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700744 + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700745 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700746 if (me != null) {
747 addLocked(di, curTimeNano, ev.flags,
748 RawInputEvent.CLASS_TRACKBALL, me);
749 }
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700750
751 ms.finish();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 }
753 }
754 }
755 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700756
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700757 } catch (RuntimeException exc) {
758 Log.e(TAG, "InputReaderThread uncaught exception", exc);
759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
761 }
762 };
763
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700764 private boolean isInsideDisplay(InputDevice dev) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700765 final InputDevice.AbsoluteInfo absx = dev.absX;
766 final InputDevice.AbsoluteInfo absy = dev.absY;
767 final InputDevice.MotionState absm = dev.mAbs;
768 if (absx == null || absy == null || absm == null) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700769 return true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700770 }
771
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700772 if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
773 && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
774 && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
775 && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700776 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700777 + absm.mNextData[MotionEvent.SAMPLE_X]
778 + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700779 + ") inside of display");
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700780 return true;
781 }
782
783 return false;
784 }
785
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700786 private VirtualKey findVirtualKey(InputDevice dev) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700787 final int N = mVirtualKeys.size();
788 if (N <= 0) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700789 return null;
790 }
791
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700792 final InputDevice.MotionState absm = dev.mAbs;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700793 for (int i=0; i<N; i++) {
794 VirtualKey sb = mVirtualKeys.get(i);
795 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700796 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test ("
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700797 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
798 + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700799 + sb.scancode + " - (" + sb.hitLeft
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700800 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
801 + sb.hitBottom + ")");
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700802 if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
803 absm.mNextData[MotionEvent.SAMPLE_Y])) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700804 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
805 return sb;
806 }
807 }
808
809 return null;
810 }
811
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -0700812 private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
813 long curTime, long curTimeNano) {
814 if (isInsideDisplay(di)) {
815 // Didn't consume event.
816 return false;
817 }
818
819
820 VirtualKey vk = findVirtualKey(di);
821 if (vk != null) {
822 final InputDevice.MotionState ms = di.mAbs;
823 mPressedVirtualKey = vk;
824 vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
825 ms.mLastNumPointers = ms.mNextNumPointers;
826 di.mKeyDownTime = curTime;
827 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
828 "Generate key down for: " + vk.scancode
829 + " (keycode=" + vk.lastKeycode + ")");
830 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
831 vk.lastKeycode, 0, vk.scancode,
832 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
833 mHapticFeedbackCallback.virtualKeyFeedback(event);
834 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
835 event);
836 }
837
838 // We always consume the event, even if we didn't
839 // generate a key event. There are two reasons for
840 // this: to avoid spurious touches when holding
841 // the edges of the device near the touchscreen,
842 // and to avoid reporting events if there are virtual
843 // keys on the touchscreen outside of the display
844 // area.
845 // Note that for all of this we are only looking at the
846 // first pointer, since what we are handling here is the
847 // first pointer going down, and this is the coordinate
848 // that will be used to dispatch the event.
849 if (false) {
850 final InputDevice.AbsoluteInfo absx = di.absX;
851 final InputDevice.AbsoluteInfo absy = di.absY;
852 final InputDevice.MotionState absm = di.mAbs;
853 Log.v(TAG, "Rejecting ("
854 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
855 + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
856 + absx.minValue + "," + absy.minValue
857 + ")-(" + absx.maxValue + ","
858 + absx.maxValue + ")");
859 }
860 return true;
861 }
862
863 private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
864 long curTime, long curTimeNano) {
865 VirtualKey vk = mPressedVirtualKey;
866 if (vk == null) {
867 return false;
868 }
869
870 final InputDevice.MotionState ms = di.mAbs;
871 if (ms.mNextNumPointers <= 0) {
872 mPressedVirtualKey = null;
873 ms.mLastNumPointers = 0;
874 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Generate key up for: " + vk.scancode);
875 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
876 vk.lastKeycode, 0, vk.scancode,
877 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
878 mHapticFeedbackCallback.virtualKeyFeedback(event);
879 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
880 event);
881 return true;
882
883 } else if (isInsideDisplay(di)) {
884 // Whoops the pointer has moved into
885 // the display area! Cancel the
886 // virtual key and start a pointer
887 // motion.
888 mPressedVirtualKey = null;
889 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Cancel key up for: " + vk.scancode);
890 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
891 vk.lastKeycode, 0, vk.scancode,
892 KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
893 mHapticFeedbackCallback.virtualKeyFeedback(event);
894 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
895 event);
896 ms.mLastNumPointers = 0;
897 return false;
898 }
899
900 return true;
901 }
902
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 /**
904 * Returns a new meta state for the given keys and old state.
905 */
906 private static final int makeMetaState(int keycode, boolean down, int old) {
907 int mask;
908 switch (keycode) {
909 case KeyEvent.KEYCODE_ALT_LEFT:
910 mask = KeyEvent.META_ALT_LEFT_ON;
911 break;
912 case KeyEvent.KEYCODE_ALT_RIGHT:
913 mask = KeyEvent.META_ALT_RIGHT_ON;
914 break;
915 case KeyEvent.KEYCODE_SHIFT_LEFT:
916 mask = KeyEvent.META_SHIFT_LEFT_ON;
917 break;
918 case KeyEvent.KEYCODE_SHIFT_RIGHT:
919 mask = KeyEvent.META_SHIFT_RIGHT_ON;
920 break;
921 case KeyEvent.KEYCODE_SYM:
922 mask = KeyEvent.META_SYM_ON;
923 break;
924 default:
925 return old;
926 }
927 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
928 & (down ? (old | mask) : (old & ~mask));
929 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
930 result |= KeyEvent.META_ALT_ON;
931 }
932 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
933 result |= KeyEvent.META_SHIFT_ON;
934 }
935 return result;
936 }
937
938 private void computeGlobalMetaStateLocked() {
939 int i = mDevices.size();
940 mGlobalMetaState = 0;
941 while ((--i) >= 0) {
942 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
943 }
944 mHaveGlobalMetaState = true;
945 }
946
947 /*
948 * Return true if you want the event to get passed on to the
949 * rest of the system, and false if you've handled it and want
950 * it dropped.
951 */
952 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
953
954 InputDevice getInputDevice(int deviceId) {
955 synchronized (mFirst) {
956 return getInputDeviceLocked(deviceId);
957 }
958 }
959
960 private InputDevice getInputDeviceLocked(int deviceId) {
961 return mDevices.get(deviceId);
962 }
963
964 public void setOrientation(int orientation) {
965 synchronized(mFirst) {
966 mOrientation = orientation;
967 switch (orientation) {
968 case Surface.ROTATION_90:
969 mKeyRotationMap = KEY_90_MAP;
970 break;
971 case Surface.ROTATION_180:
972 mKeyRotationMap = KEY_180_MAP;
973 break;
974 case Surface.ROTATION_270:
975 mKeyRotationMap = KEY_270_MAP;
976 break;
977 default:
978 mKeyRotationMap = null;
979 break;
980 }
981 }
982 }
983
984 public int rotateKeyCode(int keyCode) {
985 synchronized(mFirst) {
986 return rotateKeyCodeLocked(keyCode);
987 }
988 }
989
990 private int rotateKeyCodeLocked(int keyCode) {
991 int[] map = mKeyRotationMap;
992 if (map != null) {
993 final int N = map.length;
994 for (int i=0; i<N; i+=2) {
995 if (map[i] == keyCode) {
996 return map[i+1];
997 }
998 }
999 }
1000 return keyCode;
1001 }
1002
1003 boolean hasEvents() {
1004 synchronized (mFirst) {
1005 return mFirst.next != mLast;
1006 }
1007 }
1008
1009 /*
1010 * returns true if we returned an event, and false if we timed out
1011 */
1012 QueuedEvent getEvent(long timeoutMS) {
1013 long begin = SystemClock.uptimeMillis();
1014 final long end = begin+timeoutMS;
1015 long now = begin;
1016 synchronized (mFirst) {
1017 while (mFirst.next == mLast && end > now) {
1018 try {
1019 mWakeLock.release();
1020 mFirst.wait(end-now);
1021 }
1022 catch (InterruptedException e) {
1023 }
1024 now = SystemClock.uptimeMillis();
1025 if (begin > now) {
1026 begin = now;
1027 }
1028 }
1029 if (mFirst.next == mLast) {
1030 return null;
1031 }
1032 QueuedEvent p = mFirst.next;
1033 mFirst.next = p.next;
1034 mFirst.next.prev = mFirst;
1035 p.inQueue = false;
1036 return p;
1037 }
1038 }
1039
Dianne Hackborn83fe3f52009-09-12 23:38:30 -07001040 /**
1041 * Return true if the queue has an up event pending that corresponds
1042 * to the same key as the given key event.
1043 */
1044 boolean hasKeyUpEvent(KeyEvent origEvent) {
1045 synchronized (mFirst) {
1046 final int keyCode = origEvent.getKeyCode();
1047 QueuedEvent cur = mLast.prev;
1048 while (cur.prev != null) {
1049 if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
1050 KeyEvent ke = (KeyEvent)cur.event;
1051 if (ke.getAction() == KeyEvent.ACTION_UP
1052 && ke.getKeyCode() == keyCode) {
1053 return true;
1054 }
1055 }
1056 cur = cur.prev;
1057 }
1058 }
1059
1060 return false;
1061 }
1062
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 void recycleEvent(QueuedEvent ev) {
1064 synchronized (mFirst) {
1065 //Log.i(TAG, "Recycle event: " + ev);
1066 if (ev.event == ev.inputDevice.mAbs.currentMove) {
1067 ev.inputDevice.mAbs.currentMove = null;
1068 }
1069 if (ev.event == ev.inputDevice.mRel.currentMove) {
1070 if (false) Log.i(TAG, "Detach rel " + ev.event);
1071 ev.inputDevice.mRel.currentMove = null;
Dianne Hackborn0dd7cb42009-08-04 05:49:43 -07001072 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
1073 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 }
1075 recycleLocked(ev);
1076 }
1077 }
1078
1079 void filterQueue(FilterCallback cb) {
1080 synchronized (mFirst) {
1081 QueuedEvent cur = mLast.prev;
1082 while (cur.prev != null) {
1083 switch (cb.filterEvent(cur)) {
1084 case FILTER_REMOVE:
1085 cur.prev.next = cur.next;
1086 cur.next.prev = cur.prev;
1087 break;
1088 case FILTER_ABORT:
1089 return;
1090 }
1091 cur = cur.prev;
1092 }
1093 }
1094 }
1095
Michael Chan53071d62009-05-13 17:29:48 -07001096 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 int flags, int classType, Object event) {
1098 QueuedEvent ev;
1099 if (mCacheCount == 0) {
1100 ev = new QueuedEvent();
1101 } else {
1102 ev = mCache;
1103 ev.inQueue = false;
1104 mCache = ev.next;
1105 mCacheCount--;
1106 }
1107 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -07001108 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 ev.flags = flags;
1110 ev.classType = classType;
1111 ev.event = event;
1112 return ev;
1113 }
1114
1115 private void recycleLocked(QueuedEvent ev) {
1116 if (ev.inQueue) {
1117 throw new RuntimeException("Event already in queue!");
1118 }
1119 if (mCacheCount < 10) {
1120 mCacheCount++;
1121 ev.next = mCache;
1122 mCache = ev;
1123 ev.inQueue = true;
1124 }
1125 }
1126
Michael Chan53071d62009-05-13 17:29:48 -07001127 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 int classType, Object event) {
1129 boolean poke = mFirst.next == mLast;
1130
Michael Chan53071d62009-05-13 17:29:48 -07001131 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -07001133 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 p = p.prev;
1135 }
1136
1137 ev.next = p.next;
1138 ev.prev = p;
1139 p.next = ev;
1140 ev.next.prev = ev;
1141 ev.inQueue = true;
1142
1143 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -07001144 long time;
1145 if (MEASURE_LATENCY) {
1146 time = System.nanoTime();
1147 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 mFirst.notify();
1149 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -07001150 if (MEASURE_LATENCY) {
1151 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
1152 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 }
1154 }
1155
1156 private InputDevice newInputDevice(int deviceId) {
1157 int classes = getDeviceClasses(deviceId);
1158 String name = getDeviceName(deviceId);
Dianne Hackborna8f60182009-09-01 19:01:50 -07001159 InputDevice.AbsoluteInfo absX = null;
1160 InputDevice.AbsoluteInfo absY = null;
1161 InputDevice.AbsoluteInfo absPressure = null;
1162 InputDevice.AbsoluteInfo absSize = null;
1163 if (classes != 0) {
1164 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
1165 + ", name=" + name
1166 + ", classes=" + Integer.toHexString(classes));
1167 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
1168 absX = loadAbsoluteInfo(deviceId,
1169 RawInputEvent.ABS_MT_POSITION_X, "X");
1170 absY = loadAbsoluteInfo(deviceId,
1171 RawInputEvent.ABS_MT_POSITION_Y, "Y");
1172 absPressure = loadAbsoluteInfo(deviceId,
1173 RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
1174 absSize = loadAbsoluteInfo(deviceId,
1175 RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
1176 } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
1177 absX = loadAbsoluteInfo(deviceId,
1178 RawInputEvent.ABS_X, "X");
1179 absY = loadAbsoluteInfo(deviceId,
1180 RawInputEvent.ABS_Y, "Y");
1181 absPressure = loadAbsoluteInfo(deviceId,
1182 RawInputEvent.ABS_PRESSURE, "Pressure");
1183 absSize = loadAbsoluteInfo(deviceId,
1184 RawInputEvent.ABS_TOOL_WIDTH, "Size");
1185 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 }
1187
1188 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
1189 }
1190
1191 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
1192 String name) {
1193 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
1194 if (getAbsoluteInfo(id, channel, info)
1195 && info.minValue != info.maxValue) {
1196 Log.i(TAG, " " + name + ": min=" + info.minValue
1197 + " max=" + info.maxValue
1198 + " flat=" + info.flat
1199 + " fuzz=" + info.fuzz);
1200 info.range = info.maxValue-info.minValue;
1201 return info;
1202 }
1203 Log.i(TAG, " " + name + ": unknown values");
1204 return null;
1205 }
1206 private static native boolean readEvent(RawInputEvent outEvent);
1207}