package com.iiordanov.bVNC.input;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.MotionEvent;
import com.iiordanov.bVNC.RfbConnectable;
import com.iiordanov.bVNC.RemoteCanvas;
import com.iiordanov.bVNC.input.RemoteVncPointer.MouseScrollRunnable;
public class RemoteRdpPointer extends RemotePointer {
private static final String TAG = "RemoteRdpPointer";
private final static int PTRFLAGS_WHEEL = 0x0200;
private final static int PTRFLAGS_WHEEL_NEGATIVE = 0x0100;
public final static int PTRFLAGS_DOWN = 0x8000;
private final static int MOUSE_BUTTON_NONE = 0x0000;
private final static int MOUSE_BUTTON_MOVE = 0x0800;
public final static int MOUSE_BUTTON_LEFT = 0x1000;
public final static int MOUSE_BUTTON_RIGHT = 0x2000;
public static final int MOUSE_BUTTON_MIDDLE = 0x4000;
public static final int MOUSE_BUTTON_SCROLL_UP = PTRFLAGS_WHEEL|0x0078;
public static final int MOUSE_BUTTON_SCROLL_DOWN = PTRFLAGS_WHEEL|PTRFLAGS_WHEEL_NEGATIVE|0x0088;
// TODO: Figure out if possible
//public static final int MOUSE_BUTTON_SCROLL_LEFT = 32;
//public static final int MOUSE_BUTTON_SCROLL_RIGHT = 64;
public MouseScrollRunnable scrollRunnable;
public RemoteRdpPointer (RfbConnectable r, RemoteCanvas v, Handler h) {
super(r,v,h);
scrollRunnable = new MouseScrollRunnable();
}
public int getX() {
return mouseX;
}
public int getY() {
return mouseY;
}
public void setX(int newX) {
mouseX = newX;
}
public void setY(int newY) {
mouseY = newY;
}
/**
* Warp the mouse to x, y in the RFB coordinates
*
* @param x
* @param y
*/
public void warpMouse(int x, int y)
{
vncCanvas.invalidateMousePosition();
mouseX=x;
mouseY=y;
vncCanvas.invalidateMousePosition();
//android.util.Log.i(TAG, "warp mouse to " + x + "," + y);
//processPointerEvent(getX(), getY(), MotionEvent.ACTION_MOVE, 0, false, false, false, false, 0);
rfb.writePointerEvent(x, y, 0, MOUSE_BUTTON_MOVE);
}
public void mouseFollowPan()
{
if (vncCanvas.getMouseFollowPan())
{
int scrollx = vncCanvas.getAbsoluteX();
int scrolly = vncCanvas.getAbsoluteY();
int width = vncCanvas.getVisibleWidth();
int height = vncCanvas.getVisibleHeight();
//Log.i(TAG,"scrollx " + scrollx + " scrolly " + scrolly + " mouseX " + mouseX +" Y " + mouseY + " w " + width + " h " + height);
if (mouseX < scrollx || mouseX >= scrollx + width || mouseY < scrolly || mouseY >= scrolly + height) {
warpMouse(scrollx + width/2, scrolly + height / 2);
}
}
}
/**
* Moves the scroll while the volume key is held down
*
* @author Michael A. MacDonald
*/
public class MouseScrollRunnable implements Runnable
{
int delay = 100;
public int scrollButton = 0;
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if (rfb != null && rfb.isInNormalProtocol()) {
rfb.writePointerEvent(mouseX, mouseY, 0, scrollButton|PTRFLAGS_DOWN);
try {Thread.sleep(20);} catch (InterruptedException e) {}
rfb.writePointerEvent(mouseX, mouseY, 0, scrollButton);
handler.postDelayed(this, delay);
}
}
}
public boolean handleHardwareButtons(int keyCode, KeyEvent evt, int combinedMetastate) {
boolean down = (evt.getAction() == KeyEvent.ACTION_DOWN) ||
(evt.getAction() == KeyEvent.ACTION_MULTIPLE);
if (down)
pointerMask = PTRFLAGS_DOWN;
else
pointerMask = 0;
int mouseChange = 0;
//int direction = 0;
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
mouseChange = RemoteRdpPointer.MOUSE_BUTTON_SCROLL_DOWN;
//direction = 1;
} else {
mouseChange = RemoteRdpPointer.MOUSE_BUTTON_SCROLL_UP;
//direction = 0;
}
if (shouldBeRightClick (evt)) {
pointerMask |= RemoteRdpPointer.MOUSE_BUTTON_RIGHT;
rfb.writePointerEvent(getX(), getY(), combinedMetastate, pointerMask);
return true;
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
pointerMask |= mouseChange;
if (down) {
// If not auto-repeat
if (scrollRunnable.scrollButton != mouseChange) {
scrollRunnable.scrollButton = mouseChange;
handler.postDelayed(scrollRunnable, 200);
}
} else {
handler.removeCallbacks(scrollRunnable);
scrollRunnable.scrollButton = 0;
}
//processPointerEvent(getX(), getY(), evt.getAction(), combinedMetastate, down, false, false, true, direction);
rfb.writePointerEvent(getX(), getY(), combinedMetastate, pointerMask);
return true;
}
return false;
}
/**
* Overloaded processPointerEvent method which supports mouse scroll button.
* @param evt motion event; x and y must already have been converted from screen coordinates
* to remote frame buffer coordinates.
* @param downEvent True if "mouse button" (touch or trackball button) is down when this happens
* @param useRightButton If true, event is interpreted as happening with right mouse button
* @param useMiddleButton If true, event is interpreted as click happening with middle mouse button
* @param useScrollButton If true, event is interpreted as click happening with mouse scroll button
* @param direction Indicates the direction of the scroll event: 0 for up, 1 for down, 2 for left, 3 for right.
* @return true if event was actually sent
*/
public boolean processPointerEvent(MotionEvent evt, boolean downEvent,
boolean useRightButton, boolean useMiddleButton, boolean useScrollButton, int direction) {
return processPointerEvent((int)evt.getX(),(int)evt.getY(), evt.getActionMasked(),
evt.getMetaState(), downEvent, useRightButton, useMiddleButton, useScrollButton, direction);
}
/**
* Overloaded processPointerEvent method which supports middle mouse button.
* @param evt motion event; x and y must already have been converted from screen coordinates
* to remote frame buffer coordinates.
* @param downEvent True if "mouse button" (touch or trackball button) is down when this happens
* @param useRightButton If true, event is interpreted as happening with right mouse button
* @param useMiddleButton If true, event is interpreted as click happening with middle mouse button
* @return true if event was actually sent
*/
public boolean processPointerEvent(MotionEvent evt, boolean downEvent,
boolean useRightButton, boolean useMiddleButton) {
return processPointerEvent((int)evt.getX(),(int)evt.getY(), evt.getActionMasked(),
evt.getMetaState(), downEvent, useRightButton, useMiddleButton, false, -1);
}
/**
* Convert a motion event to a format suitable for sending over the wire
* @param evt motion event; x and y must already have been converted from screen coordinates
* to remote frame buffer coordinates.
* @param downEvent True if "mouse button" (touch or trackball button) is down when this happens
* @param useRightButton If true, event is interpreted as happening with right mouse button
* @return true if event was actually sent
*/
public boolean processPointerEvent(MotionEvent evt, boolean downEvent, boolean useRightButton) {
return processPointerEvent((int)evt.getX(),(int)evt.getY(), evt.getAction(),
evt.getMetaState(), downEvent, useRightButton, false, false, -1);
}
/**
* Overloaded processPointerEvent method which supports right mouse button.
* @param evt motion event; x and y must already have been converted from screen coordinates
* to remote frame buffer coordinates.
* @param downEvent True if "mouse button" (touch or trackball button) is down when this happens
* @param useRightButton If true, event is interpreted as happening with right mouse button
* @return true if event was actually sent
*/
public boolean processPointerEvent(int x, int y, int action, int modifiers, boolean mouseIsDown, boolean useRightButton) {
return processPointerEvent(x, y, action, modifiers, mouseIsDown, useRightButton, false, false, -1);
}
public boolean processPointerEvent(int x, int y, int action, int modifiers, boolean mouseIsDown, boolean useRightButton,
boolean useMiddleButton, boolean useScrollButton, int direction) {
if (rfb != null && rfb.isInNormalProtocol()) {
if (useRightButton) {
//android.util.Log.e(TAG, "Mouse button right");
if (prevPointerMask == MOUSE_BUTTON_RIGHT)
pointerMask = MOUSE_BUTTON_MOVE;
else
pointerMask = MOUSE_BUTTON_RIGHT;
} else if (useMiddleButton) {
//android.util.Log.e(TAG, "Mouse button middle");
if (prevPointerMask == MOUSE_BUTTON_MIDDLE)
pointerMask = MOUSE_BUTTON_MOVE;
else
pointerMask = MOUSE_BUTTON_MIDDLE;
} else if (action == MotionEvent.ACTION_DOWN) {
//android.util.Log.e(TAG, "Mouse button left");
pointerMask = MOUSE_BUTTON_LEFT;
} else if (useScrollButton) {
if ( direction == 0 ) {
//android.util.Log.e(TAG, "Scrolling up");
pointerMask = MOUSE_BUTTON_SCROLL_UP;
} else if ( direction == 1 ) {
//android.util.Log.e(TAG, "Scrolling down");
pointerMask = MOUSE_BUTTON_SCROLL_DOWN;
}/* else if ( direction == 2 ) {
android.util.Log.e("", "Scrolling left");
pointerMask = MOUSE_BUTTON_SCROLL_LEFT;
} else if ( direction == 3 ) {
android.util.Log.e("", "Scrolling right");
pointerMask = MOUSE_BUTTON_SCROLL_RIGHT;
}*/
} else if (action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_HOVER_MOVE) {
//android.util.Log.e(TAG, "Mouse moving");
pointerMask = MOUSE_BUTTON_MOVE;
} else {
//android.util.Log.e(TAG, "Setting previous mouse action with mouse not down.");
// If none of the conditions are satisfied, then set the pointer mask to
// the previous mask so we can unpress any pressed buttons.
pointerMask = prevPointerMask;
}
// Save the previous pointer mask other than action_move, so we can
// send it with the pointer flag "not down" to clear the action. Since my bluetooth mouse
// Sends mouse-hover instead of the button which was released, there is an exception here.
if (pointerMask != MOUSE_BUTTON_MOVE || action == MotionEvent.ACTION_HOVER_MOVE) {
// If this is a new mouse down event, release previous button pressed to avoid confusing the remote OS.
if (prevPointerMask != 0 && prevPointerMask != pointerMask) {
//android.util.Log.e(TAG, "Releasing previous mouse button: " + prevPointerMask);
rfb.writePointerEvent(mouseX, mouseY, modifiers|vncCanvas.getKeyboard().getMetaState(), prevPointerMask);
}
prevPointerMask = pointerMask;
}
// Xrdp doesn't like it when we add ptrflags_down to scroll events...
if (mouseIsDown && !useScrollButton) {
//android.util.Log.e(TAG, "Mouse pointer is down");
pointerMask = pointerMask | PTRFLAGS_DOWN;
} else {
//android.util.Log.e(TAG, "Mouse pointer is up");
prevPointerMask = 0;
}
vncCanvas.invalidateMousePosition();
mouseX = x;
mouseY = y;
if ( mouseX < 0) mouseX=0;
else if ( mouseX >= rfb.framebufferWidth()) mouseX = rfb.framebufferWidth() - 1;
if ( mouseY < 0) mouseY=0;
else if ( mouseY >= rfb.framebufferHeight()) mouseY = rfb.framebufferHeight() - 1;
vncCanvas.invalidateMousePosition();
// Sending pointerMask == 0 causes the RDP connection to close, so we're being extra careful.
if (pointerMask != 0) {
//android.util.Log.e(TAG, "Sending mouse button: " + pointerMask);
rfb.writePointerEvent(mouseX, mouseY, modifiers|vncCanvas.getKeyboard().getMetaState(), pointerMask);
}
return true;
}
return false;
}
}