Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

/*******************************************************************************
 * 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

        }
}