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