blob: fd6b813e7adabb1ffe7cdba99e3e3df2f5865895 [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;
Michael Chan53071d62009-05-13 17:29:48 -070021import android.os.LatencyTimer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.os.PowerManager;
Michael Chan53071d62009-05-13 17:29:48 -070023import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.util.Log;
25import android.util.SparseArray;
26import android.view.Display;
27import android.view.KeyEvent;
28import android.view.MotionEvent;
29import android.view.RawInputEvent;
30import android.view.Surface;
31import android.view.WindowManagerPolicy;
32
Dianne Hackborne3dd8842009-07-14 12:06:54 -070033import java.io.BufferedReader;
34import java.io.FileInputStream;
35import java.io.FileNotFoundException;
36import java.io.IOException;
37import java.io.InputStreamReader;
38import java.util.ArrayList;
39
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040public abstract class KeyInputQueue {
41 static final String TAG = "KeyInputQueue";
42
Dianne Hackborne3dd8842009-07-14 12:06:54 -070043 static final boolean DEBUG_VIRTUAL_KEYS = false;
44
45 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
46 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48 int mGlobalMetaState = 0;
49 boolean mHaveGlobalMetaState = false;
50
51 final QueuedEvent mFirst;
52 final QueuedEvent mLast;
53 QueuedEvent mCache;
54 int mCacheCount;
55
56 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070057 int mDisplayWidth;
58 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
60 int mOrientation = Surface.ROTATION_0;
61 int[] mKeyRotationMap = null;
62
Dianne Hackborne3dd8842009-07-14 12:06:54 -070063 VirtualKey mPressedVirtualKey = null;
64
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 PowerManager.WakeLock mWakeLock;
66
67 static final int[] KEY_90_MAP = new int[] {
68 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
69 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
70 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
71 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
72 };
73
74 static final int[] KEY_180_MAP = new int[] {
75 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
76 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
77 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
78 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
79 };
80
81 static final int[] KEY_270_MAP = new int[] {
82 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
83 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
84 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
85 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
86 };
87
88 public static final int FILTER_REMOVE = 0;
89 public static final int FILTER_KEEP = 1;
90 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -070091
92 private static final boolean MEASURE_LATENCY = false;
93 private LatencyTimer lt;
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 public interface FilterCallback {
96 int filterEvent(QueuedEvent ev);
97 }
98
99 static class QueuedEvent {
100 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700101 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 int flags; // From the raw event
103 int classType; // One of the class constants in InputEvent
104 Object event;
105 boolean inQueue;
106
107 void copyFrom(QueuedEvent that) {
108 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700109 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 this.flags = that.flags;
111 this.classType = that.classType;
112 this.event = that.event;
113 }
114
115 @Override
116 public String toString() {
117 return "QueuedEvent{"
118 + Integer.toHexString(System.identityHashCode(this))
119 + " " + event + "}";
120 }
121
122 // not copied
123 QueuedEvent prev;
124 QueuedEvent next;
125 }
126
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700127 /**
128 * A key that exists as a part of the touch-screen, outside of the normal
129 * display area of the screen.
130 */
131 static class VirtualKey {
132 int scancode;
133 int centerx;
134 int centery;
135 int width;
136 int height;
137
138 int hitLeft;
139 int hitTop;
140 int hitRight;
141 int hitBottom;
142
143 InputDevice lastDevice;
144 int lastKeycode;
145
146 boolean checkHit(int x, int y) {
147 return (x >= hitLeft && x <= hitRight
148 && y >= hitTop && y <= hitBottom);
149 }
150
151 void computeHitRect(InputDevice dev, int dw, int dh) {
152 if (dev == lastDevice) {
153 return;
154 }
155
156 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
157 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
158
159 lastDevice = dev;
160
161 int minx = dev.absX.minValue;
162 int maxx = dev.absX.maxValue;
163
164 int halfw = width/2;
165 int left = centerx - halfw;
166 int right = centerx + halfw;
167 hitLeft = minx + ((left*maxx-minx)/dw);
168 hitRight = minx + ((right*maxx-minx)/dw);
169
170 int miny = dev.absY.minValue;
171 int maxy = dev.absY.maxValue;
172
173 int halfh = height/2;
174 int top = centery - halfh;
175 int bottom = centery + halfh;
176 hitTop = miny + ((top*maxy-miny)/dh);
177 hitBottom = miny + ((bottom*maxy-miny)/dh);
178 }
179 }
180
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 KeyInputQueue(Context context) {
Michael Chan53071d62009-05-13 17:29:48 -0700182 if (MEASURE_LATENCY) {
183 lt = new LatencyTimer(100, 1000);
184 }
185
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700186 try {
187 FileInputStream fis = new FileInputStream(
188 "/sys/board_properties/virtualkeys.synaptics-rmi-touchscreen");
189 InputStreamReader isr = new InputStreamReader(fis);
190 BufferedReader br = new BufferedReader(isr);
191 String str = br.readLine();
192 if (str != null) {
193 String[] it = str.split(":");
194 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
195 final int N = it.length-6;
196 for (int i=0; i<=N; i+=6) {
197 if (!"0x01".equals(it[i])) {
198 Log.w(TAG, "Unknown virtual key type at elem #" + i
199 + ": " + it[i]);
200 continue;
201 }
202 try {
203 VirtualKey sb = new VirtualKey();
204 sb.scancode = Integer.parseInt(it[i+1]);
205 sb.centerx = Integer.parseInt(it[i+2]);
206 sb.centery = Integer.parseInt(it[i+3]);
207 sb.width = Integer.parseInt(it[i+4]);
208 sb.height = Integer.parseInt(it[i+5]);
209 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
210 + sb.scancode + ": center=" + sb.centerx + ","
211 + sb.centery + " size=" + sb.width + "x"
212 + sb.height);
213 mVirtualKeys.add(sb);
214 } catch (NumberFormatException e) {
215 Log.w(TAG, "Bad number at region " + i + " in: "
216 + str, e);
217 }
218 }
219 }
220 br.close();
221 } catch (FileNotFoundException e) {
222 Log.i(TAG, "No virtual keys found");
223 } catch (IOException e) {
224 Log.w(TAG, "Error reading virtual keys", e);
225 }
226
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 PowerManager pm = (PowerManager)context.getSystemService(
228 Context.POWER_SERVICE);
229 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
230 "KeyInputQueue");
231 mWakeLock.setReferenceCounted(false);
232
233 mFirst = new QueuedEvent();
234 mLast = new QueuedEvent();
235 mFirst.next = mLast;
236 mLast.prev = mFirst;
237
238 mThread.start();
239 }
240
241 public void setDisplay(Display display) {
242 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700243
244 // We assume at this point that the display dimensions reflect the
245 // natural, unrotated display. We will perform hit tests for soft
246 // buttons based on that display.
247 mDisplayWidth = display.getWidth();
248 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 }
250
251 public void getInputConfiguration(Configuration config) {
252 synchronized (mFirst) {
253 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
254 config.keyboard = Configuration.KEYBOARD_NOKEYS;
255 config.navigation = Configuration.NAVIGATION_NONAV;
256
257 final int N = mDevices.size();
258 for (int i=0; i<N; i++) {
259 InputDevice d = mDevices.valueAt(i);
260 if (d != null) {
261 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
262 config.touchscreen
263 = Configuration.TOUCHSCREEN_FINGER;
264 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
265 }
266 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
267 config.keyboard
268 = Configuration.KEYBOARD_QWERTY;
269 //Log.i("foo", "***** HAVE QWERTY!");
270 }
271 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
272 config.navigation
273 = Configuration.NAVIGATION_TRACKBALL;
274 //Log.i("foo", "***** HAVE TRACKBALL!");
275 }
276 }
277 }
278 }
279 }
280
281 public static native String getDeviceName(int deviceId);
282 public static native int getDeviceClasses(int deviceId);
283 public static native boolean getAbsoluteInfo(int deviceId, int axis,
284 InputDevice.AbsoluteInfo outInfo);
285 public static native int getSwitchState(int sw);
286 public static native int getSwitchState(int deviceId, int sw);
287 public static native int getScancodeState(int sw);
288 public static native int getScancodeState(int deviceId, int sw);
289 public static native int getKeycodeState(int sw);
290 public static native int getKeycodeState(int deviceId, int sw);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700291 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
293
294 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
295 long eventTime, boolean down, int keycode, int repeatCount,
296 int scancode, int flags) {
297 return new KeyEvent(
298 downTime, eventTime,
299 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
300 keycode, repeatCount,
301 device != null ? device.mMetaKeysState : 0,
302 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700303 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 }
305
306 Thread mThread = new Thread("InputDeviceReader") {
307 public void run() {
308 android.os.Process.setThreadPriority(
309 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
310
311 try {
312 RawInputEvent ev = new RawInputEvent();
313 while (true) {
314 InputDevice di;
315
316 // block, doesn't release the monitor
317 readEvent(ev);
318
319 boolean send = false;
320 boolean configChanged = false;
321
322 if (false) {
323 Log.i(TAG, "Input event: dev=0x"
324 + Integer.toHexString(ev.deviceId)
325 + " type=0x" + Integer.toHexString(ev.type)
326 + " scancode=" + ev.scancode
327 + " keycode=" + ev.keycode
328 + " value=" + ev.value);
329 }
330
331 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
332 synchronized (mFirst) {
333 di = newInputDevice(ev.deviceId);
334 mDevices.put(ev.deviceId, di);
335 configChanged = true;
336 }
337 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
338 synchronized (mFirst) {
339 Log.i(TAG, "Device removed: id=0x"
340 + Integer.toHexString(ev.deviceId));
341 di = mDevices.get(ev.deviceId);
342 if (di != null) {
343 mDevices.delete(ev.deviceId);
344 configChanged = true;
345 } else {
346 Log.w(TAG, "Bad device id: " + ev.deviceId);
347 }
348 }
349 } else {
350 di = getInputDevice(ev.deviceId);
351
352 // first crack at it
353 send = preprocessEvent(di, ev);
354
355 if (ev.type == RawInputEvent.EV_KEY) {
356 di.mMetaKeysState = makeMetaState(ev.keycode,
357 ev.value != 0, di.mMetaKeysState);
358 mHaveGlobalMetaState = false;
359 }
360 }
361
362 if (di == null) {
363 continue;
364 }
365
366 if (configChanged) {
367 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700368 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
370 null);
371 }
372 }
373
374 if (!send) {
375 continue;
376 }
377
378 synchronized (mFirst) {
379 // NOTE: The event timebase absolutely must be the same
380 // timebase as SystemClock.uptimeMillis().
381 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
382 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700383 final long curTimeNano = System.nanoTime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
385
386 final int classes = di.classes;
387 final int type = ev.type;
388 final int scancode = ev.scancode;
389 send = false;
390
391 // Is it a key event?
392 if (type == RawInputEvent.EV_KEY &&
393 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
394 (scancode < RawInputEvent.BTN_FIRST ||
395 scancode > RawInputEvent.BTN_LAST)) {
396 boolean down;
397 if (ev.value != 0) {
398 down = true;
399 di.mDownTime = curTime;
400 } else {
401 down = false;
402 }
403 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700404 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 RawInputEvent.CLASS_KEYBOARD,
406 newKeyEvent(di, di.mDownTime, curTime, down,
407 keycode, 0, scancode,
408 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
409 ? KeyEvent.FLAG_WOKE_HERE : 0));
410 } else if (ev.type == RawInputEvent.EV_KEY) {
411 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
412 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
413 di.mAbs.changed = true;
414 di.mAbs.down = ev.value != 0;
415 }
416 if (ev.scancode == RawInputEvent.BTN_MOUSE &&
417 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
418 di.mRel.changed = true;
419 di.mRel.down = ev.value != 0;
420 send = true;
421 }
422
423 } else if (ev.type == RawInputEvent.EV_ABS &&
424 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
425 if (ev.scancode == RawInputEvent.ABS_X) {
426 di.mAbs.changed = true;
427 di.mAbs.x = ev.value;
428 } else if (ev.scancode == RawInputEvent.ABS_Y) {
429 di.mAbs.changed = true;
430 di.mAbs.y = ev.value;
431 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
432 di.mAbs.changed = true;
433 di.mAbs.pressure = ev.value;
434 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
435 di.mAbs.changed = true;
436 di.mAbs.size = ev.value;
437 }
438
439 } else if (ev.type == RawInputEvent.EV_REL &&
440 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
441 // Add this relative movement into our totals.
442 if (ev.scancode == RawInputEvent.REL_X) {
443 di.mRel.changed = true;
444 di.mRel.x += ev.value;
445 } else if (ev.scancode == RawInputEvent.REL_Y) {
446 di.mRel.changed = true;
447 di.mRel.y += ev.value;
448 }
449 }
450
451 if (send || ev.type == RawInputEvent.EV_SYN) {
452 if (mDisplay != null) {
453 if (!mHaveGlobalMetaState) {
454 computeGlobalMetaStateLocked();
455 }
456
457 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700458
459 InputDevice.MotionState ms = di.mAbs;
460 if (ms.changed) {
461 ms.changed = false;
462
463 boolean doMotion = true;
464
465 // Look for virtual buttons.
466 VirtualKey vk = mPressedVirtualKey;
467 if (vk != null) {
468 doMotion = false;
469 if (!ms.down) {
470 mPressedVirtualKey = null;
471 ms.lastDown = ms.down;
472 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
473 "Generate key up for: " + vk.scancode);
474 addLocked(di, curTimeNano, ev.flags,
475 RawInputEvent.CLASS_KEYBOARD,
476 newKeyEvent(di, di.mDownTime,
477 curTime, false,
478 vk.lastKeycode,
479 0, vk.scancode, 0));
480 }
481 } else if (ms.down && !ms.lastDown) {
482 vk = findSoftButton(di);
483 if (vk != null) {
484 doMotion = false;
485 mPressedVirtualKey = vk;
486 vk.lastKeycode = scancodeToKeycode(
487 di.id, vk.scancode);
488 ms.lastDown = ms.down;
489 di.mDownTime = curTime;
490 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
491 "Generate key down for: " + vk.scancode
492 + " (keycode=" + vk.lastKeycode + ")");
493 addLocked(di, curTimeNano, ev.flags,
494 RawInputEvent.CLASS_KEYBOARD,
495 newKeyEvent(di, di.mDownTime,
496 curTime, true,
497 vk.lastKeycode, 0,
498 vk.scancode, 0));
499 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700501
502 if (doMotion) {
503 me = ms.generateMotion(di, curTime,
504 curTimeNano, true, mDisplay,
505 mOrientation, mGlobalMetaState);
506 if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
507 + " y=" + di.mAbs.y + " ev=" + me);
508 if (me != null) {
509 if (WindowManagerPolicy.WATCH_POINTER) {
510 Log.i(TAG, "Enqueueing: " + me);
511 }
512 addLocked(di, curTimeNano, ev.flags,
513 RawInputEvent.CLASS_TOUCHSCREEN, me);
514 }
515 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700517
518 ms = di.mRel;
519 if (ms.changed) {
520 ms.changed = false;
521
522 me = ms.generateMotion(di, curTime,
523 curTimeNano, false, mDisplay,
524 mOrientation, mGlobalMetaState);
525 if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
526 + " y=" + di.mRel.y + " ev=" + me);
527 if (me != null) {
528 addLocked(di, curTimeNano, ev.flags,
529 RawInputEvent.CLASS_TRACKBALL, me);
530 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 }
532 }
533 }
534 }
535 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700536
537 } catch (RuntimeException exc) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 Log.e(TAG, "InputReaderThread uncaught exception", exc);
539 }
540 }
541 };
542
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700543 private VirtualKey findSoftButton(InputDevice dev) {
544 final int N = mVirtualKeys.size();
545 if (N <= 0) {
546 return null;
547 }
548
549 final InputDevice.AbsoluteInfo absx = dev.absX;
550 final InputDevice.AbsoluteInfo absy = dev.absY;
551 final InputDevice.MotionState absm = dev.mAbs;
552 if (absx == null || absy == null || absm == null) {
553 return null;
554 }
555
556 if (absm.x >= absx.minValue && absm.x <= absx.maxValue
557 && absm.y >= absy.minValue && absm.y <= absy.maxValue) {
558 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input (" + absm.x
559 + "," + absm.y + ") inside of display");
560 return null;
561 }
562
563 for (int i=0; i<N; i++) {
564 VirtualKey sb = mVirtualKeys.get(i);
565 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
566 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test (" + absm.x + ","
567 + absm.y + ") in code " + sb.scancode + " - (" + sb.hitLeft
568 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
569 + sb.hitBottom + ")");
570 if (sb.checkHit(absm.x, absm.y)) {
571 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
572 return sb;
573 }
574 }
575
576 return null;
577 }
578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 /**
580 * Returns a new meta state for the given keys and old state.
581 */
582 private static final int makeMetaState(int keycode, boolean down, int old) {
583 int mask;
584 switch (keycode) {
585 case KeyEvent.KEYCODE_ALT_LEFT:
586 mask = KeyEvent.META_ALT_LEFT_ON;
587 break;
588 case KeyEvent.KEYCODE_ALT_RIGHT:
589 mask = KeyEvent.META_ALT_RIGHT_ON;
590 break;
591 case KeyEvent.KEYCODE_SHIFT_LEFT:
592 mask = KeyEvent.META_SHIFT_LEFT_ON;
593 break;
594 case KeyEvent.KEYCODE_SHIFT_RIGHT:
595 mask = KeyEvent.META_SHIFT_RIGHT_ON;
596 break;
597 case KeyEvent.KEYCODE_SYM:
598 mask = KeyEvent.META_SYM_ON;
599 break;
600 default:
601 return old;
602 }
603 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
604 & (down ? (old | mask) : (old & ~mask));
605 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
606 result |= KeyEvent.META_ALT_ON;
607 }
608 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
609 result |= KeyEvent.META_SHIFT_ON;
610 }
611 return result;
612 }
613
614 private void computeGlobalMetaStateLocked() {
615 int i = mDevices.size();
616 mGlobalMetaState = 0;
617 while ((--i) >= 0) {
618 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
619 }
620 mHaveGlobalMetaState = true;
621 }
622
623 /*
624 * Return true if you want the event to get passed on to the
625 * rest of the system, and false if you've handled it and want
626 * it dropped.
627 */
628 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
629
630 InputDevice getInputDevice(int deviceId) {
631 synchronized (mFirst) {
632 return getInputDeviceLocked(deviceId);
633 }
634 }
635
636 private InputDevice getInputDeviceLocked(int deviceId) {
637 return mDevices.get(deviceId);
638 }
639
640 public void setOrientation(int orientation) {
641 synchronized(mFirst) {
642 mOrientation = orientation;
643 switch (orientation) {
644 case Surface.ROTATION_90:
645 mKeyRotationMap = KEY_90_MAP;
646 break;
647 case Surface.ROTATION_180:
648 mKeyRotationMap = KEY_180_MAP;
649 break;
650 case Surface.ROTATION_270:
651 mKeyRotationMap = KEY_270_MAP;
652 break;
653 default:
654 mKeyRotationMap = null;
655 break;
656 }
657 }
658 }
659
660 public int rotateKeyCode(int keyCode) {
661 synchronized(mFirst) {
662 return rotateKeyCodeLocked(keyCode);
663 }
664 }
665
666 private int rotateKeyCodeLocked(int keyCode) {
667 int[] map = mKeyRotationMap;
668 if (map != null) {
669 final int N = map.length;
670 for (int i=0; i<N; i+=2) {
671 if (map[i] == keyCode) {
672 return map[i+1];
673 }
674 }
675 }
676 return keyCode;
677 }
678
679 boolean hasEvents() {
680 synchronized (mFirst) {
681 return mFirst.next != mLast;
682 }
683 }
684
685 /*
686 * returns true if we returned an event, and false if we timed out
687 */
688 QueuedEvent getEvent(long timeoutMS) {
689 long begin = SystemClock.uptimeMillis();
690 final long end = begin+timeoutMS;
691 long now = begin;
692 synchronized (mFirst) {
693 while (mFirst.next == mLast && end > now) {
694 try {
695 mWakeLock.release();
696 mFirst.wait(end-now);
697 }
698 catch (InterruptedException e) {
699 }
700 now = SystemClock.uptimeMillis();
701 if (begin > now) {
702 begin = now;
703 }
704 }
705 if (mFirst.next == mLast) {
706 return null;
707 }
708 QueuedEvent p = mFirst.next;
709 mFirst.next = p.next;
710 mFirst.next.prev = mFirst;
711 p.inQueue = false;
712 return p;
713 }
714 }
715
716 void recycleEvent(QueuedEvent ev) {
717 synchronized (mFirst) {
718 //Log.i(TAG, "Recycle event: " + ev);
719 if (ev.event == ev.inputDevice.mAbs.currentMove) {
720 ev.inputDevice.mAbs.currentMove = null;
721 }
722 if (ev.event == ev.inputDevice.mRel.currentMove) {
723 if (false) Log.i(TAG, "Detach rel " + ev.event);
724 ev.inputDevice.mRel.currentMove = null;
725 ev.inputDevice.mRel.x = 0;
726 ev.inputDevice.mRel.y = 0;
727 }
728 recycleLocked(ev);
729 }
730 }
731
732 void filterQueue(FilterCallback cb) {
733 synchronized (mFirst) {
734 QueuedEvent cur = mLast.prev;
735 while (cur.prev != null) {
736 switch (cb.filterEvent(cur)) {
737 case FILTER_REMOVE:
738 cur.prev.next = cur.next;
739 cur.next.prev = cur.prev;
740 break;
741 case FILTER_ABORT:
742 return;
743 }
744 cur = cur.prev;
745 }
746 }
747 }
748
Michael Chan53071d62009-05-13 17:29:48 -0700749 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 int flags, int classType, Object event) {
751 QueuedEvent ev;
752 if (mCacheCount == 0) {
753 ev = new QueuedEvent();
754 } else {
755 ev = mCache;
756 ev.inQueue = false;
757 mCache = ev.next;
758 mCacheCount--;
759 }
760 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -0700761 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 ev.flags = flags;
763 ev.classType = classType;
764 ev.event = event;
765 return ev;
766 }
767
768 private void recycleLocked(QueuedEvent ev) {
769 if (ev.inQueue) {
770 throw new RuntimeException("Event already in queue!");
771 }
772 if (mCacheCount < 10) {
773 mCacheCount++;
774 ev.next = mCache;
775 mCache = ev;
776 ev.inQueue = true;
777 }
778 }
779
Michael Chan53071d62009-05-13 17:29:48 -0700780 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 int classType, Object event) {
782 boolean poke = mFirst.next == mLast;
783
Michael Chan53071d62009-05-13 17:29:48 -0700784 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -0700786 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 p = p.prev;
788 }
789
790 ev.next = p.next;
791 ev.prev = p;
792 p.next = ev;
793 ev.next.prev = ev;
794 ev.inQueue = true;
795
796 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -0700797 long time;
798 if (MEASURE_LATENCY) {
799 time = System.nanoTime();
800 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 mFirst.notify();
802 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -0700803 if (MEASURE_LATENCY) {
804 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
805 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 }
807 }
808
809 private InputDevice newInputDevice(int deviceId) {
810 int classes = getDeviceClasses(deviceId);
811 String name = getDeviceName(deviceId);
812 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
813 + ", name=" + name
814 + ", classes=" + Integer.toHexString(classes));
815 InputDevice.AbsoluteInfo absX;
816 InputDevice.AbsoluteInfo absY;
817 InputDevice.AbsoluteInfo absPressure;
818 InputDevice.AbsoluteInfo absSize;
819 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
820 absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
821 absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
822 absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
823 absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
824 } else {
825 absX = null;
826 absY = null;
827 absPressure = null;
828 absSize = null;
829 }
830
831 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
832 }
833
834 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
835 String name) {
836 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
837 if (getAbsoluteInfo(id, channel, info)
838 && info.minValue != info.maxValue) {
839 Log.i(TAG, " " + name + ": min=" + info.minValue
840 + " max=" + info.maxValue
841 + " flat=" + info.flat
842 + " fuzz=" + info.fuzz);
843 info.range = info.maxValue-info.minValue;
844 return info;
845 }
846 Log.i(TAG, " " + name + ": unknown values");
847 return null;
848 }
849 private static native boolean readEvent(RawInputEvent outEvent);
850}