From 65d83ceb6aa9b606da0b1eb0894d04ec8c35a68a Mon Sep 17 00:00:00 2001
From: Jason Gerecke <killertofu@gmail.com>
Date: Tue, 24 Dec 2013 09:39:38 -0800
Subject: [PATCH v2 3/7] Define and implement new Ecore_Event_Axis_Update events

These events watch for updates to arbitrary device axes (e.g. the
pressure or tilt axes of a pen, the primary axis of a USB control
knob, etc.)

Patch v2:
 * Properly free axis events when we're done with them
---
 src/lib/ecore_input/Ecore_Input.h      | 38 +++++++++++++++
 src/lib/ecore_input/ecore_input.c      |  3 ++
 src/lib/ecore_x/xlib/ecore_x_events.c  | 49 +++++++++++++++++++
 src/lib/ecore_x/xlib/ecore_x_private.h | 10 ++++
 src/lib/ecore_x/xlib/ecore_x_xi2.c     | 88 ++++++++++++++++++++++++++++++++++
 5 files changed, 188 insertions(+)

diff --git a/src/lib/ecore_input/Ecore_Input.h b/src/lib/ecore_input/Ecore_Input.h
index 46e3e6e..636b67e 100644
--- a/src/lib/ecore_input/Ecore_Input.h
+++ b/src/lib/ecore_input/Ecore_Input.h
@@ -47,6 +47,7 @@ extern "C" {
    EAPI extern int ECORE_EVENT_MOUSE_WHEEL;
    EAPI extern int ECORE_EVENT_MOUSE_IN;
    EAPI extern int ECORE_EVENT_MOUSE_OUT;
+   EAPI extern int ECORE_EVENT_AXIS_UPDATE;
 
 #define ECORE_EVENT_MODIFIER_SHIFT      0x0001
 #define ECORE_EVENT_MODIFIER_CTRL       0x0002
@@ -72,6 +73,7 @@ extern "C" {
    typedef struct _Ecore_Event_Mouse_Move   Ecore_Event_Mouse_Move;
    typedef struct _Ecore_Event_Mouse_IO     Ecore_Event_Mouse_IO;
    typedef struct _Ecore_Event_Modifiers    Ecore_Event_Modifiers;
+   typedef struct _Ecore_Event_Axis_Update  Ecore_Event_Axis_Update;
    
    typedef enum _Ecore_Event_Modifier
      {
@@ -208,6 +210,42 @@ extern "C" {
         } multi;
      };
    
+   enum _Ecore_Axis_Label
+     {
+        ECORE_AXIS_LABEL_UNKNOWN,
+        ECORE_AXIS_LABEL_X,             /* Position along physical X axis; not window relative */
+        ECORE_AXIS_LABEL_Y,             /* Position along physical Y axis; not window relative */
+        ECORE_AXIS_LABEL_PRESSURE,      /* Force applied to tool tip; Range: [0.0, 1.0]. Unit: Unitless */
+        ECORE_AXIS_LABEL_DISTANCE,      /* Relative distance along physical Z axis; Range: [0.0, 1.0] */
+        ECORE_AXIS_LABEL_AZIMUTH,       /* Angle of tool about the Z axis from positive X axis; Range: [-PI, PI] radians */
+        ECORE_AXIS_LABEL_TILT,          /* Angle of tool about plane of sensor from positive Z axis; Range: [0.0, PI] radians */
+        ECORE_AXIS_LABEL_TWIST,         /* Angle of tool about tool's major axis from tool's "natural" position; Range: [-PI, PI] radians */
+        ECORE_AXIS_LABEL_TOUCH_WIDTH_MAJOR,   /* Length of contact ellipse along AZIMUTH */
+        ECORE_AXIS_LABEL_TOUCH_WIDTH_MINOR,   /* Length of contact ellipse perpendicular to AZIMUTH */
+        ECORE_AXIS_LABEL_TOOL_WIDTH_MAJOR,    /* Length of tool ellipse along AZIMUTH */
+        ECORE_AXIS_LABEL_TOOL_WIDTH_MINOR     /* Length of tool ellipse perpendicular to AZIMUTH */
+   };
+
+   struct _Ecore_Axis
+     {
+        enum _Ecore_Axis_Label label;
+        double value;
+     };
+
+   struct _Ecore_Event_Axis_Update
+     {
+        Ecore_Window     window;
+        Ecore_Window     root_window;
+        Ecore_Window     event_window;
+
+        unsigned int timestamp;
+        int device;
+        int toolid;
+
+        int naxis;
+        struct _Ecore_Axis *axis;
+     };
+
    struct _Ecore_Event_Mouse_IO
      {
         Ecore_Window     window;
diff --git a/src/lib/ecore_input/ecore_input.c b/src/lib/ecore_input/ecore_input.c
index 8f4b1f9..ac8b60d 100644
--- a/src/lib/ecore_input/ecore_input.c
+++ b/src/lib/ecore_input/ecore_input.c
@@ -22,6 +22,7 @@ EAPI int ECORE_EVENT_MOUSE_MOVE = 0;
 EAPI int ECORE_EVENT_MOUSE_WHEEL = 0;
 EAPI int ECORE_EVENT_MOUSE_IN = 0;
 EAPI int ECORE_EVENT_MOUSE_OUT = 0;
+EAPI int ECORE_EVENT_AXIS_UPDATE = 0;
 
 static int _ecore_event_init_count = 0;
 
@@ -52,6 +53,7 @@ ecore_event_init(void)
    ECORE_EVENT_MOUSE_WHEEL = ecore_event_type_new();
    ECORE_EVENT_MOUSE_IN = ecore_event_type_new();
    ECORE_EVENT_MOUSE_OUT = ecore_event_type_new();
+   ECORE_EVENT_AXIS_UPDATE = ecore_event_type_new();
 
    return _ecore_event_init_count;
 }
@@ -70,6 +72,7 @@ ecore_event_shutdown(void)
    ECORE_EVENT_MOUSE_WHEEL = 0;
    ECORE_EVENT_MOUSE_IN = 0;
    ECORE_EVENT_MOUSE_OUT = 0;
+   ECORE_EVENT_AXIS_UPDATE = 0;
    eina_log_domain_unregister(_ecore_input_log_dom);
    _ecore_input_log_dom = -1;
    ecore_shutdown();
diff --git a/src/lib/ecore_x/xlib/ecore_x_events.c b/src/lib/ecore_x/xlib/ecore_x_events.c
index d4049d2..7fcdf95 100644
--- a/src/lib/ecore_x/xlib/ecore_x_events.c
+++ b/src/lib/ecore_x/xlib/ecore_x_events.c
@@ -270,6 +270,55 @@ _ecore_mouse_move(unsigned int timestamp,
 }
 
 static void
+_ecore_x_event_free_axis_update_event(void *data, void *ev)
+{
+   Ecore_Event_Axis_Update *e = ev;
+   if (data)
+     {
+         free(e->axis);
+     }
+   free(e);
+}
+
+void
+_ecore_x_axis_update(Ecore_Window window,
+                     Ecore_Window event_window,
+                     Ecore_Window root_window,
+                     unsigned int timestamp,
+                     int devid,
+                     int toolid,
+                     int naxis,
+                     struct _Ecore_Axis *axis)
+{
+   Ecore_Event_Axis_Update *e;
+   int i;
+
+   e = malloc(sizeof(Ecore_Event_Axis_Update));
+   if (!e)
+    return;
+
+   e->window = window;
+   e->event_window = event_window;
+   e->root_window = root_window;
+
+   e->timestamp = timestamp;
+
+   e->device = devid;
+   e->toolid = toolid;
+
+   e->naxis = naxis;
+   e->axis = axis;
+
+   INF("Axis update [%d] (%d) with %d events:", ECORE_EVENT_AXIS_UPDATE, e->device, e->naxis);
+   for (i = 0; i < naxis; i++)
+      INF("AXIS %d = %f", e->axis[i].label, e->axis[i].value);
+
+   ecore_event_add(ECORE_EVENT_AXIS_UPDATE, e, _ecore_x_event_free_axis_update_event, NULL);
+
+   _ecore_x_event_last_time = timestamp;
+}
+
+static void
 _ecore_key_press(int event,
                  XKeyEvent *xevent)
 {
diff --git a/src/lib/ecore_x/xlib/ecore_x_private.h b/src/lib/ecore_x/xlib/ecore_x_private.h
index cbfa4f7..f45f8b8 100644
--- a/src/lib/ecore_x/xlib/ecore_x_private.h
+++ b/src/lib/ecore_x/xlib/ecore_x_private.h
@@ -344,6 +344,16 @@ void _ecore_x_input_shutdown(void);
 void _ecore_x_input_handler(XEvent *xevent);
 /* from sync */
 
+void
+_ecore_x_axis_update(Ecore_Window window,
+                     Ecore_Window event_window,
+                     Ecore_Window root_window,
+                     unsigned int timestamp,
+                     int devid,
+                     int toolid,
+                     int naxis,
+                     struct _Ecore_Axis *axis);
+
 void _ecore_mouse_move(unsigned int timestamp,
                        unsigned int xmodifiers,
                        int x,
diff --git a/src/lib/ecore_x/xlib/ecore_x_xi2.c b/src/lib/ecore_x/xlib/ecore_x_xi2.c
index 573e423..b3dcbb7 100644
--- a/src/lib/ecore_x/xlib/ecore_x_xi2.c
+++ b/src/lib/ecore_x/xlib/ecore_x_xi2.c
@@ -10,6 +10,7 @@
 
 #ifdef ECORE_XI2
 #include "Ecore_Input.h"
+#include <xorg/xserver-properties.h>
 #endif /* ifdef ECORE_XI2 */
 
 int _ecore_x_xi2_opcode = -1;
@@ -416,6 +417,89 @@ _ecore_x_input_multi_handler(XEvent *xevent)
 #endif /* ifdef ECORE_XI2 */
 }
 
+int count_bits(long n) {
+  unsigned int c; // c accumulates the total bits set in v
+  for (c = 0; n; c++)
+    n &= n - 1; // clear the least significant bit set
+  return c;
+}
+
+void
+_ecore_x_input_axis_handler(XEvent *xevent, XIDeviceInfo *dev)
+{
+#ifdef ECORE_XI2
+   if (xevent->type != GenericEvent) return;
+
+   XIDeviceEvent *evd = (XIDeviceEvent *)(xevent->xcookie.data);
+   int devid = evd->deviceid;
+
+   int n = count_bits(*evd->valuators.mask);
+   struct _Ecore_Axis *axis = calloc(n, sizeof(struct _Ecore_Axis));
+   if (!axis)
+      return;
+
+   struct _Ecore_Axis *axis_ptr = axis;
+   int i;
+   int j = 0;
+   for (i = 0; i < dev->num_classes; i++)
+      {
+         if (dev->classes[i]->type == XIValuatorClass)
+            {
+               XIValuatorClassInfo* class = ((XIValuatorClassInfo*)dev->classes[i]);
+
+               //TODO: Cache the results of XInternAtom and migrate to XInternAtoms
+               //TODO: Add support for TILT and AZIMUTH
+               //FIXME: Stop assuming the TWIST range is a whole circle!
+               if (*evd->valuators.mask & (1 << class->number))
+                  {
+                     if (class->label == XInternAtom(evd->display, AXIS_LABEL_PROP_ABS_X, 1))
+                        {
+                           axis_ptr->label = ECORE_AXIS_LABEL_X;
+                           axis_ptr->value = evd->valuators.values[j];
+                        }
+                     else if (class->label == XInternAtom(evd->display, AXIS_LABEL_PROP_ABS_Y, 1))
+                        {
+                           axis_ptr->label = ECORE_AXIS_LABEL_Y;
+                           axis_ptr->value = evd->valuators.values[j];
+                        }
+                     else if (class->label == XInternAtom(evd->display, AXIS_LABEL_PROP_ABS_PRESSURE, 1))
+                        {
+                           axis_ptr->label = ECORE_AXIS_LABEL_PRESSURE;
+                           axis_ptr->value = (evd->valuators.values[j] - class->min) / (class->max - class->min);
+                        }
+                     else if (class->label == XInternAtom(evd->display, AXIS_LABEL_PROP_ABS_DISTANCE, 1))
+                        {
+                           axis_ptr->label = ECORE_AXIS_LABEL_DISTANCE;
+                           axis_ptr->value = (evd->valuators.values[j] - class->min) / (class->max - class->min);
+                        }
+                     else if (class->label == XInternAtom(evd->display, AXIS_LABEL_PROP_ABS_RZ, 1) ||
+                              class->label == XInternAtom(evd->display, AXIS_LABEL_PROP_ABS_WHEEL, 1))
+                        {
+                           axis_ptr->label = ECORE_AXIS_LABEL_TWIST;
+                           axis_ptr->value = 6.28318530718 * (evd->valuators.values[j] - class->min) / (class->max - class->min);
+                        }
+                     else
+                        {
+                        axis_ptr->label = ECORE_AXIS_LABEL_UNKNOWN;
+                        axis_ptr->value = evd->valuators.values[j];
+                        }
+                     axis_ptr++;
+                  }
+               j++;
+            }
+      }
+
+   _ecore_x_axis_update(evd->child ? evd->child : evd->event,
+                        evd->event,
+                        evd->root,
+                        evd->time,
+                        devid,
+                        evd->detail,
+                        n,
+                        axis);
+#endif /* ifdef ECORE_XI2 */
+}
+
 XIDeviceInfo*
 _ecore_x_input_device_lookup(int deviceid)
 {
@@ -467,6 +551,10 @@ _ecore_x_input_handler(XEvent *xevent)
          else if (dev->use == XIFloatingSlave)
             _ecore_x_input_mouse_handler(xevent);
 
+         if (dev->use != XIMasterPointer)
+           {
+              _ecore_x_input_axis_handler(xevent, dev);
+           }
          break;
          }
       }
-- 
2.1.0

