Add Digital Pen hovering cursor support

Add digital pen hovering cursor support so
that whenever the digital pen is enabled,
the hovering cursor will be shown.

Change-Id: I1b57448603486292ac501fd46b0e8d815b9eb232
diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h
index 20179ae..2fbf3e1 100644
--- a/services/inputflinger/EventHub.h
+++ b/services/inputflinger/EventHub.h
@@ -57,6 +57,12 @@
 #define MSC_ANDROID_TIME_SEC 0x6
 #define MSC_ANDROID_TIME_USEC 0x7
 
+/**
+ * code for a special button that is used to show/hide a
+ * circular hovering cursor in the input framework.
+ */
+#define BTN_USF_HOVERING_CURSOR         0x230
+
 namespace android {
 
 enum {
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index 8634e42..677561c 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -2593,6 +2593,7 @@
 TouchInputMapper::TouchInputMapper(InputDevice* device) :
         InputMapper(device),
         mSource(0), mDeviceMode(DEVICE_MODE_DISABLED),
+        mHasExternalHoveringCursorControl(false), mExternalHoveringCursorVisible(false),
         mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0),
         mSurfaceOrientation(DISPLAY_ORIENTATION_0) {
 }
@@ -2864,6 +2865,8 @@
                         && getDevice()->isExternal();
     }
 
+    mHasExternalHoveringCursorControl = getDevice()->hasKey(BTN_USF_HOVERING_CURSOR);
+
     // Initial downs on external touch devices should wake the device.
     // Normally we don't do this for internal touch screens to prevent them from waking
     // up in your pocket but you can enable it using the input device configuration.
@@ -3060,7 +3063,7 @@
     }
 
     // Create pointer controller if needed.
-    if (mDeviceMode == DEVICE_MODE_POINTER ||
+    if (mDeviceMode == DEVICE_MODE_POINTER || mHasExternalHoveringCursorControl ||
             (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
         if (mPointerController == NULL) {
             mPointerController = getPolicy()->obtainPointerController(getDeviceId());
@@ -3730,6 +3733,20 @@
     if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
         sync(rawEvent->when);
     }
+    if (mHasExternalHoveringCursorControl && rawEvent->type == EV_KEY) {
+        if (rawEvent->code == BTN_USF_HOVERING_CURSOR && mPointerController != NULL) {
+            if (rawEvent->value) {
+                // show a hover cursor
+                mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_STYLUS_HOVER);
+                mPointerController->unfade(android::PointerControllerInterface::TRANSITION_IMMEDIATE);
+                mExternalHoveringCursorVisible = true;
+            } else {
+                // hide the cursor
+                mPointerController->fade(android::PointerControllerInterface::TRANSITION_IMMEDIATE);
+                mExternalHoveringCursorVisible = false;
+            }
+        }
+    }
 }
 
 void TouchInputMapper::sync(nsecs_t when) {
@@ -3861,6 +3878,25 @@
                         mCurrentCookedPointerData.touchingIdBits);
             }
 
+            if (mHasExternalHoveringCursorControl && mPointerController != NULL) {
+                if (mExternalHoveringCursorVisible) {
+                    // find the pointer position from the first touch point (either touching or hovering)
+                    uint32_t index = MAX_POINTERS;
+                    if (!mCurrentRawPointerData.touchingIdBits.isEmpty()) {
+                        index = mCurrentCookedPointerData.idToIndex[mCurrentRawPointerData.touchingIdBits.firstMarkedBit()];
+                    } else if (!mCurrentRawPointerData.hoveringIdBits.isEmpty()) {
+                        index = mCurrentCookedPointerData.idToIndex[mCurrentRawPointerData.hoveringIdBits.firstMarkedBit()];
+                    }
+                    if (index < MAX_POINTERS)
+                    {
+                        float x = mCurrentCookedPointerData.pointerCoords[index].getX();
+                        float y = mCurrentCookedPointerData.pointerCoords[index].getY();
+                        mPointerController->setPosition(x, y);
+                        mPointerController->unfade(android::PointerControllerInterface::TRANSITION_IMMEDIATE);
+                    }
+                }
+            }
+
             dispatchHoverExit(when, policyFlags);
             dispatchTouches(when, policyFlags);
             dispatchHoverEnterAndMove(when, policyFlags);
diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h
index c5896d4..7bb85c3 100644
--- a/services/inputflinger/InputReader.h
+++ b/services/inputflinger/InputReader.h
@@ -1369,6 +1369,11 @@
 
     Vector<VirtualKey> mVirtualKeys;
 
+    // true if we support external hovering cursor control
+    bool mHasExternalHoveringCursorControl;
+    // visibility state of hovering cursor (true=visible)
+    bool mExternalHoveringCursorVisible;
+
     virtual void configureParameters();
     virtual void dumpParameters(String8& dump);
     virtual void configureRawPointerAxes();
diff --git a/services/inputflinger/PointerControllerInterface.h b/services/inputflinger/PointerControllerInterface.h
index e94dd94..2c4015b 100644
--- a/services/inputflinger/PointerControllerInterface.h
+++ b/services/inputflinger/PointerControllerInterface.h
@@ -79,6 +79,8 @@
         PRESENTATION_POINTER,
         // Show spots and a spot anchor in place of the mouse pointer.
         PRESENTATION_SPOT,
+        // Show a stylus hovering icon (small circle)
+        PRESENTATION_STYLUS_HOVER,
     };
 
     /* Sets the mode of the pointer controller. */