package com.badlogic.gdx.input;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.Set;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.utils.GdxRuntimeException;
/**
*
An {@link Input} implementation that receives touch, key, accelerometer
* and compass events from a remote Android device. Just instantiate it
* and specify the port it should listen on for incoming connections (default
* 8190). Then store the new RemoteInput instance in Gdx.input. That's it.
*
* On your Android device you can use the gdx-remote application available
* on the Google Code page as an APK or in SVN (extensions/gdx-remote). Open it,
* specify the IP address and the port of the PC your libgdx app is running on
* and then tap away.
*
* The touch coordinates will be translated to the desktop window's coordinate
* system, no matter the orientation of the device
*
* @author mzechner
*
*/
public class RemoteInput implements Runnable, Input {
class KeyEvent {
static final int KEY_DOWN = 0;
static final int KEY_UP = 1;
static final int KEY_TYPED = 2;
long timeStamp;
int type;
int keyCode;
char keyChar;
}
class TouchEvent {
static final int TOUCH_DOWN = 0;
static final int TOUCH_UP = 1;
static final int TOUCH_DRAGGED = 2;
long timeStamp;
int type;
int x;
int y;
int pointer;
}
class EventTrigger implements Runnable {
TouchEvent touchEvent;
KeyEvent keyEvent;
public EventTrigger(TouchEvent touchEvent, KeyEvent keyEvent) {
this.touchEvent = touchEvent;
this.keyEvent = keyEvent;
}
@Override public void run () {
justTouched = false;
if(processor != null) {
if(touchEvent != null) {
touchX[touchEvent.pointer] = touchEvent.x;
touchY[touchEvent.pointer] = touchEvent.y;
switch(touchEvent.type) {
case TouchEvent.TOUCH_DOWN:
processor.touchDown(touchEvent.x, touchEvent.y, touchEvent.pointer, Input.Buttons.LEFT);
isTouched[touchEvent.pointer] = true;
justTouched = true;
break;
case TouchEvent.TOUCH_UP:
processor.touchUp(touchEvent.x, touchEvent.y, touchEvent.pointer, Input.Buttons.LEFT);
isTouched[touchEvent.pointer] = false;
break;
case TouchEvent.TOUCH_DRAGGED:
processor.touchDragged(touchEvent.x, touchEvent.y, touchEvent.pointer);
break;
}
}
if(keyEvent != null) {
switch(keyEvent.type) {
case KeyEvent.KEY_DOWN:
processor.keyDown(keyEvent.keyCode);
keys.add(keyEvent.keyCode);
break;
case KeyEvent.KEY_UP:
processor.keyUp(keyEvent.keyCode);
keys.remove(keyEvent.keyCode);
break;
case KeyEvent.KEY_TYPED:
processor.keyTyped(keyEvent.keyChar);
break;
}
}
} else {
if(touchEvent != null) {
touchX[touchEvent.pointer] = touchEvent.x;
touchY[touchEvent.pointer] = touchEvent.y;
if(touchEvent.type == TouchEvent.TOUCH_DOWN) {
isTouched[touchEvent.pointer] = true;
justTouched = true;
}
if(touchEvent.type == TouchEvent.TOUCH_UP) {
isTouched[touchEvent.pointer] = false;
}
}
if(keyEvent != null) {
if(keyEvent.type == KeyEvent.KEY_DOWN) keys.add(keyEvent.keyCode);
if(keyEvent.type == KeyEvent.KEY_UP) keys.remove(keyEvent.keyCode);
}
}
}
}
public static int DEFAULT_PORT = 8190;
private ServerSocket serverSocket;
private float[] accel = new float[3];
private float[] compass = new float[3];
private boolean multiTouch = false;
private float remoteWidth = 0;
private float remoteHeight = 0;
private Set keys = new HashSet();
private int[] touchX = new int[20];
private int[] touchY = new int[20];
private boolean isTouched[] = new boolean[20];
private boolean justTouched = false;
private InputProcessor processor = null;
private final int port;
public final String[] ips;
public RemoteInput() {
this(DEFAULT_PORT);
}
public RemoteInput(int port) {
try {
this.port = port;
serverSocket = new ServerSocket(port);
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.start();
InetAddress[] allByName = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName());
ips = new String[allByName.length];
for(int i = 0; i < allByName.length; i++) {
ips[i] = allByName[i].getHostAddress();
}
} catch(Exception e) {
throw new GdxRuntimeException("Couldn't open listening socket at port '" + port+ "'", e);
}
}
@Override public void run () {
while(true) {
try {
System.out.println("listening, port " + port);
Socket socket = null;
while(true) {
socket = serverSocket.accept();
break;
}
socket.setTcpNoDelay(true);
socket.setSoTimeout(3000);
DataInputStream in = new DataInputStream(socket.getInputStream());
multiTouch = in.readBoolean();
while(true) {
int event = in.readInt();
KeyEvent keyEvent = null;
TouchEvent touchEvent = null;
switch(event) {
case RemoteSender.ACCEL:
accel[0] = in.readFloat();
accel[1] = in.readFloat();
accel[2] = in.readFloat();
break;
case RemoteSender.COMPASS:
compass[0] = in.readFloat();
compass[1] = in.readFloat();
compass[2] = in.readFloat();
break;
case RemoteSender.SIZE:
remoteWidth = in.readFloat();
remoteHeight = in.readFloat();
break;
case RemoteSender.KEY_DOWN:
keyEvent = new KeyEvent();
keyEvent.keyCode = in.readInt();
keyEvent.type = KeyEvent.KEY_DOWN;
break;
case RemoteSender.KEY_UP:
keyEvent = new KeyEvent();
keyEvent.keyCode = in.readInt();
keyEvent.type = KeyEvent.KEY_UP;
break;
case RemoteSender.KEY_TYPED:
keyEvent = new KeyEvent();
keyEvent.keyChar = in.readChar();
keyEvent.type = KeyEvent.KEY_TYPED;
break;
case RemoteSender.TOUCH_DOWN:
touchEvent = new TouchEvent();
touchEvent.x = (int)((in.readInt() / remoteWidth) * Gdx.graphics.getWidth());
touchEvent.y = (int)((in.readInt() / remoteHeight) * Gdx.graphics.getHeight());
touchEvent.pointer = in.readInt();
touchEvent.type = TouchEvent.TOUCH_DOWN;
break;
case RemoteSender.TOUCH_UP:
touchEvent = new TouchEvent();
touchEvent.x = (int)((in.readInt() / remoteWidth) * Gdx.graphics.getWidth());
touchEvent.y = (int)((in.readInt() / remoteHeight) * Gdx.graphics.getHeight());
touchEvent.pointer = in.readInt();
touchEvent.type = TouchEvent.TOUCH_UP;
break;
case RemoteSender.TOUCH_DRAGGED:
touchEvent = new TouchEvent();
touchEvent.x = (int)((in.readInt() / remoteWidth) * Gdx.graphics.getWidth());
touchEvent.y = (int)((in.readInt() / remoteHeight) * Gdx.graphics.getHeight());
touchEvent.pointer = in.readInt();
touchEvent.type = TouchEvent.TOUCH_DRAGGED;
break;
}
Gdx.app.postRunnable(new EventTrigger(touchEvent, keyEvent));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override public boolean isAccelerometerAvailable () {
return true;
}
@Override public float getAccelerometerX () {
return accel[0];
}
@Override public float getAccelerometerY () {
return accel[1];
}
@Override public float getAccelerometerZ () {
return accel[2];
}
@Override public int getX () {
return touchX[0];
}
@Override public int getX (int pointer) {
return touchX[pointer];
}
@Override public int getY () {
return touchY[0];
}
@Override public int getY (int pointer) {
return touchY[pointer];
}
@Override public boolean isTouched () {
return isTouched[0];
}
@Override public boolean justTouched () {
return justTouched;
}
@Override public boolean isTouched (int pointer) {
return isTouched[pointer];
}
@Override public boolean isButtonPressed (int button) {
if(button != Buttons.LEFT) return false;
for(int i = 0; i < isTouched.length; i++)
if(isTouched[i]) return true;
return false;
}
@Override public boolean isKeyPressed (int key) {
return keys.contains(key);
}
@Override public void getTextInput (TextInputListener listener, String title, String text) {
Gdx.app.getInput().getTextInput(listener, title, text);
}
@Override public void setOnscreenKeyboardVisible (boolean visible) {
}
@Override public boolean supportsOnscreenKeyboard () {
return false;
}
@Override public boolean supportsMultitouch () {
return multiTouch;
}
@Override public boolean supportsVibrator () {
return true;
}
@Override public void vibrate (int milliseconds) {
}
@Override public void vibrate (long[] pattern, int repeat) {
}
@Override public void cancelVibrate () {
}
@Override public boolean supportsCompass () {
return true;
}
@Override public float getAzimuth () {
return compass[0];
}
@Override public float getPitch () {
return compass[1];
}
@Override public float getRoll () {
return compass[2];
}
@Override public void setCatchBackKey (boolean catchBack) {
}
@Override public void setInputProcessor (InputProcessor processor) {
this.processor = processor;
}
/**
* @return the IP addresses {@link RemoteSender} or gdx-remote should connect to. Most likely the LAN addresses if behind a NAT.
*/
public String[] getIPs() {
return ips;
}
}