aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/wintab/build.xml76
-rw-r--r--plugins/wintab/src/java/net/java/games/input/WinTabButtonComponent.java29
-rw-r--r--plugins/wintab/src/java/net/java/games/input/WinTabComponent.java195
-rw-r--r--plugins/wintab/src/java/net/java/games/input/WinTabContext.java74
-rw-r--r--plugins/wintab/src/java/net/java/games/input/WinTabCursorComponent.java28
-rw-r--r--plugins/wintab/src/java/net/java/games/input/WinTabDevice.java131
-rw-r--r--plugins/wintab/src/java/net/java/games/input/WinTabEnvironmentPlugin.java69
-rw-r--r--plugins/wintab/src/java/net/java/games/input/WinTabPacket.java11
-rw-r--r--plugins/wintab/src/native/build.xml70
-rw-r--r--plugins/wintab/src/native/net_java_games_input_WinTabContext.c85
-rw-r--r--plugins/wintab/src/native/net_java_games_input_WinTabDevice.c107
11 files changed, 875 insertions, 0 deletions
diff --git a/plugins/wintab/build.xml b/plugins/wintab/build.xml
new file mode 100644
index 0000000..cc36299
--- /dev/null
+++ b/plugins/wintab/build.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<project name="WinTab Input Plugin" basedir="." default="all">
+ <target name="init">
+ <mkdir dir="classes"/>
+ <mkdir dir="bin"/>
+ <condition property="wintab" >
+ <os family="Windows" />
+ </condition>
+ </target>
+
+ <target name="compile" depends="init">
+ <javac srcdir="../windows/src/java" destdir="classes" debug="true" deprecation="true" source="1.4" target="1.4">
+ <include name="**/DummyWindow.java"/>
+ </javac>
+
+ <javac srcdir="src/java" destdir="classes" debug="true" deprecation="true" source="1.4" target="1.4">
+ <!-- To add something to the classpath: -->
+ <classpath>
+ <pathelement location="../../coreAPI/bin/jinput-core.jar"/>
+ <pathelement location="../../lib/jutils.jar"/>
+ </classpath>
+ </javac>
+ </target>
+
+ <target depends="init,compile" name="create_jniheaders">
+ <javah destdir="src/native">
+ <classpath>
+ <pathelement path="classes"/>
+ <pathelement location="../../coreAPI/classes"/>
+ </classpath>
+ <class name="net.java.games.input.DummyWindow"/>
+ <class name="net.java.games.input.WinTabContext"/>
+ <class name="net.java.games.input.WinTabDevice"/>
+ <class name="net.java.games.input.WinTabComponent"/>
+ </javah>
+ </target>
+
+ <target name="compile_native" depends="init,create_jniheaders" if="wintab">
+ <ant dir="src/native" target="compile"/>
+ <copy todir="bin">
+ <fileset dir="src/native" includes="*.dll"/>
+ </copy>
+ </target>
+
+ <target name="jar" depends="init,compile">
+ <jar jarfile="bin/wintab.jar" compress="true" basedir="classes">
+ <include name="**/*.class"/>
+ </jar>
+ </target>
+
+ <target name="all" depends="compile,compile_native,jar" description="Build everything.">
+ </target>
+
+ <target name="javadoc" depends="init" description="Javadoc for my API.">
+ <mkdir dir="apidocs"/>
+ <javadoc packagenames="net.*"
+ destdir="apidocs"
+ additionalparam="-source 1.4">
+ <sourcepath>
+ <pathelement location="src/java"/>
+ </sourcepath>
+ <classpath>
+ <pathelement location="../windows/src/java"/>
+ <pathelement location="../../bin/jinput-core.jar"/>
+ <pathelement location="../../lib/jutils.jar"/>
+ </classpath>
+ </javadoc>
+ </target>
+
+ <target name="clean" depends="init" description="Clean all build products.">
+ <delete dir="classes" failonerror="no"/>
+ <delete dir="bin" failonerror="no"/>
+ <delete dir="apidocs" failonerror="no"/>
+ <ant inheritAll="false" antfile="src/native/build.xml" target="clean"/>
+ </target>
+</project>
diff --git a/plugins/wintab/src/java/net/java/games/input/WinTabButtonComponent.java b/plugins/wintab/src/java/net/java/games/input/WinTabButtonComponent.java
new file mode 100644
index 0000000..ff6f425
--- /dev/null
+++ b/plugins/wintab/src/java/net/java/games/input/WinTabButtonComponent.java
@@ -0,0 +1,29 @@
+package net.java.games.input;
+
+import net.java.games.input.Component.Identifier;
+
+public class WinTabButtonComponent extends WinTabComponent {
+
+ private int index;
+
+ protected WinTabButtonComponent(WinTabContext context, int parentDevice, String name, Identifier id, int index) {
+ super(context, parentDevice, name, id);
+ this.index = index;
+ }
+
+ public Event processPacket(WinTabPacket packet) {
+ Event newEvent = null;
+
+ float newValue = ((packet.PK_BUTTONS & (int)Math.pow(2, index))>0) ? 1.0f : 0.0f;
+ if(newValue!=getPollData()) {
+ lastKnownValue = newValue;
+
+ //Generate an event
+ newEvent = new Event();
+ newEvent.set(this, newValue, packet.PK_TIME*1000);
+ return newEvent;
+ }
+
+ return newEvent;
+ }
+}
diff --git a/plugins/wintab/src/java/net/java/games/input/WinTabComponent.java b/plugins/wintab/src/java/net/java/games/input/WinTabComponent.java
new file mode 100644
index 0000000..91ae205
--- /dev/null
+++ b/plugins/wintab/src/java/net/java/games/input/WinTabComponent.java
@@ -0,0 +1,195 @@
+package net.java.games.input;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import net.java.games.input.Component.Identifier;
+
+public class WinTabComponent extends AbstractComponent {
+
+ public final static int XAxis = 1;
+ public final static int YAxis = 2;
+ public final static int ZAxis = 3;
+ public final static int NPressureAxis = 4;
+ public final static int TPressureAxis = 5;
+ public final static int OrientationAxis = 6;
+ public final static int RotationAxis = 7;
+
+ private int parentDevice;
+ private int min;
+ private int max;
+ private WinTabContext context;
+ protected float lastKnownValue;
+ private boolean analog;
+
+ protected WinTabComponent(WinTabContext context, int parentDevice, String name, Identifier id, int min, int max) {
+ super(name, id);
+ this.parentDevice = parentDevice;
+ this.min = min;
+ this.max = max;
+ this.context = context;
+ analog = true;
+ }
+
+ protected WinTabComponent(WinTabContext context, int parentDevice, String name, Identifier id) {
+ super(name, id);
+ this.parentDevice = parentDevice;
+ this.min = 0;
+ this.max = 1;
+ this.context = context;
+ analog = false;
+ }
+
+ protected float poll() throws IOException {
+ return lastKnownValue;
+ }
+
+ public boolean isAnalog() {
+ return analog;
+ }
+
+ public boolean isRelative() {
+ // All axis are absolute
+ return false;
+ }
+
+ public static List createComponents(WinTabContext context, int parentDevice, int axisId, int[] axisRanges) {
+ List components = new ArrayList();
+ Identifier id;
+ switch(axisId) {
+ case XAxis:
+ id = Identifier.Axis.X;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[0], axisRanges[1]));
+ break;
+ case YAxis:
+ id = Identifier.Axis.Y;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[0], axisRanges[1]));
+ break;
+ case ZAxis:
+ id = Identifier.Axis.Z;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[0], axisRanges[1]));
+ break;
+ case NPressureAxis:
+ id = Identifier.Axis.X_FORCE;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[0], axisRanges[1]));
+ break;
+ case TPressureAxis:
+ id = Identifier.Axis.Y_FORCE;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[0], axisRanges[1]));
+ break;
+ case OrientationAxis:
+ id = Identifier.Axis.RX;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[0], axisRanges[1]));
+ id = Identifier.Axis.RY;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[2], axisRanges[3]));
+ id = Identifier.Axis.RZ;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[4], axisRanges[5]));
+ break;
+ case RotationAxis:
+ id = Identifier.Axis.RX;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[0], axisRanges[1]));
+ id = Identifier.Axis.RY;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[2], axisRanges[3]));
+ id = Identifier.Axis.RZ;
+ components.add(new WinTabComponent(context, parentDevice, id.getName(), id, axisRanges[4], axisRanges[5]));
+ break;
+ }
+
+ return components;
+ }
+
+ public static Collection createButtons(WinTabContext context, int deviceIndex, int numberOfButtons) {
+ List buttons = new ArrayList();
+ Identifier id;
+
+ for(int i=0;i<numberOfButtons;i++) {
+ try {
+ Class buttonIdClass = Identifier.Button.class;
+ Field idField = buttonIdClass.getField("_" + i);
+ id = (Identifier)idField.get(null);
+ buttons.add(new WinTabButtonComponent(context, deviceIndex, id.getName(), id, i));
+ } catch (SecurityException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ return buttons;
+ }
+
+ public Event processPacket(WinTabPacket packet) {
+ // Set this to be the old value incase we don't change it
+ float newValue=lastKnownValue;
+
+ if(getIdentifier()==Identifier.Axis.X) {
+ newValue = normalise(packet.PK_X);
+ }
+ if(getIdentifier()==Identifier.Axis.Y) {
+ newValue = normalise(packet.PK_Y);
+ }
+ if(getIdentifier()==Identifier.Axis.Z) {
+ newValue = normalise(packet.PK_Z);
+ }
+ if(getIdentifier()==Identifier.Axis.X_FORCE) {
+ newValue = normalise(packet.PK_NORMAL_PRESSURE);
+ }
+ if(getIdentifier()==Identifier.Axis.Y_FORCE) {
+ newValue = normalise(packet.PK_TANGENT_PRESSURE);
+ }
+ if(getIdentifier()==Identifier.Axis.RX) {
+ newValue = normalise(packet.PK_ORIENTATION_ALT);
+ }
+ if(getIdentifier()==Identifier.Axis.RY) {
+ newValue = normalise(packet.PK_ORIENTATION_AZ);
+ }
+ if(getIdentifier()==Identifier.Axis.RZ) {
+ newValue = normalise(packet.PK_ORIENTATION_TWIST);
+ }
+ if(newValue!=getPollData()) {
+ lastKnownValue = newValue;
+
+ //Generate an event
+ Event newEvent = new Event();
+ newEvent.set(this, newValue, packet.PK_TIME*1000);
+ return newEvent;
+ }
+
+ return null;
+ }
+
+ private float normalise(float value) {
+ if(max == min) return value;
+ float bottom = max - min;
+ return (value - min)/bottom;
+ }
+
+ public static Collection createCursors(WinTabContext context, int deviceIndex, String[] cursorNames) {
+ Identifier id;
+ List cursors = new ArrayList();
+
+ for(int i=0;i<cursorNames.length;i++) {
+ if(cursorNames[i].matches("Puck")) {
+ id = Identifier.Button.TOOL_FINGER;
+ } else if(cursorNames[i].matches("Eraser.*")) {
+ id = Identifier.Button.TOOL_RUBBER;
+ } else {
+ id = Identifier.Button.TOOL_PEN;
+ }
+ cursors.add(new WinTabCursorComponent(context, deviceIndex, id.getName(), id, i));
+ }
+
+ return cursors;
+ }
+}
diff --git a/plugins/wintab/src/java/net/java/games/input/WinTabContext.java b/plugins/wintab/src/java/net/java/games/input/WinTabContext.java
new file mode 100644
index 0000000..185f4c2
--- /dev/null
+++ b/plugins/wintab/src/java/net/java/games/input/WinTabContext.java
@@ -0,0 +1,74 @@
+package net.java.games.input;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class WinTabContext {
+
+ private DummyWindow window;
+ private long hCTX;
+ private Controller[] controllers;
+
+ public WinTabContext(DummyWindow window) {
+ this.window = window;
+ }
+
+ public Controller[] getControllers() {
+ if(hCTX==0) {
+ throw new IllegalStateException("Context must be open before getting the controllers");
+ }
+ return controllers;
+ }
+
+ public synchronized void open() {
+ ControllerEnvironment.logln("Opening context");
+ this.hCTX = nOpen(window.getHwnd());
+ List devices = new ArrayList();
+
+ int numSupportedDevices = nGetNumberOfSupportedDevices();
+ ControllerEnvironment.logln(numSupportedDevices + " devices maximum supported");
+ for(int i=0;i<numSupportedDevices;i++) {
+ WinTabDevice newDevice = WinTabDevice.createDevice(this,i);
+ if(newDevice!=null) {
+ devices.add(newDevice);
+ }
+ }
+
+ controllers = (Controller[])devices.toArray(new Controller[0]);
+ }
+
+ public synchronized void close() {
+ ControllerEnvironment.logln("Closing context");
+ nClose(hCTX);
+ }
+
+ public synchronized void processEvents() {
+ WinTabPacket[] packets = nGetPackets(hCTX);
+ //ControllerEnvironment.logln("Packets read: " + packets.length);
+ for(int i=0;i<packets.length;i++) {
+ //ControllerEnvironment.logln("Packet time: " + packets[i].PK_TIME);
+ //ControllerEnvironment.logln("Packet x: " + packets[i].PK_X);
+ //ControllerEnvironment.logln("Packet y: " + packets[i].PK_Y);
+ //ControllerEnvironment.logln("Packet z: " + packets[i].PK_Z);
+ //ControllerEnvironment.logln("Packet buttons: " + packets[i].PK_BUTTONS);
+ //ControllerEnvironment.logln("Packet cursor: " + packets[i].PK_CURSOR);
+ //ControllerEnvironment.logln("Packet Normal Pressure: " + packets[i].PK_NORMAL_PRESSURE);
+ //ControllerEnvironment.logln("Packet Tangent Pressure: " + packets[i].PK_TANGENT_PRESSURE);
+ //ControllerEnvironment.logln("Packet Alt: " + packets[i].PK_ORIENTATION_ALT);
+ //ControllerEnvironment.logln("Packet Az: " + packets[i].PK_ORIENTATION_AZ);
+ //ControllerEnvironment.logln("Packet Twist: " + packets[i].PK_ORIENTATION_TWIST);
+
+ // TODO I can't seem to find a way to identify which device the packet is for
+ // This is not good.
+ // NASTY HACK based of assumptions that might very well be wrong
+ // Just send it to the first device
+ ((WinTabDevice)(getControllers()[0])).processPacket(packets[i]);
+ }
+ }
+
+ private final static native int nGetNumberOfSupportedDevices();
+ private final static native long nOpen(long hwnd);
+ private final static native void nClose(long hCtx);
+ private final static native WinTabPacket[] nGetPackets(long hCtx);
+
+}
diff --git a/plugins/wintab/src/java/net/java/games/input/WinTabCursorComponent.java b/plugins/wintab/src/java/net/java/games/input/WinTabCursorComponent.java
new file mode 100644
index 0000000..5f65399
--- /dev/null
+++ b/plugins/wintab/src/java/net/java/games/input/WinTabCursorComponent.java
@@ -0,0 +1,28 @@
+package net.java.games.input;
+
+import net.java.games.input.Component.Identifier;
+
+public class WinTabCursorComponent extends WinTabComponent {
+
+ private int index;
+
+ protected WinTabCursorComponent(WinTabContext context, int parentDevice, String name, Identifier id, int index) {
+ super(context, parentDevice, name, id);
+ this.index = index;
+ }
+
+ public Event processPacket(WinTabPacket packet) {
+ Event newEvent = null;
+ if(packet.PK_CURSOR==index && lastKnownValue==0) {
+ lastKnownValue = 1;
+ newEvent = new Event();
+ newEvent.set(this, lastKnownValue, packet.PK_TIME*1000);
+ } else if(packet.PK_CURSOR!=index && lastKnownValue==1) {
+ lastKnownValue = 0;
+ newEvent = new Event();
+ newEvent.set(this, lastKnownValue, packet.PK_TIME*1000);
+ }
+
+ return newEvent;
+ }
+}
diff --git a/plugins/wintab/src/java/net/java/games/input/WinTabDevice.java b/plugins/wintab/src/java/net/java/games/input/WinTabDevice.java
new file mode 100644
index 0000000..261338a
--- /dev/null
+++ b/plugins/wintab/src/java/net/java/games/input/WinTabDevice.java
@@ -0,0 +1,131 @@
+package net.java.games.input;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.java.games.input.Component.Identifier;
+
+public class WinTabDevice extends AbstractController {
+ private int deviceIndex;
+ private WinTabContext context;
+ private List eventList = new ArrayList();
+
+ private WinTabDevice(WinTabContext context, int index, String name, Component[] components) {
+ super(name, components, new Controller[0], new Rumbler[0]);
+ this.deviceIndex = index;
+ this.context = context;
+ }
+
+ protected boolean getNextDeviceEvent(Event event) throws IOException {
+ if(eventList.size()>0) {
+ Event ourEvent = (Event)eventList.remove(0);
+ event.set(ourEvent);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected void pollDevice() throws IOException {
+ // Get the data off the native queue.
+ context.processEvents();
+
+ super.pollDevice();
+ }
+
+ public Type getType() {
+ return Type.TRACKPAD;
+ }
+
+ public void processPacket(WinTabPacket packet) {
+ Component[] components = getComponents();
+ for(int i=0;i<components.length;i++) {
+ Event event = ((WinTabComponent)components[i]).processPacket(packet);
+ if(event!=null) {
+ eventList.add(event);
+ }
+ }
+ }
+
+ public static WinTabDevice createDevice(WinTabContext context, int deviceIndex) {
+ String name = nGetName(deviceIndex);
+ ControllerEnvironment.logln("Device " + deviceIndex + ", name: " + name);
+ List componentsList = new ArrayList();
+
+ int[] axisDetails = nGetAxisDetails(deviceIndex, WinTabComponent.XAxis);
+ if(axisDetails.length==0) {
+ ControllerEnvironment.logln("ZAxis not supported");
+ } else {
+ ControllerEnvironment.logln("Xmin: " + axisDetails[0] + ", Xmax: " + axisDetails[1]);
+ componentsList.addAll(WinTabComponent.createComponents(context, deviceIndex, WinTabComponent.XAxis, axisDetails));
+ }
+
+ axisDetails = nGetAxisDetails(deviceIndex, WinTabComponent.YAxis);
+ if(axisDetails.length==0) {
+ ControllerEnvironment.logln("YAxis not supported");
+ } else {
+ ControllerEnvironment.logln("Ymin: " + axisDetails[0] + ", Ymax: " + axisDetails[1]);
+ componentsList.addAll(WinTabComponent.createComponents(context, deviceIndex, WinTabComponent.YAxis, axisDetails));
+ }
+
+ axisDetails = nGetAxisDetails(deviceIndex, WinTabComponent.ZAxis);
+ if(axisDetails.length==0) {
+ ControllerEnvironment.logln("ZAxis not supported");
+ } else {
+ ControllerEnvironment.logln("Zmin: " + axisDetails[0] + ", Zmax: " + axisDetails[1]);
+ componentsList.addAll(WinTabComponent.createComponents(context, deviceIndex, WinTabComponent.ZAxis, axisDetails));
+ }
+
+ axisDetails = nGetAxisDetails(deviceIndex, WinTabComponent.NPressureAxis);
+ if(axisDetails.length==0) {
+ ControllerEnvironment.logln("NPressureAxis not supported");
+ } else {
+ ControllerEnvironment.logln("NPressMin: " + axisDetails[0] + ", NPressMax: " + axisDetails[1]);
+ componentsList.addAll(WinTabComponent.createComponents(context, deviceIndex, WinTabComponent.NPressureAxis, axisDetails));
+ }
+
+ axisDetails = nGetAxisDetails(deviceIndex, WinTabComponent.TPressureAxis);
+ if(axisDetails.length==0) {
+ ControllerEnvironment.logln("TPressureAxis not supported");
+ } else {
+ ControllerEnvironment.logln("TPressureAxismin: " + axisDetails[0] + ", TPressureAxismax: " + axisDetails[1]);
+ componentsList.addAll(WinTabComponent.createComponents(context, deviceIndex, WinTabComponent.TPressureAxis, axisDetails));
+ }
+
+ axisDetails = nGetAxisDetails(deviceIndex, WinTabComponent.OrientationAxis);
+ if(axisDetails.length==0) {
+ ControllerEnvironment.logln("OrientationAxis not supported");
+ } else {
+ ControllerEnvironment.logln("OrientationAxis mins/maxs: " + axisDetails[0] + "," + axisDetails[1] + ", " + axisDetails[2] + "," + axisDetails[3] + ", " + axisDetails[4] + "," + axisDetails[5]);
+ componentsList.addAll(WinTabComponent.createComponents(context, deviceIndex, WinTabComponent.OrientationAxis, axisDetails));
+ }
+
+ axisDetails = nGetAxisDetails(deviceIndex, WinTabComponent.RotationAxis);
+ if(axisDetails.length==0) {
+ ControllerEnvironment.logln("RotationAxis not supported");
+ } else {
+ ControllerEnvironment.logln("RotationAxis is supported (by the device, not by this plugin)");
+ componentsList.addAll(WinTabComponent.createComponents(context, deviceIndex, WinTabComponent.RotationAxis, axisDetails));
+ }
+
+ String[] cursorNames = nGetCursorNames(deviceIndex);
+ componentsList.addAll(WinTabComponent.createCursors(context, deviceIndex, cursorNames));
+ for(int i=0;i<cursorNames.length;i++) {
+ ControllerEnvironment.logln("Cursor " + i + "'s name: " + cursorNames[i]);
+ }
+
+ int numberOfButtons = nGetMaxButtonCount(deviceIndex);
+ ControllerEnvironment.logln("Device has " + numberOfButtons + " buttons");
+ componentsList.addAll(WinTabComponent.createButtons(context, deviceIndex, numberOfButtons));
+
+ Component[] components = (Component[])componentsList.toArray(new Component[0]);
+
+ return new WinTabDevice(context, deviceIndex, name, components);
+ }
+
+ private final static native String nGetName(int deviceIndex);
+ private final static native int[] nGetAxisDetails(int deviceIndex, int axisId);
+ private final static native String[] nGetCursorNames(int deviceIndex);
+ private final static native int nGetMaxButtonCount(int deviceIndex);
+}
diff --git a/plugins/wintab/src/java/net/java/games/input/WinTabEnvironmentPlugin.java b/plugins/wintab/src/java/net/java/games/input/WinTabEnvironmentPlugin.java
new file mode 100644
index 0000000..aa18f2b
--- /dev/null
+++ b/plugins/wintab/src/java/net/java/games/input/WinTabEnvironmentPlugin.java
@@ -0,0 +1,69 @@
+package net.java.games.input;
+
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.java.games.util.plugins.Plugin;
+
+public class WinTabEnvironmentPlugin extends ControllerEnvironment implements Plugin {
+ static {
+ DefaultControllerEnvironment.loadLibrary("jinput-wintab");
+ }
+
+ private final Controller[] controllers;
+ private final List active_devices = new ArrayList();
+ private final DummyWindow window;
+ private final WinTabContext winTabContext;
+
+ /** Creates new DirectInputEnvironment */
+ public WinTabEnvironmentPlugin() {
+ DummyWindow window = null;
+ WinTabContext winTabContext = null;
+ Controller[] controllers = new Controller[]{};
+ try {
+ window = new DummyWindow();
+ winTabContext = new WinTabContext(window);
+ try {
+ winTabContext.open();
+ controllers = winTabContext.getControllers();
+ } catch (Exception e) {
+ window.destroy();
+ throw e;
+ }
+ } catch (Exception e) {
+ ControllerEnvironment.logln("Failed to enumerate devices: " + e.getMessage());
+ e.printStackTrace();
+ }
+ this.window = window;
+ this.controllers = controllers;
+ this.winTabContext = winTabContext;
+ AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public final Object run() {
+ Runtime.getRuntime().addShutdownHook(new ShutdownHook());
+ return null;
+ }
+ });
+ }
+
+ public Controller[] getControllers() {
+ return controllers;
+ }
+
+ private final class ShutdownHook extends Thread {
+ public final void run() {
+ /* Release the devices to kill off active force feedback effects */
+ for (int i = 0; i < active_devices.size(); i++) {
+ // TODO free the devices
+ }
+ //Close the context
+ winTabContext.close();
+ /* We won't release the window since it is
+ * owned by the thread that created the environment.
+ */
+ }
+ }
+}
diff --git a/plugins/wintab/src/java/net/java/games/input/WinTabPacket.java b/plugins/wintab/src/java/net/java/games/input/WinTabPacket.java
new file mode 100644
index 0000000..28f1fd4
--- /dev/null
+++ b/plugins/wintab/src/java/net/java/games/input/WinTabPacket.java
@@ -0,0 +1,11 @@
+package net.java.games.input;
+
+public class WinTabPacket {
+ long PK_TIME;
+ int PK_X, PK_Y, PK_Z, PK_BUTTONS, PK_NORMAL_PRESSURE, PK_TANGENT_PRESSURE, PK_CURSOR;
+ int PK_ORIENTATION_ALT, PK_ORIENTATION_AZ, PK_ORIENTATION_TWIST;
+
+ public WinTabPacket() {
+
+ }
+}
diff --git a/plugins/wintab/src/native/build.xml b/plugins/wintab/src/native/build.xml
new file mode 100644
index 0000000..152ef24
--- /dev/null
+++ b/plugins/wintab/src/native/build.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+
+<project name="JInput wintab port, Native code" basedir="." default="compile">
+ <property environment="env"/>
+ <property name="wintabhome" location="${env.WINTAB_DIR}"/>
+ <property name="sdkhome" location="c:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2"/>
+
+ <target name="compile_dir">
+ <echo message="${compiledir}"/>
+ <echo message="wintab: ${wintabhome}"/>
+ <apply dir="${compiledir}" failonerror="true" executable="cl" dest="${compiledir}" skipemptyfilesets="true">
+ <arg line="/Ox /Wp64 /W2 /nologo /c"/>
+ <arg value="/I${sdkhome}\include"/>
+ <arg value="/I${wintabhome}\include"/>
+ <arg value="/I${java.home}\..\include"/>
+ <arg value="/I${java.home}\..\include\win32"/>
+ <arg value="/I${commonhome}/src/native"/>
+ <arg value="/I.."/>
+ <srcfile/>
+ <fileset dir="${compiledir}" includes="*.c"/>
+ <mapper type="glob" from="*.c" to="*.obj"/>
+ </apply>
+ </target>
+
+ <!-- <target name="link" unless="nolink">-->
+ <target name="link">
+ <apply dir="." parallel="true" executable="cl" failonerror="true">
+ <arg line="/LD /nologo"/>
+ <srcfile/>
+ <arg line="/Fe${dllname} /link"/>
+ <arg value="/LIBPATH:${java.home}\lib"/>
+ <arg value="/LIBPATH:${wintabhome}\lib\I386"/>
+ <arg value="/LIBPATH:${sdkhome}\lib"/>
+ <arg line="/DLL ${libs}"/>
+ <fileset dir="${commonhome}/src/native" includes="*.obj"/>
+ <fileset dir="." includes="*.obj"/>
+ <fileset dir="../../../windows/src/native" includes="*.obj"/>
+ </apply>
+ </target>
+
+ <target name="clean">
+ <delete>
+ <fileset dir="." includes="*.obj"/>
+ <fileset dir="." includes="*.dll"/>
+ </delete>
+ </target>
+
+ <target name="compile">
+ <property name="wintablibs" value="Kernel32.lib WINTAB32.LIB User32.lib"/>
+ <property name="commonhome" location="../../../common"/>
+ <property name="dllname" value="jinput-wintab.dll"/>
+ <antcall target="compile_dir">
+ <param name="compiledir" location="${commonhome}/src/native"/>
+ </antcall>
+ <antcall target="compile_dir">
+ <param name="compiledir" location="."/>
+ </antcall>
+ <antcall target="compile_dir">
+ <param name="compiledir" location="../../../windows/src/native"/>
+ </antcall>
+ <!-- <uptodate property="nolink" targetfile="${dllname}">
+ <srcfiles dir="." includes="*.obj"/>
+ </uptodate>-->
+ <antcall target="link">
+ <param name="dllname" value="${dllname}"/>
+ <param name="libs" value="${wintablibs}"/>
+ </antcall>
+ </target>
+</project>
+
diff --git a/plugins/wintab/src/native/net_java_games_input_WinTabContext.c b/plugins/wintab/src/native/net_java_games_input_WinTabContext.c
new file mode 100644
index 0000000..10863f9
--- /dev/null
+++ b/plugins/wintab/src/native/net_java_games_input_WinTabContext.c
@@ -0,0 +1,85 @@
+#include <windows.h>
+#include <stdio.h>
+
+#include <jni.h>
+#include "net_java_games_input_WinTabContext.h"
+#include <wintab.h>
+//#define PACKETDATA ( PK_X | PK_Y | PK_Z | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ROTATION | PK_ORIENTATION | PK_CURSOR )
+#define PACKETDATA ( PK_TIME | PK_X | PK_Y | PK_Z | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR )
+#define PACKETMODE 0
+#include <pktdef.h>
+#include <util.h>
+
+#define MAX_PACKETS 20
+
+JNIEXPORT jlong JNICALL Java_net_java_games_input_WinTabContext_nOpen(JNIEnv *env, jclass unused, jlong hWnd_long) {
+ LOGCONTEXT context;
+ HWND hWnd = (HWND)(INT_PTR)hWnd_long;
+ HCTX hCtx = NULL;
+
+ /* get default region */
+ WTInfo(WTI_DEFCONTEXT, 0, &context);
+
+ wsprintf(context.lcName, "JInput Digitizing");
+ context.lcPktData = PACKETDATA;
+ context.lcPktMode = PACKETMODE;
+ context.lcMoveMask = PACKETDATA;
+ context.lcBtnUpMask = context.lcBtnDnMask;
+
+ /* open the region */
+ hCtx = WTOpen(hWnd, &context, TRUE);
+
+ return (jlong)(intptr_t)hCtx;
+}
+
+JNIEXPORT void JNICALL Java_net_java_games_input_WinTabContext_nClose(JNIEnv *env, jclass unused, jlong hCtx_long) {
+ WTClose((HCTX)(INT_PTR)hCtx_long);
+}
+
+JNIEXPORT jint JNICALL Java_net_java_games_input_WinTabContext_nGetNumberOfSupportedDevices(JNIEnv *env, jclass unused) {
+ int numDevices;
+ WTInfo(WTI_INTERFACE, IFC_NDEVICES, &numDevices);
+ return numDevices;
+}
+
+JNIEXPORT jobjectArray JNICALL Java_net_java_games_input_WinTabContext_nGetPackets(JNIEnv *env, jclass unused, jlong hCtx_long) {
+
+ jobjectArray retval;
+ int i=0;
+ PACKET packets[MAX_PACKETS];
+ int numberRead = WTPacketsGet((HCTX)(INT_PTR)hCtx_long, MAX_PACKETS, packets);
+ jclass winTabPacketClass = (*env)->FindClass(env, "net/java/games/input/WinTabPacket");
+ jfieldID packetTimeField = (*env)->GetFieldID(env, winTabPacketClass, "PK_TIME", "J");
+ jfieldID packetXAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_X", "I");
+ jfieldID packetYAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_Y", "I");
+ jfieldID packetZAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_Z", "I");
+ jfieldID packetButtonsField = (*env)->GetFieldID(env, winTabPacketClass, "PK_BUTTONS", "I");
+ jfieldID packetNPressureAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_NORMAL_PRESSURE", "I");
+ jfieldID packetTPressureAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_TANGENT_PRESSURE", "I");
+ jfieldID packetCursorField = (*env)->GetFieldID(env, winTabPacketClass, "PK_CURSOR", "I");
+ jfieldID packetOrientationAltAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_ORIENTATION_ALT", "I");
+ jfieldID packetOrientationAzAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_ORIENTATION_AZ", "I");
+ jfieldID packetOrientationTwistAxisField = (*env)->GetFieldID(env, winTabPacketClass, "PK_ORIENTATION_TWIST", "I");
+ jobject prototypePacket = newJObject(env, "net/java/games/input/WinTabPacket", "()V");
+
+ retval = (*env)->NewObjectArray(env, numberRead, winTabPacketClass, NULL);
+ for(i=0;i<numberRead;i++) {
+ jobject tempPacket = newJObject(env, "net/java/games/input/WinTabPacket", "()V");
+
+ (*env)->SetLongField(env, tempPacket, packetTimeField, packets[i].pkTime);
+ (*env)->SetIntField(env, tempPacket, packetXAxisField, packets[i].pkX);
+ (*env)->SetIntField(env, tempPacket, packetYAxisField, packets[i].pkY);
+ (*env)->SetIntField(env, tempPacket, packetZAxisField, packets[i].pkZ);
+ (*env)->SetIntField(env, tempPacket, packetButtonsField, packets[i].pkButtons);
+ (*env)->SetIntField(env, tempPacket, packetNPressureAxisField, packets[i].pkNormalPressure);
+ (*env)->SetIntField(env, tempPacket, packetTPressureAxisField, packets[i].pkTangentPressure);
+ (*env)->SetIntField(env, tempPacket, packetCursorField, packets[i].pkCursor);
+ (*env)->SetIntField(env, tempPacket, packetOrientationAltAxisField, packets[i].pkOrientation.orAzimuth);
+ (*env)->SetIntField(env, tempPacket, packetOrientationAzAxisField, packets[i].pkOrientation.orAltitude);
+ (*env)->SetIntField(env, tempPacket, packetOrientationTwistAxisField, packets[i].pkOrientation.orTwist);
+
+ (*env)->SetObjectArrayElement(env, retval, i, tempPacket);
+ }
+
+ return retval;
+}
diff --git a/plugins/wintab/src/native/net_java_games_input_WinTabDevice.c b/plugins/wintab/src/native/net_java_games_input_WinTabDevice.c
new file mode 100644
index 0000000..45f202e
--- /dev/null
+++ b/plugins/wintab/src/native/net_java_games_input_WinTabDevice.c
@@ -0,0 +1,107 @@
+#include <windows.h>
+#include <stdio.h>
+
+#include <jni.h>
+#include "net_java_games_input_WinTabDevice.h"
+#include "net_java_games_input_WinTabComponent.h"
+#include "util.h"
+#include <wintab.h>
+#include <malloc.h>
+#define PACKETDATA ( PK_X | PK_Y | PK_Z | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR )
+#define PACKETMODE 0
+#include <pktdef.h>
+
+JNIEXPORT jstring JNICALL Java_net_java_games_input_WinTabDevice_nGetName(JNIEnv *env, jclass unused, jint deviceIndex) {
+ char name[50];
+ WTInfo(WTI_DEVICES + deviceIndex, DVC_NAME, name);
+ return (*env)->NewStringUTF(env, name);
+}
+
+JNIEXPORT jintArray JNICALL Java_net_java_games_input_WinTabDevice_nGetAxisDetails(JNIEnv *env, jclass unused, jint deviceIndex, jint axisId) {
+ UINT type;
+ AXIS threeAxisArray[3];
+ AXIS axis;
+ long threeAxisData[6];
+ long axisData[2];
+ int res;
+ jintArray retVal = NULL;
+
+ if(axisId==net_java_games_input_WinTabComponent_XAxis) type = DVC_X;
+ else if(axisId==net_java_games_input_WinTabComponent_YAxis) type = DVC_Y;
+ else if(axisId==net_java_games_input_WinTabComponent_ZAxis) type = DVC_Z;
+ else if(axisId==net_java_games_input_WinTabComponent_NPressureAxis) type = DVC_NPRESSURE;
+ else if(axisId==net_java_games_input_WinTabComponent_TPressureAxis) type = DVC_TPRESSURE;
+ else if(axisId==net_java_games_input_WinTabComponent_OrientationAxis) type = DVC_ORIENTATION;
+ else if(axisId==net_java_games_input_WinTabComponent_RotationAxis) type = DVC_ROTATION;
+
+ if(axisId==net_java_games_input_WinTabComponent_RotationAxis || axisId==net_java_games_input_WinTabComponent_OrientationAxis) {
+ res = WTInfo(WTI_DEVICES + deviceIndex, type, &threeAxisArray);
+ if(res!=0) {
+ threeAxisData[0] = threeAxisArray[0].axMin;
+ threeAxisData[1] = threeAxisArray[0].axMax;
+ threeAxisData[2] = threeAxisArray[1].axMin;
+ threeAxisData[3] = threeAxisArray[1].axMax;
+ threeAxisData[4] = threeAxisArray[2].axMin;
+ threeAxisData[5] = threeAxisArray[2].axMax;
+ retVal = (*env)->NewIntArray(env, 6);
+ (*env)->SetIntArrayRegion(env, retVal, 0, 6, threeAxisData);
+ }
+ } else {
+ res = WTInfo(WTI_DEVICES + deviceIndex, type, &axis);
+ if(res!=0) {
+ axisData[0] = axis.axMin;
+ axisData[1] = axis.axMax;
+ retVal = (*env)->NewIntArray(env, 2);
+ (*env)->SetIntArrayRegion(env, retVal, 0, 2, axisData);
+ }
+ }
+
+ if(retVal==NULL) {
+ retVal = (*env)->NewIntArray(env, 0);
+ }
+
+ return retVal;
+}
+
+JNIEXPORT jobjectArray JNICALL Java_net_java_games_input_WinTabDevice_nGetCursorNames(JNIEnv *env, jclass unused, jint deviceId) {
+ int numberCursorTypes;
+ int firstCursorType;
+ char name[50];
+ int i;
+ jclass stringClass = (*env)->FindClass(env, "java/lang/String");
+ jstring nameString;
+ jobjectArray retval;
+
+ WTInfo(WTI_DEVICES + deviceId, DVC_NCSRTYPES, &numberCursorTypes);
+ WTInfo(WTI_DEVICES + deviceId, DVC_FIRSTCSR, &firstCursorType);
+
+ retval = (*env)->NewObjectArray(env, numberCursorTypes, stringClass, NULL);
+
+ for(i=0;i<numberCursorTypes;i++) {
+ WTInfo(WTI_CURSORS + i + firstCursorType, CSR_NAME, name);
+ nameString = (*env)->NewStringUTF(env, name);
+ (*env)->SetObjectArrayElement(env, retval, i-firstCursorType, nameString);
+ }
+
+ return retval;
+}
+
+JNIEXPORT jint JNICALL Java_net_java_games_input_WinTabDevice_nGetMaxButtonCount(JNIEnv *env, jclass unused, jint deviceId) {
+ int numberCursorTypes;
+ int firstCursorType;
+ byte buttonCount;
+ int i;
+ byte retval=0;
+
+ WTInfo(WTI_DEVICES + deviceId, DVC_NCSRTYPES, &numberCursorTypes);
+ WTInfo(WTI_DEVICES + deviceId, DVC_FIRSTCSR, &firstCursorType);
+
+ for(i=0;i<numberCursorTypes;i++) {
+ WTInfo(WTI_CURSORS + i + firstCursorType, CSR_BUTTONS, &buttonCount);
+ if(buttonCount>retval) {
+ retval = buttonCount;
+ }
+ }
+
+ return (jint)retval;
+}