changeset 6069:66411c75ff6b

RT-35300 [Monocle] Implement touch screen coordinate transforms
author Daniel Blaukopf <daniel.blaukopf@oracle.com>
date Mon, 13 Jan 2014 09:41:14 +0200
parents 9069b6bff181
children f487abfe1990
files modules/graphics/src/main/java/com/sun/glass/ui/monocle/input/TouchLookahead.java modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/GetEvent.java modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxInputDevice.java modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxInputDeviceRegistry.java modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxTouchProcessor.java modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxTouchTransform.java modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/SysFS.java
diffstat 7 files changed, 317 insertions(+), 82 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/monocle/input/TouchLookahead.java	Mon Jan 13 09:41:14 2014 +0200
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.glass.ui.monocle.input;
+
+/**
+ * TouchLookahead handles compression of touch event streams by folding
+ * together adjacent events that differ only in their coordinates but not in
+ * number of touch points or the IDs assigned to those points.
+ *
+ * pullState(..) gets the current state from TouchInput
+ * pushState() updates TouchInput with new touch point data
+ * flushState() must be called at the end of event processing to clear the
+ * pipeline.
+ */
+public class TouchLookahead {
+
+    private TouchInput touch = TouchInput.getInstance();
+    private TouchState previousState = new TouchState();
+    private TouchState state = new TouchState();
+    private boolean assignIDs;
+    private boolean processedFirstEvent;
+
+    public TouchState getState() {
+        return state;
+    }
+
+    /** Sets whether or not we are asking TouchInput to assign touch point IDs */
+    public void setAssignIDs(boolean assignIDs) {
+        this.assignIDs = assignIDs;
+    }
+
+    /**
+     * Updates the local touch point state from TouchInput
+     *
+     * @param clearPoints Whether to clear touch point data in the updated local
+     *                    state. Stateless Touch processors getting their input
+     *                    with drivers that send each touch point on every event
+     *                    might need to set this; touch processors using drivers
+     *                    that send only the delta from the previous state will
+     *                    not want to clear the points.
+     */
+    public void pullState(boolean clearPoints) {
+        touch.getState(state);
+        processedFirstEvent = false;
+        if (clearPoints) {
+            state.clear();
+        }
+    }
+
+    public void pushState() {
+        if (processedFirstEvent) {
+            // fold together TouchStates that have the same touch point count
+            // and IDs. For Protocol A devices the touch IDs are not initialized
+            // yet, which means the only differentiator will be the number of
+            // points.
+            boolean fold = true;
+            if (state.getPointCount() != previousState.getPointCount()) {
+                fold = false;
+            }
+            if (!assignIDs) {
+                state.sortPointsByID();
+                for (int i = 0; fold && i < previousState.getPointCount(); i++) {
+                    if (state.getPoint(i).id != previousState.getPoint(i).id) {
+                        fold = false;
+                    }
+                }
+            }
+            if (!fold) {
+                // the events are different. Send "previousState".
+                touch.setState(previousState, true);
+            }
+        } else {
+            processedFirstEvent = true;
+        }
+        state.copyTo(previousState);
+    }
+
+    public void flushState() {
+        touch.setState(previousState, assignIDs);
+        processedFirstEvent = false;
+    }
+
+}
--- a/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/GetEvent.java	Sun Jan 12 21:47:04 2014 +0200
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/GetEvent.java	Mon Jan 13 09:41:14 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -86,6 +86,7 @@
                         System.out.println("  relative=" + device.isRelative());
                         System.out.println("  5-way=" + device.is5Way());
                         System.out.println("  fullKeyboard=" + device.isFullKeyboard());
+                        System.out.println("  PRODUCT=" + device.getProduct());
                         devices.add(sysPath);
                     } else if (action.equals("remove")) {
                         devices.remove(devPath);
--- a/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxInputDevice.java	Sun Jan 12 21:47:04 2014 +0200
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxInputDevice.java	Mon Jan 13 09:41:14 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -67,7 +67,7 @@
     private ExecutorService executor;
     private EventProcessor processor = new EventProcessor();
     private LinuxEventBuffer buffer = new LinuxEventBuffer();
-
+    private Map<String,String> uevent;
 
     /**
      * Create a new com.sun.glass.ui.monocle.input.LinuxInputDevice on the given
@@ -89,6 +89,7 @@
                 devNode, capabilities.get("abs"));
         this.in = new FileInputStream(devNode).getChannel();
         this.executor = NativePlatformFactory.getNativePlatform().getExecutor();
+        this.uevent = SysFS.readUEvent(sysPath);
     }
 
     public void setInputProcessor(LinuxInputProcessor inputProcessor) {
@@ -168,6 +169,10 @@
         return absCaps == null ? null : absCaps.get(axis);
     }
 
+    String getProduct() {
+        return uevent.get("PRODUCT");
+    }
+
     @Override
     public boolean isTouch() {
         return "1".equals(udevManifest.get("ID_INPUT_TOUCHSCREEN"))
--- a/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxInputDeviceRegistry.java	Sun Jan 12 21:47:04 2014 +0200
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxInputDeviceRegistry.java	Mon Jan 13 09:41:14 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -96,7 +96,7 @@
 
     private LinuxInputProcessor createInputProcessor(LinuxInputDevice device) {
         if (device.isTouch()) {
-            return new LinuxTouchProcessor();
+            return new LinuxTouchProcessor(device);
         } else if (device.isRelative()) {
             return new LinuxMouseProcessor();
         } else {
--- a/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxTouchProcessor.java	Sun Jan 12 21:47:04 2014 +0200
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxTouchProcessor.java	Mon Jan 13 09:41:14 2014 +0200
@@ -25,35 +25,32 @@
 
 package com.sun.glass.ui.monocle.linux;
 
-import com.sun.glass.ui.monocle.NativePlatformFactory;
-import com.sun.glass.ui.monocle.input.TouchInput;
-import com.sun.glass.ui.monocle.input.TouchState;
+import com.sun.glass.ui.monocle.input.TouchLookahead;
 
 public class LinuxTouchProcessor implements LinuxInputProcessor {
 
-    private TouchInput touch = TouchInput.getInstance();
-    private TouchState previousState = new TouchState();
-    private TouchState state = new TouchState();
-    private boolean processedFirstEvent;
+    private final TouchLookahead tl = new TouchLookahead();
+    private final LinuxTouchTransform transform;
+
+    LinuxTouchProcessor(LinuxInputDevice device) {
+        tl.setAssignIDs(true);
+        transform = new LinuxTouchTransform(device);
+    }
 
     @Override
     public void processEvents(LinuxInputDevice device) {
         LinuxEventBuffer buffer = device.getBuffer();
-        touch.getState(state);
-        state.clear();
-        processedFirstEvent = false;
+        tl.pullState(true);
         while (buffer.hasNextEvent()) {
             switch (buffer.getEventType()) {
                 case Input.EV_ABS: {
-                    int pixelValue = toPixelValue(device,
-                                                  buffer.getEventCode(),
-                                                  buffer.getEventValue());
-                    switch (buffer.getEventCode()) {
+                    int value = transform.getValue(buffer);
+                    switch (transform.getAxis(buffer)) {
                         case Input.ABS_X:
-                            state.getPointForID(0, true).x = pixelValue;
+                            tl.getState().getPointForID(0, true).x = value;
                             break;
                         case Input.ABS_Y:
-                            state.getPointForID(0, true).y = pixelValue;
+                            tl.getState().getPointForID(0, true).y = value;
                             break;
                     }
                     break;
@@ -61,9 +58,8 @@
                 case Input.EV_SYN:
                     switch (buffer.getEventCode()) {
                         case Input.SYN_REPORT:
-                            sendEvent();
-                            touch.getState(state);
-                            state.clear();
+                            tl.pushState();
+                            tl.pullState(true);
                             break;
                         default: // ignore
                     }
@@ -71,63 +67,7 @@
             }
             buffer.nextEvent();
         }
-        touch.setState(previousState, true);
-    }
-
-    private void sendEvent() {
-        if (processedFirstEvent) {
-            // fold together TouchStates that have the same touch point count
-            // and IDs. For Protocol A devices the touch IDs are not initialized
-            // yet, which means the only differentiator will be the number of
-            // points.
-            boolean fold = true;
-            if (state.getPointCount() != previousState.getPointCount()) {
-                fold = false;
-            }
-            for (int i = 0; fold && i < previousState.getPointCount(); i++) {
-                if (state.getPoint(i).id != previousState.getPoint(i).id) {
-                    fold = false;
-                }
-            }
-            if (!fold) {
-                // the events are different. Send "previousState".
-                touch.setState(previousState, true);
-            }
-        } else {
-            processedFirstEvent = true;
-        }
-        state.copyTo(previousState);
-    }
-
-    private static int toPixelValue(LinuxInputDevice device, int axis, int value) {
-        switch (axis) {
-            case Input.ABS_X:
-            case Input.ABS_MT_POSITION_X:
-                return toPixelX(device, axis, value);
-            case Input.ABS_Y:
-            case Input.ABS_MT_POSITION_Y:
-                return toPixelY(device, axis, value);
-            default:
-                return value;
-        }
-    }
-
-    private static int toPixelX(LinuxInputDevice device, int axis, int value) {
-        AbsoluteInputCapabilities caps = device.getAbsoluteInputCapabilities(axis);
-        int minimum = caps.getMinimum();
-        int maximum = caps.getMaximum();
-        int screenWidth = NativePlatformFactory.getNativePlatform().getScreen().getWidth();
-        int pixel = ((value - minimum) * screenWidth) / (maximum - minimum);
-        return pixel;
-    }
-
-    private static int toPixelY(LinuxInputDevice device, int axis, int value) {
-        AbsoluteInputCapabilities caps = device.getAbsoluteInputCapabilities(axis);
-        int minimum = caps.getMinimum();
-        int maximum = caps.getMaximum();
-        int screenHeight = NativePlatformFactory.getNativePlatform().getScreen().getHeight();
-        int pixel = ((value - minimum) * screenHeight) / (maximum - minimum);
-        return pixel;
+        tl.flushState();
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/LinuxTouchTransform.java	Mon Jan 13 09:41:14 2014 +0200
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.glass.ui.monocle.linux;
+
+import com.sun.glass.ui.monocle.NativePlatformFactory;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+
+/**
+ * Handles conversion between raw axis values received from Linux input devices
+ * and screen pixel values.
+ * By default input values are mapped form the device input range to the screen
+ * dimensions. The device input range can be overriden in using system properties
+ * of the form:
+ *
+ * monocle.input.PRODUCT_ID.KEY=VALUE
+ *
+ * PRODUCT_ID is the product descriptor received from udev
+ * KEY is one of minX, minY, maxX, maxY or flipXY
+ *
+ * flipXY is a boolean value; the others are integers.
+ */
+public class LinuxTouchTransform {
+
+    private LinuxInputDevice device;
+    private int[] axes = new int[2];
+    private double[] translates = new double[2];
+    private double[] scalars = new double[2];
+    private boolean flipXY;
+    private int[] mins = new int[2];
+    private int[] maxs = new int[2];
+
+    LinuxTouchTransform(LinuxInputDevice device) {
+        this.device = device;
+        Arrays.fill(axes, -1);
+        String product = device.getProduct();
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                flipXY = Boolean.getBoolean("monocle.input."
+                                            + product + ".flipXY");
+                return null;
+            }
+        });
+    }
+
+    /** Gets the transformed pixel coordinate of the current event in the buffer
+     *  provided.
+     *
+     * @param buffer A LinuxEventBuffer containing a raw event line
+     * @return a transformed value in screen space
+     */
+    public int getValue(LinuxEventBuffer buffer) {
+        int axis = buffer.getEventCode();
+        int value = buffer.getEventValue();
+        int i;
+        for (i = 0; i < axes.length && axes[i] != -1; i++) {
+            if (axes[i] == axis) {
+                return transform(i, value);
+            }
+        }
+        if (i == axes.length) {
+            axes = Arrays.copyOf(axes, axes.length * 2);
+            Arrays.fill(axes, i + 1, axes.length - 1, -1);
+            translates = Arrays.copyOf(translates, translates.length * 2);
+            scalars = Arrays.copyOf(scalars, scalars.length * 2);
+            mins = Arrays.copyOf(mins, mins.length * 2);
+            maxs = Arrays.copyOf(maxs, maxs.length * 2);
+        }
+        initTransform(axis, i);
+        return transform(i, value);
+    }
+
+    /** Gets the transformed axis number of the current event in the buffer
+     *  provided.
+     *
+     * @param buffer A LinuxEventBuffer containing a raw event line
+     * @return an axis number (e.g. Input.ABS_X)
+     */
+
+    public int getAxis(LinuxEventBuffer buffer) {
+        int axis = buffer.getEventCode();
+        if (flipXY) {
+            switch (axis) {
+                case Input.ABS_X: return Input.ABS_Y;
+                case Input.ABS_Y: return Input.ABS_X;
+                case Input.ABS_MT_POSITION_X: return Input.ABS_MT_POSITION_Y;
+                case Input.ABS_MT_POSITION_Y: return Input.ABS_MT_POSITION_X;
+                default:
+                    return axis;
+            }
+        } else {
+            return axis;
+        }
+    }
+
+    private void initTransform(int axis, int index) {
+        double range;
+        String axisName;
+        switch (axis) {
+            case Input.ABS_X:
+            case Input.ABS_MT_POSITION_X:
+                range = NativePlatformFactory.getNativePlatform()
+                        .getScreen().getWidth();
+                axisName = "X";
+                break;
+            case Input.ABS_Y:
+            case Input.ABS_MT_POSITION_Y:
+                range = NativePlatformFactory.getNativePlatform()
+                        .getScreen().getHeight();
+                axisName = "Y";
+                break;
+            default:
+                // we don't know what to do with this range,
+                // so don't transform it at all.
+                translates[index] = 0.0;
+                scalars[index] = 1.0;
+                return;
+        }
+        AbsoluteInputCapabilities caps = device.getAbsoluteInputCapabilities(axis);
+        String product = device.getProduct();
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                int minimum = Integer.getInteger(
+                        "monocle.input." + product + ".min" + axisName,
+                        caps.getMinimum());
+                int maximum = Integer.getInteger(
+                        "monocle.input." + product + ".max" + axisName,
+                        caps.getMaximum());
+                translates[index] = -minimum;
+                scalars[index] = ((double) (range)) / (maximum - minimum);
+                return null;
+            }
+        });
+    }
+
+    private int transform(int index, int value) {
+        return (int) Math.round((value + translates[index]) * scalars[index]);
+    }
+
+}
--- a/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/SysFS.java	Sun Jan 12 21:47:04 2014 +0200
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/monocle/linux/SysFS.java	Mon Jan 13 09:41:14 2014 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -76,6 +76,23 @@
         return capsMap;
     }
 
+    static Map<String, String> readUEvent(File sysPath) {
+        Map<String, String> uevent = new HashMap();
+        File f = new File(sysPath, "device/uevent");
+        try {
+            BufferedReader r = new BufferedReader(new FileReader(f));
+            for (String line; (line = r.readLine()) != null;) {
+                int i = line.indexOf("=");
+                if (i >= 0) {
+                    uevent.put(line.substring(0, i), line.substring(i + 1));
+                }
+            }
+        } catch (IOException e) {
+            // return an empty map
+        }
+        return uevent;
+    }
+
     /** Fires udev notification events for devices of the given type */
     public static void triggerUdevNotification(String sysClass) {
         File[] devices = new File("/sys/class/" + sysClass).listFiles();