/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
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;
/** <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*
* <p>
* The touch coordinates will be translated to the desktop window's coordinate system, no matter the orientation of the device
* </p>
*
* @author mzechner */
public class RemoteInput
implements Runnable, Input
{
public interface RemoteInputListener
{
void onConnected
();
void onDisconnected
();
}
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 boolean connected =
false;
private RemoteInputListener listener
;
Set<Integer> keys =
new HashSet<Integer>();
int[] touchX =
new int[20];
int[] touchY =
new int[20];
boolean isTouched
[] =
new boolean[20];
boolean justTouched =
false;
InputProcessor processor =
null;
private final int port
;
public final String[] ips
;
public RemoteInput
() {
this(DEFAULT_PORT
);
}
public RemoteInput
(RemoteInputListener listener
) {
this(DEFAULT_PORT, listener
);
}
public RemoteInput
(int port
) {
this(port,
null);
}
public RemoteInput
(int port, RemoteInputListener listener
) {
this.
listener = listener
;
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 {
connected =
false;
if (listener
!=
null)
listener.
onDisconnected();
System.
out.
println("listening, port " + port
);
Socket socket =
null;
socket = serverSocket.
accept();
socket.
setTcpNoDelay(true);
socket.
setSoTimeout(3000);
connected =
true;
if (listener
!=
null)
listener.
onConnected();
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();
}
}
}
public boolean isConnected
() {
return connected
;
}
@
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 getPlaceholderTextInput
(TextInputListener listener,
String title,
String placeholder
) {
Gdx.
app.
getInput().
getPlaceholderTextInput(listener, title, placeholder
);
}
@
Override
public void setOnscreenKeyboardVisible
(boolean visible
) {
}
@
Override
public void vibrate
(int milliseconds
) {
}
@
Override
public void vibrate
(long[] pattern,
int repeat
) {
}
@
Override
public void cancelVibrate
() {
}
@
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
;
}
@
Override
public InputProcessor getInputProcessor
() {
return this.
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
;
}
@
Override
public boolean isPeripheralAvailable
(Peripheral peripheral
) {
if (peripheral == Peripheral.
Accelerometer) return true;
if (peripheral == Peripheral.
Compass) return true;
if (peripheral == Peripheral.
MultitouchScreen) return multiTouch
;
return false;
}
@
Override
public int getRotation
() {
return 0;
}
@
Override
public Orientation getNativeOrientation
() {
return Orientation.
Landscape;
}
@
Override
public void setCursorCatched
(boolean catched
) {
}
@
Override
public boolean isCursorCatched
() {
return false;
}
@
Override
public int getDeltaX
() {
// TODO Auto-generated method stub
return 0;
}
@
Override
public int getDeltaX
(int pointer
) {
return 0;
}
@
Override
public int getDeltaY
() {
return 0;
}
@
Override
public int getDeltaY
(int pointer
) {
return 0;
}
@
Override
public void setCursorPosition
(int x,
int y
) {
}
@
Override
public void setCatchMenuKey
(boolean catchMenu
) {
// TODO Auto-generated method stub
}
@
Override
public long getCurrentEventTime
() {
// TODO Auto-generated method stub
return 0;
}
@
Override
public void getRotationMatrix
(float[] matrix
) {
// TODO Auto-generated method stub
}
}