RemoteInput.java revision 0124e6dde9484f4cbc804ba0bd52ff308cf746b6
1package com.badlogic.gdx.input; 2 3import java.io.DataInputStream; 4import java.io.IOException; 5import java.net.InetAddress; 6import java.net.ServerSocket; 7import java.net.Socket; 8import java.util.HashSet; 9import java.util.Set; 10 11import com.badlogic.gdx.Gdx; 12import com.badlogic.gdx.Input; 13import com.badlogic.gdx.InputProcessor; 14import com.badlogic.gdx.utils.GdxRuntimeException; 15 16/** 17 * <p>An {@link Input} implementation that receives touch, key, accelerometer 18 * and compass events from a remote Android device. Just instantiate it 19 * and specify the port it should listen on for incoming connections (default 20 * 8190). Then store the new RemoteInput instance in Gdx.input. That's it.</p> 21 * 22 * <p>On your Android device you can use the gdx-remote application available 23 * on the Google Code page as an APK or in SVN (extensions/gdx-remote). Open it, 24 * specify the IP address and the port of the PC your libgdx app is running on 25 * and then tap away.</p> 26 * 27 * <p>The touch coordinates will be translated to the desktop window's coordinate 28 * system, no matter the orientation of the device</p> 29 * 30 * @author mzechner 31 * 32 */ 33public class RemoteInput implements Runnable, Input { 34 class KeyEvent { 35 static final int KEY_DOWN = 0; 36 static final int KEY_UP = 1; 37 static final int KEY_TYPED = 2; 38 39 long timeStamp; 40 int type; 41 int keyCode; 42 char keyChar; 43 } 44 45 class TouchEvent { 46 static final int TOUCH_DOWN = 0; 47 static final int TOUCH_UP = 1; 48 static final int TOUCH_DRAGGED = 2; 49 50 long timeStamp; 51 int type; 52 int x; 53 int y; 54 int pointer; 55 } 56 57 class EventTrigger implements Runnable { 58 TouchEvent touchEvent; 59 KeyEvent keyEvent; 60 61 public EventTrigger(TouchEvent touchEvent, KeyEvent keyEvent) { 62 this.touchEvent = touchEvent; 63 this.keyEvent = keyEvent; 64 } 65 66 @Override public void run () { 67 justTouched = false; 68 if(processor != null) { 69 if(touchEvent != null) { 70 touchX[touchEvent.pointer] = touchEvent.x; 71 touchY[touchEvent.pointer] = touchEvent.y; 72 switch(touchEvent.type) { 73 case TouchEvent.TOUCH_DOWN: 74 processor.touchDown(touchEvent.x, touchEvent.y, touchEvent.pointer, Input.Buttons.LEFT); 75 isTouched[touchEvent.pointer] = true; 76 justTouched = true; 77 break; 78 case TouchEvent.TOUCH_UP: 79 processor.touchUp(touchEvent.x, touchEvent.y, touchEvent.pointer, Input.Buttons.LEFT); 80 isTouched[touchEvent.pointer] = false; 81 break; 82 case TouchEvent.TOUCH_DRAGGED: 83 processor.touchDragged(touchEvent.x, touchEvent.y, touchEvent.pointer); 84 break; 85 } 86 } 87 if(keyEvent != null) { 88 switch(keyEvent.type) { 89 case KeyEvent.KEY_DOWN: 90 processor.keyDown(keyEvent.keyCode); 91 keys.add(keyEvent.keyCode); 92 break; 93 case KeyEvent.KEY_UP: 94 processor.keyUp(keyEvent.keyCode); 95 keys.remove(keyEvent.keyCode); 96 break; 97 case KeyEvent.KEY_TYPED: 98 processor.keyTyped(keyEvent.keyChar); 99 break; 100 } 101 } 102 } else { 103 if(touchEvent != null) { 104 touchX[touchEvent.pointer] = touchEvent.x; 105 touchY[touchEvent.pointer] = touchEvent.y; 106 if(touchEvent.type == TouchEvent.TOUCH_DOWN) { 107 isTouched[touchEvent.pointer] = true; 108 justTouched = true; 109 } 110 if(touchEvent.type == TouchEvent.TOUCH_UP) { 111 isTouched[touchEvent.pointer] = false; 112 } 113 } 114 if(keyEvent != null) { 115 if(keyEvent.type == KeyEvent.KEY_DOWN) keys.add(keyEvent.keyCode); 116 if(keyEvent.type == KeyEvent.KEY_UP) keys.remove(keyEvent.keyCode); 117 } 118 } 119 } 120 } 121 122 public static int DEFAULT_PORT = 8190; 123 private ServerSocket serverSocket; 124 private float[] accel = new float[3]; 125 private float[] compass = new float[3]; 126 private boolean multiTouch = false; 127 private float remoteWidth = 0; 128 private float remoteHeight = 0; 129 private Set<Integer> keys = new HashSet<Integer>(); 130 private int[] touchX = new int[20]; 131 private int[] touchY = new int[20]; 132 private boolean isTouched[] = new boolean[20]; 133 private boolean justTouched = false; 134 private InputProcessor processor = null; 135 private final int port; 136 public final String[] ips; 137 138 public RemoteInput() { 139 this(DEFAULT_PORT); 140 } 141 142 public RemoteInput(int port) { 143 try { 144 this.port = port; 145 serverSocket = new ServerSocket(port); 146 Thread thread = new Thread(this); 147 thread.setDaemon(true); 148 thread.start(); 149 InetAddress[] allByName = InetAddress.getAllByName(InetAddress.getLocalHost().getHostName()); 150 ips = new String[allByName.length]; 151 for(int i = 0; i < allByName.length; i++) { 152 ips[i] = allByName[i].getHostAddress(); 153 } 154 } catch(Exception e) { 155 throw new GdxRuntimeException("Couldn't open listening socket at port '" + port+ "'", e); 156 } 157 } 158 159 @Override public void run () { 160 while(true) { 161 try { 162 System.out.println("listening, port " + port); 163 Socket socket = null; 164 while(true) { 165 socket = serverSocket.accept(); 166 break; 167 } 168 socket.setTcpNoDelay(true); 169 socket.setSoTimeout(3000); 170 171 DataInputStream in = new DataInputStream(socket.getInputStream()); 172 multiTouch = in.readBoolean(); 173 while(true) { 174 int event = in.readInt(); 175 KeyEvent keyEvent = null; 176 TouchEvent touchEvent = null; 177 switch(event) { 178 case RemoteSender.ACCEL: 179 accel[0] = in.readFloat(); 180 accel[1] = in.readFloat(); 181 accel[2] = in.readFloat(); 182 break; 183 case RemoteSender.COMPASS: 184 compass[0] = in.readFloat(); 185 compass[1] = in.readFloat(); 186 compass[2] = in.readFloat(); 187 break; 188 case RemoteSender.SIZE: 189 remoteWidth = in.readFloat(); 190 remoteHeight = in.readFloat(); 191 break; 192 case RemoteSender.KEY_DOWN: 193 keyEvent = new KeyEvent(); 194 keyEvent.keyCode = in.readInt(); 195 keyEvent.type = KeyEvent.KEY_DOWN; 196 break; 197 case RemoteSender.KEY_UP: 198 keyEvent = new KeyEvent(); 199 keyEvent.keyCode = in.readInt(); 200 keyEvent.type = KeyEvent.KEY_UP; 201 break; 202 case RemoteSender.KEY_TYPED: 203 keyEvent = new KeyEvent(); 204 keyEvent.keyChar = in.readChar(); 205 keyEvent.type = KeyEvent.KEY_TYPED; 206 break; 207 case RemoteSender.TOUCH_DOWN: 208 touchEvent = new TouchEvent(); 209 touchEvent.x = (int)((in.readInt() / remoteWidth) * Gdx.graphics.getWidth()); 210 touchEvent.y = (int)((in.readInt() / remoteHeight) * Gdx.graphics.getHeight()); 211 touchEvent.pointer = in.readInt(); 212 touchEvent.type = TouchEvent.TOUCH_DOWN; 213 break; 214 case RemoteSender.TOUCH_UP: 215 touchEvent = new TouchEvent(); 216 touchEvent.x = (int)((in.readInt() / remoteWidth) * Gdx.graphics.getWidth()); 217 touchEvent.y = (int)((in.readInt() / remoteHeight) * Gdx.graphics.getHeight()); 218 touchEvent.pointer = in.readInt(); 219 touchEvent.type = TouchEvent.TOUCH_UP; 220 break; 221 case RemoteSender.TOUCH_DRAGGED: 222 touchEvent = new TouchEvent(); 223 touchEvent.x = (int)((in.readInt() / remoteWidth) * Gdx.graphics.getWidth()); 224 touchEvent.y = (int)((in.readInt() / remoteHeight) * Gdx.graphics.getHeight()); 225 touchEvent.pointer = in.readInt(); 226 touchEvent.type = TouchEvent.TOUCH_DRAGGED; 227 break; 228 } 229 230 Gdx.app.postRunnable(new EventTrigger(touchEvent, keyEvent)); 231 } 232 } catch (IOException e) { 233 e.printStackTrace(); 234 } 235 } 236 } 237 238 @Override public boolean isAccelerometerAvailable () { 239 return true; 240 } 241 242 @Override public float getAccelerometerX () { 243 return accel[0]; 244 } 245 246 @Override public float getAccelerometerY () { 247 return accel[1]; 248 } 249 250 @Override public float getAccelerometerZ () { 251 return accel[2]; 252 } 253 254 @Override public int getX () { 255 return touchX[0]; 256 } 257 258 @Override public int getX (int pointer) { 259 return touchX[pointer]; 260 } 261 262 @Override public int getY () { 263 return touchY[0]; 264 } 265 266 @Override public int getY (int pointer) { 267 return touchY[pointer]; 268 } 269 270 @Override public boolean isTouched () { 271 return isTouched[0]; 272 } 273 274 @Override public boolean justTouched () { 275 return justTouched; 276 } 277 278 @Override public boolean isTouched (int pointer) { 279 return isTouched[pointer]; 280 } 281 282 @Override public boolean isButtonPressed (int button) { 283 if(button != Buttons.LEFT) return false; 284 for(int i = 0; i < isTouched.length; i++) 285 if(isTouched[i]) return true; 286 return false; 287 } 288 289 @Override public boolean isKeyPressed (int key) { 290 return keys.contains(key); 291 } 292 293 @Override public void getTextInput (TextInputListener listener, String title, String text) { 294 Gdx.app.getInput().getTextInput(listener, title, text); 295 } 296 297 @Override public void setOnscreenKeyboardVisible (boolean visible) { 298 } 299 300 @Override public boolean supportsOnscreenKeyboard () { 301 return false; 302 } 303 304 @Override public boolean supportsMultitouch () { 305 return multiTouch; 306 } 307 308 @Override public boolean supportsVibrator () { 309 return true; 310 } 311 312 @Override public void vibrate (int milliseconds) { 313 314 } 315 316 @Override public void vibrate (long[] pattern, int repeat) { 317 318 } 319 320 @Override public void cancelVibrate () { 321 322 } 323 324 @Override public boolean supportsCompass () { 325 return true; 326 } 327 328 @Override public float getAzimuth () { 329 return compass[0]; 330 } 331 332 @Override public float getPitch () { 333 return compass[1]; 334 } 335 336 @Override public float getRoll () { 337 return compass[2]; 338 } 339 340 @Override public void setCatchBackKey (boolean catchBack) { 341 342 } 343 344 @Override public void setInputProcessor (InputProcessor processor) { 345 this.processor = processor; 346 } 347 348 /** 349 * @return the IP addresses {@link RemoteSender} or gdx-remote should connect to. Most likely the LAN addresses if behind a NAT. 350 */ 351 public String[] getIPs() { 352 return ips; 353 } 354} 355