Jump to content

Multitouch possible in theory?


Guest lemmyc

Recommended Posts

Guest totiadrenalin

BB. I really think that the multitouch is near the finale.

Because this is single touch, and gesture second touch.

So now you need to hack a beet more the kernel, so the second finger will stay at the exact point where you put it, and the first finger will gesture the movements.

And I still think that the problem is where I already told you in the framework two.

Because with the hacked demo MT maps, browser, and MT photosotr, I can get really not that bad pich&zoom.

Maybe if I try to restore the firmware of the touchcontroller I'll get even better second finger GESTURE.

So old PALL!

It's about time to put an and of this suffering about the MT.

:lol:

EDIT:

I found the secret of the G1 MT in the browser.


/**
* MultiTouchController.java
*
* © Luke Hutchison ([email protected])
*
* Released under the Apache License v2.
*/
package com.android.browser;

import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.view.MotionEvent;

/**
* A class that simplifies the implementation of multitouch in applications. Subclass this and read the fields here as needed in
* subclasses.
*
* @author Luke Hutchison
*/
public class MultiTouchController<T> {

/**
* Time in ms required after a change in event status (e.g. putting down or lifting off the second finger) before events
* actually do anything -- helps eliminate noisy jumps that happen on change of status
*/
private static final long EVENT_SETTLE_TIME_INTERVAL = 100;

// The biggest possible abs val of the change in x or y between multitouch events
// (larger dx/dy events are ignored) -- helps eliminate jumping on finger 2 up/down
private static final float MAX_MULTITOUCH_POS_JUMP_SIZE = 30.0f;

// The biggest possible abs val of the change in multitouchWidth or multitouchHeight between
// multitouch events (larger-jump events are ignored) -- helps eliminate jumping on finger 2 up/down
private static final float MAX_MULTITOUCH_DIM_JUMP_SIZE = 40.0f;

// The smallest possible distance between multitouch points (used to avoid div-by-zero errors)
private static final float MIN_MULTITOUCH_SEPARATION = 10.0f;

// --

MultiTouchObjectCanvas<T> objectCanvas;

private PointInfo currPt, prevPt;

// --

private T draggedObject = null;

private long dragStartTime, dragSettleTime;

// Conversion from object coords to screen coords, and from drag width to object scale
private float objDraggedPointX, objDraggedPointY, objStartScale;

private PositionAndScale objPosAndScale = new PositionAndScale();

// --

/** Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses */
private boolean handleSingleTouchEvents;

// --

private static final int MODE_NOTHING = 0;

private static final int MODE_DRAG = 1;

private static final int MODE_STRETCH = 2;

private int dragMode = MODE_NOTHING;

// ------------------------------------------------------------------------------------

/** Constructor that sets handleSingleTouchEvents to true */
public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res) {
this(objectCanvas, res, true);
}

/** Full constructor */
public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res, boolean handleSingleTouchEvents) {
this.currPt = new PointInfo(res);
this.prevPt = new PointInfo(res);
this.handleSingleTouchEvents = handleSingleTouchEvents;
this.objectCanvas = objectCanvas;
}

// ------------------------------------------------------------------------------------

/**
* Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses.
* Default: true
*/
protected void setHandleSingleTouchEvents(boolean handleSingleTouchEvents) {
this.handleSingleTouchEvents = handleSingleTouchEvents;
}

/**
* Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses.
* Default: true
*/
protected boolean getHandleSingleTouchEvents() {
return handleSingleTouchEvents;
}

// ------------------------------------------------------------------------------------

/** Process incoming touch events */
public boolean onTouchEvent(MotionEvent event) {
if (dragMode == MODE_NOTHING && !handleSingleTouchEvents && event.getSize() <= 1.0f)
// Not handling initial single touch events, just pass them on
return false;

// Handle history first, if any (we sometimes get history with ACTION_MOVE events)
int histLen = event.getHistorySize();
for (int i = 0; i < histLen; i++)
decodeTouchEvent(event.getHistoricalX(i), event.getHistoricalY(i), event.getHistoricalPressure(i), event
.getHistoricalSize(i), true, MotionEvent.ACTION_MOVE, event.getHistoricalEventTime(i));

// Handle actual event at end of history
decodeTouchEvent(event.getX(), event.getY(), event.getPressure(), event.getSize(),
event.getAction() != MotionEvent.ACTION_UP && event.getAction() != MotionEvent.ACTION_CANCEL, event.getAction(),
event.getEventTime());
return true;
}

private void decodeTouchEvent(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) {
// Decode size field for multitouch events and then handle the event
prevPt.set(currPt);
currPt.set(x, y, pressure, undecodedSize, down, action, eventTime);
multiTouchController();
}

// ------------------------------------------------------------------------------------

/** Start dragging/stretching, or reset drag/stretch to current point if something goes out of range */
private void resetDrag() {
if (draggedObject == null)
return;

// Get dragged object position and scale
objectCanvas.getPositionAndScale(draggedObject, objPosAndScale);

// Figure out the object coords of the drag start point's screen coords.
// All stretching should be around this point in object-coord-space.
float scaleInv = (objPosAndScale.scale == 0.0f ? 1.0f : 1.0f / objPosAndScale.scale);
objDraggedPointX = (currPt.getX() - objPosAndScale.xOff) * scaleInv;
objDraggedPointY = (currPt.getY() - objPosAndScale.yOff) * scaleInv;

// Figure out ratio between object scale factor and multitouch diameter (they are linearly correlated)
float diam = currPt.getMultiTouchDiameter();
objStartScale = objPosAndScale.scale / (diam == 0.0f ? 1.0f : diam);
}

/** Drag/stretch the dragged object to the current touch position and diameter */
private void performDrag() {
// Don't do anything if we're not dragging anything
if (draggedObject == null)
return;

// Calc new position of dragged object
float scale = (objPosAndScale.scale == 0.0f ? 1.0f : objPosAndScale.scale);
float newObjPosX = currPt.getX() - objDraggedPointX * scale;
float newObjPosY = currPt.getY() - objDraggedPointY * scale;

// Get new drag diameter (avoiding divsion by zero), and calculate new drag scale
float diam;
if (!currPt.isMultiTouch) {
// Single-touch, no change in scale
diam = 1.0f;
} else {
diam = currPt.getMultiTouchDiameter();
if (diam < MIN_MULTITOUCH_SEPARATION)
diam = MIN_MULTITOUCH_SEPARATION;
}
float newScale = diam * objStartScale;

// Get the new obj coords and scale, and set them (notifying the subclass of the change)
objPosAndScale.set(newObjPosX, newObjPosY, newScale);
boolean success = objectCanvas.setPositionAndScale(draggedObject, objPosAndScale, currPt);
if (!success)
; // If we could't set those params, do nothing currently
}

/** The main single-touch and multi-touch logic */
private void multiTouchController() {

switch (dragMode) {
case MODE_NOTHING:
// Not doing anything currently
if (currPt.isDown() && !currPt.isDownPrev()) {
// Start a new single-point drag
draggedObject = objectCanvas.getDraggableObjectAtPoint(currPt);
if (draggedObject != null) {
// Started a new single-point drag
dragMode = MODE_DRAG;
objectCanvas.selectObject(draggedObject, currPt);
resetDrag();
// Don't need any settling time if just placing one finger, there is no noise
dragStartTime = dragSettleTime = currPt.getEventTime();
}
}
break;

case MODE_DRAG:
// Currently in a single-point drag
if (!currPt.isDown()) {
// First finger was released, stop dragging
dragMode = MODE_NOTHING;
objectCanvas.selectObject((draggedObject = null), currPt);

} else if (currPt.isMultiTouch()) {
// Point 1 was already down and point 2 was just placed down
dragMode = MODE_STRETCH;
// Restart the drag with the new drag position (that is at the midpoint between the touchpoints)
resetDrag();
// Need to let events settle before moving things, to help with event noise on touchdown
dragStartTime = currPt.getEventTime();
dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL;

} else {
// Point 1 is still down and point 2 did not change state, just do single-point drag to new location
if (currPt.getEventTime() < dragSettleTime) {
// Ignore the first few events if we just stopped stretching, because if finger 2 was kept down while
// finger 1 is lifted, then point 1 gets mapped to finger 2. Restart the drag from the new position.
resetDrag();
} else {
// Keep dragging, move to new point
performDrag();
}
}
break;

case MODE_STRETCH:
// Two-point stretch
if (!currPt.isMultiTouch() || !currPt.isDown()) {
// Dropped one or both points, stop stretching

if (!currPt.isDown()) {
// Dropped both points, go back to doing nothing
dragMode = MODE_NOTHING;
objectCanvas.selectObject((draggedObject = null), currPt);

} else {
// Just dropped point 2, downgrade to a single-point drag
dragMode = MODE_DRAG;
// Restart the drag with the single-finger position
resetDrag();
// Ignore the first few events after the drop, in case we dropped finger 1 and left finger 2 down
dragStartTime = currPt.getEventTime();
dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL;
}
} else {
// Keep stretching

if (Math.abs(currPt.getX() - prevPt.getX()) > MAX_MULTITOUCH_POS_JUMP_SIZE
|| Math.abs(currPt.getY() - prevPt.getY()) > MAX_MULTITOUCH_POS_JUMP_SIZE
|| Math.abs(currPt.getMultiTouchWidth() - prevPt.getMultiTouchWidth()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE
|| Math.abs(currPt.getMultiTouchHeight() - prevPt.getMultiTouchHeight()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE) {
// Jumped too far, probably event noise, reset and ignore events for a bit
resetDrag();
dragStartTime = currPt.getEventTime();
dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL;

} else if (currPt.eventTime < dragSettleTime) {
// Events have not yet settled, reset
resetDrag();
} else {
// Stretch to new position and size
performDrag();
}
}
break;
}
}

// ------------------------------------------------------------------------------------

/** A class that packages up all MotionEvent information with all derived multitouch information (if available) */
public static class PointInfo {
private float x, y, dx, dy, size, diameter, diameterSq, angle, pressure;

private boolean down, downPrev, isMultiTouch, diameterSqIsCalculated, diameterIsCalculated, angleIsCalculated;

private int action;

private long eventTime;

// --

private Resources res;

private int displayWidth, displayHeight;

// --

public PointInfo(Resources res) {
this.res = res;
DisplayMetrics metrics = res.getDisplayMetrics();
this.displayWidth = metrics.widthPixels;
this.displayHeight = metrics.heightPixels;
}

// --

public PointInfo(PointInfo other) {
this.set(other);
}

/** Copy all fields */
public void set(PointInfo other) {
this.displayWidth = other.displayWidth;
this.displayHeight = other.displayHeight;
this.x = other.x;
this.y = other.y;
this.dx = other.dx;
this.dy = other.dy;
this.size = other.size;
this.diameter = other.diameter;
this.diameterSq = other.diameterSq;
this.angle = other.angle;
this.pressure = other.pressure;
this.down = other.down;
this.action = other.action;
this.downPrev = other.downPrev;
this.isMultiTouch = other.isMultiTouch;
this.diameterIsCalculated = other.diameterIsCalculated;
this.diameterSqIsCalculated = other.diameterSqIsCalculated;
this.angleIsCalculated = other.angleIsCalculated;
this.eventTime = other.eventTime;
}

/** Setter for use in event decoding */
private void set(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) {
this.x = x;
this.y = y;
this.pressure = pressure;
this.downPrev = this.down;
this.down = down;
this.action = action;
this.eventTime = eventTime;
// 1.0f is the max value for size on an unpatched kernel -- make this backwards-compatible
this.isMultiTouch = (undecodedSize > 1.0f);
if (isMultiTouch) {
// Multitouch event, decipher size field, which contains dx and dy

// dx and dy come packaged in a 12-pt fixed-point number that has been converted into a float
int szi = (int) undecodedSize;
int dxi = szi >> 12;
int dyi = szi & ((1 << 12) - 1);

// Get display size and rotation, scale fixed point to screen coords
dx = Math.min(displayWidth, displayHeight) * dxi / (float) ((1 << 12) - 1);
dy = Math.max(displayWidth, displayHeight) * dyi / (float) ((1 << 12) - 1);
if (screenIsRotated()) {
// Swap x,y if screen is rotated
float tmp = dx;
dx = dy;
dy = tmp;
}

} else {
// Single-touch event
dx = dy = diameter = 0.0f;
// undecodedSize == 0 for single-touch when the multitouch kernel patch is applied
this.size = undecodedSize;
}
// Need to re-calculate the expensive params if they're needed
diameterSqIsCalculated = diameterIsCalculated = angleIsCalculated = false;
}

private boolean screenIsRotated() {
return res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
}

// Fast integer sqrt, by Jim Ulery. Should be faster than Math.sqrt()
private int julery_isqrt(int val) {
int temp, g = 0, b = 0x8000, bshft = 15;
do {
if (val >= (temp = (((g << 1) + B) << bshft--))) {
g += b;
val -= temp;
}
} while ((b >>= 1) > 0);
return g;
}

/** Calculate the squared diameter of the multitouch event, and cache it. Use this if you don't need to perform the sqrt. */
public float getMultiTouchDiameterSq() {
if (!diameterSqIsCalculated) {
diameterSq = (isMultiTouch ? dx * dx + dy * dy : 0.0f);
diameterSqIsCalculated = true;
}
return diameterSq;
}

/** Calculate the diameter of the multitouch event, and cache it. Uses fast int sqrt but gives accuracy to 1/16px. */
public float getMultiTouchDiameter() {
if (!diameterIsCalculated) {
// Get 1/16 pixel's worth of subpixel accuracy, works on screens up to 2048x2048
// before we get overflow (at which point you can reduce or eliminate subpix
// accuracy, or use longs in julery_isqrt())
float diamSq = getMultiTouchDiameterSq();
diameter = (diamSq == 0.0f ? 0.0f : (float) julery_isqrt((int) (256 * diamSq)) / 16.0f);
// Make sure diameter is never less than dx or dy, for trig purposes
if (diameter < dx)
diameter = dx;
if (diameter < dy)
diameter = dy;
diameterIsCalculated = true;
}
return diameter;
}

/**
* Calculate the angle of a multitouch event, and cache it. Actually gives the smaller of the two angles between the x axis
* and the line between the two touchpoints, so range is [0,Math.PI/2]. Uses Math.atan2().
*/
public float getMultiTouchAngle() {
if (!angleIsCalculated) {
angle = (float) Math.atan2(dy, dx);
angleIsCalculated = true;
}
return angle;
}

public float getX() {
return x;
}

public float getY() {
return y;
}

public float getMultiTouchWidth() {
return dx;
}

public float getMultiTouchHeight() {
return dy;
}

public float getSize() {
return size;
}

public float getPressure() {
return pressure;
}

public boolean isDown() {
return down;
}

public boolean isDownPrev() {
return downPrev;
}

public int getAction() {
return action;
}

public boolean isMultiTouch() {
return isMultiTouch;
}

public long getEventTime() {
return eventTime;
}
}

// ------------------------------------------------------------------------------------

/** A class that is used to store scroll offsets and scale information for objects that are managed by the multitouch controller */
public static class PositionAndScale {
private float xOff, yOff, scale;

public PositionAndScale() {
}

public void set(float xOff, float yOff, float scale) {
this.xOff = xOff;
this.yOff = yOff;
this.scale = scale;
}

public float getXOff() {
return xOff;
}

public float getYOff() {
return yOff;
}

public float getScale() {
return scale;
}
}

// ------------------------------------------------------------------------------------

public static interface MultiTouchObjectCanvas<T> {

/** See if there is a draggable object at the current point. Returns the object at the point, or null if nothing to drag. */
public T getDraggableObjectAtPoint(PointInfo pt);

/**
* Get the screen coords of the dragged object's origin, and scale multiplier to convert screen coords to obj coords. Call
* the .set() method on the passed PositionAndScale object.
*/
public void getPositionAndScale(T obj, PositionAndScale objPosAndScaleOut);

/**
* Set the position and scale of the dragged object, in object coords. Return true for success, or false if those parameters
* are out of range.
*/
public boolean setPositionAndScale(T obj, PositionAndScale newObjPosAndScale, PointInfo touchPoint);

/**
* Select an object at the given point. Can be used to bring the object to top etc. Only called when first touchpoint goes
* down, not when multitouch is initiated. Also called with null when drag op stops.
*/
public void selectObject(T obj, PointInfo pt);
}
}

[/codebox]

EDIT2:

And the Final Product.

The patched KeyInputQueue.java

[codebox]
/*
* Copyright © 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* [url="http://www.apache.org/licenses/LICENSE-2.0"]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.android.server;

import android.content.Context;
import android.content.res.Configuration;
import android.os.SystemClock;
import android.os.PowerManager;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RawInputEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;

public abstract class KeyInputQueue {
static final String TAG = "KeyInputQueue";

SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();

int mGlobalMetaState = 0;
boolean mHaveGlobalMetaState = false;

final QueuedEvent mFirst;
final QueuedEvent mLast;
QueuedEvent mCache;
int mCacheCount;

Display mDisplay = null;

int mOrientation = Surface.ROTATION_0;
int[] mKeyRotationMap = null;

PowerManager.WakeLock mWakeLock;

static final int[] KEY_90_MAP = new int[] {
KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
};

static final int[] KEY_180_MAP = new int[] {
KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
};

static final int[] KEY_270_MAP = new int[] {
KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
};

public static final int FILTER_REMOVE = 0;
public static final int FILTER_KEEP = 1;
public static final int FILTER_ABORT = -1;

public interface FilterCallback {
int filterEvent(QueuedEvent ev);
}

static class QueuedEvent {
InputDevice inputDevice;
long when;
int flags; // From the raw event
int classType; // One of the class constants in InputEvent
Object event;
boolean inQueue;

void copyFrom(QueuedEvent that) {
this.inputDevice = that.inputDevice;
this.when = that.when;
this.flags = that.flags;
this.classType = that.classType;
this.event = that.event;
}

@Override
public String toString() {
return "QueuedEvent{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + event + "}";
}

// not copied
QueuedEvent prev;
QueuedEvent next;
}

KeyInputQueue(Context context) {
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"KeyInputQueue");
mWakeLock.setReferenceCounted(false);

mFirst = new QueuedEvent();
mLast = new QueuedEvent();
mFirst.next = mLast;
mLast.prev = mFirst;

mThread.start();
}

public void setDisplay(Display display) {
mDisplay = display;
}

public void getInputConfiguration(Configuration config) {
synchronized (mFirst) {
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
config.keyboard = Configuration.KEYBOARD_NOKEYS;
config.navigation = Configuration.NAVIGATION_NONAV;

final int N = mDevices.size();
for (int i=0; i<N; i++) {
InputDevice d = mDevices.valueAt(i);
if (d != null) {
if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
config.touchscreen
= Configuration.TOUCHSCREEN_FINGER;
//Log.i("foo", "***** HAVE TOUCHSCREEN!");
}
if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
config.keyboard
= Configuration.KEYBOARD_QWERTY;
//Log.i("foo", "***** HAVE QWERTY!");
}
if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
config.navigation
= Configuration.NAVIGATION_TRACKBALL;
//Log.i("foo", "***** HAVE TRACKBALL!");
}
}
}
}
}

public static native String getDeviceName(int deviceId);
public static native int getDeviceClasses(int deviceId);
public static native boolean getAbsoluteInfo(int deviceId, int axis,
InputDevice.AbsoluteInfo outInfo);
public static native int getSwitchState(int sw);
public static native int getSwitchState(int deviceId, int sw);
public static native int getScancodeState(int sw);
public static native int getScancodeState(int deviceId, int sw);
public static native int getKeycodeState(int sw);
public static native int getKeycodeState(int deviceId, int sw);
public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);

public static KeyEvent newKeyEvent(InputDevice device, long downTime,
long eventTime, boolean down, int keycode, int repeatCount,
int scancode, int flags) {
return new KeyEvent(
downTime, eventTime,
down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
keycode, repeatCount,
device != null ? device.mMetaKeysState : 0,
device != null ? device.id : -1, scancode,
flags | KeyEvent.FLAG_FROM_SYSTEM);
}

Thread mThread = new Thread("InputDeviceReader") {
public void run() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);

try {
RawInputEvent ev = new RawInputEvent();
while (true) {
InputDevice di;

// block, doesn't release the monitor
readEvent(ev);

boolean send = false;
boolean configChanged = false;

if (false) {
Log.i(TAG, "Input event: dev=0x"
+ Integer.toHexString(ev.deviceId)
+ " type=0x" + Integer.toHexString(ev.type)
+ " scancode=" + ev.scancode
+ " keycode=" + ev.keycode
+ " value=" + ev.value);
}

if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
synchronized (mFirst) {
di = newInputDevice(ev.deviceId);
mDevices.put(ev.deviceId, di);
configChanged = true;
}
} else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
synchronized (mFirst) {
Log.i(TAG, "Device removed: id=0x"
+ Integer.toHexString(ev.deviceId));
di = mDevices.get(ev.deviceId);
if (di != null) {
mDevices.delete(ev.deviceId);
configChanged = true;
} else {
Log.w(TAG, "Bad device id: " + ev.deviceId);
}
}
} else {
di = getInputDevice(ev.deviceId);

// first crack at it
send = preprocessEvent(di, ev);

if (ev.type == RawInputEvent.EV_KEY) {
di.mMetaKeysState = makeMetaState(ev.keycode,
ev.value != 0, di.mMetaKeysState);
mHaveGlobalMetaState = false;
}
}

if (di == null) {
continue;
}

if (configChanged) {
synchronized (mFirst) {
addLocked(di, SystemClock.uptimeMillis(), 0,
RawInputEvent.CLASS_CONFIGURATION_CHANGED,
null);
}
}

if (!send) {
continue;
}

synchronized (mFirst) {
// NOTE: The event timebase absolutely must be the same
// timebase as SystemClock.uptimeMillis().
//curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
final long curTime = SystemClock.uptimeMillis();
//Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());

final int classes = di.classes;
final int type = ev.type;
final int scancode = ev.scancode;
send = false;

// Is it a key event?
if (type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST ||
scancode > RawInputEvent.BTN_LAST)) {
boolean down;
if (ev.value != 0) {
down = true;
di.mDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
} else if (ev.type == RawInputEvent.EV_KEY) {
if (ev.scancode == RawInputEvent.BTN_TOUCH &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
di.mAbs.changed = true;
di.mAbs.down = ev.value != 0;
}
if (ev.scancode == RawInputEvent.BTN_MOUSE &&
(classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
di.mRel.changed = true;
di.mRel.down = ev.value != 0;
send = true;
}

} else if (ev.type == RawInputEvent.EV_ABS &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
if (ev.scancode == RawInputEvent.ABS_X) {
di.mAbs.changed = true;
di.mAbs.x = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_Y) {
di.mAbs.changed = true;
di.mAbs.y = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
di.mAbs.changed = true;
di.mAbs.pressure = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
di.mAbs.changed = true;
di.mAbs.size = ev.value;
}

} else if (ev.type == RawInputEvent.EV_REL &&
(classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
// Add this relative movement into our totals.
if (ev.scancode == RawInputEvent.REL_X) {
di.mRel.changed = true;
di.mRel.x += ev.value;
} else if (ev.scancode == RawInputEvent.REL_Y) {
di.mRel.changed = true;
di.mRel.y += ev.value;
}
}

if (send || ev.type == RawInputEvent.EV_SYN) {
if (mDisplay != null) {
if (!mHaveGlobalMetaState) {
computeGlobalMetaStateLocked();
}

MotionEvent me;
me = di.mAbs.generateMotion(di, curTime, true,
mDisplay, mOrientation, mGlobalMetaState);
if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
+ " y=" + di.mAbs.y + " ev=" + me);
if (me != null) {
if (WindowManagerPolicy.WATCH_POINTER) {
Log.i(TAG, "Enqueueing: " + me);
}
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_TOUCHSCREEN, me);
}
me = di.mRel.generateMotion(di, curTime, false,
mDisplay, mOrientation, mGlobalMetaState);
if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
+ " y=" + di.mRel.y + " ev=" + me);
if (me != null) {
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_TRACKBALL, me);
}
}
}
}
}
}
catch (RuntimeException exc) {
Log.e(TAG, "InputReaderThread uncaught exception", exc);
}
}
};

/**
* Returns a new meta state for the given keys and old state.
*/
private static final int makeMetaState(int keycode, boolean down, int old) {
int mask;
switch (keycode) {
case KeyEvent.KEYCODE_ALT_LEFT:
mask = KeyEvent.META_ALT_LEFT_ON;
break;
case KeyEvent.KEYCODE_ALT_RIGHT:
mask = KeyEvent.META_ALT_RIGHT_ON;
break;
case KeyEvent.KEYCODE_SHIFT_LEFT:
mask = KeyEvent.META_SHIFT_LEFT_ON;
break;
case KeyEvent.KEYCODE_SHIFT_RIGHT:
mask = KeyEvent.META_SHIFT_RIGHT_ON;
break;
case KeyEvent.KEYCODE_SYM:
mask = KeyEvent.META_SYM_ON;
break;
default:
return old;
}
int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
& (down ? (old | mask) : (old & ~mask));
if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
result |= KeyEvent.META_ALT_ON;
}
if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
result |= KeyEvent.META_SHIFT_ON;
}
return result;
}

private void computeGlobalMetaStateLocked() {
int i = mDevices.size();
mGlobalMetaState = 0;
while ((--i) >= 0) {
mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
}
mHaveGlobalMetaState = true;
}

/*
* Return true if you want the event to get passed on to the
* rest of the system, and false if you've handled it and want
* it dropped.
*/
abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);

InputDevice getInputDevice(int deviceId) {
synchronized (mFirst) {
return getInputDeviceLocked(deviceId);
}
}

private InputDevice getInputDeviceLocked(int deviceId) {
return mDevices.get(deviceId);
}

public void setOrientation(int orientation) {
synchronized(mFirst) {
mOrientation = orientation;
switch (orientation) {
case Surface.ROTATION_90:
mKeyRotationMap = KEY_90_MAP;
break;
case Surface.ROTATION_180:
mKeyRotationMap = KEY_180_MAP;
break;
case Surface.ROTATION_270:
mKeyRotationMap = KEY_270_MAP;
break;
default:
mKeyRotationMap = null;
break;
}
}
}

public int rotateKeyCode(int keyCode) {
synchronized(mFirst) {
return rotateKeyCodeLocked(keyCode);
}
}

private int rotateKeyCodeLocked(int keyCode) {
int[] map = mKeyRotationMap;
if (map != null) {
final int N = map.length;
for (int i=0; i<N; i+=2) {
if (map[i] == keyCode) {
return map[i+1];
}
}
}
return keyCode;
}

boolean hasEvents() {
synchronized (mFirst) {
return mFirst.next != mLast;
}
}

/*
* returns true if we returned an event, and false if we timed out
*/
QueuedEvent getEvent(long timeoutMS) {
long begin = SystemClock.uptimeMillis();
final long end = begin+timeoutMS;
long now = begin;
synchronized (mFirst) {
while (mFirst.next == mLast && end > now) {
try {
mWakeLock.release();
mFirst.wait(end-now);
}
catch (InterruptedException e) {
}
now = SystemClock.uptimeMillis();
if (begin > now) {
begin = now;
}
}
if (mFirst.next == mLast) {
return null;
}
QueuedEvent p = mFirst.next;
mFirst.next = p.next;
mFirst.next.prev = mFirst;
p.inQueue = false;
return p;
}
}

void recycleEvent(QueuedEvent ev) {
synchronized (mFirst) {
//Log.i(TAG, "Recycle event: " + ev);
if (ev.event == ev.inputDevice.mAbs.currentMove) {
ev.inputDevice.mAbs.currentMove = null;
}
if (ev.event == ev.inputDevice.mRel.currentMove) {
if (false) Log.i(TAG, "Detach rel " + ev.event);
ev.inputDevice.mRel.currentMove = null;
ev.inputDevice.mRel.x = 0;
ev.inputDevice.mRel.y = 0;
}
recycleLocked(ev);
}
}

void filterQueue(FilterCallback cb) {
synchronized (mFirst) {
QueuedEvent cur = mLast.prev;
while (cur.prev != null) {
switch (cb.filterEvent(cur)) {
case FILTER_REMOVE:
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
break;
case FILTER_ABORT:
return;
}
cur = cur.prev;
}
}
}

private QueuedEvent obtainLocked(InputDevice device, long when,
int flags, int classType, Object event) {
QueuedEvent ev;
if (mCacheCount == 0) {
ev = new QueuedEvent();
} else {
ev = mCache;
ev.inQueue = false;
mCache = ev.next;
mCacheCount--;
}
ev.inputDevice = device;
ev.when = when;
ev.flags = flags;
ev.classType = classType;
ev.event = event;
return ev;
}

private void recycleLocked(QueuedEvent ev) {
if (ev.inQueue) {
throw new RuntimeException("Event already in queue!");
}
if (mCacheCount < 10) {
mCacheCount++;
ev.next = mCache;
mCache = ev;
ev.inQueue = true;
}
}

private void addLocked(InputDevice device, long when, int flags,
int classType, Object event) {
boolean poke = mFirst.next == mLast;

QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
QueuedEvent p = mLast.prev;
while (p != mFirst && ev.when < p.when) {
p = p.prev;
}

ev.next = p.next;
ev.prev = p;
p.next = ev;
ev.next.prev = ev;
ev.inQueue = true;

if (poke) {
mFirst.notify();
mWakeLock.acquire();
}
}

private InputDevice newInputDevice(int deviceId) {
int classes = getDeviceClasses(deviceId);
String name = getDeviceName(deviceId);
Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
+ ", name=" + name
+ ", classes=" + Integer.toHexString(classes));
InputDevice.AbsoluteInfo absX;
InputDevice.AbsoluteInfo absY;
InputDevice.AbsoluteInfo absPressure;
InputDevice.AbsoluteInfo absSize;
if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
} else {
absX = null;
absY = null;
absPressure = null;
absSize = null;
}

return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
}

private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
String name) {
InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
if (getAbsoluteInfo(id, channel, info)
&& info.minValue != info.maxValue) {
Log.i(TAG, " " + name + ": min=" + info.minValue
+ " max=" + info.maxValue
+ " flat=" + info.flat
+ " fuzz=" + info.fuzz);
info.range = info.maxValue-info.minValue;
return info;
}
Log.i(TAG, " " + name + ": unknown values");
return null;
}
private static native boolean readEvent(RawInputEvent outEvent);
}

syntax highlighted by Code2HTML, v. 0.9.1

Just put it in place: ANDROID_HOME/frameworks/base/services/java/com/android/server/

And recompile the Android.

That's all we need.

Maybe this isn't exact what we need, because this file is from G1, But at least we get some point.

KeyInputQueue.java

Best regards

TOTI

MultiTouchController.zip

Edited by totiadrenalin
Link to comment
Share on other sites

Guest thesinger

Guys i just tried to move the home screen on the froyo with 2 fingers and worked really nice it worked even with 3 fingers on the screan and showed me no problem. :lol: And i have nothing other installed then the Froyo 0.51 from fibb.

Edited by thesinger
Link to comment
Share on other sites

Guest whackster
Guys i just tried to move the home screen on the froyo with 2 fingers and worked really nice it worked even with 3 fingers on the screan and showed me no problem. B) And i have nothing other installed then the Froyo 0.51 from fibb.

Holy s%#t its working!!!!!!! B)

Seriously, why don't you use an app like multitouch tester, there are a couple different ones on the market and they provide a much better oversight on what's going on with the touchscreen.

Sorry, couldn't resist going OT.

Oh, and Dan? Where's Dan? :lol:

Link to comment
Share on other sites

Guest DanWilson
Holy s%#t its working!!!!!!! B)

Seriously, why don't you use an app like multitouch tester, there are a couple different ones on the market and they provide a much better oversight on what's going on with the touchscreen.

Sorry, couldn't resist going OT.

Oh, and Dan? Where's Dan? :lol:

Huh?

I think he's just using all 3 fingers at once, causing the screen to think he has one huge finger, and as such, it works.

Prove me wrong.

Link to comment
Share on other sites

Holy s%#t its working!!!!!!! B)

Seriously, why don't you use an app like multitouch tester, there are a couple different ones on the market and they provide a much better oversight on what's going on with the touchscreen.

Sorry, couldn't resist going OT.

Oh, and Dan? Where's Dan? B)

What? Where?How? :lol: where do i push this?

Edited by flip360
Link to comment
Share on other sites

Guest whackster
Huh?

I think he's just using all 3 fingers at once, causing the screen to think he has one huge finger, and as such, it works.

Prove me wrong.

Just try it, works with four fingers too! Five even if you squeeze them together, but that could be a limit of the screen size :lol:

Oh, and B) <--- click the smiley.

Just feeling bored this Sunday, don't hold it against me, Toti and BB are doing an amazing job with MT, I have no doubts in them.

Link to comment
Share on other sites

Guest thesinger

OOhhh i forgot put 3 fingers in the pointer location in the dev tools and it will show the one of the fingers location.i think that is becouse it needs an other firmwere which mt is suported in the softwere.proof for this is the count of the fingers in the pointer location in the top of the screan 0/1 that means the software suports only one touch.

Link to comment
Share on other sites

Guest DanWilson
OOhhh i forgot put 3 fingers in the pointer location in the dev tools and it will show the one of the fingers location.i think that is becouse it needs an other firmwere which mt is suported in the softwere.proof for this is the count of the fingers in the pointer location in the top of the screan 0/1 that means the software suports only one touch.

Hence why we need a custom kernel and custom touch controller firmware.

Link to comment
Share on other sites

Guest thesinger
Hence why we need a custom kernel and custom touch controller firmware.

I don't now i'm not a pro like bb and toti but i think we need a android version that is built in multitouch like the one in the htc hero.

Link to comment
Share on other sites

Guest DanWilson
I don't now i'm not a pro like bb and toti but i think we need a android version that is built in multitouch like the one in the htc hero.

Yah but we'll need the kernel and controller first.

The version of Android would come after that.

Link to comment
Share on other sites

Guest totiadrenalin
Hence why we need a custom kernel and custom touch controller firmware.

No!

Only the Patched Kernel.

Because This isn't really multitouch screen, But a singletouch with second finger gesture.

So maybe it recognize up to 3 fingers, but it's still singletouch.

Because It only gesture the second finger.

That's why you can move the screen of the launch app with 3 fingers.

But without the patched drivers there's no chance do provide second finger gesture.

So don't rush, and let BB correct the kernel, and ad what I post in my previews post.

You will get then a real second finger gesture.

A Dual touch.

Link to comment
Share on other sites

Guest totiadrenalin
BB. I really think that the multitouch is near the finale.

Because this is single touch, and gesture second touch.

So now you need to hack a beet more the kernel, so the second finger will stay at the exact point where you put it, and the first finger will gesture the movements.

And I still think that the problem is where I already told you in the framework two.

Because with the hacked demo MT maps, browser, and MT photosotr, I can get really not that bad pich&zoom.

Maybe if I try to restore the firmware of the touchcontroller I'll get even better second finger GESTURE.

So old PALL!

It's about time to put an and of this suffering about the MT.

:lol:

EDIT:

I found the secret of the G1 MT in the browser.


/**
* MultiTouchController.java
*
* © Luke Hutchison ([email protected])
*
* Released under the Apache License v2.
*/
package com.android.browser;

import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.view.MotionEvent;

/**
* A class that simplifies the implementation of multitouch in applications. Subclass this and read the fields here as needed in
* subclasses.
*
* @author Luke Hutchison
*/
public class MultiTouchController<T> {

/**
* Time in ms required after a change in event status (e.g. putting down or lifting off the second finger) before events
* actually do anything -- helps eliminate noisy jumps that happen on change of status
*/
private static final long EVENT_SETTLE_TIME_INTERVAL = 100;

// The biggest possible abs val of the change in x or y between multitouch events
// (larger dx/dy events are ignored) -- helps eliminate jumping on finger 2 up/down
private static final float MAX_MULTITOUCH_POS_JUMP_SIZE = 30.0f;

// The biggest possible abs val of the change in multitouchWidth or multitouchHeight between
// multitouch events (larger-jump events are ignored) -- helps eliminate jumping on finger 2 up/down
private static final float MAX_MULTITOUCH_DIM_JUMP_SIZE = 40.0f;

// The smallest possible distance between multitouch points (used to avoid div-by-zero errors)
private static final float MIN_MULTITOUCH_SEPARATION = 10.0f;

// --

MultiTouchObjectCanvas<T> objectCanvas;

private PointInfo currPt, prevPt;

// --

private T draggedObject = null;

private long dragStartTime, dragSettleTime;

// Conversion from object coords to screen coords, and from drag width to object scale
private float objDraggedPointX, objDraggedPointY, objStartScale;

private PositionAndScale objPosAndScale = new PositionAndScale();

// --

/** Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses */
private boolean handleSingleTouchEvents;

// --

private static final int MODE_NOTHING = 0;

private static final int MODE_DRAG = 1;

private static final int MODE_STRETCH = 2;

private int dragMode = MODE_NOTHING;

// ------------------------------------------------------------------------------------

/** Constructor that sets handleSingleTouchEvents to true */
public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res) {
this(objectCanvas, res, true);
}

/** Full constructor */
public MultiTouchController(MultiTouchObjectCanvas<T> objectCanvas, Resources res, boolean handleSingleTouchEvents) {
this.currPt = new PointInfo(res);
this.prevPt = new PointInfo(res);
this.handleSingleTouchEvents = handleSingleTouchEvents;
this.objectCanvas = objectCanvas;
}

// ------------------------------------------------------------------------------------

/**
* Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses.
* Default: true
*/
protected void setHandleSingleTouchEvents(boolean handleSingleTouchEvents) {
this.handleSingleTouchEvents = handleSingleTouchEvents;
}

/**
* Whether to handle single-touch events/drags before multi-touch is initiated or not; if not, they are handled by subclasses.
* Default: true
*/
protected boolean getHandleSingleTouchEvents() {
return handleSingleTouchEvents;
}

// ------------------------------------------------------------------------------------

/** Process incoming touch events */
public boolean onTouchEvent(MotionEvent event) {
if (dragMode == MODE_NOTHING && !handleSingleTouchEvents && event.getSize() <= 1.0f)
// Not handling initial single touch events, just pass them on
return false;

// Handle history first, if any (we sometimes get history with ACTION_MOVE events)
int histLen = event.getHistorySize();
for (int i = 0; i < histLen; i++)
decodeTouchEvent(event.getHistoricalX(i), event.getHistoricalY(i), event.getHistoricalPressure(i), event
.getHistoricalSize(i), true, MotionEvent.ACTION_MOVE, event.getHistoricalEventTime(i));

// Handle actual event at end of history
decodeTouchEvent(event.getX(), event.getY(), event.getPressure(), event.getSize(),
event.getAction() != MotionEvent.ACTION_UP && event.getAction() != MotionEvent.ACTION_CANCEL, event.getAction(),
event.getEventTime());
return true;
}

private void decodeTouchEvent(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) {
// Decode size field for multitouch events and then handle the event
prevPt.set(currPt);
currPt.set(x, y, pressure, undecodedSize, down, action, eventTime);
multiTouchController();
}

// ------------------------------------------------------------------------------------

/** Start dragging/stretching, or reset drag/stretch to current point if something goes out of range */
private void resetDrag() {
if (draggedObject == null)
return;

// Get dragged object position and scale
objectCanvas.getPositionAndScale(draggedObject, objPosAndScale);

// Figure out the object coords of the drag start point's screen coords.
// All stretching should be around this point in object-coord-space.
float scaleInv = (objPosAndScale.scale == 0.0f ? 1.0f : 1.0f / objPosAndScale.scale);
objDraggedPointX = (currPt.getX() - objPosAndScale.xOff) * scaleInv;
objDraggedPointY = (currPt.getY() - objPosAndScale.yOff) * scaleInv;

// Figure out ratio between object scale factor and multitouch diameter (they are linearly correlated)
float diam = currPt.getMultiTouchDiameter();
objStartScale = objPosAndScale.scale / (diam == 0.0f ? 1.0f : diam);
}

/** Drag/stretch the dragged object to the current touch position and diameter */
private void performDrag() {
// Don't do anything if we're not dragging anything
if (draggedObject == null)
return;

// Calc new position of dragged object
float scale = (objPosAndScale.scale == 0.0f ? 1.0f : objPosAndScale.scale);
float newObjPosX = currPt.getX() - objDraggedPointX * scale;
float newObjPosY = currPt.getY() - objDraggedPointY * scale;

// Get new drag diameter (avoiding divsion by zero), and calculate new drag scale
float diam;
if (!currPt.isMultiTouch) {
// Single-touch, no change in scale
diam = 1.0f;
} else {
diam = currPt.getMultiTouchDiameter();
if (diam < MIN_MULTITOUCH_SEPARATION)
diam = MIN_MULTITOUCH_SEPARATION;
}
float newScale = diam * objStartScale;

// Get the new obj coords and scale, and set them (notifying the subclass of the change)
objPosAndScale.set(newObjPosX, newObjPosY, newScale);
boolean success = objectCanvas.setPositionAndScale(draggedObject, objPosAndScale, currPt);
if (!success)
; // If we could't set those params, do nothing currently
}

/** The main single-touch and multi-touch logic */
private void multiTouchController() {

switch (dragMode) {
case MODE_NOTHING:
// Not doing anything currently
if (currPt.isDown() && !currPt.isDownPrev()) {
// Start a new single-point drag
draggedObject = objectCanvas.getDraggableObjectAtPoint(currPt);
if (draggedObject != null) {
// Started a new single-point drag
dragMode = MODE_DRAG;
objectCanvas.selectObject(draggedObject, currPt);
resetDrag();
// Don't need any settling time if just placing one finger, there is no noise
dragStartTime = dragSettleTime = currPt.getEventTime();
}
}
break;

case MODE_DRAG:
// Currently in a single-point drag
if (!currPt.isDown()) {
// First finger was released, stop dragging
dragMode = MODE_NOTHING;
objectCanvas.selectObject((draggedObject = null), currPt);

} else if (currPt.isMultiTouch()) {
// Point 1 was already down and point 2 was just placed down
dragMode = MODE_STRETCH;
// Restart the drag with the new drag position (that is at the midpoint between the touchpoints)
resetDrag();
// Need to let events settle before moving things, to help with event noise on touchdown
dragStartTime = currPt.getEventTime();
dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL;

} else {
// Point 1 is still down and point 2 did not change state, just do single-point drag to new location
if (currPt.getEventTime() < dragSettleTime) {
// Ignore the first few events if we just stopped stretching, because if finger 2 was kept down while
// finger 1 is lifted, then point 1 gets mapped to finger 2. Restart the drag from the new position.
resetDrag();
} else {
// Keep dragging, move to new point
performDrag();
}
}
break;

case MODE_STRETCH:
// Two-point stretch
if (!currPt.isMultiTouch() || !currPt.isDown()) {
// Dropped one or both points, stop stretching

if (!currPt.isDown()) {
// Dropped both points, go back to doing nothing
dragMode = MODE_NOTHING;
objectCanvas.selectObject((draggedObject = null), currPt);

} else {
// Just dropped point 2, downgrade to a single-point drag
dragMode = MODE_DRAG;
// Restart the drag with the single-finger position
resetDrag();
// Ignore the first few events after the drop, in case we dropped finger 1 and left finger 2 down
dragStartTime = currPt.getEventTime();
dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL;
}
} else {
// Keep stretching

if (Math.abs(currPt.getX() - prevPt.getX()) > MAX_MULTITOUCH_POS_JUMP_SIZE
|| Math.abs(currPt.getY() - prevPt.getY()) > MAX_MULTITOUCH_POS_JUMP_SIZE
|| Math.abs(currPt.getMultiTouchWidth() - prevPt.getMultiTouchWidth()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE
|| Math.abs(currPt.getMultiTouchHeight() - prevPt.getMultiTouchHeight()) * .5f > MAX_MULTITOUCH_DIM_JUMP_SIZE) {
// Jumped too far, probably event noise, reset and ignore events for a bit
resetDrag();
dragStartTime = currPt.getEventTime();
dragSettleTime = dragStartTime + EVENT_SETTLE_TIME_INTERVAL;

} else if (currPt.eventTime < dragSettleTime) {
// Events have not yet settled, reset
resetDrag();
} else {
// Stretch to new position and size
performDrag();
}
}
break;
}
}

// ------------------------------------------------------------------------------------

/** A class that packages up all MotionEvent information with all derived multitouch information (if available) */
public static class PointInfo {
private float x, y, dx, dy, size, diameter, diameterSq, angle, pressure;

private boolean down, downPrev, isMultiTouch, diameterSqIsCalculated, diameterIsCalculated, angleIsCalculated;

private int action;

private long eventTime;

// --

private Resources res;

private int displayWidth, displayHeight;

// --

public PointInfo(Resources res) {
this.res = res;
DisplayMetrics metrics = res.getDisplayMetrics();
this.displayWidth = metrics.widthPixels;
this.displayHeight = metrics.heightPixels;
}

// --

public PointInfo(PointInfo other) {
this.set(other);
}

/** Copy all fields */
public void set(PointInfo other) {
this.displayWidth = other.displayWidth;
this.displayHeight = other.displayHeight;
this.x = other.x;
this.y = other.y;
this.dx = other.dx;
this.dy = other.dy;
this.size = other.size;
this.diameter = other.diameter;
this.diameterSq = other.diameterSq;
this.angle = other.angle;
this.pressure = other.pressure;
this.down = other.down;
this.action = other.action;
this.downPrev = other.downPrev;
this.isMultiTouch = other.isMultiTouch;
this.diameterIsCalculated = other.diameterIsCalculated;
this.diameterSqIsCalculated = other.diameterSqIsCalculated;
this.angleIsCalculated = other.angleIsCalculated;
this.eventTime = other.eventTime;
}

/** Setter for use in event decoding */
private void set(float x, float y, float pressure, float undecodedSize, boolean down, int action, long eventTime) {
this.x = x;
this.y = y;
this.pressure = pressure;
this.downPrev = this.down;
this.down = down;
this.action = action;
this.eventTime = eventTime;
// 1.0f is the max value for size on an unpatched kernel -- make this backwards-compatible
this.isMultiTouch = (undecodedSize > 1.0f);
if (isMultiTouch) {
// Multitouch event, decipher size field, which contains dx and dy

// dx and dy come packaged in a 12-pt fixed-point number that has been converted into a float
int szi = (int) undecodedSize;
int dxi = szi >> 12;
int dyi = szi & ((1 << 12) - 1);

// Get display size and rotation, scale fixed point to screen coords
dx = Math.min(displayWidth, displayHeight) * dxi / (float) ((1 << 12) - 1);
dy = Math.max(displayWidth, displayHeight) * dyi / (float) ((1 << 12) - 1);
if (screenIsRotated()) {
// Swap x,y if screen is rotated
float tmp = dx;
dx = dy;
dy = tmp;
}

} else {
// Single-touch event
dx = dy = diameter = 0.0f;
// undecodedSize == 0 for single-touch when the multitouch kernel patch is applied
this.size = undecodedSize;
}
// Need to re-calculate the expensive params if they're needed
diameterSqIsCalculated = diameterIsCalculated = angleIsCalculated = false;
}

private boolean screenIsRotated() {
return res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
}

// Fast integer sqrt, by Jim Ulery. Should be faster than Math.sqrt()
private int julery_isqrt(int val) {
int temp, g = 0, b = 0x8000, bshft = 15;
do {
if (val >= (temp = (((g << 1) + B) << bshft--))) {
g += b;
val -= temp;
}
} while ((b >>= 1) > 0);
return g;
}

/** Calculate the squared diameter of the multitouch event, and cache it. Use this if you don't need to perform the sqrt. */
public float getMultiTouchDiameterSq() {
if (!diameterSqIsCalculated) {
diameterSq = (isMultiTouch ? dx * dx + dy * dy : 0.0f);
diameterSqIsCalculated = true;
}
return diameterSq;
}

/** Calculate the diameter of the multitouch event, and cache it. Uses fast int sqrt but gives accuracy to 1/16px. */
public float getMultiTouchDiameter() {
if (!diameterIsCalculated) {
// Get 1/16 pixel's worth of subpixel accuracy, works on screens up to 2048x2048
// before we get overflow (at which point you can reduce or eliminate subpix
// accuracy, or use longs in julery_isqrt())
float diamSq = getMultiTouchDiameterSq();
diameter = (diamSq == 0.0f ? 0.0f : (float) julery_isqrt((int) (256 * diamSq)) / 16.0f);
// Make sure diameter is never less than dx or dy, for trig purposes
if (diameter < dx)
diameter = dx;
if (diameter < dy)
diameter = dy;
diameterIsCalculated = true;
}
return diameter;
}

/**
* Calculate the angle of a multitouch event, and cache it. Actually gives the smaller of the two angles between the x axis
* and the line between the two touchpoints, so range is [0,Math.PI/2]. Uses Math.atan2().
*/
public float getMultiTouchAngle() {
if (!angleIsCalculated) {
angle = (float) Math.atan2(dy, dx);
angleIsCalculated = true;
}
return angle;
}

public float getX() {
return x;
}

public float getY() {
return y;
}

public float getMultiTouchWidth() {
return dx;
}

public float getMultiTouchHeight() {
return dy;
}

public float getSize() {
return size;
}

public float getPressure() {
return pressure;
}

public boolean isDown() {
return down;
}

public boolean isDownPrev() {
return downPrev;
}

public int getAction() {
return action;
}

public boolean isMultiTouch() {
return isMultiTouch;
}

public long getEventTime() {
return eventTime;
}
}

// ------------------------------------------------------------------------------------

/** A class that is used to store scroll offsets and scale information for objects that are managed by the multitouch controller */
public static class PositionAndScale {
private float xOff, yOff, scale;

public PositionAndScale() {
}

public void set(float xOff, float yOff, float scale) {
this.xOff = xOff;
this.yOff = yOff;
this.scale = scale;
}

public float getXOff() {
return xOff;
}

public float getYOff() {
return yOff;
}

public float getScale() {
return scale;
}
}

// ------------------------------------------------------------------------------------

public static interface MultiTouchObjectCanvas<T> {

/** See if there is a draggable object at the current point. Returns the object at the point, or null if nothing to drag. */
public T getDraggableObjectAtPoint(PointInfo pt);

/**
* Get the screen coords of the dragged object's origin, and scale multiplier to convert screen coords to obj coords. Call
* the .set() method on the passed PositionAndScale object.
*/
public void getPositionAndScale(T obj, PositionAndScale objPosAndScaleOut);

/**
* Set the position and scale of the dragged object, in object coords. Return true for success, or false if those parameters
* are out of range.
*/
public boolean setPositionAndScale(T obj, PositionAndScale newObjPosAndScale, PointInfo touchPoint);

/**
* Select an object at the given point. Can be used to bring the object to top etc. Only called when first touchpoint goes
* down, not when multitouch is initiated. Also called with null when drag op stops.
*/
public void selectObject(T obj, PointInfo pt);
}
}

[/codebox]

EDIT2:

And the Final Product.

The patched KeyInputQueue.java

[codebox]
/*
* Copyright © 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* [url="http://www.apache.org/licenses/LICENSE-2.0"]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.android.server;

import android.content.Context;
import android.content.res.Configuration;
import android.os.SystemClock;
import android.os.PowerManager;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RawInputEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;

public abstract class KeyInputQueue {
static final String TAG = "KeyInputQueue";

SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();

int mGlobalMetaState = 0;
boolean mHaveGlobalMetaState = false;

final QueuedEvent mFirst;
final QueuedEvent mLast;
QueuedEvent mCache;
int mCacheCount;

Display mDisplay = null;

int mOrientation = Surface.ROTATION_0;
int[] mKeyRotationMap = null;

PowerManager.WakeLock mWakeLock;

static final int[] KEY_90_MAP = new int[] {
KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_DOWN,
};

static final int[] KEY_180_MAP = new int[] {
KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
};

static final int[] KEY_270_MAP = new int[] {
KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
KeyEvent.KEYCODE_DPAD_RIGHT, KeyEvent.KEYCODE_DPAD_DOWN,
};

public static final int FILTER_REMOVE = 0;
public static final int FILTER_KEEP = 1;
public static final int FILTER_ABORT = -1;

public interface FilterCallback {
int filterEvent(QueuedEvent ev);
}

static class QueuedEvent {
InputDevice inputDevice;
long when;
int flags; // From the raw event
int classType; // One of the class constants in InputEvent
Object event;
boolean inQueue;

void copyFrom(QueuedEvent that) {
this.inputDevice = that.inputDevice;
this.when = that.when;
this.flags = that.flags;
this.classType = that.classType;
this.event = that.event;
}

@Override
public String toString() {
return "QueuedEvent{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + event + "}";
}

// not copied
QueuedEvent prev;
QueuedEvent next;
}

KeyInputQueue(Context context) {
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"KeyInputQueue");
mWakeLock.setReferenceCounted(false);

mFirst = new QueuedEvent();
mLast = new QueuedEvent();
mFirst.next = mLast;
mLast.prev = mFirst;

mThread.start();
}

public void setDisplay(Display display) {
mDisplay = display;
}

public void getInputConfiguration(Configuration config) {
synchronized (mFirst) {
config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
config.keyboard = Configuration.KEYBOARD_NOKEYS;
config.navigation = Configuration.NAVIGATION_NONAV;

final int N = mDevices.size();
for (int i=0; i<N; i++) {
InputDevice d = mDevices.valueAt(i);
if (d != null) {
if ((d.classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
config.touchscreen
= Configuration.TOUCHSCREEN_FINGER;
//Log.i("foo", "***** HAVE TOUCHSCREEN!");
}
if ((d.classes&RawInputEvent.CLASS_ALPHAKEY) != 0) {
config.keyboard
= Configuration.KEYBOARD_QWERTY;
//Log.i("foo", "***** HAVE QWERTY!");
}
if ((d.classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
config.navigation
= Configuration.NAVIGATION_TRACKBALL;
//Log.i("foo", "***** HAVE TRACKBALL!");
}
}
}
}
}

public static native String getDeviceName(int deviceId);
public static native int getDeviceClasses(int deviceId);
public static native boolean getAbsoluteInfo(int deviceId, int axis,
InputDevice.AbsoluteInfo outInfo);
public static native int getSwitchState(int sw);
public static native int getSwitchState(int deviceId, int sw);
public static native int getScancodeState(int sw);
public static native int getScancodeState(int deviceId, int sw);
public static native int getKeycodeState(int sw);
public static native int getKeycodeState(int deviceId, int sw);
public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);

public static KeyEvent newKeyEvent(InputDevice device, long downTime,
long eventTime, boolean down, int keycode, int repeatCount,
int scancode, int flags) {
return new KeyEvent(
downTime, eventTime,
down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
keycode, repeatCount,
device != null ? device.mMetaKeysState : 0,
device != null ? device.id : -1, scancode,
flags | KeyEvent.FLAG_FROM_SYSTEM);
}

Thread mThread = new Thread("InputDeviceReader") {
public void run() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);

try {
RawInputEvent ev = new RawInputEvent();
while (true) {
InputDevice di;

// block, doesn't release the monitor
readEvent(ev);

boolean send = false;
boolean configChanged = false;

if (false) {
Log.i(TAG, "Input event: dev=0x"
+ Integer.toHexString(ev.deviceId)
+ " type=0x" + Integer.toHexString(ev.type)
+ " scancode=" + ev.scancode
+ " keycode=" + ev.keycode
+ " value=" + ev.value);
}

if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
synchronized (mFirst) {
di = newInputDevice(ev.deviceId);
mDevices.put(ev.deviceId, di);
configChanged = true;
}
} else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
synchronized (mFirst) {
Log.i(TAG, "Device removed: id=0x"
+ Integer.toHexString(ev.deviceId));
di = mDevices.get(ev.deviceId);
if (di != null) {
mDevices.delete(ev.deviceId);
configChanged = true;
} else {
Log.w(TAG, "Bad device id: " + ev.deviceId);
}
}
} else {
di = getInputDevice(ev.deviceId);

// first crack at it
send = preprocessEvent(di, ev);

if (ev.type == RawInputEvent.EV_KEY) {
di.mMetaKeysState = makeMetaState(ev.keycode,
ev.value != 0, di.mMetaKeysState);
mHaveGlobalMetaState = false;
}
}

if (di == null) {
continue;
}

if (configChanged) {
synchronized (mFirst) {
addLocked(di, SystemClock.uptimeMillis(), 0,
RawInputEvent.CLASS_CONFIGURATION_CHANGED,
null);
}
}

if (!send) {
continue;
}

synchronized (mFirst) {
// NOTE: The event timebase absolutely must be the same
// timebase as SystemClock.uptimeMillis().
//curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
final long curTime = SystemClock.uptimeMillis();
//Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());

final int classes = di.classes;
final int type = ev.type;
final int scancode = ev.scancode;
send = false;

// Is it a key event?
if (type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST ||
scancode > RawInputEvent.BTN_LAST)) {
boolean down;
if (ev.value != 0) {
down = true;
di.mDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
} else if (ev.type == RawInputEvent.EV_KEY) {
if (ev.scancode == RawInputEvent.BTN_TOUCH &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
di.mAbs.changed = true;
di.mAbs.down = ev.value != 0;
}
if (ev.scancode == RawInputEvent.BTN_MOUSE &&
(classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
di.mRel.changed = true;
di.mRel.down = ev.value != 0;
send = true;
}

} else if (ev.type == RawInputEvent.EV_ABS &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
if (ev.scancode == RawInputEvent.ABS_X) {
di.mAbs.changed = true;
di.mAbs.x = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_Y) {
di.mAbs.changed = true;
di.mAbs.y = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
di.mAbs.changed = true;
di.mAbs.pressure = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
di.mAbs.changed = true;
di.mAbs.size = ev.value;
}

} else if (ev.type == RawInputEvent.EV_REL &&
(classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
// Add this relative movement into our totals.
if (ev.scancode == RawInputEvent.REL_X) {
di.mRel.changed = true;
di.mRel.x += ev.value;
} else if (ev.scancode == RawInputEvent.REL_Y) {
di.mRel.changed = true;
di.mRel.y += ev.value;
}
}

if (send || ev.type == RawInputEvent.EV_SYN) {
if (mDisplay != null) {
if (!mHaveGlobalMetaState) {
computeGlobalMetaStateLocked();
}

MotionEvent me;
me = di.mAbs.generateMotion(di, curTime, true,
mDisplay, mOrientation, mGlobalMetaState);
if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
+ " y=" + di.mAbs.y + " ev=" + me);
if (me != null) {
if (WindowManagerPolicy.WATCH_POINTER) {
Log.i(TAG, "Enqueueing: " + me);
}
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_TOUCHSCREEN, me);
}
me = di.mRel.generateMotion(di, curTime, false,
mDisplay, mOrientation, mGlobalMetaState);
if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
+ " y=" + di.mRel.y + " ev=" + me);
if (me != null) {
addLocked(di, curTime, ev.flags,
RawInputEvent.CLASS_TRACKBALL, me);
}
}
}
}
}
}
catch (RuntimeException exc) {
Log.e(TAG, "InputReaderThread uncaught exception", exc);
}
}
};

/**
* Returns a new meta state for the given keys and old state.
*/
private static final int makeMetaState(int keycode, boolean down, int old) {
int mask;
switch (keycode) {
case KeyEvent.KEYCODE_ALT_LEFT:
mask = KeyEvent.META_ALT_LEFT_ON;
break;
case KeyEvent.KEYCODE_ALT_RIGHT:
mask = KeyEvent.META_ALT_RIGHT_ON;
break;
case KeyEvent.KEYCODE_SHIFT_LEFT:
mask = KeyEvent.META_SHIFT_LEFT_ON;
break;
case KeyEvent.KEYCODE_SHIFT_RIGHT:
mask = KeyEvent.META_SHIFT_RIGHT_ON;
break;
case KeyEvent.KEYCODE_SYM:
mask = KeyEvent.META_SYM_ON;
break;
default:
return old;
}
int result = ~(KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)
& (down ? (old | mask) : (old & ~mask));
if (0 != (result & (KeyEvent.META_ALT_LEFT_ON | KeyEvent.META_ALT_RIGHT_ON))) {
result |= KeyEvent.META_ALT_ON;
}
if (0 != (result & (KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_RIGHT_ON))) {
result |= KeyEvent.META_SHIFT_ON;
}
return result;
}

private void computeGlobalMetaStateLocked() {
int i = mDevices.size();
mGlobalMetaState = 0;
while ((--i) >= 0) {
mGlobalMetaState |= mDevices.valueAt(i).mMetaKeysState;
}
mHaveGlobalMetaState = true;
}

/*
* Return true if you want the event to get passed on to the
* rest of the system, and false if you've handled it and want
* it dropped.
*/
abstract boolean preprocessEvent(InputDevice device, RawInputEvent event);

InputDevice getInputDevice(int deviceId) {
synchronized (mFirst) {
return getInputDeviceLocked(deviceId);
}
}

private InputDevice getInputDeviceLocked(int deviceId) {
return mDevices.get(deviceId);
}

public void setOrientation(int orientation) {
synchronized(mFirst) {
mOrientation = orientation;
switch (orientation) {
case Surface.ROTATION_90:
mKeyRotationMap = KEY_90_MAP;
break;
case Surface.ROTATION_180:
mKeyRotationMap = KEY_180_MAP;
break;
case Surface.ROTATION_270:
mKeyRotationMap = KEY_270_MAP;
break;
default:
mKeyRotationMap = null;
break;
}
}
}

public int rotateKeyCode(int keyCode) {
synchronized(mFirst) {
return rotateKeyCodeLocked(keyCode);
}
}

private int rotateKeyCodeLocked(int keyCode) {
int[] map = mKeyRotationMap;
if (map != null) {
final int N = map.length;
for (int i=0; i<N; i+=2) {
if (map[i] == keyCode) {
return map[i+1];
}
}
}
return keyCode;
}

boolean hasEvents() {
synchronized (mFirst) {
return mFirst.next != mLast;
}
}

/*
* returns true if we returned an event, and false if we timed out
*/
QueuedEvent getEvent(long timeoutMS) {
long begin = SystemClock.uptimeMillis();
final long end = begin+timeoutMS;
long now = begin;
synchronized (mFirst) {
while (mFirst.next == mLast && end > now) {
try {
mWakeLock.release();
mFirst.wait(end-now);
}
catch (InterruptedException e) {
}
now = SystemClock.uptimeMillis();
if (begin > now) {
begin = now;
}
}
if (mFirst.next == mLast) {
return null;
}
QueuedEvent p = mFirst.next;
mFirst.next = p.next;
mFirst.next.prev = mFirst;
p.inQueue = false;
return p;
}
}

void recycleEvent(QueuedEvent ev) {
synchronized (mFirst) {
//Log.i(TAG, "Recycle event: " + ev);
if (ev.event == ev.inputDevice.mAbs.currentMove) {
ev.inputDevice.mAbs.currentMove = null;
}
if (ev.event == ev.inputDevice.mRel.currentMove) {
if (false) Log.i(TAG, "Detach rel " + ev.event);
ev.inputDevice.mRel.currentMove = null;
ev.inputDevice.mRel.x = 0;
ev.inputDevice.mRel.y = 0;
}
recycleLocked(ev);
}
}

void filterQueue(FilterCallback cb) {
synchronized (mFirst) {
QueuedEvent cur = mLast.prev;
while (cur.prev != null) {
switch (cb.filterEvent(cur)) {
case FILTER_REMOVE:
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
break;
case FILTER_ABORT:
return;
}
cur = cur.prev;
}
}
}

private QueuedEvent obtainLocked(InputDevice device, long when,
int flags, int classType, Object event) {
QueuedEvent ev;
if (mCacheCount == 0) {
ev = new QueuedEvent();
} else {
ev = mCache;
ev.inQueue = false;
mCache = ev.next;
mCacheCount--;
}
ev.inputDevice = device;
ev.when = when;
ev.flags = flags;
ev.classType = classType;
ev.event = event;
return ev;
}

private void recycleLocked(QueuedEvent ev) {
if (ev.inQueue) {
throw new RuntimeException("Event already in queue!");
}
if (mCacheCount < 10) {
mCacheCount++;
ev.next = mCache;
mCache = ev;
ev.inQueue = true;
}
}

private void addLocked(InputDevice device, long when, int flags,
int classType, Object event) {
boolean poke = mFirst.next == mLast;

QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
QueuedEvent p = mLast.prev;
while (p != mFirst && ev.when < p.when) {
p = p.prev;
}

ev.next = p.next;
ev.prev = p;
p.next = ev;
ev.next.prev = ev;
ev.inQueue = true;

if (poke) {
mFirst.notify();
mWakeLock.acquire();
}
}

private InputDevice newInputDevice(int deviceId) {
int classes = getDeviceClasses(deviceId);
String name = getDeviceName(deviceId);
Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
+ ", name=" + name
+ ", classes=" + Integer.toHexString(classes));
InputDevice.AbsoluteInfo absX;
InputDevice.AbsoluteInfo absY;
InputDevice.AbsoluteInfo absPressure;
InputDevice.AbsoluteInfo absSize;
if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
} else {
absX = null;
absY = null;
absPressure = null;
absSize = null;
}

return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
}

private InputDevice.AbsoluteInfo loadAbsoluteInfo(int id, int channel,
String name) {
InputDevice.AbsoluteInfo info = new InputDevice.AbsoluteInfo();
if (getAbsoluteInfo(id, channel, info)
&& info.minValue != info.maxValue) {
Log.i(TAG, " " + name + ": min=" + info.minValue
+ " max=" + info.maxValue
+ " flat=" + info.flat
+ " fuzz=" + info.fuzz);
info.range = info.maxValue-info.minValue;
return info;
}
Log.i(TAG, " " + name + ": unknown values");
return null;
}
private static native boolean readEvent(RawInputEvent outEvent);
}

syntax highlighted by Code2HTML, v. 0.9.1

Just put it in place: ANDROID_HOME/frameworks/base/services/java/com/android/server/

And recompile the Android.

That's all we need.

Maybe this isn't exact what we need, because this file is from G1, But at least we get some point.

KeyInputQueue.java

Best regards

TOTI

This is All he need.

So don't rush for nothing.

Just please be patient, and wait not for long.

I'm sure that he'll do it in no time.

Edited by totiadrenalin
Link to comment
Share on other sites

Guest DanWilson
No!

Only the Patched Kernel.

Because This isn't really multitouch screen, But a singletouch with second finger gesture.

So maybe it recognize up to 3 fingers, but it's still singletouch.

Because It only gesture the second finger.

That's why you can move the screen of the launch app with 3 fingers.

But without the patched drivers there's no chance do provide second finger gesture.

So don't rush, and let BB correct the kernel, and ad what I post in my previews post.

You will get then a real second finger gesture.

A Dual touch.

This is all I really want.

But I thought for anything other than one finger in the corner, other finger moving needed the custom drivers / firmware.

Or have I missed it?

Link to comment
Share on other sites

Guest totiadrenalin
This is all I really want.

But I thought for anything other than one finger in the corner, other finger moving needed the custom drivers / firmware.

Or have I missed it?

Maybe you are right?

But what if I flash the touchcontroller?

I didn't get what I tough that I should get.

Link to comment
Share on other sites

Guest thesinger
No!

Only the Patched Kernel.

Because This isn't really multitouch screen, But a singletouch with second finger gesture.

So maybe it recognize up to 3 fingers, but it's still singletouch.

Because It only gesture the second finger.

That's why you can move the screen of the launch app with 3 fingers.

But without the patched drivers there's no chance do provide second finger gesture.

So don't rush, and let BB correct the kernel, and ad what I post in my previews post.

You will get then a real second finger gesture.

A Dual touch.

Okey so then we are going to let BB finish the kernel and try it out.Good work guys. :lol:

Link to comment
Share on other sites

Guest totiadrenalin
I don't now i'm not a pro like bb and toti but i think we need a android version that is built in multitouch like the one in the htc hero.

HEHE!!

I'm not a pro programer two.

This is my hobby, Like several others.

I'm graduated Guitarist. - a Musician.

Sorry that I disappoint you, but I just want to tell the truth.

Best regards

TOTI

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.