diff options
9 files changed, 225 insertions, 33 deletions
diff --git a/coreAPI/src/java/net/java/games/input/DefaultControllerEnvironment.java b/coreAPI/src/java/net/java/games/input/DefaultControllerEnvironment.java index 3ce2b2e..39db61a 100644 --- a/coreAPI/src/java/net/java/games/input/DefaultControllerEnvironment.java +++ b/coreAPI/src/java/net/java/games/input/DefaultControllerEnvironment.java @@ -123,13 +123,13 @@ class DefaultControllerEnvironment extends ControllerEnvironment { pluginClasses = pluginClasses + " net.java.games.input.OSXEnvironmentPlugin"; } else if(osName.equals("Windows 98") || osName.equals("Windows 2000") || osName.equals("Windows XP")) { pluginClasses = pluginClasses + " net.java.games.input.DirectInputEnvironmentPlugin"; - pluginClasses = pluginClasses + " net.java.games.input.RawInputEnvironmentPlugin"; +// pluginClasses = pluginClasses + " net.java.games.input.RawInputEnvironmentPlugin"; } else if (osName.startsWith("Windows")) { System.out.println("WARNING: Found unknown Windows version: " + osName); System.out.println("Attempting to use default windows plug-in."); System.out.flush(); pluginClasses = pluginClasses + " net.java.games.input.DirectInputEnvironmentPlugin"; - pluginClasses = pluginClasses + " net.java.games.input.RawInputEnvironmentPlugin"; +// pluginClasses = pluginClasses + " net.java.games.input.RawInputEnvironmentPlugin"; } else { System.out.println("Trying to use default plugin, OS name " + osName +" not recognised"); } diff --git a/plugins/linux/src/java/net/java/games/input/LinuxDeviceTask.java b/plugins/linux/src/java/net/java/games/input/LinuxDeviceTask.java new file mode 100644 index 0000000..3dbdeda --- /dev/null +++ b/plugins/linux/src/java/net/java/games/input/LinuxDeviceTask.java @@ -0,0 +1,62 @@ +/** + * Copyright (C) 2003 Jeremy Booth ([email protected]) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. Redistributions in binary + * form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * The name of the author may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + */ +package net.java.games.input; + +import java.io.IOException; + +abstract class LinuxDeviceTask { + public final static int INPROGRESS = 1; + public final static int COMPLETED = 2; + public final static int FAILED = 3; + + private Object result; + private IOException exception; + private int state = INPROGRESS; + + public final void doExecute() { + try { + result = execute(); + state = COMPLETED; + } catch (IOException e) { + exception = e; + state = FAILED; + } + } + + public final IOException getException() { + return exception; + } + + public final Object getResult() { + return result; + } + + public final int getState() { + return state; + } + + protected abstract Object execute() throws IOException; +} diff --git a/plugins/linux/src/java/net/java/games/input/LinuxDeviceThread.java b/plugins/linux/src/java/net/java/games/input/LinuxDeviceThread.java new file mode 100644 index 0000000..62d1849 --- /dev/null +++ b/plugins/linux/src/java/net/java/games/input/LinuxDeviceThread.java @@ -0,0 +1,91 @@ +/** + * Copyright (C) 2003 Jeremy Booth ([email protected]) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. Redistributions in binary + * form must reproduce the above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or other materials provided + * with the distribution. + * The name of the author may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + */ +package net.java.games.input; + +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; + +/** + * Linux doesn't have proper support for force feedback + * from different threads since it relies on PIDs + * to determine ownership of a particular effect slot. + * Therefore we have to hack around this by + * making sure everything related to FF + * (including the final device close that performs + * and implicit deletion of all the process' effects) + * is run on a single thread. + */ +final class LinuxDeviceThread extends Thread { + private final List tasks = new ArrayList(); + + public LinuxDeviceThread() { + setDaemon(true); + start(); + } + + public synchronized final void run() { + while (true) { + if (!tasks.isEmpty()) { + LinuxDeviceTask task = (LinuxDeviceTask)tasks.remove(0); + task.doExecute(); + synchronized (task) { + task.notify(); + } + } else { + try { + wait(); + } catch (InterruptedException e) { + // ignore + } + } + } + } + + public final Object execute(LinuxDeviceTask task) throws IOException { + synchronized (this) { + tasks.add(task); + notify(); + } + synchronized (task) { + while (task.getState() == LinuxDeviceTask.INPROGRESS) { + try { + task.wait(); + } catch (InterruptedException e) { + // ignore + } + } + } + switch (task.getState()) { + case LinuxDeviceTask.COMPLETED: + return task.getResult(); + case LinuxDeviceTask.FAILED: + throw task.getException(); + default: + throw new RuntimeException("Invalid task state: " + task.getState()); + } + } +} diff --git a/plugins/linux/src/java/net/java/games/input/LinuxEnvironmentPlugin.java b/plugins/linux/src/java/net/java/games/input/LinuxEnvironmentPlugin.java index 628ee04..d6afb96 100644 --- a/plugins/linux/src/java/net/java/games/input/LinuxEnvironmentPlugin.java +++ b/plugins/linux/src/java/net/java/games/input/LinuxEnvironmentPlugin.java @@ -41,6 +41,7 @@ import java.security.PrivilegedAction; public final class LinuxEnvironmentPlugin extends ControllerEnvironment implements Plugin { private final Controller[] controllers; private final List devices = new ArrayList(); + private final static LinuxDeviceThread device_thread = new LinuxDeviceThread(); static { AccessController.doPrivileged( @@ -52,6 +53,10 @@ public final class LinuxEnvironmentPlugin extends ControllerEnvironment implemen }); } + public final static Object execute(LinuxDeviceTask task) throws IOException { + return device_thread.execute(task); + } + public LinuxEnvironmentPlugin() { this.controllers = enumerateControllers(); System.out.println("Linux plugin claims to have found " + controllers.length + " controllers"); diff --git a/plugins/linux/src/java/net/java/games/input/LinuxEventDevice.java b/plugins/linux/src/java/net/java/games/input/LinuxEventDevice.java index f17e05d..bcd941a 100644 --- a/plugins/linux/src/java/net/java/games/input/LinuxEventDevice.java +++ b/plugins/linux/src/java/net/java/games/input/LinuxEventDevice.java @@ -369,20 +369,16 @@ final class LinuxEventDevice implements LinuxDevice { } private final static native String nGetName(long fd) throws IOException; - public final synchronized void close() throws IOException { - if (!closed) { - try { - if (rumblers != null) - for (int i = 0; i < rumblers.length; i++) { - rumblers[i].rumble(0); - // erasing effects seems to be unsupported on logitech devices - //rumblers[i].erase(); - } - } finally { - closed = true; + public synchronized final void close() throws IOException { + if (closed) + return; + closed = true; + LinuxEnvironmentPlugin.execute(new LinuxDeviceTask() { + protected final Object execute() throws IOException { nClose(fd); + return null; } - } + }); } private final static native void nClose(long fd) throws IOException; diff --git a/plugins/linux/src/java/net/java/games/input/LinuxForceFeedbackEffect.java b/plugins/linux/src/java/net/java/games/input/LinuxForceFeedbackEffect.java index 2e3a0e7..528cbff 100644 --- a/plugins/linux/src/java/net/java/games/input/LinuxForceFeedbackEffect.java +++ b/plugins/linux/src/java/net/java/games/input/LinuxForceFeedbackEffect.java @@ -33,39 +33,42 @@ import java.io.IOException; abstract class LinuxForceFeedbackEffect implements Rumbler { private final LinuxEventDevice device; private final int ff_id; + private final WriteTask write_task = new WriteTask(); + private final UploadTask upload_task = new UploadTask(); public LinuxForceFeedbackEffect(LinuxEventDevice device) throws IOException { this.device = device; - this.ff_id = upload(-1, 0); + this.ff_id = upload_task.doUpload(-1, 0); } protected abstract int upload(int id, float intensity) throws IOException; - private final void write(int value) throws IOException { - device.writeEvent(NativeDefinitions.EV_FF, ff_id, value); - } - protected final LinuxEventDevice getDevice() { return device; } - public final void rumble(float intensity) { + public synchronized final void rumble(float intensity) { try { if (intensity > 0) { - upload(ff_id, intensity); - write(1); + upload_task.doUpload(ff_id, intensity); + write_task.write(1); } else { - write(0); + write_task.write(0); } } catch (IOException e) { ControllerEnvironment.logln("Failed to rumble: " + e); } } + /* + * Erase doesn't seem to be implemented on Logitech joysticks, + * so we'll rely on the kernel cleaning up on device close + */ +/* public final void erase() throws IOException { device.eraseEffect(ff_id); } - +*/ public final String getAxisName() { return null; } @@ -73,4 +76,35 @@ abstract class LinuxForceFeedbackEffect implements Rumbler { public final Component.Identifier getAxisIdentifier() { return null; } + + private final class UploadTask extends LinuxDeviceTask { + private int id; + private float intensity; + + public final int doUpload(int id, float intensity) throws IOException { + this.id = id; + this.intensity = intensity; + LinuxEnvironmentPlugin.execute(this); + return this.id; + } + + protected final Object execute() throws IOException { + this.id = upload(id, intensity); + return null; + } + } + + private final class WriteTask extends LinuxDeviceTask { + private int value; + + public final void write(int value) throws IOException { + this.value = value; + LinuxEnvironmentPlugin.execute(this); + } + + protected final Object execute() throws IOException { + device.writeEvent(NativeDefinitions.EV_FF, ff_id, value); + return null; + } + } } diff --git a/plugins/linux/src/java/net/java/games/input/LinuxRumbleFF.java b/plugins/linux/src/java/net/java/games/input/LinuxRumbleFF.java index e76bd31..578793a 100644 --- a/plugins/linux/src/java/net/java/games/input/LinuxRumbleFF.java +++ b/plugins/linux/src/java/net/java/games/input/LinuxRumbleFF.java @@ -49,6 +49,6 @@ final class LinuxRumbleFF extends LinuxForceFeedbackEffect { weak_magnitude = (int)(0xc000*intensity); } - return getDevice().uploadRumbleEffect(id, 0, 0, 0, 0, 0, strong_magnitude, weak_magnitude); + return getDevice().uploadRumbleEffect(id, 0, 0, 0, -1, 0, strong_magnitude, weak_magnitude); } } diff --git a/plugins/windows/src/java/net/java/games/input/DirectInputEnvironmentPlugin.java b/plugins/windows/src/java/net/java/games/input/DirectInputEnvironmentPlugin.java index 46c9b7d..a7fa563 100644 --- a/plugins/windows/src/java/net/java/games/input/DirectInputEnvironmentPlugin.java +++ b/plugins/windows/src/java/net/java/games/input/DirectInputEnvironmentPlugin.java @@ -140,12 +140,16 @@ public final class DirectInputEnvironmentPlugin extends ControllerEnvironment im return createMouseFromDevice(device); case IDirectInputDevice.DI8DEVTYPE_KEYBOARD: return createKeyboardFromDevice(device); - case IDirectInputDevice.DI8DEVTYPE_JOYSTICK: - return createControllerFromDevice(device, Controller.Type.STICK); case IDirectInputDevice.DI8DEVTYPE_GAMEPAD: return createControllerFromDevice(device, Controller.Type.GAMEPAD); + case IDirectInputDevice.DI8DEVTYPE_DRIVING: + return createControllerFromDevice(device, Controller.Type.WHEEL); + case IDirectInputDevice.DI8DEVTYPE_FLIGHT: + /* Fall through */ + case IDirectInputDevice.DI8DEVTYPE_JOYSTICK: + /* Fall through */ default: - return null; + return createControllerFromDevice(device, Controller.Type.STICK); } } diff --git a/plugins/windows/src/java/net/java/games/input/IDirectInputDevice.java b/plugins/windows/src/java/net/java/games/input/IDirectInputDevice.java index ef061cd..2e3e22f 100644 --- a/plugins/windows/src/java/net/java/games/input/IDirectInputDevice.java +++ b/plugins/windows/src/java/net/java/games/input/IDirectInputDevice.java @@ -250,11 +250,11 @@ final class IDirectInputDevice { private final List createRumblers() throws IOException { DIDeviceObject x_axis = lookupObjectByGUID(GUID_XAxis); - DIDeviceObject y_axis = lookupObjectByGUID(GUID_YAxis); - if(x_axis == null || y_axis == null) +// DIDeviceObject y_axis = lookupObjectByGUID(GUID_YAxis); + if(x_axis == null/* || y_axis == null*/) return rumblers; - DIDeviceObject[] axes = {x_axis, y_axis}; - long[] directions = {0, 0}; + DIDeviceObject[] axes = {x_axis/*, y_axis*/}; + long[] directions = {0/*, 0*/}; for (int i = 0; i < effects.size(); i++) { DIEffectInfo info = (DIEffectInfo)effects.get(i); if ((info.getEffectType() & 0xff) == DIEFT_PERIODIC && @@ -270,7 +270,7 @@ final class IDirectInputDevice { for (int i = 0; i < axis_ids.length; i++) { axis_ids[i] = axes[i].getDIIdentifier(); } - long effect_address = nCreatePeriodicEffect(address, info.getGUID(), DIEFF_CARTESIAN | DIEFF_OBJECTIDS, INFINITE, 0, 0, DIEB_NOTRIGGER, 0, axis_ids, directions, 0, 0, 0, 0, DI_FFNOMINALMAX, 0, 0, 50000, 0); + long effect_address = nCreatePeriodicEffect(address, info.getGUID(), DIEFF_CARTESIAN | DIEFF_OBJECTIDS, INFINITE, 0, DI_FFNOMINALMAX, DIEB_NOTRIGGER, 0, axis_ids, directions, 0, 0, 0, 0, DI_FFNOMINALMAX, 0, 0, 50000, 0); return new IDirectInputEffect(effect_address, info); } private final static native long nCreatePeriodicEffect(long address, byte[] effect_guid, int flags, int duration, int sample_period, int gain, int trigger_button, int trigger_repeat_interval, int[] axis_ids, long[] directions, int envelope_attack_level, int envelope_attack_time, int envelope_fade_level, int envelope_fade_time, int periodic_magnitude, int periodic_offset, int periodic_phase, int periodic_period, int start_delay) throws IOException; |