2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server;
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.content.res.Resources;
22 import android.os.Environment;
23 import android.os.LatencyTimer;
24 import android.os.PowerManager;
25 import android.os.SystemClock;
26 import android.os.SystemProperties;
27 import android.util.Slog;
28 import android.util.SparseArray;
29 import android.util.Xml;
30 import android.view.Display;
31 import android.view.KeyEvent;
32 import android.view.MotionEvent;
33 import android.view.RawInputEvent;
34 import android.view.Surface;
35 import android.view.WindowManagerPolicy;
37 import com.android.internal.util.XmlUtils;
39 import org.xmlpull.v1.XmlPullParser;
41 import java.io.BufferedReader;
43 import java.io.FileInputStream;
44 import java.io.FileNotFoundException;
45 import java.io.FileReader;
46 import java.io.IOException;
47 import java.io.InputStreamReader;
48 import java.io.PrintWriter;
49 import java.util.ArrayList;
51 public abstract class KeyInputQueue {
52 static final String TAG = "KeyInputQueue";
53 static final int UPKEY_KEYWORD = 19;
54 static final int DOWNKEY_KEYWORD = 20;
56 static final boolean DEBUG = false;
57 static final boolean DEBUG_VIRTUAL_KEYS = false;
58 static final boolean DEBUG_POINTERS = false;
59 static final boolean DEBUG_MOUSE = false;
62 * Turn on some hacks we have to improve the touch interaction with a
63 * certain device whose screen currently is not all that good.
65 static boolean BAD_TOUCH_HACK = false;
68 * Turn on some hacks to improve touch interaction with another device
69 * where touch coordinate data can get corrupted.
71 static boolean JUMPY_TOUCH_HACK = false;
73 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
75 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
76 final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
77 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
78 final HapticFeedbackCallback mHapticFeedbackCallback;
80 int mGlobalMetaState = 0;
81 boolean mHaveGlobalMetaState = false;
83 final QueuedEvent mFirst;
84 final QueuedEvent mLast;
88 Display mDisplay = null;
94 int mOrientation = Surface.ROTATION_0;
95 int[] mKeyRotationMap = null;
97 VirtualKey mPressedVirtualKey = null;
99 PowerManager.WakeLock mWakeLock;
101 static final int[] KEY_90_MAP = new int[] {
102 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
103 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
104 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
105 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
108 static final int[] KEY_180_MAP = new int[] {
109 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
110 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
111 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
112 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
115 static final int[] KEY_270_MAP = new int[] {
116 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
117 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
118 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
119 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
122 public static final int FILTER_REMOVE = 0;
123 public static final int FILTER_KEEP = 1;
124 public static final int FILTER_ABORT = -1;
126 private static final boolean MEASURE_LATENCY = false;
127 private LatencyTimer lt;
129 public interface FilterCallback {
130 int filterEvent(QueuedEvent ev);
133 public interface HapticFeedbackCallback {
134 void virtualKeyFeedback(KeyEvent event);
137 static class QueuedEvent {
138 InputDevice inputDevice;
140 int flags; // From the raw event
141 int classType; // One of the class constants in InputEvent
145 void copyFrom(QueuedEvent that) {
146 this.inputDevice = that.inputDevice;
147 this.whenNano = that.whenNano;
148 this.flags = that.flags;
149 this.classType = that.classType;
150 this.event = that.event;
154 public String toString() {
155 return "QueuedEvent{"
156 + Integer.toHexString(System.identityHashCode(this))
166 * A key that exists as a part of the touch-screen, outside of the normal
167 * display area of the screen.
169 static class VirtualKey {
181 InputDevice lastDevice;
184 boolean checkHit(int x, int y) {
185 return (x >= hitLeft && x <= hitRight
186 && y >= hitTop && y <= hitBottom);
189 void computeHitRect(InputDevice dev, int dw, int dh) {
190 if (dev == lastDevice) {
194 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "computeHitRect for " + scancode
195 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
199 int minx = dev.absX.minValue;
200 int maxx = dev.absX.maxValue;
203 int left = centerx - halfw;
204 int right = centerx + halfw;
205 hitLeft = minx + ((left*maxx-minx)/dw);
206 hitRight = minx + ((right*maxx-minx)/dw);
208 int miny = dev.absY.minValue;
209 int maxy = dev.absY.maxValue;
211 int halfh = height/2;
212 int top = centery - halfh;
213 int bottom = centery + halfh;
214 hitTop = miny + ((top*maxy-miny)/dh);
215 hitBottom = miny + ((bottom*maxy-miny)/dh);
219 private void readVirtualKeys(String deviceName) {
221 FileInputStream fis = new FileInputStream(
222 "/sys/board_properties/virtualkeys." + deviceName);
223 InputStreamReader isr = new InputStreamReader(fis);
224 BufferedReader br = new BufferedReader(isr, 2048);
225 String str = br.readLine();
227 String[] it = str.split(":");
228 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "***** VIRTUAL KEYS: " + it);
229 final int N = it.length-6;
230 for (int i=0; i<=N; i+=6) {
231 if (!"0x01".equals(it[i])) {
232 Slog.w(TAG, "Unknown virtual key type at elem #" + i
237 VirtualKey sb = new VirtualKey();
238 sb.scancode = Integer.parseInt(it[i+1]);
239 sb.centerx = Integer.parseInt(it[i+2]);
240 sb.centery = Integer.parseInt(it[i+3]);
241 sb.width = Integer.parseInt(it[i+4]);
242 sb.height = Integer.parseInt(it[i+5]);
243 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Virtual key "
244 + sb.scancode + ": center=" + sb.centerx + ","
245 + sb.centery + " size=" + sb.width + "x"
247 mVirtualKeys.add(sb);
248 } catch (NumberFormatException e) {
249 Slog.w(TAG, "Bad number at region " + i + " in: "
255 } catch (FileNotFoundException e) {
256 Slog.i(TAG, "No virtual keys found");
257 } catch (IOException e) {
258 Slog.w(TAG, "Error reading virtual keys", e);
262 private void readExcludedDevices() {
263 // Read partner-provided list of excluded input devices
264 XmlPullParser parser = null;
265 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
266 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
267 FileReader confreader = null;
269 confreader = new FileReader(confFile);
270 parser = Xml.newPullParser();
271 parser.setInput(confreader);
272 XmlUtils.beginDocument(parser, "devices");
275 XmlUtils.nextElement(parser);
276 if (!"device".equals(parser.getName())) {
279 String name = parser.getAttributeValue(null, "name");
281 if (DEBUG) Slog.v(TAG, "addExcludedDevice " + name);
282 addExcludedDevice(name);
285 } catch (FileNotFoundException e) {
286 // It's ok if the file does not exist.
287 } catch (Exception e) {
288 Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
290 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
294 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
295 if (MEASURE_LATENCY) {
296 lt = new LatencyTimer(100, 1000);
299 Resources r = context.getResources();
300 BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
302 JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
304 mHapticFeedbackCallback = hapticFeedbackCallback;
306 readExcludedDevices();
308 PowerManager pm = (PowerManager)context.getSystemService(
309 Context.POWER_SERVICE);
310 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
312 mWakeLock.setReferenceCounted(false);
314 mFirst = new QueuedEvent();
315 mLast = new QueuedEvent();
322 public void setDisplay(Display display) {
325 // We assume at this point that the display dimensions reflect the
326 // natural, unrotated display. We will perform hit tests for soft
327 // buttons based on that display.
328 mDisplayWidth = display.getWidth();
329 mDisplayHeight = display.getHeight();
330 mCx = mDisplayWidth / 2;
331 mCy = mDisplayHeight / 2;
334 public void getInputConfiguration(Configuration config) {
335 synchronized (mFirst) {
336 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
337 config.keyboard = Configuration.KEYBOARD_NOKEYS;
338 config.navigation = Configuration.NAVIGATION_NONAV;
340 final int N = mDevices.size();
341 for (int i=0; i<N; i++) {
342 InputDevice d = mDevices.valueAt(i);
344 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
346 = Configuration.TOUCHSCREEN_FINGER;
347 //Slog.i("foo", "***** HAVE TOUCHSCREEN!");
349 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
351 = Configuration.KEYBOARD_QWERTY;
352 //Slog.i("foo", "***** HAVE QWERTY!");
354 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
356 = Configuration.NAVIGATION_TRACKBALL;
357 //Slog.i("foo", "***** HAVE TRACKBALL!");
358 } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
360 = Configuration.NAVIGATION_DPAD;
361 //Slog.i("foo", "***** HAVE DPAD!");
368 public int getScancodeState(int code) {
369 synchronized (mFirst) {
370 VirtualKey vk = mPressedVirtualKey;
372 if (vk.scancode == code) {
376 return nativeGetScancodeState(code);
380 public int getScancodeState(int deviceId, int code) {
381 synchronized (mFirst) {
382 VirtualKey vk = mPressedVirtualKey;
384 if (vk.scancode == code) {
388 return nativeGetScancodeState(deviceId, code);
392 public int getTrackballScancodeState(int code) {
393 synchronized (mFirst) {
394 final int N = mDevices.size();
395 for (int i=0; i<N; i++) {
396 InputDevice dev = mDevices.valueAt(i);
397 if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
398 int res = nativeGetScancodeState(dev.id, code);
409 public int getDPadScancodeState(int code) {
410 synchronized (mFirst) {
411 final int N = mDevices.size();
412 for (int i=0; i<N; i++) {
413 InputDevice dev = mDevices.valueAt(i);
414 if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
415 int res = nativeGetScancodeState(dev.id, code);
426 public int getKeycodeState(int code) {
427 synchronized (mFirst) {
428 VirtualKey vk = mPressedVirtualKey;
430 if (vk.lastKeycode == code) {
434 return nativeGetKeycodeState(code);
438 public int getKeycodeState(int deviceId, int code) {
439 synchronized (mFirst) {
440 VirtualKey vk = mPressedVirtualKey;
442 if (vk.lastKeycode == code) {
446 return nativeGetKeycodeState(deviceId, code);
450 public int getTrackballKeycodeState(int code) {
451 synchronized (mFirst) {
452 final int N = mDevices.size();
453 for (int i=0; i<N; i++) {
454 InputDevice dev = mDevices.valueAt(i);
455 if ((dev.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
456 int res = nativeGetKeycodeState(dev.id, code);
467 public int getDPadKeycodeState(int code) {
468 synchronized (mFirst) {
469 final int N = mDevices.size();
470 for (int i=0; i<N; i++) {
471 InputDevice dev = mDevices.valueAt(i);
472 if ((dev.classes&RawInputEvent.CLASS_DPAD) != 0) {
473 int res = nativeGetKeycodeState(dev.id, code);
484 public static native String getDeviceName(int deviceId);
485 public static native int getDeviceClasses(int deviceId);
486 public static native void addExcludedDevice(String deviceName);
487 public static native boolean getAbsoluteInfo(int deviceId, int axis,
488 InputDevice.AbsoluteInfo outInfo);
489 public static native int getSwitchState(int sw);
490 public static native int getSwitchState(int deviceId, int sw);
491 public static native int nativeGetScancodeState(int code);
492 public static native int nativeGetScancodeState(int deviceId, int code);
493 public static native int nativeGetKeycodeState(int code);
494 public static native int nativeGetKeycodeState(int deviceId, int code);
495 public static native int scancodeToKeycode(int deviceId, int scancode);
496 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
498 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
499 long eventTime, boolean down, int keycode, int repeatCount,
500 int scancode, int flags) {
503 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
504 keycode, repeatCount,
505 device != null ? device.mMetaKeysState : 0,
506 device != null ? device.id : -1, scancode,
507 flags | KeyEvent.FLAG_FROM_SYSTEM);
510 Thread mThread = new Thread("InputDeviceReader") {
512 if (DEBUG) Slog.v(TAG, "InputDeviceReader.run()");
513 android.os.Process.setThreadPriority(
514 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
516 RawInputEvent ev = new RawInputEvent();
521 // block, doesn't release the monitor
524 boolean send = false;
525 boolean configChanged = false;
528 Slog.i(TAG, "Input event: dev=0x"
529 + Integer.toHexString(ev.deviceId)
530 + " type=0x" + Integer.toHexString(ev.type)
531 + " scancode=" + ev.scancode
532 + " keycode=" + ev.keycode
533 + " value=" + ev.value);
536 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
537 synchronized (mFirst) {
538 di = newInputDevice(ev.deviceId);
539 if (di.classes != 0) {
540 // If this device is some kind of input class,
542 mDevices.put(ev.deviceId, di);
543 if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
544 readVirtualKeys(di.name);
546 // The configuration may have changed because
548 configChanged = true;
550 // We won't do anything with this device.
551 mIgnoredDevices.put(ev.deviceId, di);
552 Slog.i(TAG, "Ignoring non-input device: id=0x"
553 + Integer.toHexString(di.id)
554 + ", name=" + di.name);
557 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
558 synchronized (mFirst) {
560 Slog.i(TAG, "Device removed: id=0x"
561 + Integer.toHexString(ev.deviceId));
563 di = mDevices.get(ev.deviceId);
565 mDevices.delete(ev.deviceId);
566 // The configuration may have changed because
568 configChanged = true;
569 } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
570 mIgnoredDevices.remove(ev.deviceId);
572 Slog.w(TAG, "Removing bad device id: "
573 + Integer.toHexString(ev.deviceId));
578 di = getInputDevice(ev.deviceId);
580 // This may be some junk from an ignored device.
585 send = preprocessEvent(di, ev);
587 if (ev.type == RawInputEvent.EV_KEY) {
588 di.mMetaKeysState = makeMetaState(ev.keycode,
589 ev.value != 0, di.mMetaKeysState);
590 mHaveGlobalMetaState = false;
595 synchronized (mFirst) {
596 addLocked(di, System.nanoTime(), 0,
597 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
606 synchronized (mFirst) {
607 // NOTE: The event timebase absolutely must be the same
608 // timebase as SystemClock.uptimeMillis().
609 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
610 final long curTime = SystemClock.uptimeMillis();
611 final long curTimeNano = System.nanoTime();
612 //Slog.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
614 final int classes = di.classes;
615 final int type = ev.type;
616 final int scancode = ev.scancode;
619 // Is it a key event?
620 if (type == RawInputEvent.EV_KEY &&
621 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
622 (scancode < RawInputEvent.BTN_FIRST ||
623 scancode > RawInputEvent.BTN_LAST)) {
627 di.mKeyDownTime = curTime;
631 int keycode = rotateKeyCodeLocked(ev.keycode);
632 addLocked(di, curTimeNano, ev.flags,
633 RawInputEvent.CLASS_KEYBOARD,
634 newKeyEvent(di, di.mKeyDownTime, curTime, down,
635 keycode, 0, scancode,
636 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
637 ? KeyEvent.FLAG_WOKE_HERE : 0));
639 } else if (ev.type == RawInputEvent.EV_KEY) {
640 // Single touch protocol: touch going down or up.
641 if ((ev.scancode == RawInputEvent.BTN_TOUCH ||
642 ev.scancode == RawInputEvent.BTN_MOUSE) &&
643 (classes&(RawInputEvent.CLASS_TOUCHSCREEN
644 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
645 == RawInputEvent.CLASS_TOUCHSCREEN) {
646 di.mAbs.changed = true;
647 di.mAbs.mDown[0] = ev.value != 0;
649 // Trackball (mouse) protocol: press down or up.
650 } else if (ev.scancode == RawInputEvent.BTN_MOUSE) {
651 if ((classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
652 di.mRel.changed = true;
653 di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
655 } else if ((classes&RawInputEvent.CLASS_MOUSE) != 0) {
657 Slog.i(TAG, "Mouse key event found, down:"
658 + ev.value + " was :" +
659 di.mAbs.mDown[0] + " Send " + send);
660 di.mAbs.changed = true;
661 di.mAbs.mNextNumPointers = (ev.value != 0) ? 1 : 2;
664 } else if ((ev.scancode == RawInputEvent.BTN_RIGHT ||
665 ev.scancode == RawInputEvent.BTN_MIDDLE) &&
666 (classes&RawInputEvent.CLASS_MOUSE) != 0) {
667 boolean down = (ev.value != 0);
669 di.mKeyDownTime = curTime;
671 addLocked(di, curTime, ev.flags,
672 RawInputEvent.CLASS_KEYBOARD,
673 newKeyEvent(di, di.mKeyDownTime, curTime, down,
674 (ev.scancode == RawInputEvent.BTN_RIGHT)
675 ? KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_MENU,
677 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
678 ? KeyEvent.FLAG_WOKE_HERE : 0));
681 // Process position events from multitouch protocol.
682 } else if (ev.type == RawInputEvent.EV_ABS &&
683 (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
684 if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
685 di.mAbs.changed = true;
686 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
687 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
688 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
689 di.mAbs.changed = true;
690 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
691 + MotionEvent.SAMPLE_X] = ev.value;
692 if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
693 + di.mAbs.mAddingPointerOffset
695 } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
696 di.mAbs.changed = true;
697 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
698 + MotionEvent.SAMPLE_Y] = ev.value;
699 if (DEBUG_POINTERS) Slog.v(TAG, "MT @"
700 + di.mAbs.mAddingPointerOffset
702 } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
703 di.mAbs.changed = true;
704 di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
705 + MotionEvent.SAMPLE_SIZE] = ev.value;
708 // Process position events from single touch protocol.
709 } else if (ev.type == RawInputEvent.EV_ABS &&
710 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
711 if (ev.scancode == RawInputEvent.ABS_X ||
712 ev.scancode == RawInputEvent.ABS_Z) {
713 di.mAbs.changed = true;
714 di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
715 } else if (ev.scancode == RawInputEvent.ABS_Y ||
716 ev.scancode == RawInputEvent.ABS_RX) {
717 di.mAbs.changed = true;
718 di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
719 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
720 di.mAbs.changed = true;
721 di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
722 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
723 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
724 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
725 di.mAbs.changed = true;
726 di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
727 di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
728 + MotionEvent.SAMPLE_SIZE] = ev.value;
731 // Process movement events from trackball (mouse) protocol.
732 } else if (ev.type == RawInputEvent.EV_REL) {
734 Slog.i(TAG, "rel event found, class :" + classes + " mouse value: " + RawInputEvent.CLASS_MOUSE);
735 if ((classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
736 // Add this relative movement into our totals.
737 if (ev.scancode == RawInputEvent.REL_X) {
738 di.mRel.changed = true;
739 di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
740 } else if (ev.scancode == RawInputEvent.REL_Y) {
741 di.mRel.changed = true;
742 di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
744 } else if ((classes&RawInputEvent.CLASS_MOUSE) != 0) {
745 if (ev.scancode == RawInputEvent.REL_X) {
746 di.mAbs.changed = true;
747 mCx += (int)ev.value;
748 mCx = ((mCx < 0) ? 0 : (mCx >= mDisplayWidth ? mDisplayWidth - 1 : mCx));
749 di.mAbs.mNextData[MotionEvent.SAMPLE_X] = mCx;
750 } else if (ev.scancode == RawInputEvent.REL_Y) {
751 di.mAbs.changed = true;
752 mCy += (int)ev.value;
753 mCy = ((mCy < 0) ? 0 : (mCy >= mDisplayHeight ? mDisplayHeight - 1 : mCy));
754 di.mAbs.mNextData[MotionEvent.SAMPLE_Y] = mCy;
755 } else if (ev.scancode == RawInputEvent.REL_WHEEL &&
756 (classes&RawInputEvent.CLASS_MOUSE) != 0) {
761 di.mKeyDownTime = curTime;
766 keycode = rotateKeyCodeLocked(DOWNKEY_KEYWORD);
767 } else if(ev.value > 0){
768 keycode = rotateKeyCodeLocked(UPKEY_KEYWORD);
770 keycode = rotateKeyCodeLocked(ev.keycode);
772 addLocked(di, curTime, ev.flags,
773 RawInputEvent.CLASS_KEYBOARD,
774 newKeyEvent(di, di.mKeyDownTime, curTime, down,
775 keycode, 0, scancode,
776 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
777 ? KeyEvent.FLAG_WOKE_HERE : 0));
778 addLocked(di, curTime, ev.flags,
779 RawInputEvent.CLASS_KEYBOARD,
780 newKeyEvent(di, di.mKeyDownTime, curTime, !down,
781 keycode, 0, scancode,
782 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
783 ? KeyEvent.FLAG_WOKE_HERE : 0));
788 // Handle multitouch protocol sync: tells us that the
789 // driver has returned all data for -one- of the pointers
790 // that is currently down.
791 if (ev.type == RawInputEvent.EV_SYN
792 && ev.scancode == RawInputEvent.SYN_MT_REPORT
793 && di.mAbs != null) {
794 di.mAbs.changed = true;
795 if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
796 // If the value is <= 0, the pointer is not
797 // down, so keep it in the count.
799 if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
800 + MotionEvent.SAMPLE_PRESSURE] != 0) {
801 final int num = di.mAbs.mNextNumPointers+1;
802 di.mAbs.mNextNumPointers = num;
803 if (DEBUG_POINTERS) Slog.v(TAG,
804 "MT_REPORT: now have " + num + " pointers");
805 final int newOffset = (num <= InputDevice.MAX_POINTERS)
806 ? (num * MotionEvent.NUM_SAMPLE_DATA)
807 : (InputDevice.MAX_POINTERS *
808 MotionEvent.NUM_SAMPLE_DATA);
809 di.mAbs.mAddingPointerOffset = newOffset;
810 di.mAbs.mNextData[newOffset
811 + MotionEvent.SAMPLE_PRESSURE] = 0;
813 if (DEBUG_POINTERS) Slog.v(TAG, "MT_REPORT: no pointer");
817 // Handle general event sync: all data for the current
818 // event update has been delivered.
819 } else if (send || (ev.type == RawInputEvent.EV_SYN
820 && ev.scancode == RawInputEvent.SYN_REPORT)) {
821 if (mDisplay != null) {
822 if (!mHaveGlobalMetaState) {
823 computeGlobalMetaStateLocked();
828 InputDevice.MotionState ms = di.mAbs;
830 ms.everChanged = true;
833 if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
834 |RawInputEvent.CLASS_TOUCHSCREEN_MT))
835 == RawInputEvent.CLASS_TOUCHSCREEN) {
836 ms.mNextNumPointers = 0;
838 System.arraycopy(di.curTouchVals, 0,
840 MotionEvent.NUM_SAMPLE_DATA);
841 ms.mNextNumPointers++;
845 if (BAD_TOUCH_HACK) {
848 if (JUMPY_TOUCH_HACK) {
849 ms.dropJumpyPoint(di);
852 boolean doMotion = !monitorVirtualKey(di,
853 ev, curTime, curTimeNano);
855 if (doMotion && ms.mNextNumPointers > 0
856 && (ms.mLastNumPointers == 0
857 || ms.mSkipLastPointers)) {
858 doMotion = !generateVirtualKeyDown(di,
859 ev, curTime, curTimeNano);
863 // XXX Need to be able to generate
864 // multiple events here, for example
865 // if two fingers change up/down state
868 me = ms.generateAbsMotion(di, curTime,
869 curTimeNano, mDisplay,
870 mOrientation, mGlobalMetaState);
871 if (DEBUG_POINTERS || DEBUG_MOUSE)
872 Slog.v(TAG, "Absolute: x="
873 + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
875 + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
878 if (WindowManagerPolicy.WATCH_POINTER) {
879 Slog.i(TAG, "Enqueueing: " + me);
881 if ((classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
882 addLocked(di, curTime, ev.flags,
883 RawInputEvent.CLASS_TOUCHSCREEN, me);
884 } else if ((classes & RawInputEvent.CLASS_MOUSE) != 0) {
885 addLocked(di, curTime, ev.flags,
886 RawInputEvent.CLASS_MOUSE, me);
888 Slog.w(TAG, "Unknown classes? " + classes);
891 } while (ms.hasMore());
893 // We are consuming movement in the
894 // virtual key area... but still
895 // propagate this to the previous
896 // data for comparisons.
897 int num = ms.mNextNumPointers;
898 if (num > InputDevice.MAX_POINTERS) {
899 num = InputDevice.MAX_POINTERS;
901 System.arraycopy(ms.mNextData, 0,
903 num * MotionEvent.NUM_SAMPLE_DATA);
904 ms.mLastNumPointers = num;
905 ms.mSkipLastPointers = true;
913 ms.everChanged = true;
916 me = ms.generateRelMotion(di, curTime,
918 mOrientation, mGlobalMetaState);
919 if (false) Slog.v(TAG, "Relative: x="
920 + di.mRel.mNextData[MotionEvent.SAMPLE_X]
922 + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
925 addLocked(di, curTimeNano, ev.flags,
926 RawInputEvent.CLASS_TRACKBALL, me);
933 } catch (RuntimeException exc) {
934 Slog.e(TAG, "InputReaderThread uncaught exception", exc);
940 private boolean isInsideDisplay(InputDevice dev) {
941 final InputDevice.AbsoluteInfo absx = dev.absX;
942 final InputDevice.AbsoluteInfo absy = dev.absY;
943 final InputDevice.MotionState absm = dev.mAbs;
944 if (absx == null || absy == null || absm == null) {
948 if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
949 && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
950 && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
951 && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
952 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Input ("
953 + absm.mNextData[MotionEvent.SAMPLE_X]
954 + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
955 + ") inside of display");
962 private VirtualKey findVirtualKey(InputDevice dev) {
963 final int N = mVirtualKeys.size();
968 final InputDevice.MotionState absm = dev.mAbs;
969 for (int i=0; i<N; i++) {
970 VirtualKey sb = mVirtualKeys.get(i);
971 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
972 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit test ("
973 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
974 + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
975 + sb.scancode + " - (" + sb.hitLeft
976 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
977 + sb.hitBottom + ")");
978 if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
979 absm.mNextData[MotionEvent.SAMPLE_Y])) {
980 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Hit!");
988 private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
989 long curTime, long curTimeNano) {
990 if (isInsideDisplay(di)) {
991 // Didn't consume event.
996 VirtualKey vk = findVirtualKey(di);
998 final InputDevice.MotionState ms = di.mAbs;
999 mPressedVirtualKey = vk;
1000 vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
1001 ms.mLastNumPointers = ms.mNextNumPointers;
1002 di.mKeyDownTime = curTime;
1003 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG,
1004 "Generate key down for: " + vk.scancode
1005 + " (keycode=" + vk.lastKeycode + ")");
1006 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
1007 vk.lastKeycode, 0, vk.scancode,
1008 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
1009 mHapticFeedbackCallback.virtualKeyFeedback(event);
1010 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
1014 // We always consume the event, even if we didn't
1015 // generate a key event. There are two reasons for
1016 // this: to avoid spurious touches when holding
1017 // the edges of the device near the touchscreen,
1018 // and to avoid reporting events if there are virtual
1019 // keys on the touchscreen outside of the display
1021 // Note that for all of this we are only looking at the
1022 // first pointer, since what we are handling here is the
1023 // first pointer going down, and this is the coordinate
1024 // that will be used to dispatch the event.
1026 final InputDevice.AbsoluteInfo absx = di.absX;
1027 final InputDevice.AbsoluteInfo absy = di.absY;
1028 final InputDevice.MotionState absm = di.mAbs;
1029 Slog.v(TAG, "Rejecting ("
1030 + absm.mNextData[MotionEvent.SAMPLE_X] + ","
1031 + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
1032 + absx.minValue + "," + absy.minValue
1033 + ")-(" + absx.maxValue + ","
1034 + absx.maxValue + ")");
1039 private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
1040 long curTime, long curTimeNano) {
1041 VirtualKey vk = mPressedVirtualKey;
1046 final InputDevice.MotionState ms = di.mAbs;
1047 if (ms.mNextNumPointers <= 0) {
1048 mPressedVirtualKey = null;
1049 ms.mLastNumPointers = 0;
1050 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Generate key up for: " + vk.scancode);
1051 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
1052 vk.lastKeycode, 0, vk.scancode,
1053 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
1054 mHapticFeedbackCallback.virtualKeyFeedback(event);
1055 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
1059 } else if (isInsideDisplay(di)) {
1060 // Whoops the pointer has moved into
1061 // the display area! Cancel the
1062 // virtual key and start a pointer
1064 mPressedVirtualKey = null;
1065 if (DEBUG_VIRTUAL_KEYS) Slog.v(TAG, "Cancel key up for: " + vk.scancode);
1066 KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
1067 vk.lastKeycode, 0, vk.scancode,
1068 KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
1069 mHapticFeedbackCallback.virtualKeyFeedback(event);
1070 addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
1072 ms.mLastNumPointers = 0;
1080 * Returns a new meta state for the given keys and old state.
1082 private static final int makeMetaState(int keycode, boolean down, int old) {
1085 case KeyEvent.KEYCODE_ALT_LEFT:
1086 mask = KeyEvent.META_ALT_LEFT_ON;
1088 case KeyEvent.KEYCODE_ALT_RIGHT:
1089 mask = KeyEvent.META_ALT_RIGHT_ON;
1091 case KeyEvent.KEYCODE_SHIFT_LEFT:
1092 mask = KeyEvent.META_SHIFT_LEFT_ON;
1094 case KeyEvent.KEYCODE_SHIFT_RIGHT:
1095 mask = KeyEvent.META_SHIFT_RIGHT_ON;
1097 case KeyEvent.KEYCODE_SYM:
1098 mask = KeyEvent.META_SYM_ON;
1103 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
1104 & (down ? (old | mask) : (old & ~mask));
1105 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
1106 result |= KeyEvent.META_ALT_ON;
1108 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
1109 result |= KeyEvent.META_SHIFT_ON;
1114 private void computeGlobalMetaStateLocked() {
1115 int i = mDevices.size();
1116 mGlobalMetaState = 0;
1117 while ((--i) >= 0) {
1118 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
1120 mHaveGlobalMetaState = true;
1124 * Return true if you want the event to get passed on to the
1125 * rest of the system, and false if you've handled it and want
1128 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
1130 InputDevice getInputDevice(int deviceId) {
1131 synchronized (mFirst) {
1132 return getInputDeviceLocked(deviceId);
1136 private InputDevice getInputDeviceLocked(int deviceId) {
1137 return mDevices.get(deviceId);
1140 public void setOrientation(int orientation) {
1141 synchronized(mFirst) {
1142 mOrientation = orientation;
1143 switch (orientation) {
1144 case Surface.ROTATION_90:
1145 mKeyRotationMap = KEY_90_MAP;
1147 case Surface.ROTATION_180:
1148 mKeyRotationMap = KEY_180_MAP;
1150 case Surface.ROTATION_270:
1151 mKeyRotationMap = KEY_270_MAP;
1154 mKeyRotationMap = null;
1160 public int rotateKeyCode(int keyCode) {
1161 synchronized(mFirst) {
1162 return rotateKeyCodeLocked(keyCode);
1166 private int rotateKeyCodeLocked(int keyCode) {
1167 int[] map = mKeyRotationMap;
1169 final int N = map.length;
1170 for (int i=0; i<N; i+=2) {
1171 if (map[i] == keyCode) {
1179 boolean hasEvents() {
1180 synchronized (mFirst) {
1181 return mFirst.next != mLast;
1186 * returns true if we returned an event, and false if we timed out
1188 QueuedEvent getEvent(long timeoutMS) {
1189 long begin = SystemClock.uptimeMillis();
1190 final long end = begin+timeoutMS;
1192 synchronized (mFirst) {
1193 while (mFirst.next == mLast && end > now) {
1195 mWakeLock.release();
1196 mFirst.wait(end-now);
1198 catch (InterruptedException e) {
1200 now = SystemClock.uptimeMillis();
1205 if (mFirst.next == mLast) {
1208 QueuedEvent p = mFirst.next;
1209 mFirst.next = p.next;
1210 mFirst.next.prev = mFirst;
1217 * Return true if the queue has an up event pending that corresponds
1218 * to the same key as the given key event.
1220 boolean hasKeyUpEvent(KeyEvent origEvent) {
1221 synchronized (mFirst) {
1222 final int keyCode = origEvent.getKeyCode();
1223 QueuedEvent cur = mLast.prev;
1224 while (cur.prev != null) {
1225 if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
1226 KeyEvent ke = (KeyEvent)cur.event;
1227 if (ke.getAction() == KeyEvent.ACTION_UP
1228 && ke.getKeyCode() == keyCode) {
1239 void recycleEvent(QueuedEvent ev) {
1240 synchronized (mFirst) {
1241 //Slog.i(TAG, "Recycle event: " + ev);
1242 if (ev.event == ev.inputDevice.mAbs.currentMove) {
1243 ev.inputDevice.mAbs.currentMove = null;
1245 if (ev.event == ev.inputDevice.mRel.currentMove) {
1246 if (false) Slog.i(TAG, "Detach rel " + ev.event);
1247 ev.inputDevice.mRel.currentMove = null;
1248 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
1249 ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
1255 void filterQueue(FilterCallback cb) {
1256 synchronized (mFirst) {
1257 QueuedEvent cur = mLast.prev;
1258 while (cur.prev != null) {
1259 switch (cb.filterEvent(cur)) {
1261 cur.prev.next = cur.next;
1262 cur.next.prev = cur.prev;
1272 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
1273 int flags, int classType, Object event) {
1275 if (mCacheCount == 0) {
1276 ev = new QueuedEvent();
1283 ev.inputDevice = device;
1284 ev.whenNano = whenNano;
1286 ev.classType = classType;
1291 private void recycleLocked(QueuedEvent ev) {
1293 throw new RuntimeException("Event already in queue!");
1295 if (mCacheCount < 10) {
1303 private void addLocked(InputDevice device, long whenNano, int flags,
1304 int classType, Object event) {
1305 boolean poke = mFirst.next == mLast;
1307 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
1308 QueuedEvent p = mLast.prev;
1309 while (p != mFirst && ev.whenNano < p.whenNano) {
1321 if (MEASURE_LATENCY) {
1322 time = System.nanoTime();
1325 mWakeLock.acquire();
1326 if (MEASURE_LATENCY) {
1327 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
1332 private InputDevice newInputDevice(int deviceId) {
1333 int classes = getDeviceClasses(deviceId);
1334 String name = getDeviceName(deviceId);
1335 InputDevice.AbsoluteInfo absX = null;
1336 InputDevice.AbsoluteInfo absY = null;
1337 InputDevice.AbsoluteInfo absPressure = null;
1338 InputDevice.AbsoluteInfo absSize = null;
1340 Slog.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
1342 + ", classes=" + Integer.toHexString(classes));
1343 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
1344 absX = loadAbsoluteInfo(deviceId,
1345 RawInputEvent.ABS_MT_POSITION_X, "X");
1346 absY = loadAbsoluteInfo(deviceId,
1347 RawInputEvent.ABS_MT_POSITION_Y, "Y");
1348 absPressure = loadAbsoluteInfo(deviceId,
1349 RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
1350 absSize = loadAbsoluteInfo(deviceId,
1351 RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
1352 } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
1353 absX = loadAbsoluteInfo(deviceId,
1354 RawInputEvent.ABS_X, "X");
1355 absY = loadAbsoluteInfo(deviceId,
1356 RawInputEvent.ABS_Y, "Y");
1357 absPressure = loadAbsoluteInfo(deviceId,
1358 RawInputEvent.ABS_PRESSURE, "Pressure");
1359 absSize = loadAbsoluteInfo(deviceId,
1360 RawInputEvent.ABS_TOOL_WIDTH, "Size");
1364 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
1367 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
1369 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
1370 if (getAbsoluteInfo(id, channel, info)
1371 && info.minValue != info.maxValue) {
1372 Slog.i(TAG, " " + name + ": min=" + info.minValue
1373 + " max=" + info.maxValue
1374 + " flat=" + info.flat
1375 + " fuzz=" + info.fuzz);
1376 info.range = info.maxValue-info.minValue;
1379 Slog.i(TAG, " " + name + ": unknown values");
1382 private static native boolean readEvent(RawInputEvent outEvent);
1384 void dump(PrintWriter pw, String prefix) {
1385 synchronized (mFirst) {
1386 for (int i=0; i<mDevices.size(); i++) {
1387 InputDevice dev = mDevices.valueAt(i);
1388 pw.print(prefix); pw.print("Device #");
1389 pw.print(mDevices.keyAt(i)); pw.print(" ");
1390 pw.print(dev.name); pw.print(" (classes=0x");
1391 pw.print(Integer.toHexString(dev.classes));
1393 pw.print(prefix); pw.print(" mKeyDownTime=");
1394 pw.print(dev.mKeyDownTime); pw.print(" mMetaKeysState=");
1395 pw.println(dev.mMetaKeysState);
1396 if (dev.absX != null) {
1397 pw.print(prefix); pw.print(" absX: "); dev.absX.dump(pw);
1400 if (dev.absY != null) {
1401 pw.print(prefix); pw.print(" absY: "); dev.absY.dump(pw);
1404 if (dev.absPressure != null) {
1405 pw.print(prefix); pw.print(" absPressure: ");
1406 dev.absPressure.dump(pw); pw.println("");
1408 if (dev.absSize != null) {
1409 pw.print(prefix); pw.print(" absSize: ");
1410 dev.absSize.dump(pw); pw.println("");
1412 if (dev.mAbs.everChanged) {
1413 pw.print(prefix); pw.println(" mAbs:");
1414 dev.mAbs.dump(pw, prefix + " ");
1416 if (dev.mRel.everChanged) {
1417 pw.print(prefix); pw.println(" mRel:");
1418 dev.mRel.dump(pw, prefix + " ");
1422 for (int i=0; i<mIgnoredDevices.size(); i++) {
1423 InputDevice dev = mIgnoredDevices.valueAt(i);
1424 pw.print(prefix); pw.print("Ignored Device #");
1425 pw.print(mIgnoredDevices.keyAt(i)); pw.print(" ");
1426 pw.print(dev.name); pw.print(" (classes=0x");
1427 pw.print(Integer.toHexString(dev.classes));
1431 for (int i=0; i<mVirtualKeys.size(); i++) {
1432 VirtualKey vk = mVirtualKeys.get(i);
1433 pw.print(prefix); pw.print("Virtual Key #");
1434 pw.print(i); pw.println(":");
1435 pw.print(prefix); pw.print(" scancode="); pw.println(vk.scancode);
1436 pw.print(prefix); pw.print(" centerx="); pw.print(vk.centerx);
1437 pw.print(" centery="); pw.print(vk.centery);
1438 pw.print(" width="); pw.print(vk.width);
1439 pw.print(" height="); pw.println(vk.height);
1440 pw.print(prefix); pw.print(" hitLeft="); pw.print(vk.hitLeft);
1441 pw.print(" hitTop="); pw.print(vk.hitTop);
1442 pw.print(" hitRight="); pw.print(vk.hitRight);
1443 pw.print(" hitBottom="); pw.println(vk.hitBottom);
1444 if (vk.lastDevice != null) {
1445 pw.print(prefix); pw.print(" lastDevice=#");
1446 pw.println(vk.lastDevice.id);
1448 if (vk.lastKeycode != 0) {
1449 pw.print(prefix); pw.print(" lastKeycode=");
1450 pw.println(vk.lastKeycode);
1454 pw.print(prefix); pw.print(" Default keyboard: ");
1455 pw.println(SystemProperties.get("hw.keyboards.0.devname"));
1456 pw.print(prefix); pw.print(" mGlobalMetaState=");
1457 pw.print(mGlobalMetaState); pw.print(" mHaveGlobalMetaState=");
1458 pw.println(mHaveGlobalMetaState);
1459 pw.print(prefix); pw.print(" mDisplayWidth=");
1460 pw.print(mDisplayWidth); pw.print(" mDisplayHeight=");
1461 pw.println(mDisplayHeight);
1462 pw.print(prefix); pw.print(" mOrientation=");
1463 pw.println(mOrientation);
1464 if (mPressedVirtualKey != null) {
1465 pw.print(prefix); pw.print(" mPressedVirtualKey.scancode=");
1466 pw.println(mPressedVirtualKey.scancode);