blob: e0ee7ed6698abf91eaeef0fc63c438102218899d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.content.Context;
20import android.content.res.Configuration;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040021import android.os.Environment;
Michael Chan53071d62009-05-13 17:29:48 -070022import android.os.LatencyTimer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.os.PowerManager;
Michael Chan53071d62009-05-13 17:29:48 -070024import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.util.Log;
26import android.util.SparseArray;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040027import android.util.Xml;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.view.Display;
29import android.view.KeyEvent;
30import android.view.MotionEvent;
31import android.view.RawInputEvent;
32import android.view.Surface;
33import android.view.WindowManagerPolicy;
34
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040035import com.android.internal.util.XmlUtils;
36
37import org.xmlpull.v1.XmlPullParser;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040038
Dianne Hackborne3dd8842009-07-14 12:06:54 -070039import java.io.BufferedReader;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040040import java.io.File;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070041import java.io.FileInputStream;
42import java.io.FileNotFoundException;
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040043import java.io.FileReader;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070044import java.io.IOException;
45import java.io.InputStreamReader;
46import java.util.ArrayList;
47
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048public abstract class KeyInputQueue {
49 static final String TAG = "KeyInputQueue";
50
Dianne Hackborne3dd8842009-07-14 12:06:54 -070051 static final boolean DEBUG_VIRTUAL_KEYS = false;
52
Mike Lockwood1d9dfc52009-07-16 11:11:18 -040053 private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
54
Dianne Hackborne3dd8842009-07-14 12:06:54 -070055 final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
56 final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
Dianne Hackbornddca3ee2009-07-23 19:01:31 -070057 final HapticFeedbackCallback mHapticFeedbackCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
59 int mGlobalMetaState = 0;
60 boolean mHaveGlobalMetaState = false;
61
62 final QueuedEvent mFirst;
63 final QueuedEvent mLast;
64 QueuedEvent mCache;
65 int mCacheCount;
66
67 Display mDisplay = null;
Dianne Hackborne3dd8842009-07-14 12:06:54 -070068 int mDisplayWidth;
69 int mDisplayHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
71 int mOrientation = Surface.ROTATION_0;
72 int[] mKeyRotationMap = null;
73
Dianne Hackborne3dd8842009-07-14 12:06:54 -070074 VirtualKey mPressedVirtualKey = null;
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 PowerManager.WakeLock mWakeLock;
77
78 static final int[] KEY_90_MAP = new int[] {
79 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
80 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
81 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
82 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
83 };
84
85 static final int[] KEY_180_MAP = new int[] {
86 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
87 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
88 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
89 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
90 };
91
92 static final int[] KEY_270_MAP = new int[] {
93 KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
94 KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
95 KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
96 KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
97 };
98
99 public static final int FILTER_REMOVE = 0;
100 public static final int FILTER_KEEP = 1;
101 public static final int FILTER_ABORT = -1;
Michael Chan53071d62009-05-13 17:29:48 -0700102
103 private static final boolean MEASURE_LATENCY = false;
104 private LatencyTimer lt;
105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 public interface FilterCallback {
107 int filterEvent(QueuedEvent ev);
108 }
109
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700110 public interface HapticFeedbackCallback {
111 void virtualKeyFeedback(KeyEvent event);
112 }
113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 static class QueuedEvent {
115 InputDevice inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700116 long whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 int flags; // From the raw event
118 int classType; // One of the class constants in InputEvent
119 Object event;
120 boolean inQueue;
121
122 void copyFrom(QueuedEvent that) {
123 this.inputDevice = that.inputDevice;
Michael Chan53071d62009-05-13 17:29:48 -0700124 this.whenNano = that.whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 this.flags = that.flags;
126 this.classType = that.classType;
127 this.event = that.event;
128 }
129
130 @Override
131 public String toString() {
132 return "QueuedEvent{"
133 + Integer.toHexString(System.identityHashCode(this))
134 + " " + event + "}";
135 }
136
137 // not copied
138 QueuedEvent prev;
139 QueuedEvent next;
140 }
141
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700142 /**
143 * A key that exists as a part of the touch-screen, outside of the normal
144 * display area of the screen.
145 */
146 static class VirtualKey {
147 int scancode;
148 int centerx;
149 int centery;
150 int width;
151 int height;
152
153 int hitLeft;
154 int hitTop;
155 int hitRight;
156 int hitBottom;
157
158 InputDevice lastDevice;
159 int lastKeycode;
160
161 boolean checkHit(int x, int y) {
162 return (x >= hitLeft && x <= hitRight
163 && y >= hitTop && y <= hitBottom);
164 }
165
166 void computeHitRect(InputDevice dev, int dw, int dh) {
167 if (dev == lastDevice) {
168 return;
169 }
170
171 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
172 + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
173
174 lastDevice = dev;
175
176 int minx = dev.absX.minValue;
177 int maxx = dev.absX.maxValue;
178
179 int halfw = width/2;
180 int left = centerx - halfw;
181 int right = centerx + halfw;
182 hitLeft = minx + ((left*maxx-minx)/dw);
183 hitRight = minx + ((right*maxx-minx)/dw);
184
185 int miny = dev.absY.minValue;
186 int maxy = dev.absY.maxValue;
187
188 int halfh = height/2;
189 int top = centery - halfh;
190 int bottom = centery + halfh;
191 hitTop = miny + ((top*maxy-miny)/dh);
192 hitBottom = miny + ((bottom*maxy-miny)/dh);
193 }
194 }
Michael Chan53071d62009-05-13 17:29:48 -0700195
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400196 private void readVirtualKeys() {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700197 try {
198 FileInputStream fis = new FileInputStream(
199 "/sys/board_properties/virtualkeys.synaptics-rmi-touchscreen");
200 InputStreamReader isr = new InputStreamReader(fis);
201 BufferedReader br = new BufferedReader(isr);
202 String str = br.readLine();
203 if (str != null) {
204 String[] it = str.split(":");
205 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
206 final int N = it.length-6;
207 for (int i=0; i<=N; i+=6) {
208 if (!"0x01".equals(it[i])) {
209 Log.w(TAG, "Unknown virtual key type at elem #" + i
210 + ": " + it[i]);
211 continue;
212 }
213 try {
214 VirtualKey sb = new VirtualKey();
215 sb.scancode = Integer.parseInt(it[i+1]);
216 sb.centerx = Integer.parseInt(it[i+2]);
217 sb.centery = Integer.parseInt(it[i+3]);
218 sb.width = Integer.parseInt(it[i+4]);
219 sb.height = Integer.parseInt(it[i+5]);
220 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
221 + sb.scancode + ": center=" + sb.centerx + ","
222 + sb.centery + " size=" + sb.width + "x"
223 + sb.height);
224 mVirtualKeys.add(sb);
225 } catch (NumberFormatException e) {
226 Log.w(TAG, "Bad number at region " + i + " in: "
227 + str, e);
228 }
229 }
230 }
231 br.close();
232 } catch (FileNotFoundException e) {
233 Log.i(TAG, "No virtual keys found");
234 } catch (IOException e) {
235 Log.w(TAG, "Error reading virtual keys", e);
236 }
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400237 }
238
239 private void readExcludedDevices() {
240 // Read partner-provided list of excluded input devices
241 XmlPullParser parser = null;
242 // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
243 File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
244 FileReader confreader = null;
245 try {
246 confreader = new FileReader(confFile);
247 parser = Xml.newPullParser();
248 parser.setInput(confreader);
249 XmlUtils.beginDocument(parser, "devices");
250
251 while (true) {
252 XmlUtils.nextElement(parser);
253 if (!"device".equals(parser.getName())) {
254 break;
255 }
256 String name = parser.getAttributeValue(null, "name");
257 if (name != null) {
258 Log.d(TAG, "addExcludedDevice " + name);
259 addExcludedDevice(name);
260 }
261 }
262 } catch (FileNotFoundException e) {
263 // It's ok if the file does not exist.
264 } catch (Exception e) {
265 Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
266 } finally {
267 try { if (confreader != null) confreader.close(); } catch (IOException e) { }
268 }
269 }
270
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700271 KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400272 if (MEASURE_LATENCY) {
273 lt = new LatencyTimer(100, 1000);
274 }
275
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700276 mHapticFeedbackCallback = hapticFeedbackCallback;
277
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400278 readVirtualKeys();
279 readExcludedDevices();
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700280
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 PowerManager pm = (PowerManager)context.getSystemService(
282 Context.POWER_SERVICE);
283 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
284 "KeyInputQueue");
285 mWakeLock.setReferenceCounted(false);
286
287 mFirst = new QueuedEvent();
288 mLast = new QueuedEvent();
289 mFirst.next = mLast;
290 mLast.prev = mFirst;
291
292 mThread.start();
293 }
294
295 public void setDisplay(Display display) {
296 mDisplay = display;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700297
298 // We assume at this point that the display dimensions reflect the
299 // natural, unrotated display. We will perform hit tests for soft
300 // buttons based on that display.
301 mDisplayWidth = display.getWidth();
302 mDisplayHeight = display.getHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 }
304
305 public void getInputConfiguration(Configuration config) {
306 synchronized (mFirst) {
307 config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
308 config.keyboard = Configuration.KEYBOARD_NOKEYS;
309 config.navigation = Configuration.NAVIGATION_NONAV;
310
311 final int N = mDevices.size();
312 for (int i=0; i<N; i++) {
313 InputDevice d = mDevices.valueAt(i);
314 if (d != null) {
315 if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
316 config.touchscreen
317 = Configuration.TOUCHSCREEN_FINGER;
318 //Log.i("foo", "***** HAVE TOUCHSCREEN!");
319 }
320 if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
321 config.keyboard
322 = Configuration.KEYBOARD_QWERTY;
323 //Log.i("foo", "***** HAVE QWERTY!");
324 }
325 if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
326 config.navigation
327 = Configuration.NAVIGATION_TRACKBALL;
328 //Log.i("foo", "***** HAVE TRACKBALL!");
329 }
330 }
331 }
332 }
333 }
334
335 public static native String getDeviceName(int deviceId);
336 public static native int getDeviceClasses(int deviceId);
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400337 public static native void addExcludedDevice(String deviceName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 public static native boolean getAbsoluteInfo(int deviceId, int axis,
339 InputDevice.AbsoluteInfo outInfo);
340 public static native int getSwitchState(int sw);
341 public static native int getSwitchState(int deviceId, int sw);
342 public static native int getScancodeState(int sw);
343 public static native int getScancodeState(int deviceId, int sw);
344 public static native int getKeycodeState(int sw);
345 public static native int getKeycodeState(int deviceId, int sw);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700346 public static native int scancodeToKeycode(int deviceId, int scancode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
348
349 public static KeyEvent newKeyEvent(InputDevice device, long downTime,
350 long eventTime, boolean down, int keycode, int repeatCount,
351 int scancode, int flags) {
352 return new KeyEvent(
353 downTime, eventTime,
354 down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
355 keycode, repeatCount,
356 device != null ? device.mMetaKeysState : 0,
357 device != null ? device.id : -1, scancode,
The Android Open Source Project10592532009-03-18 17:39:46 -0700358 flags | KeyEvent.FLAG_FROM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 }
360
361 Thread mThread = new Thread("InputDeviceReader") {
362 public void run() {
Mike Lockwood1d9dfc52009-07-16 11:11:18 -0400363 Log.d(TAG, "InputDeviceReader.run()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 android.os.Process.setThreadPriority(
365 android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
366
367 try {
368 RawInputEvent ev = new RawInputEvent();
369 while (true) {
370 InputDevice di;
371
372 // block, doesn't release the monitor
373 readEvent(ev);
374
375 boolean send = false;
376 boolean configChanged = false;
377
378 if (false) {
379 Log.i(TAG, "Input event: dev=0x"
380 + Integer.toHexString(ev.deviceId)
381 + " type=0x" + Integer.toHexString(ev.type)
382 + " scancode=" + ev.scancode
383 + " keycode=" + ev.keycode
384 + " value=" + ev.value);
385 }
386
387 if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
388 synchronized (mFirst) {
389 di = newInputDevice(ev.deviceId);
390 mDevices.put(ev.deviceId, di);
391 configChanged = true;
392 }
393 } else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
394 synchronized (mFirst) {
395 Log.i(TAG, "Device removed: id=0x"
396 + Integer.toHexString(ev.deviceId));
397 di = mDevices.get(ev.deviceId);
398 if (di != null) {
399 mDevices.delete(ev.deviceId);
400 configChanged = true;
401 } else {
402 Log.w(TAG, "Bad device id: " + ev.deviceId);
403 }
404 }
405 } else {
406 di = getInputDevice(ev.deviceId);
407
408 // first crack at it
409 send = preprocessEvent(di, ev);
410
411 if (ev.type == RawInputEvent.EV_KEY) {
412 di.mMetaKeysState = makeMetaState(ev.keycode,
413 ev.value != 0, di.mMetaKeysState);
414 mHaveGlobalMetaState = false;
415 }
416 }
417
418 if (di == null) {
419 continue;
420 }
421
422 if (configChanged) {
423 synchronized (mFirst) {
Michael Chan53071d62009-05-13 17:29:48 -0700424 addLocked(di, System.nanoTime(), 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 RawInputEvent.CLASS_CONFIGURATION_CHANGED,
426 null);
427 }
428 }
429
430 if (!send) {
431 continue;
432 }
433
434 synchronized (mFirst) {
435 // NOTE: The event timebase absolutely must be the same
436 // timebase as SystemClock.uptimeMillis().
437 //curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
438 final long curTime = SystemClock.uptimeMillis();
Michael Chan53071d62009-05-13 17:29:48 -0700439 final long curTimeNano = System.nanoTime();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 //Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
441
442 final int classes = di.classes;
443 final int type = ev.type;
444 final int scancode = ev.scancode;
445 send = false;
446
447 // Is it a key event?
448 if (type == RawInputEvent.EV_KEY &&
449 (classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
450 (scancode < RawInputEvent.BTN_FIRST ||
451 scancode > RawInputEvent.BTN_LAST)) {
452 boolean down;
453 if (ev.value != 0) {
454 down = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700455 di.mKeyDownTime = curTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 } else {
457 down = false;
458 }
459 int keycode = rotateKeyCodeLocked(ev.keycode);
Michael Chan53071d62009-05-13 17:29:48 -0700460 addLocked(di, curTimeNano, ev.flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700462 newKeyEvent(di, di.mKeyDownTime, curTime, down,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 keycode, 0, scancode,
464 ((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
465 ? KeyEvent.FLAG_WOKE_HERE : 0));
466 } else if (ev.type == RawInputEvent.EV_KEY) {
467 if (ev.scancode == RawInputEvent.BTN_TOUCH &&
468 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
469 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700470 di.mAbs.mDown[0] = ev.value != 0;
471 } else if (ev.scancode == RawInputEvent.BTN_2 &&
472 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
473 di.mAbs.changed = true;
474 di.mAbs.mDown[1] = ev.value != 0;
475 } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
477 di.mRel.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700478 di.mRel.mDown[0] = ev.value != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 send = true;
480 }
481
482 } else if (ev.type == RawInputEvent.EV_ABS &&
483 (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700484 // Finger 1
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 if (ev.scancode == RawInputEvent.ABS_X) {
486 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700487 di.mAbs.mCurData[MotionEvent.SAMPLE_X] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 } else if (ev.scancode == RawInputEvent.ABS_Y) {
489 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700490 di.mAbs.mCurData[MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 } else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
492 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700493 di.mAbs.mCurData[MotionEvent.SAMPLE_PRESSURE] = ev.value;
494 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
495 + MotionEvent.SAMPLE_PRESSURE] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 } else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
497 di.mAbs.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700498 di.mAbs.mCurData[MotionEvent.SAMPLE_SIZE] = ev.value;
499 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
500 + MotionEvent.SAMPLE_SIZE] = ev.value;
501
502 // Finger 2
503 } else if (ev.scancode == RawInputEvent.ABS_HAT0X) {
504 di.mAbs.changed = true;
505 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
506 + MotionEvent.SAMPLE_X] = ev.value;
507 } else if (ev.scancode == RawInputEvent.ABS_HAT0Y) {
508 di.mAbs.changed = true;
509 di.mAbs.mCurData[MotionEvent.NUM_SAMPLE_DATA
510 + MotionEvent.SAMPLE_Y] = ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 }
512
513 } else if (ev.type == RawInputEvent.EV_REL &&
514 (classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
515 // Add this relative movement into our totals.
516 if (ev.scancode == RawInputEvent.REL_X) {
517 di.mRel.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700518 di.mRel.mCurData[MotionEvent.SAMPLE_X] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 } else if (ev.scancode == RawInputEvent.REL_Y) {
520 di.mRel.changed = true;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700521 di.mRel.mCurData[MotionEvent.SAMPLE_Y] += ev.value;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 }
523 }
524
525 if (send || ev.type == RawInputEvent.EV_SYN) {
526 if (mDisplay != null) {
527 if (!mHaveGlobalMetaState) {
528 computeGlobalMetaStateLocked();
529 }
530
531 MotionEvent me;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700532
533 InputDevice.MotionState ms = di.mAbs;
534 if (ms.changed) {
535 ms.changed = false;
536
537 boolean doMotion = true;
538
539 // Look for virtual buttons.
540 VirtualKey vk = mPressedVirtualKey;
541 if (vk != null) {
542 doMotion = false;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700543 if (!ms.mDown[0]) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700544 mPressedVirtualKey = null;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700545 ms.mLastDown[0] = ms.mDown[0];
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700546 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
547 "Generate key up for: " + vk.scancode);
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700548 KeyEvent event = newKeyEvent(di,
549 di.mKeyDownTime, curTime, false,
550 vk.lastKeycode,
551 0, vk.scancode,
552 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
553 mHapticFeedbackCallback.virtualKeyFeedback(event);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700554 addLocked(di, curTimeNano, ev.flags,
555 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700556 event);
557 } else if (isInsideDisplay(di)) {
558 // Whoops the pointer has moved into
559 // the display area! Cancel the
560 // virtual key and start a pointer
561 // motion.
562 mPressedVirtualKey = null;
563 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
564 "Cancel key up for: " + vk.scancode);
565 KeyEvent event = newKeyEvent(di,
566 di.mKeyDownTime, curTime, false,
567 vk.lastKeycode,
568 0, vk.scancode,
569 KeyEvent.FLAG_CANCELED |
570 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
571 mHapticFeedbackCallback.virtualKeyFeedback(event);
572 addLocked(di, curTimeNano, ev.flags,
573 RawInputEvent.CLASS_KEYBOARD,
574 event);
575 doMotion = true;
576 for (int i=InputDevice.MAX_POINTERS-1; i>=0; i--) {
577 ms.mLastDown[i] = false;
578 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700579 }
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700580 }
581 if (doMotion && ms.mDown[0] && !ms.mLastDown[0]) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700582 vk = findSoftButton(di);
583 if (vk != null) {
584 doMotion = false;
585 mPressedVirtualKey = vk;
586 vk.lastKeycode = scancodeToKeycode(
587 di.id, vk.scancode);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700588 ms.mLastDown[0] = ms.mDown[0];
589 di.mKeyDownTime = curTime;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700590 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
591 "Generate key down for: " + vk.scancode
592 + " (keycode=" + vk.lastKeycode + ")");
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700593 KeyEvent event = newKeyEvent(di,
594 di.mKeyDownTime, curTime, true,
595 vk.lastKeycode, 0,
596 vk.scancode,
597 KeyEvent.FLAG_VIRTUAL_HARD_KEY);
598 mHapticFeedbackCallback.virtualKeyFeedback(event);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700599 addLocked(di, curTimeNano, ev.flags,
600 RawInputEvent.CLASS_KEYBOARD,
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700601 event);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700604
605 if (doMotion) {
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700606 // XXX Need to be able to generate
607 // multiple events here, for example
608 // if two fingers change up/down state
609 // at the same time.
610 me = ms.generateAbsMotion(di, curTime,
611 curTimeNano, mDisplay,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700612 mOrientation, mGlobalMetaState);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700613 if (false) Log.v(TAG, "Absolute: x="
614 + di.mAbs.mCurData[MotionEvent.SAMPLE_X]
615 + " y="
616 + di.mAbs.mCurData[MotionEvent.SAMPLE_Y]
617 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700618 if (me != null) {
619 if (WindowManagerPolicy.WATCH_POINTER) {
620 Log.i(TAG, "Enqueueing: " + me);
621 }
622 addLocked(di, curTimeNano, ev.flags,
623 RawInputEvent.CLASS_TOUCHSCREEN, me);
624 }
625 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800626 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700627
628 ms = di.mRel;
629 if (ms.changed) {
630 ms.changed = false;
631
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700632 me = ms.generateRelMotion(di, curTime,
633 curTimeNano,
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700634 mOrientation, mGlobalMetaState);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700635 if (false) Log.v(TAG, "Relative: x="
636 + di.mRel.mCurData[MotionEvent.SAMPLE_X]
637 + " y="
638 + di.mRel.mCurData[MotionEvent.SAMPLE_Y]
639 + " ev=" + me);
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700640 if (me != null) {
641 addLocked(di, curTimeNano, ev.flags,
642 RawInputEvent.CLASS_TRACKBALL, me);
643 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 }
645 }
646 }
647 }
648 }
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700649
650 } catch (RuntimeException exc) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 Log.e(TAG, "InputReaderThread uncaught exception", exc);
652 }
653 }
654 };
655
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700656 private boolean isInsideDisplay(InputDevice dev) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700657 final InputDevice.AbsoluteInfo absx = dev.absX;
658 final InputDevice.AbsoluteInfo absy = dev.absY;
659 final InputDevice.MotionState absm = dev.mAbs;
660 if (absx == null || absy == null || absm == null) {
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700661 return true;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700662 }
663
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700664 if (absm.mCurData[MotionEvent.SAMPLE_X] >= absx.minValue
665 && absm.mCurData[MotionEvent.SAMPLE_X] <= absx.maxValue
666 && absm.mCurData[MotionEvent.SAMPLE_Y] >= absy.minValue
667 && absm.mCurData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
668 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input ("
669 + absm.mCurData[MotionEvent.SAMPLE_X]
670 + "," + absm.mCurData[MotionEvent.SAMPLE_Y]
671 + ") inside of display");
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700672 return true;
673 }
674
675 return false;
676 }
677
678 private VirtualKey findSoftButton(InputDevice dev) {
679 final int N = mVirtualKeys.size();
680 if (N <= 0) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700681 return null;
682 }
683
Dianne Hackbornddca3ee2009-07-23 19:01:31 -0700684 if (isInsideDisplay(dev)) {
685 return null;
686 }
687
688 final InputDevice.MotionState absm = dev.mAbs;
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700689 for (int i=0; i<N; i++) {
690 VirtualKey sb = mVirtualKeys.get(i);
691 sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700692 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test ("
693 + absm.mCurData[MotionEvent.SAMPLE_X] + ","
694 + absm.mCurData[MotionEvent.SAMPLE_Y] + ") in code "
695 + sb.scancode + " - (" + sb.hitLeft
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700696 + "," + sb.hitTop + ")-(" + sb.hitRight + ","
697 + sb.hitBottom + ")");
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700698 if (sb.checkHit(absm.mCurData[MotionEvent.SAMPLE_X],
699 absm.mCurData[MotionEvent.SAMPLE_Y])) {
Dianne Hackborne3dd8842009-07-14 12:06:54 -0700700 if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
701 return sb;
702 }
703 }
704
705 return null;
706 }
707
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 /**
709 * Returns a new meta state for the given keys and old state.
710 */
711 private static final int makeMetaState(int keycode, boolean down, int old) {
712 int mask;
713 switch (keycode) {
714 case KeyEvent.KEYCODE_ALT_LEFT:
715 mask = KeyEvent.META_ALT_LEFT_ON;
716 break;
717 case KeyEvent.KEYCODE_ALT_RIGHT:
718 mask = KeyEvent.META_ALT_RIGHT_ON;
719 break;
720 case KeyEvent.KEYCODE_SHIFT_LEFT:
721 mask = KeyEvent.META_SHIFT_LEFT_ON;
722 break;
723 case KeyEvent.KEYCODE_SHIFT_RIGHT:
724 mask = KeyEvent.META_SHIFT_RIGHT_ON;
725 break;
726 case KeyEvent.KEYCODE_SYM:
727 mask = KeyEvent.META_SYM_ON;
728 break;
729 default:
730 return old;
731 }
732 int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
733 & (down ? (old | mask) : (old & ~mask));
734 if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
735 result |= KeyEvent.META_ALT_ON;
736 }
737 if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
738 result |= KeyEvent.META_SHIFT_ON;
739 }
740 return result;
741 }
742
743 private void computeGlobalMetaStateLocked() {
744 int i = mDevices.size();
745 mGlobalMetaState = 0;
746 while ((--i) >= 0) {
747 mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
748 }
749 mHaveGlobalMetaState = true;
750 }
751
752 /*
753 * Return true if you want the event to get passed on to the
754 * rest of the system, and false if you've handled it and want
755 * it dropped.
756 */
757 abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);
758
759 InputDevice getInputDevice(int deviceId) {
760 synchronized (mFirst) {
761 return getInputDeviceLocked(deviceId);
762 }
763 }
764
765 private InputDevice getInputDeviceLocked(int deviceId) {
766 return mDevices.get(deviceId);
767 }
768
769 public void setOrientation(int orientation) {
770 synchronized(mFirst) {
771 mOrientation = orientation;
772 switch (orientation) {
773 case Surface.ROTATION_90:
774 mKeyRotationMap = KEY_90_MAP;
775 break;
776 case Surface.ROTATION_180:
777 mKeyRotationMap = KEY_180_MAP;
778 break;
779 case Surface.ROTATION_270:
780 mKeyRotationMap = KEY_270_MAP;
781 break;
782 default:
783 mKeyRotationMap = null;
784 break;
785 }
786 }
787 }
788
789 public int rotateKeyCode(int keyCode) {
790 synchronized(mFirst) {
791 return rotateKeyCodeLocked(keyCode);
792 }
793 }
794
795 private int rotateKeyCodeLocked(int keyCode) {
796 int[] map = mKeyRotationMap;
797 if (map != null) {
798 final int N = map.length;
799 for (int i=0; i<N; i+=2) {
800 if (map[i] == keyCode) {
801 return map[i+1];
802 }
803 }
804 }
805 return keyCode;
806 }
807
808 boolean hasEvents() {
809 synchronized (mFirst) {
810 return mFirst.next != mLast;
811 }
812 }
813
814 /*
815 * returns true if we returned an event, and false if we timed out
816 */
817 QueuedEvent getEvent(long timeoutMS) {
818 long begin = SystemClock.uptimeMillis();
819 final long end = begin+timeoutMS;
820 long now = begin;
821 synchronized (mFirst) {
822 while (mFirst.next == mLast && end > now) {
823 try {
824 mWakeLock.release();
825 mFirst.wait(end-now);
826 }
827 catch (InterruptedException e) {
828 }
829 now = SystemClock.uptimeMillis();
830 if (begin > now) {
831 begin = now;
832 }
833 }
834 if (mFirst.next == mLast) {
835 return null;
836 }
837 QueuedEvent p = mFirst.next;
838 mFirst.next = p.next;
839 mFirst.next.prev = mFirst;
840 p.inQueue = false;
841 return p;
842 }
843 }
844
845 void recycleEvent(QueuedEvent ev) {
846 synchronized (mFirst) {
847 //Log.i(TAG, "Recycle event: " + ev);
848 if (ev.event == ev.inputDevice.mAbs.currentMove) {
849 ev.inputDevice.mAbs.currentMove = null;
850 }
851 if (ev.event == ev.inputDevice.mRel.currentMove) {
852 if (false) Log.i(TAG, "Detach rel " + ev.event);
853 ev.inputDevice.mRel.currentMove = null;
Dianne Hackborn9822d2b2009-07-20 17:33:15 -0700854 ev.inputDevice.mRel.mCurData[MotionEvent.SAMPLE_X] = 0;
855 ev.inputDevice.mRel.mCurData[MotionEvent.SAMPLE_Y] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 }
857 recycleLocked(ev);
858 }
859 }
860
861 void filterQueue(FilterCallback cb) {
862 synchronized (mFirst) {
863 QueuedEvent cur = mLast.prev;
864 while (cur.prev != null) {
865 switch (cb.filterEvent(cur)) {
866 case FILTER_REMOVE:
867 cur.prev.next = cur.next;
868 cur.next.prev = cur.prev;
869 break;
870 case FILTER_ABORT:
871 return;
872 }
873 cur = cur.prev;
874 }
875 }
876 }
877
Michael Chan53071d62009-05-13 17:29:48 -0700878 private QueuedEvent obtainLocked(InputDevice device, long whenNano,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 int flags, int classType, Object event) {
880 QueuedEvent ev;
881 if (mCacheCount == 0) {
882 ev = new QueuedEvent();
883 } else {
884 ev = mCache;
885 ev.inQueue = false;
886 mCache = ev.next;
887 mCacheCount--;
888 }
889 ev.inputDevice = device;
Michael Chan53071d62009-05-13 17:29:48 -0700890 ev.whenNano = whenNano;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 ev.flags = flags;
892 ev.classType = classType;
893 ev.event = event;
894 return ev;
895 }
896
897 private void recycleLocked(QueuedEvent ev) {
898 if (ev.inQueue) {
899 throw new RuntimeException("Event already in queue!");
900 }
901 if (mCacheCount < 10) {
902 mCacheCount++;
903 ev.next = mCache;
904 mCache = ev;
905 ev.inQueue = true;
906 }
907 }
908
Michael Chan53071d62009-05-13 17:29:48 -0700909 private void addLocked(InputDevice device, long whenNano, int flags,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 int classType, Object event) {
911 boolean poke = mFirst.next == mLast;
912
Michael Chan53071d62009-05-13 17:29:48 -0700913 QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 QueuedEvent p = mLast.prev;
Michael Chan53071d62009-05-13 17:29:48 -0700915 while (p != mFirst && ev.whenNano < p.whenNano) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 p = p.prev;
917 }
918
919 ev.next = p.next;
920 ev.prev = p;
921 p.next = ev;
922 ev.next.prev = ev;
923 ev.inQueue = true;
924
925 if (poke) {
Michael Chan53071d62009-05-13 17:29:48 -0700926 long time;
927 if (MEASURE_LATENCY) {
928 time = System.nanoTime();
929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 mFirst.notify();
931 mWakeLock.acquire();
Michael Chan53071d62009-05-13 17:29:48 -0700932 if (MEASURE_LATENCY) {
933 lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
934 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 }
936 }
937
938 private InputDevice newInputDevice(int deviceId) {
939 int classes = getDeviceClasses(deviceId);
940 String name = getDeviceName(deviceId);
941 Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
942 + ", name=" + name
943 + ", classes=" + Integer.toHexString(classes));
944 InputDevice.AbsoluteInfo absX;
945 InputDevice.AbsoluteInfo absY;
946 InputDevice.AbsoluteInfo absPressure;
947 InputDevice.AbsoluteInfo absSize;
948 if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
949 absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
950 absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
951 absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
952 absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
953 } else {
954 absX = null;
955 absY = null;
956 absPressure = null;
957 absSize = null;
958 }
959
960 return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
961 }
962
963 private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
964 String name) {
965 InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
966 if (getAbsoluteInfo(id, channel, info)
967 && info.minValue != info.maxValue) {
968 Log.i(TAG, " " + name + ": min=" + info.minValue
969 + " max=" + info.maxValue
970 + " flat=" + info.flat
971 + " fuzz=" + info.fuzz);
972 info.range = info.maxValue-info.minValue;
973 return info;
974 }
975 Log.i(TAG, " " + name + ": unknown values");
976 return null;
977 }
978 private static native boolean readEvent(RawInputEvent outEvent);
979}