// Copyright 2011-2012 Paulo Augusto Peccin. See licence.txt distributed with this file.
package org.javatari.pc.controls;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import org.javatari.atari.controls.ConsoleControls;
import org.javatari.atari.controls.ConsoleControlsSocket;
import org.javatari.general.av.video.VideoMonitor;
import org.javatari.parameters.Parameters;
import org.javatari.pc.screen.Screen;
import org.joy.Joy;
import org.joy.Joy.Info;
import org.joy.Joystick;
public final class JoystickConsoleControls implements ConsoleControls {
public JoystickConsoleControls(AWTConsoleControls awtControls) {
super();
this.awtControls = awtControls;
}
public void connect(ConsoleControlsSocket socket) {
consoleControlsSocket = socket;
}
public void connectScreen(Screen screen) {
videoMonitor = screen.monitor();
}
public void powerOn() {
swappedMode = false;
start();
}
public void powerOff() {
stop();
}
public void paddleMode(boolean state) {
paddleMode = state;
joy0State.xPosition = joy1State.xPosition = -1;
}
public synchronized void toggleMode() {
if (!started) {
swappedMode = false;
start();
} else
swappedMode = !swappedMode;
if (started) videoMonitor.showOSD("Joystick input " + (swappedMode ? "Swapped" : "Normal"), true);
else if (devices.isEmpty()) videoMonitor.showOSD("No Joysticks detected!", true);
else videoMonitor.showOSD("Joysticks are disabled! Open Settings", true);
}
public boolean isStarted() {
return started;
}
public Joystick getJoystick0() {
return joystick0;
}
public Joystick getJoystick1() {
return joystick1;
}
public Vector<DeviceOption> getJoystick0DeviceOptions() {
return joystick0DeviceOptions;
}
public Vector<DeviceOption> getJoystick1DeviceOptions() {
return joystick1DeviceOptions;
}
public DeviceOption getJoystick0DeviceOption() {
return joystick0DeviceOption;
}
public DeviceOption getJoystick1DeviceOption() {
return joystick1DeviceOption;
}
public synchronized void start() {
stop();
if (deviceListFailed) return;
try {
devices = Joy.listDevices();
} catch (UnsupportedOperationException e) {
System.out.println(e.getMessage());
deviceListFailed = true;
devices = new ArrayList<Info>();
joystick0DeviceOptions = joystick1DeviceOptions = new Vector<DeviceOption>();
joystick0DeviceOption = joystick1DeviceOption = null;
return;
}
initPreferences();
setupDeviceOptions(joy0Prefs.device, joy1Prefs.device);
if (joystick0 == null && joystick1 == null) return;
applyPreferences();
updaterActive();
}
private void setupDeviceOptions(int joy0Device, int joy1Device) {
// Determine auto options
Info auto0 = devices.size() > 0 ? devices.get(0) : null;
Info auto1 = devices.size() > 1 ? devices.get(1) : null;
// List all options for joystick0 and joystick1
joystick0DeviceOptions = new Vector<DeviceOption>();
joystick0DeviceOptions.add(new DeviceOption(Parameters.JOY_DEVICE_AUTO, auto0));
joystick0DeviceOptions.add(new DeviceOption(Parameters.JOY_DEVICE_NONE, null));
joystick1DeviceOptions = new Vector<DeviceOption>();
joystick1DeviceOptions.add(new DeviceOption(Parameters.JOY_DEVICE_AUTO, auto1));
joystick1DeviceOptions.add(new DeviceOption(Parameters.JOY_DEVICE_NONE, null));
for (Info info : devices) {
joystick0DeviceOptions.add(new DeviceOption(info.id, info));
joystick1DeviceOptions.add(new DeviceOption(info.id, info));
}
// Set chosen options
joystick0DeviceOption = null;
for (DeviceOption opt : joystick0DeviceOptions)
if (joy0Device == opt.device) joystick0DeviceOption = opt;
if (joystick0DeviceOption == null) joystick0DeviceOption = joystick0DeviceOptions.get(0); // Use Auto if no option possible
joystick1DeviceOption = null;
for (DeviceOption opt : joystick1DeviceOptions)
if (joy1Device == opt.device) joystick1DeviceOption = opt;
if (joystick1DeviceOption == null) joystick1DeviceOption = joystick1DeviceOptions.get(0); // Use Auto if no option possible
// Set actual devices according to chosen options
if (joystick0DeviceOption.info != null) joystick0 = Joystick.create(joystick0DeviceOption.info);
if (joystick1DeviceOption.info != null) joystick1 = Joystick.create(joystick1DeviceOption.info);
}
private synchronized void stop() {
updaterInactive();
initControls();
}
public void initPreferences() {
joy0Prefs = new Preferences();
joy1Prefs = new Preferences();
joy0Prefs.device = Parameters.JOY_P0_DEVICE;
joy0Prefs.xAxis = Parameters.JOY_P0_XAXIS;
joy0Prefs.xAxisSignal = Parameters.JOY_P0_XAXIS_SIGNAL;
joy0Prefs.yAxis = Parameters.JOY_P0_YAXIS;
joy0Prefs.yAxisSignal = Parameters.JOY_P0_YAXIS_SIGNAL;
joy0Prefs.paddleAxis = Parameters.JOY_P0_PAD_AXIS;
joy0Prefs.paddleAxisSignal = Parameters.JOY_P0_PAD_AXIS_SIGNAL;
joy0Prefs.button = Parameters.JOY_P0_BUTTON;
joy0Prefs.button2 = Parameters.JOY_P0_BUTTON2;
joy0Prefs.select = Parameters.JOY_P0_SELECT;
joy0Prefs.reset = Parameters.JOY_P0_RESET;
joy0Prefs.pause = Parameters.JOY_P0_PAUSE;
joy0Prefs.fastSpeed = Parameters.JOY_P0_FAST_SPPED;
joy0Prefs.paddleCenter = (int) ((float)Parameters.JOY_P0_PADDLE_CENTER / 100 * -190) + 190 - 5;
joy0Prefs.paddleSens = (int) ((float)Parameters.JOY_P0_PADDLE_SENS / 100 * -190);
joy0Prefs.deadzone = Parameters.JOY_P0_DEADZONE;
joy1Prefs.device = Parameters.JOY_P1_DEVICE;
joy1Prefs.xAxis = Parameters.JOY_P1_XAXIS;
joy1Prefs.xAxisSignal = Parameters.JOY_P1_XAXIS_SIGNAL;
joy1Prefs.yAxis = Parameters.JOY_P1_YAXIS;
joy1Prefs.yAxisSignal = Parameters.JOY_P1_YAXIS_SIGNAL;
joy1Prefs.paddleAxis = Parameters.JOY_P1_PAD_AXIS;
joy1Prefs.paddleAxisSignal = Parameters.JOY_P1_PAD_AXIS_SIGNAL;
joy1Prefs.button = Parameters.JOY_P1_BUTTON;
joy1Prefs.button2 = Parameters.JOY_P1_BUTTON2;
joy1Prefs.select = Parameters.JOY_P1_SELECT;
joy1Prefs.reset = Parameters.JOY_P1_RESET;
joy1Prefs.pause = Parameters.JOY_P1_PAUSE;
joy1Prefs.fastSpeed = Parameters.JOY_P1_FAST_SPPED;
joy1Prefs.deadzone = Parameters.JOY_P1_DEADZONE;
joy1Prefs.paddleCenter = (int) ((float)Parameters.JOY_P1_PADDLE_CENTER / 100 * -190) + 190 - 5;
joy1Prefs.paddleSens = (int) ((float)Parameters.JOY_P1_PADDLE_SENS / 100 * -190);
applyPreferences();
}
private void initControls() {
joystick0 = joystick1 = null;
joy0State = new State();
joy1State = new State();
}
public void applyPreferences() {
if (joystick0 != null) joystick0.setDigitalThreshold((float)joy0Prefs.deadzone / 100);
if (joystick1 != null) joystick1.setDigitalThreshold((float)joy1Prefs.deadzone / 100);
}
public synchronized boolean startButtonDetection(int joy, JoystickButtonDetectionListener listener) {
stopButtonDetection();
if (joy < 0 || joy > 1 || !started) return false;
Joystick joystick = joy == 0 ? joystick0 : joystick1;
if (joystick == null) return false;
joyButtonDetection = joystick;
joyButtonDetectionListener = listener;
return true;
}
public synchronized void stopButtonDetection() {
joyButtonDetection = null;
joyButtonDetectionLastState = -1;
joyButtonDetectionListener = null;
}
private synchronized void update() {
boolean ok;
if (joystick0 != null) {
ok = joystick0.update();
if (ok) update(joystick0, joy0State, joy0Prefs, !swappedMode);
else {
joystick0 = null;
videoMonitor.showOSD((p1ControlsMode ? "P2" : "P1") + " Joystick disconnected", true);
}
}
if (joystick1 != null) {
ok = joystick1.update();
if (ok) update(joystick1, joy1State, joy1Prefs, swappedMode);
else {
joystick1 = null;
videoMonitor.showOSD((p1ControlsMode ? "P1" : "P2") + " Joystick disconnected", true);
}
}
if (joystick0 == null && joystick1 == null) stop();
}
private void update(Joystick joystick, State joyState, Preferences joyPrefs, boolean player0) {
// Paddle Analog
if (paddleMode && joyPrefs.paddleSens != 0) {
float pos = joystick.getAxisNoThreshold(joyPrefs.paddleAxis) * joyPrefs.paddleAxisSignal;
int newPosition = (int) (pos * joyPrefs.paddleSens + joyPrefs.paddleCenter);
if (newPosition < 0) newPosition = 0;
else if (newPosition > 380) newPosition = 380;
if (newPosition != joyState.xPosition) {
joyState.xPosition = newPosition;
consoleControlsSocket.controlStateChanged(player0 ? Control.PADDLE0_POSITION : Control.PADDLE1_POSITION, newPosition);
}
}
// Joystick direction (Analog or POV) and Paddle Digital (Analog or POV)
int newCardinal = Joystick.CENTER;
if (!paddleMode || joyPrefs.paddleSens == 0)
newCardinal = joystick.getAxisDirectionCardinal(joyPrefs.xAxis, joyPrefs.xAxisSignal, joyPrefs.yAxis, joyPrefs.yAxisSignal);
if (newCardinal == Joystick.CENTER && joystick.getInfo().pov)
newCardinal = joystick.getPOVDirectionCardinal();
if (newCardinal != joyState.cardinal) {
boolean newUP = newCardinal == Joystick.NORTHWEST || newCardinal == Joystick.NORTH || newCardinal == Joystick.NORTHEAST;
boolean newRIGHT = newCardinal == Joystick.NORTHEAST || newCardinal == Joystick.EAST || newCardinal == Joystick.SOUTHEAST;
boolean newDOWN = newCardinal == Joystick.SOUTHEAST || newCardinal == Joystick.SOUTH || newCardinal == Joystick.SOUTHWEST;
boolean newLEFT = newCardinal == Joystick.SOUTHWEST || newCardinal == Joystick.WEST || newCardinal == Joystick.NORTHWEST;
if (player0) {
awtControls.processKeyEvent(Parameters.KEY_P0_UP, 0, newUP);
awtControls.processKeyEvent(Parameters.KEY_P0_RIGHT, 0, newRIGHT);
awtControls.processKeyEvent(Parameters.KEY_P0_DOWN, 0, newDOWN);
awtControls.processKeyEvent(Parameters.KEY_P0_LEFT, 0, newLEFT);
} else {
awtControls.processKeyEvent(Parameters.KEY_P1_UP, 0, newUP);
awtControls.processKeyEvent(Parameters.KEY_P1_RIGHT, 0, newRIGHT);
awtControls.processKeyEvent(Parameters.KEY_P1_DOWN, 0, newDOWN);
awtControls.processKeyEvent(Parameters.KEY_P1_LEFT, 0, newLEFT);
}
joyState.cardinal = newCardinal;
}
// Joystick button
if (joyButtonDetection == joystick)
detectButton();
else {
boolean newButton = joystick.getButton(joyPrefs.button) || joystick.getButton(joyPrefs.button2);
if (newButton != joyState.button) {
awtControls.processKeyEvent(player0 ? Parameters.KEY_P0_BUTTON : Parameters.KEY_P1_BUTTON, 0, newButton);
joyState.button = newButton;
}
}
// Other Console controls
boolean newSelect = joystick.getButton(joyPrefs.select);
if (newSelect != joyState.select) {
awtControls.processKeyEvent(AWTConsoleControls.KEY_SELECT, 0, newSelect);
joyState.select = newSelect;
}
boolean newReset = joystick.getButton(joyPrefs.reset);
if (newReset != joyState.reset) {
awtControls.processKeyEvent(AWTConsoleControls.KEY_RESET, 0, newReset);
joyState.reset = newReset;
}
boolean newPause = joystick.getButton(joyPrefs.pause);
if (newPause != joyState.pause) {
awtControls.processKeyEvent(AWTConsoleControls.KEY_PAUSE, KeyEvent.ALT_DOWN_MASK, newPause);
joyState.pause = newPause;
}
boolean newFastSpeed = joystick.getButton(joyPrefs.fastSpeed);
if (newFastSpeed != joyState.fastSpeed) {
awtControls.processKeyEvent(AWTConsoleControls.KEY_FAST_SPEED, 0, newFastSpeed);
joyState.fastSpeed = newFastSpeed;
}
}
private void detectButton() {
int buttonsNewState = joyButtonDetection.getButtons();
if (joyButtonDetectionLastState == -1) {
joyButtonDetectionLastState = buttonsNewState;
return;
}
if (joyButtonDetectionLastState == buttonsNewState) return;
for (int b = 0; b < joyButtonDetection.getInfo().buttons; b++) {
boolean newB = (buttonsNewState & (1 << b)) != 0;
if (newB && (joyButtonDetectionLastState & (1 << b)) == 0) { // Button has been pressed
int j = joyButtonDetection == joystick0 ? 0 : 1;
JoystickButtonDetectionListener list = joyButtonDetectionListener;
stopButtonDetection();
list.joystickButtonDetected(j, b);
return;
}
}
}
private synchronized void updaterActive() {
started = true;
if (stateUpdater != null && stateUpdater.isAlive()) return; // All set
stateUpdater = new StateUpdater();
stateUpdater.start();
}
private synchronized void updaterInactive() {
started = false;
if (stateUpdater == null) return; // All set
if (stateUpdater.isAlive())
try { stateUpdater.join(1000); } catch (InterruptedException e) {}
stateUpdater = null;
}
public boolean deviceListFailed = false;
public boolean p1ControlsMode = false;
public boolean swappedMode = false;
public boolean paddleMode = false;
private final AWTConsoleControls awtControls;
private VideoMonitor videoMonitor;
private ConsoleControlsSocket consoleControlsSocket;
private List<Info> devices = new Vector<Info>();
private Vector<DeviceOption> joystick0DeviceOptions = new Vector<DeviceOption>();
private Vector<DeviceOption> joystick1DeviceOptions = new Vector<DeviceOption>();
private DeviceOption joystick0DeviceOption, joystick1DeviceOption;
private Joystick joystick0, joystick1;
private State joy0State, joy1State;
private Preferences joy0Prefs, joy1Prefs;
private boolean started = false;
private StateUpdater stateUpdater;
private Joystick joyButtonDetection = null;
private int joyButtonDetectionLastState = -1;
private JoystickButtonDetectionListener joyButtonDetectionListener = null;
private static int JOYSTICK_UPDATE_SLEEP = 1000 / Parameters.JOYSTICK_UPDATE_RATE;
public interface JoystickButtonDetectionListener {
public void joystickButtonDetected(int joystick, int button);
}
public static class DeviceOption {
public int device;
public Info info;
public DeviceOption(int device, Info info) {
this.device = device;
this.info = info;
}
@Override
public String toString() {
return device == Parameters.JOY_DEVICE_NONE ? "None" :
device == Parameters.JOY_DEVICE_AUTO ? "Auto" + ": " + (info != null ? info : "None" ):
info.toString();
}
}
private static class State {
int cardinal = Joystick.CENTER;
boolean button, select, reset, fastSpeed, pause = false;
int xPosition = -1;
}
private static class Preferences {
public int device, xAxis, xAxisSignal, yAxis, yAxisSignal, paddleAxis, paddleAxisSignal, button, button2;
public int select, reset, pause, fastSpeed, deadzone, paddleCenter, paddleSens;
}
private class StateUpdater extends Thread {
public StateUpdater() {
super("JoystickControls Updater");
}
@Override
public void run() {
while(started) {
update();
try { sleep(JOYSTICK_UPDATE_SLEEP); } catch (InterruptedException e) {}
}
}
}
}