/**
* Copyright (C) 2013- Iordan Iordanov
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
package com.undatech.opaque.input;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Handler;
import android.view.InputDevice;
import android.view.KeyEvent;
import com.undatech.opaque.RemoteCanvas;
import com.undatech.opaque.SpiceCommunicator;
public class RemoteSpicePointer implements RemotePointer {
private static final String TAG = "RemoteSpicePointer";
public static final int SPICE_MOUSE_BUTTON_MOVE = 0;
public static final int SPICE_MOUSE_BUTTON_LEFT = 1;
public static final int SPICE_MOUSE_BUTTON_MIDDLE = 2;
public static final int SPICE_MOUSE_BUTTON_RIGHT = 3;
public static final int SPICE_MOUSE_BUTTON_UP = 4;
public static final int SPICE_MOUSE_BUTTON_DOWN = 5;
public static final int POINTER_DOWN_MASK = 0x8000;
private int prevPointerMask = 0;
/**
* Current state of "mouse" buttons
*/
private int pointerMask = 0;
private RemoteCanvas canvas;
private Context context;
private Handler handler;
private SpiceCommunicator spicecomm;
/**
* Indicates where the mouse pointer is located.
*/
public int pointerX, pointerY;
public RemoteSpicePointer (SpiceCommunicator spicecomm, RemoteCanvas canvas, Handler handler) {
this.spicecomm = spicecomm;
this.canvas = canvas;
this.context = canvas.getContext();
this.handler = handler;
pointerX = canvas.getDesktopWidth() /2;
pointerY = canvas.getDesktopHeight()/2;
}
protected boolean shouldBeRightClick (KeyEvent e) {
boolean result = false;
int keyCode = e.getKeyCode();
// If the camera button is pressed
if (keyCode == KeyEvent.KEYCODE_CAMERA) {
result = true;
// Or the back button is pressed
} else if (keyCode == KeyEvent.KEYCODE_BACK) {
// Determine SDK
boolean preGingerBread = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD;
// Whether the source is a mouse (getSource() is not available pre-Gingerbread)
boolean mouseSource = (!preGingerBread && e.getSource() == InputDevice.SOURCE_MOUSE);
// Whether the device has a qwerty keyboard
boolean noQwertyKbd = (context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_QWERTY);
// Whether the device is pre-Gingerbread or the event came from the "hard buttons"
boolean fromVirtualHardKey = preGingerBread || (e.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0;
if (mouseSource || noQwertyKbd || fromVirtualHardKey) {
result = true;
}
}
return result;
}
public int getX() {
return pointerX;
}
public int getY() {
return pointerY;
}
public void setX(int newX) {
pointerX = newX;
}
public void setY(int newY) {
pointerY = newY;
}
/**
* Move mouse pointer to specified coordinates.
*/
public void movePointer(int x, int y) {
canvas.reDrawRemotePointer();
pointerX=x;
pointerY=y;
canvas.reDrawRemotePointer();
moveMouseButtonUp (x, y, 0);
}
/**
* If necessary move the pointer to be visible.
*/
public void movePointerToMakeVisible() {
if (canvas.getMouseFollowPan()) {
int absX = canvas.getAbsX();
int absY = canvas.getAbsY();
int vW = canvas.getVisibleDesktopWidth();
int vH = canvas.getVisibleDesktopHeight();
if (pointerX < absX || pointerX >= absX + vW ||
pointerY < absY || pointerY >= absY + vH) {
movePointer(absX + vW / 2, absY + vH / 2);
}
}
}
/**
* Handles any hardware buttons designated to perform mouse events.
*/
public boolean hardwareButtonsAsMouseEvents(int keyCode, KeyEvent e, int combinedMetastate) {
boolean used = false;
boolean down = (e.getAction() == KeyEvent.ACTION_DOWN) ||
(e.getAction() == KeyEvent.ACTION_MULTIPLE);
if (down)
pointerMask = POINTER_DOWN_MASK;
else
pointerMask = 0;
if (shouldBeRightClick(e)) {
pointerMask |= RemoteSpicePointer.SPICE_MOUSE_BUTTON_RIGHT;
spicecomm.sendPointerEvent(getX(), getY(), combinedMetastate, pointerMask);
used = true;
}
return used;
}
@Override
public void leftButtonDown(int x, int y, int metaState) {
pointerMask = SPICE_MOUSE_BUTTON_LEFT | POINTER_DOWN_MASK;
sendPointerEvent (x, y, metaState, false);
}
@Override
public void middleButtonDown(int x, int y, int metaState) {
pointerMask = SPICE_MOUSE_BUTTON_MIDDLE | POINTER_DOWN_MASK;
sendPointerEvent (x, y, metaState, false);
}
@Override
public void rightButtonDown(int x, int y, int metaState) {
pointerMask = SPICE_MOUSE_BUTTON_RIGHT | POINTER_DOWN_MASK;
sendPointerEvent (x, y, metaState, false);
}
@Override
public void scrollUp(int x, int y, int metaState) {
pointerMask = SPICE_MOUSE_BUTTON_UP | POINTER_DOWN_MASK;
sendPointerEvent (x, y, metaState, false);
}
@Override
public void scrollDown(int x, int y, int metaState) {
pointerMask = SPICE_MOUSE_BUTTON_DOWN | POINTER_DOWN_MASK;
sendPointerEvent (x, y, metaState, false);
}
@Override
public void scrollLeft(int x, int y, int metaState) {
// TODO: Protocol does not support scrolling left/right yet.
}
@Override
public void scrollRight(int x, int y, int metaState) {
// TODO: Protocol does not support scrolling left/right yet.
}
@Override
public void moveMouseButtonDown (int x, int y, int metaState) {
pointerMask = SPICE_MOUSE_BUTTON_MOVE | POINTER_DOWN_MASK;
sendPointerEvent (x, y, metaState, true);
}
@Override
public void moveMouseButtonUp (int x, int y, int metaState) {
pointerMask = SPICE_MOUSE_BUTTON_MOVE;
sendPointerEvent (x, y, metaState, true);
}
@Override
public void releaseButton(int x, int y, int metaState) {
pointerMask = prevPointerMask & ~POINTER_DOWN_MASK;
prevPointerMask = 0;
sendPointerEvent (x, y, metaState, false);
}
/**
* Sends a pointer event to the server.
* @param x
* @param y
* @param metaState
* @param isMoving
*/
private void sendPointerEvent(int x, int y, int metaState, boolean isMoving) {
int combinedMetaState = metaState|canvas.getKeyboard().getMetaState();
// Save the previous pointer mask other than action_move, so we can
// send it with the pointer flag "not down" to clear the action.
if (!isMoving) {
// If this is a new mouse down event, release previous button pressed to avoid confusing the remote OS.
if (prevPointerMask != 0 && prevPointerMask != pointerMask) {
spicecomm.sendPointerEvent(pointerX, pointerY,
combinedMetaState,
prevPointerMask & ~POINTER_DOWN_MASK);
}
prevPointerMask = pointerMask;
}
canvas.reDrawRemotePointer();
pointerX = x;
pointerY = y;
// Do not let mouse pointer leave the bounds of the desktop.
if ( pointerX < 0) {
pointerX = 0;
} else if ( pointerX >= canvas.getDesktopWidth()) {
pointerX = spicecomm.framebufferWidth() - 1;
}
if ( pointerY < 0) {
pointerY=0;
} else if ( pointerY >= canvas.getDesktopHeight()) {
pointerY = spicecomm.framebufferHeight() - 1;
}
canvas.reDrawRemotePointer();
spicecomm.sendPointerEvent(pointerX, pointerY, combinedMetaState, pointerMask);
}
}