[[project @ 10] googlecode@antlersoft.com**20090109233228 Ignore-this: cb670cc9371fb52270b9d11afebecda4 Add ability to support large framebuffers by only pulling down what you need to display. Add new input mode-- use trackball to control mouse Refactor to make input modes clean Revamp VNC canvas menu and build from resources; sub-menus to select input modes and scaling Fix broken bell notification ] adddir ./androidVNC/res/menu addfile ./androidVNC/res/menu/vnccanvasactivitymenu.xml addfile ./androidVNC/src/android/androidVNC/AbstractBitmapData.java addfile ./androidVNC/src/android/androidVNC/AbstractInputHandler.java addfile ./androidVNC/src/android/androidVNC/CompactBitmapData.java addfile ./androidVNC/src/android/androidVNC/LargeBitmapData.java hunk ./androidVNC/res/menu/vnccanvasactivitymenu.xml 1 + + + + + + + + + + + + + + + + + + + + + + + + + hunk ./androidVNC/res/values/strings.xml 4 +Desktop Panning Mode +Mouse Pointer Control Mode +No Pan; Trackball Mouse +Touch Pan; Trackball Mouse +Info +Color Mode +Scaling +Disconnect +Input Mode +1:1 +Mouse @ +Fit to Screen +Ctrl-Alt-Del hunk ./androidVNC/src/android/androidVNC/AbstractBitmapData.java 1 +/** + * Copyright (C) 2009 Michael A. MacDonald + */ +package android.androidVNC; + +import java.io.IOException; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.widget.ImageView; + +/** + * Abstract interface between the VncCanvas and the bitmap and pixel data buffers that actually contain + * the data. + * This allows for implementations that use smaller bitmaps or buffers to save memory. + * @author Michael A. MacDonald + * + */ +abstract class AbstractBitmapData { + int framebufferwidth; + int framebufferheight; + int bitmapwidth; + int bitmapheight; + RfbProto rfb; + Bitmap mbitmap; + int bitmapPixels[]; + Canvas memGraphics; + boolean waitingForInput; + + AbstractBitmapData( RfbProto p) + { + rfb=p; + } + + synchronized void doneWaiting() + { + waitingForInput=false; + } + + /** + * Send a request through the protocol to get the data for the currently held bitmap + * @param incremental True if we want incremental update; false for full update + */ + abstract void writeFullUpdateRequest( boolean incremental) throws IOException; + + /** + * Determine if a rectangle in full-frame coordinates can be drawn in the existing buffer + * @param x Top left x + * @param y Top left y + * @param w width (pixels) + * @param h height (pixels) + * @return True if entire rectangle fits into current screen buffer, false otherwise + */ + abstract boolean validDraw( int x, int y, int w, int h); + + /** + * Return an offset in the bitmapPixels array of a point int full-frame coordinates + * @param x + * @param y + * @return Offset in bitmapPixels array of color data for that point + */ + abstract int offset( int x, int y); + + /** + * Update pixels in the bitmap with data from the bitmapPixels array, positioned + * in full-frame coordinates + * @param x Top left x + * @param y Top left y + * @param w width (pixels) + * @param h height (pixels) + */ + abstract void updateBitmap( int x, int y, int w, int h); + + /** + * Call in UI thread; tell ImageView we've changed + * @param v ImageView displaying bitmap data + */ + abstract void updateView(ImageView v); + + /** + * Copy a rectangle from one part of the bitmap to another + * @param src Rectangle in full-frame coordinates to be copied + * @param dest Destination rectangle in full-frame coordinates + * @param paint Paint specifier + */ + abstract void copyRect( Rect src, Rect dest, Paint paint); + + /** + * Draw a rectangle in the bitmap with coordinates given in full frame + * @param x Top left x + * @param y Top left y + * @param w width (pixels) + * @param h height (pixels) + * @param paint How to draw + */ + abstract void drawRect( int x, int y, int w, int h, Paint paint); + + /** + * Scroll position has changed. + *

+ * This method is called in the UI thread-- it updates internal status, but does + * not change the bitmap data or send a network request until syncScroll is called + * @param newx Position of left edge of visible part + * @param newy Position of right edge of visible part + */ + abstract void scrollChanged( int newx, int newy); + + /** + * Sync scroll -- called from network thread; copies scroll changes from UI to network state + */ + abstract void syncScroll(); + + /** + * Release resources + */ + void dispose() + { + if ( mbitmap!=null ) + mbitmap.recycle(); + memGraphics = null; + bitmapPixels = null; + } +} hunk ./androidVNC/src/android/androidVNC/AbstractInputHandler.java 1 +/** + * Copyright (C) 2009 Michael A. MacDonald + */ +package android.androidVNC; + +import android.view.KeyEvent; +import android.view.MotionEvent; + +/** + * The VncCanvasActivity has several different ways of handling input from the touchscreen, + * keyboard, buttons and trackball. These will be represented by different implementations + * of this interface. Putting the different modes in different classes + * will keep the logic clean. The relevant Activity callbacks in VncCanvasActivity + * are forwarded to methods in AbstractInputHandler. + *

+ * It is expected that the implementations will be contained within + * VncCanvasActivity, so they can do things like super.VncCanvasActivity.onXXX to invoke + * default behavior. + * @author Michael A. MacDonald + * + */ +interface AbstractInputHandler { + /** + * Note: Menu key code is handled before this is called + * @see android.app.Activity#onKeyDown(int keyCode, KeyEvent evt) + */ + boolean onKeyDown(int keyCode, KeyEvent evt); + /** + * Note: Menu key code is handled before this is called + * @see android.app.Activity#onKeyUp(int keyCode, KeyEvent evt) + */ + boolean onKeyUp(int keyCode, KeyEvent evt); + /* (non-Javadoc) + * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent) + */ + boolean onTrackballEvent( MotionEvent evt); + /* (non-Javadoc) + * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent) + */ + boolean onTouchEvent( MotionEvent evt); + + /** + * Return a user-friendly description for this mode; it will be displayed in a toaster + * when changing modes. + * @return + */ + CharSequence getHandlerDescription(); +} hunk ./androidVNC/src/android/androidVNC/CompactBitmapData.java 1 +/** + * Copyright (C) 2009 Michael A. MacDonald + */ +package android.androidVNC; + +import java.io.IOException; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.widget.ImageView; + +/** + * @author Michael A. MacDonald + * + */ +class CompactBitmapData extends AbstractBitmapData { + + CompactBitmapData(RfbProto rfb) + { + super(rfb); + bitmapwidth=framebufferwidth=rfb.framebufferWidth; + bitmapheight=framebufferheight=rfb.framebufferHeight; + mbitmap = Bitmap.createBitmap(rfb.framebufferWidth, rfb.framebufferHeight, Bitmap.Config.RGB_565); + memGraphics = new Canvas(mbitmap); + bitmapPixels = new int[rfb.framebufferWidth * rfb.framebufferHeight]; + } + + @Override + void writeFullUpdateRequest(boolean incremental) throws IOException { + rfb.writeFramebufferUpdateRequest(0, 0, framebufferwidth, framebufferheight, incremental); + } + + @Override + boolean validDraw(int x, int y, int w, int h) { + return true; + } + + @Override + int offset(int x, int y) { + return y * bitmapwidth + x; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#updateBitmap(int, int, int, int) + */ + @Override + void updateBitmap(int x, int y, int w, int h) { + mbitmap.setPixels(bitmapPixels, offset(x,y), bitmapwidth, x, y, w, h); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#updateView(android.widget.ImageView) + */ + @Override + void updateView(ImageView v) { + v.setImageBitmap(mbitmap); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#copyRect(android.graphics.Rect, android.graphics.Rect, android.graphics.Paint) + */ + @Override + void copyRect(Rect src, Rect dest, Paint paint) { + memGraphics.drawBitmap(mbitmap, src, dest, paint); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#drawRect(int, int, int, int, android.graphics.Paint) + */ + @Override + void drawRect(int x, int y, int w, int h, Paint paint) { + memGraphics.drawRect(x, y, x + w, y + h, paint); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#scrollChanged(int, int) + */ + @Override + void scrollChanged(int newx, int newy) { + // Don't need to do anything here + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#syncScroll() + */ + @Override + void syncScroll() { + // Don't need anything here either + + } +} hunk ./androidVNC/src/android/androidVNC/LargeBitmapData.java 1 +/** + * Copyright (C) 2009 Michael A. MacDonald + */ +package android.androidVNC; + +import java.io.IOException; + +import android.graphics.drawable.DrawableContainer; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.widget.ImageView; + +/** + * @author Michael A. MacDonald + * + */ +class LargeBitmapData extends AbstractBitmapData { + + int xoffset; + int yoffset; + int scrolledToX; + int scrolledToY; + int origwidth; + int origheight; + private LargeBitmapDrawable drawable; + private Rect invalidRect; + private Rect bitmapRect; + + class LargeBitmapDrawable extends DrawableContainer + { + + /* (non-Javadoc) + * @see android.graphics.drawable.DrawableContainer#draw(android.graphics.Canvas) + */ + @Override + public void draw(Canvas canvas) { + //android.util.Log.i("LBM", "Drawing "+xoffset+" "+yoffset); + int xoff, yoff; + synchronized ( LargeBitmapDrawable.this ) + { + xoff=xoffset; + yoff=yoffset; + } + canvas.drawBitmap(mbitmap, xoff, yoff, new Paint()); + } + + /* (non-Javadoc) + * @see android.graphics.drawable.DrawableContainer#getIntrinsicHeight() + */ + @Override + public int getIntrinsicHeight() { + return framebufferheight; + } + + /* (non-Javadoc) + * @see android.graphics.drawable.DrawableContainer#getIntrinsicWidth() + */ + @Override + public int getIntrinsicWidth() { + return framebufferwidth; + } + + /* (non-Javadoc) + * @see android.graphics.drawable.DrawableContainer#getOpacity() + */ + @Override + public int getOpacity() { + return PixelFormat.OPAQUE; + } + + /* (non-Javadoc) + * @see android.graphics.drawable.DrawableContainer#isStateful() + */ + @Override + public boolean isStateful() { + return false; + } + } + + LargeBitmapData(RfbProto p, int displayWidth, int displayHeight) + { + super(p); + framebufferwidth=rfb.framebufferWidth; + framebufferheight=rfb.framebufferHeight; + origwidth=displayWidth; + origheight=displayHeight; + bitmapwidth=origwidth * 2; + bitmapheight=origheight * 2; + mbitmap = Bitmap.createBitmap(bitmapwidth, bitmapheight, Bitmap.Config.RGB_565); + memGraphics = new Canvas(mbitmap); + bitmapPixels = new int[bitmapwidth * bitmapheight]; + drawable=new LargeBitmapDrawable(); + invalidRect=new Rect(); + bitmapRect=new Rect(0,0,bitmapwidth,bitmapheight); + } + + void clearInvalid() + { + invalidRect.bottom=invalidRect.top=yoffset; + invalidRect.left=invalidRect.right=xoffset; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#copyRect(android.graphics.Rect, android.graphics.Rect, android.graphics.Paint) + */ + @Override + void copyRect(Rect src, Rect dest, Paint paint) { + // TODO copy rect working? + throw new RuntimeException( "copyrect Not implemented"); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#drawRect(int, int, int, int, android.graphics.Paint) + */ + @Override + void drawRect(int x, int y, int w, int h, Paint paint) { + x-=xoffset; + y-=yoffset; + memGraphics.drawRect(x, y, x+w, y+h, paint); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#offset(int, int) + */ + @Override + int offset(int x, int y) { + return (y - yoffset) * bitmapwidth + x - xoffset; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#scrollChanged(int, int) + */ + @Override + synchronized void scrollChanged(int newx, int newy) { + //android.util.Log.i("LBM","scroll "+newx+" "+newy); + newx+=(framebufferwidth-origwidth)/2; + newy+=(framebufferheight-origheight)/2; + if ( ! ( newx-xoffset>=0 && newx-xoffset+origwidth<=bitmapwidth && newy-yoffset>=0 && newy-yoffset+origheight<=bitmapheight)) + { + //android.util.Log.i("LBM","scroll "+newx+" "+newy); + int xindex, yindex; + xindex = -1; + yindex = -1; + if ( newxbitmapwidth) + { + xindex=newx/origwidth; + } + if ( newybitmapheight) + { + yindex=newy/origheight; + } + if ( xindex != -1) + { + scrolledToX=xindex*origwidth; + if ( scrolledToX+bitmapwidth>framebufferwidth) + scrolledToX=framebufferwidth-bitmapwidth; + } + if ( yindex != -1) + { + scrolledToY=yindex*origheight; + if ( scrolledToY+bitmapheight>framebufferheight) + scrolledToY=framebufferheight-bitmapheight; + } + if ( waitingForInput) + syncScroll(); + } + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#updateBitmap(int, int, int, int) + */ + @Override + void updateBitmap(int x, int y, int w, int h) { + mbitmap.setPixels(bitmapPixels, offset(x,y), bitmapwidth, x-xoffset, y-yoffset, w, h); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#updateView(android.widget.ImageView) + */ + @Override + void updateView(ImageView v) { + //android.util.Log.i("LBM","Setting drawable"); + v.setImageDrawable(drawable); + v.invalidate(); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#validDraw(int, int, int, int) + */ + @Override + boolean validDraw(int x, int y, int w, int h) { + //android.util.Log.i("LBM", "Validate Drawing "+x+" "+y+" "+w+" "+h+" "+xoffset+" "+yoffset+" "+(x-xoffset>=0 && x-xoffset+w<=bitmapwidth && y-yoffset>=0 && y-yoffset+h<=bitmapheight)); + boolean result = x-xoffset>=0 && x-xoffset+w<=bitmapwidth && y-yoffset>=0 && y-yoffset+h<=bitmapheight; + if ( ! result) + { + //android.util.Log.i("LBM", "Invalid "+x+" "+y+" "+w+" "+h+" "+xoffset+" "+yoffset); + invalidRect.union( x, y, x+w, y+h); + } + return result; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#writeFullUpdateRequest(boolean) + */ + @Override + void writeFullUpdateRequest(boolean incremental) throws IOException { + rfb.writeFramebufferUpdateRequest(xoffset, yoffset, bitmapwidth, bitmapheight, incremental); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractBitmapData#syncScroll() + */ + @Override + synchronized void syncScroll() { + if ( xoffset!=scrolledToX || yoffset!=scrolledToY) + { + xoffset=scrolledToX; + yoffset=scrolledToY; + bitmapRect.top=scrolledToY; + bitmapRect.bottom=scrolledToY+bitmapheight; + bitmapRect.left=scrolledToX; + bitmapRect.right=scrolledToX+bitmapwidth; + try + { + //android.util.Log.i("LBM","update req "+xoffset+" "+yoffset); + writeFullUpdateRequest(false); + } + catch ( IOException ioe) + { + // TODO log this + } + } + else + { + if ( invalidRect.intersect( bitmapRect)) + { + try + { + //android.util.Log.i("LBM","invalid rect "+invalidRect.toString()+" "+bitmapRect.toString()); + rfb.writeFramebufferUpdateRequest(invalidRect.left, invalidRect.top, invalidRect.right-invalidRect.left, invalidRect.bottom-invalidRect.top, false); + } + catch ( IOException ioe) + { + // TODO log this + } + } + clearInvalid(); + } + waitingForInput=true; + } +} hunk ./androidVNC/src/android/androidVNC/R.java 17 - public static final int buttonGO=0x7f050004; - public static final int text1=0x7f050000; - public static final int text2=0x7f050002; - public static final int textIP=0x7f050001; - public static final int textPORT=0x7f050003; + public static final int buttonGO=0x7f060006; + public static final int groupScaling=0x7f06000b; + public static final int itemCenterMouse=0x7f060008; + public static final int itemColorMode=0x7f060009; + public static final int itemCtrlAltDel=0x7f060014; + public static final int itemDisconnect=0x7f060013; + public static final int itemFitToScreen=0x7f06000d; + public static final int itemInfo=0x7f060007; + public static final int itemInputFitToScreen=0x7f06000f; + public static final int itemInputMode=0x7f06000e; + public static final int itemInputMouse=0x7f060011; + public static final int itemInputPan=0x7f060010; + public static final int itemInputTouchPanTrackballMouse=0x7f060012; + public static final int itemOneToOne=0x7f06000c; + public static final int itemScaling=0x7f06000a; + public static final int text1=0x7f060000; + public static final int text2=0x7f060002; + public static final int text3=0x7f060004; + public static final int textIP=0x7f060001; + public static final int textPASSWORD=0x7f060005; + public static final int textPORT=0x7f060003; hunk ./androidVNC/src/android/androidVNC/R.java 43 + public static final class menu { + public static final int vnccanvasactivitymenu=0x7f050000; + } hunk ./androidVNC/src/android/androidVNC/R.java 48 + public static final int center_mouse=0x7f04000b; + public static final int color_mode=0x7f040006; + public static final int ctrl_alt_del=0x7f04000d; + public static final int disconnect=0x7f040008; + public static final int fit_to_screen=0x7f04000c; + public static final int info=0x7f040005; + public static final int input_mode=0x7f040009; + public static final int input_mode_fit_to_screen=0x7f040003; + public static final int input_mode_mouse=0x7f040002; + public static final int input_mode_panning=0x7f040001; + public static final int input_mode_touchpad_pan_trackball_mouse=0x7f040004; + public static final int one_to_one=0x7f04000a; + public static final int scaling=0x7f040007; hunk ./androidVNC/src/android/androidVNC/RfbProto.java 36 -import android.view.MotionEvent; hunk ./androidVNC/src/android/androidVNC/RfbProto.java 37 +/** + * Access the RFB protocol through a socket. + *

+ * This class has no knowledge of the android-specific UI; it sees framebuffer updates + * and input events as defined in the RFB protocol. + * + */ hunk ./androidVNC/src/android/androidVNC/RfbProto.java 314 - void writeVersionMsg() throws IOException { + synchronized void writeVersionMsg() throws IOException { hunk ./androidVNC/src/android/androidVNC/RfbProto.java 871 - void writeFramebufferUpdateRequest(int x, int y, int w, int h, + synchronized void writeFramebufferUpdateRequest(int x, int y, int w, int h, hunk ./androidVNC/src/android/androidVNC/RfbProto.java 896 - void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian, + synchronized void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian, hunk ./androidVNC/src/android/androidVNC/RfbProto.java 929 - void writeFixColourMapEntries(int firstColour, int nColours, + synchronized void writeFixColourMapEntries(int firstColour, int nColours, hunk ./androidVNC/src/android/androidVNC/RfbProto.java 958 - void writeSetEncodings(int[] encs, int len) throws IOException { + synchronized void writeSetEncodings(int[] encs, int len) throws IOException { hunk ./androidVNC/src/android/androidVNC/RfbProto.java 980 - void writeClientCutText(String text) throws IOException { + synchronized void writeClientCutText(String text) throws IOException { hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1006 - // Useful shortcuts for modifier masks. hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1007 - final static int CTRL_MASK = KeyEvent.META_SYM_ON; - final static int SHIFT_MASK = KeyEvent.META_SHIFT_ON; - final static int META_MASK = 0; - final static int ALT_MASK = KeyEvent.META_ALT_ON; - - - // - // Write a pointer event message. We may need to send modifier key events - // around it to set the correct modifier state. - // - int pointerMask = 0; - - void writePointerEvent(MotionEvent evt) throws IOException { - int modifiers = evt.getMetaState(); - - int mask2 = 2; -// int mask3 = 4; - - if (evt.getAction() == MotionEvent.ACTION_DOWN) { - if ((modifiers & KeyEvent.META_ALT_ON) != 0) { - pointerMask = mask2; - modifiers &= ~ALT_MASK; -// } else if ((modifiers & KeyEvent.META_SYM_ON) != 0) { -// pointerMask = mask3; -// modifiers &= ~META_MASK; - } else { - pointerMask = 1; - } - } else if (evt.getAction() == MotionEvent.ACTION_UP) { - pointerMask = 0; - if ((modifiers & KeyEvent.META_ALT_ON) != 0) { - modifiers &= ~ALT_MASK; - } -// else if ((modifiers & KeyEvent.META_SYM_ON) != 0) { -// modifiers &= ~META_MASK; -// } - } - - eventBufLen = 0; - writeModifierKeyEvents(modifiers); + /** + * Write a pointer event message. We may need to send modifier key events + * around it to set the correct modifier state. + * @param x + * @param y + * @param modifiers + * @param pointerMask + * @throws IOException + */ + synchronized void writePointerEvent( int x, int y, int modifiers, int pointerMask) throws IOException + { + eventBufLen = 0; + writeModifierKeyEvents(modifiers); hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1021 - int x = (int) evt.getX(); - int y = (int) evt.getY(); + eventBuf[eventBufLen++] = (byte) PointerEvent; + eventBuf[eventBufLen++] = (byte) pointerMask; + eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); + eventBuf[eventBufLen++] = (byte) (x & 0xff); + eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); + eventBuf[eventBufLen++] = (byte) (y & 0xff); hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1028 - if (x < 0) x = 0; - if (y < 0) y = 0; + // + // Always release all modifiers after an "up" event + // hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1032 - eventBuf[eventBufLen++] = (byte) PointerEvent; - eventBuf[eventBufLen++] = (byte) pointerMask; - eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (x & 0xff); - eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (y & 0xff); + if (pointerMask == 0) { + writeModifierKeyEvents(0); + } hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1036 - // - // Always release all modifiers after an "up" event - // - - if (pointerMask == 0) { - writeModifierKeyEvents(0); - } - - os.write(eventBuf, 0, eventBufLen); + os.write(eventBuf, 0, eventBufLen); hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1041 - final int CTRLALT = CTRL_MASK | ALT_MASK; + final int CTRLALT = VncCanvas.CTRL_MASK | VncCanvas.ALT_MASK; hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1067 - boolean writeKeyEvent(int keyCode, KeyEvent evt) throws IOException { + synchronized boolean writeKeyEvent(int keyCode, KeyEvent evt) throws IOException { hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1119 - if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); + if ((newModifiers & VncCanvas.CTRL_MASK) != (oldModifiers & VncCanvas.CTRL_MASK)) + writeKeyEvent(0xffe3, (newModifiers & VncCanvas.CTRL_MASK) != 0); hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1122 - if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK)) - writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0); + if ((newModifiers & VncCanvas.SHIFT_MASK) != (oldModifiers & VncCanvas.SHIFT_MASK)) + writeKeyEvent(0xffe1, (newModifiers & VncCanvas.SHIFT_MASK) != 0); hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1125 - if ((newModifiers & META_MASK) != (oldModifiers & META_MASK)) - writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0); + if ((newModifiers & VncCanvas.META_MASK) != (oldModifiers & VncCanvas.META_MASK)) + writeKeyEvent(0xffe7, (newModifiers & VncCanvas.META_MASK) != 0); hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1128 - if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK)) - writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0); + if ((newModifiers & VncCanvas.ALT_MASK) != (oldModifiers & VncCanvas.ALT_MASK)) + writeKeyEvent(0xffe9, (newModifiers & VncCanvas.ALT_MASK) != 0); hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1229 - void writeOpenChat() throws Exception { + synchronized void writeOpenChat() throws Exception { hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1237 - void writeCloseChat() throws Exception { + synchronized void writeCloseChat() throws Exception { hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1245 - void writeFinishedChat() throws Exception { + synchronized void writeFinishedChat() throws Exception { hunk ./androidVNC/src/android/androidVNC/RfbProto.java 1282 - public void writeChatMessage(String msg) throws Exception { + public synchronized void writeChatMessage(String msg) throws Exception { hunk ./androidVNC/src/android/androidVNC/Utils.java 6 -import android.app.Notification; -import android.app.NotificationManager; hunk ./androidVNC/src/android/androidVNC/Utils.java 42 - public static void notify(Context _context, String tickerText) { - NotificationManager manager = (NotificationManager) _context.getSystemService(Context.NOTIFICATION_SERVICE); - Notification n = new Notification(); - n.defaults = Notification.DEFAULT_ALL; - n.tickerText = tickerText; - n.icon = android.R.drawable.stat_sys_warning; - manager.notify(nextNoticeID(), n); - } - hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 67 -import android.graphics.Bitmap; -import android.graphics.Canvas; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 72 +import android.view.Display; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 80 + + // Available to activity + int mouseX, mouseY; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 105 - private Bitmap mbitmap; - private int bitmapPixels[]; - private Canvas memGraphics; + AbstractBitmapData bitmapData; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 183 - // because we are ouf of memory. :( + // because we are out of memory. :( hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 260 - mbitmap = Bitmap.createBitmap(rfb.framebufferWidth, rfb.framebufferHeight, Bitmap.Config.RGB_565); - memGraphics = new Canvas(mbitmap); - bitmapPixels = new int[rfb.framebufferWidth * rfb.framebufferHeight]; + Display display=((VncCanvasActivity)getContext()).getWindowManager().getDefaultDisplay(); + int dx=display.getWidth(); + int dy=display.getHeight(); + int max=(dx>dy) ? dx : dy; + if ( rfb.framebufferWidth>max*2 || rfb.framebufferHeight>max*2) + bitmapData=new LargeBitmapData(rfb,dx,dy); + else + bitmapData=new CompactBitmapData(rfb); + mouseX=rfb.framebufferWidth/2; + mouseY=rfb.framebufferHeight/2; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 292 - public void processNormalProtocol(Context context, ProgressDialog pd) throws Exception { + public void processNormalProtocol(final Context context, ProgressDialog pd) throws Exception { hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 294 - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, rfb.framebufferHeight, false); + bitmapData.writeFullUpdateRequest(false); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 300 - + bitmapData.syncScroll(); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 303 - + bitmapData.doneWaiting(); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 308 - @SuppressWarnings("unused") - boolean cursorPosReceived = false; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 336 - // - softCursorMove(rx, ry); - cursorPosReceived = true; + mouseX=rx; + mouseY=ry; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 386 - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, rfb.framebufferHeight, !fullUpdateNeeded); + bitmapData.writeFullUpdateRequest(!fullUpdateNeeded); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 394 - Utils.notify(context, "VNC Beep!"); + handler.post( new Runnable() { + public void run() { Toast.makeText( context, "VNC Beep", Toast.LENGTH_SHORT); } + }); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 428 - mbitmap.recycle(); - memGraphics = null; - bitmapPixels = null; + if ( bitmapData!=null) bitmapData.dispose(); + } + + /** + * Warp the mouse to x, y in the RFB coordinates + * @param x + * @param y + */ + void warpMouse(int x, int y) + { + mouseX=x; + mouseY=y; + try + { + rfb.writePointerEvent(x, y, 0, MOUSE_BUTTON_NONE); + } + catch ( IOException ioe) + { + Log.w(TAG,ioe); + } + } + + /* (non-Javadoc) + * @see android.view.View#onScrollChanged(int, int, int, int) + */ + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + bitmapData.scrollChanged(l, t); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 464 - int firstoffset = y * rfb.framebufferWidth + x; + boolean valid=bitmapData.validDraw(x, y, w, h); + int[] pixels=bitmapData.bitmapPixels; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 472 - - offset = dy * rfb.framebufferWidth + x; + if ( ! valid) + continue; + offset = bitmapData.offset(x, dy); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 476 - bitmapPixels[offset + i] = colorPalette[0xFF & buf[i]]; + pixels[offset + i] = colorPalette[0xFF & buf[i]]; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 485 - - offset = dy * rfb.framebufferWidth + x; + if ( ! valid) + continue; + offset = bitmapData.offset(x, dy); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 489 - bitmapPixels[offset + i] = // 0xFF << 24 | + pixels[offset + i] = // 0xFF << 24 | hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 494 + + if ( ! valid) + return; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 498 - mbitmap.setPixels(bitmapPixels, firstoffset, rfb.framebufferWidth, x, y, w, h); + bitmapData.updateBitmap( x, y, w, h); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 511 - setImageBitmap(mbitmap); + bitmapData.updateView(VncCanvas.this); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 514 - + hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 519 - + hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 568 + // Useful shortcuts for modifier masks. + + final static int CTRL_MASK = KeyEvent.META_SYM_ON; + final static int SHIFT_MASK = KeyEvent.META_SHIFT_ON; + final static int META_MASK = 0; + final static int ALT_MASK = KeyEvent.META_ALT_ON; + + private static final int MOUSE_BUTTON_NONE = 0; + private static final int MOUSE_BUTTON_1 = 1; + private static final int MOUSE_BUTTON_2 = 2; + /** + * Current state of "mouse" buttons + * Alt meta means use second mouse button + * 0 = none + * 1 = default button + * 2 = second button + */ + private int pointerMask = MOUSE_BUTTON_NONE; + + /** + * 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. ALT meta state flag is interpreted as second mouse + * button + * @return true if event was actually sent + */ hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 596 - synchronized (rfb) { - try { - rfb.writePointerEvent(evt); - } catch (Exception e) { - e.printStackTrace(); - } - rfb.notify(); + int modifiers = evt.getMetaState(); + + if (evt.getAction() == MotionEvent.ACTION_DOWN) { + if ((modifiers & KeyEvent.META_ALT_ON) != 0) { + pointerMask = MOUSE_BUTTON_2; + modifiers &= ~ALT_MASK; +// } else if ((modifiers & KeyEvent.META_SYM_ON) != 0) { +// pointerMask = mask3; +// modifiers &= ~META_MASK; + } else { + pointerMask = MOUSE_BUTTON_1; + } + } else if (evt.getAction() == MotionEvent.ACTION_UP) { + pointerMask = 0; + if ((modifiers & KeyEvent.META_ALT_ON) != 0) { + modifiers &= ~ALT_MASK; + } +// else if ((modifiers & KeyEvent.META_SYM_ON) != 0) { +// modifiers &= ~META_MASK; +// } + } + mouseX=(int)evt.getX(); + mouseY=(int)evt.getY(); + 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; + try { + rfb.writePointerEvent(mouseX,mouseY,modifiers,pointerMask); + } catch (Exception e) { + e.printStackTrace(); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 665 - return mbitmap.getWidth(); + return bitmapData.framebufferwidth; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 669 - return mbitmap.getHeight(); + return bitmapData.framebufferheight; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 673 - int xoffset = (mbitmap.getWidth() - getWidth()) / 2; + int xoffset = (bitmapData.framebufferwidth - getWidth()) / 2; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 678 - int yoffset = (mbitmap.getHeight() - getHeight()) / 2; + int yoffset = (bitmapData.framebufferheight - getHeight()) / 2; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 768 + if ( ! bitmapData.validDraw(x, y, w, h)) + return; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 787 - memGraphics.drawBitmap(mbitmap, new Rect(leftSrc, topSrc, rightSrc, bottomSrc), new Rect(leftDest, topDest, rightDest, bottomDest), paint); + bitmapData.copyRect(new Rect(leftSrc, topSrc, rightSrc, bottomSrc), new Rect(leftDest, topDest, rightDest, bottomDest), paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 797 - + boolean valid=bitmapData.validDraw(x, y, w, h); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 811 - memGraphics.drawRect(x, y, x + w, y + h, paint); + if ( valid) + bitmapData.drawRect(x, y, w, h, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 816 + if ( ! valid) + return; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 835 - memGraphics.drawRect(sx, sy, sx + sw, sy + sh, paint); + bitmapData.drawRect(sx, sy, sw, sh, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 846 + boolean valid=bitmapData.validDraw(x, y, w, h); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 860 - memGraphics.drawRect(x, y, x + w, y + h, paint); + if ( valid) + bitmapData.drawRect(x, y, w, h, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 865 + if ( ! valid) + return; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 884 - memGraphics.drawRect(sx, sy, sx + sw, sy + sh, paint); + bitmapData.drawRect(sx, sy, sw, sh, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 934 + boolean valid=bitmapData.validDraw(tx, ty, tw, th); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 948 - memGraphics.drawRect(tx, ty, tx + tw, ty + th, paint); + if ( valid ) + bitmapData.drawRect(tx, ty, tw, th, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 986 - memGraphics.drawRect(sx, sy, sx + sw, sy + sh, paint); + if ( valid) + bitmapData.drawRect(sx, sy, sw, sh, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1001 - memGraphics.drawRect(sx, sy, sx + sw, sy + sh, paint); + if ( valid) + bitmapData.drawRect(sx, sy, sw, sh, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1018 - memGraphics.drawRect(sx, sy, sx + sw, sy + sh, paint); + if ( valid ) + bitmapData.drawRect(sx, sy, sw, sh, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1046 + + boolean valid=bitmapData.validDraw(x, y, w, h); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1070 - memGraphics.drawRect(tx, ty, tx + tw, ty + th, paint); + if ( valid) + bitmapData.drawRect(tx, ty, tw, th, paint); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1088 - handleUpdatedZrleTile(tx, ty, tw, th); + if ( valid ) + handleUpdatedZrleTile(tx, ty, tw, th); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1103 - + boolean valid = bitmapData.validDraw(x, y, w, h); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1117 + + int[] pixels=bitmapData.bitmapPixels; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1126 - offset = dy * rfb.framebufferWidth + x; + if ( ! valid) + continue; + offset = bitmapData.offset(x, dy); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1130 - bitmapPixels[offset + i] = colorPalette[0xFF & buf[i]]; + pixels[offset + i] = colorPalette[0xFF & buf[i]]; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1139 - offset = dy * rfb.framebufferWidth + x; + if ( ! valid) + continue; + offset = bitmapData.offset(x, dy); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1143 - bitmapPixels[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 | (buf[i * 4 + 1] & 0xFF) << 8 | (buf[i * 4] & 0xFF); + pixels[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 | (buf[i * 4 + 1] & 0xFF) << 8 | (buf[i * 4] & 0xFF); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1147 - int firstoffset = y * rfb.framebufferWidth + x; - mbitmap.setPixels(bitmapPixels, firstoffset, rfb.framebufferWidth, x, y, w, h); + if ( ! valid) + return; + bitmapData.updateBitmap(x, y, w, h); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1291 - int offsetDst = (y * rfb.framebufferWidth + x); + int offsetDst = bitmapData.offset(x, y); + int[] destPixels=bitmapData.bitmapPixels; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1294 - System.arraycopy(zrleTilePixels, offsetSrc, bitmapPixels, offsetDst, w); + System.arraycopy(zrleTilePixels, offsetSrc, destPixels, offsetDst, w); hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1296 - offsetDst += rfb.framebufferWidth; + offsetDst += bitmapData.bitmapwidth; hunk ./androidVNC/src/android/androidVNC/VncCanvas.java 1299 - int firstoffset = y * rfb.framebufferWidth + x; - mbitmap.setPixels(bitmapPixels, firstoffset, rfb.framebufferWidth, x, y, w, h); + bitmapData.updateBitmap(x, y, w, h); hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 30 +import android.view.Display; hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 46 - private final static String TAG = "VncCanvasActivity"; hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 47 - private boolean panningMode = true; + private final static String TAG = "VncCanvasActivity"; hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 49 - private VncCanvas vncCanvas; + AbstractInputHandler inputHandler; + + VncCanvas vncCanvas; + hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 54 - private final static int MENU_ITEM_INFO = Menu.FIRST; - private final static int MENU_ITEM_SETTINGS = Menu.FIRST + 1; - private final static int MENU_ITEM_ONE2ONE = Menu.FIRST + 2; - private final static int MENU_ITEM_FITSCREEN = Menu.FIRST + 3; - private final static int MENU_ITEM_DISCONNECT = Menu.FIRST + 4; - private final static int MENU_ITEM_CTRLALTDEL = Menu.FIRST + 5; + private MenuItem[] inputModeMenuItems; + private AbstractInputHandler inputModeHandlers[]; + private static final int inputModeIds[] = { R.id.itemInputFitToScreen, R.id.itemInputMouse, R.id.itemInputPan, R.id.itemInputTouchPanTrackballMouse }; hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 88 + + inputHandler=getInputHandlerById(R.id.itemInputFitToScreen); hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 113 - super.onCreateOptionsMenu(menu); - menu.add(Menu.NONE, MENU_ITEM_INFO, 0, "Info").setShortcut('1', 'i').setIcon(android.R.drawable.ic_menu_info_details); - menu.add(Menu.NONE, MENU_ITEM_SETTINGS, 0, "Settings").setShortcut('2', 's').setIcon(android.R.drawable.ic_menu_preferences); - menu.add(Menu.NONE, MENU_ITEM_ONE2ONE, 0, "1:1").setShortcut('3', 'r').setIcon(android.R.drawable.ic_menu_zoom); - menu.add(Menu.NONE, MENU_ITEM_FITSCREEN, 0, "Fit-to-Screen").setShortcut('4', 'c').setIcon(android.R.drawable.ic_menu_zoom); - menu.add(Menu.NONE, MENU_ITEM_CTRLALTDEL, 0, "Ctrl-Alt-Del").setShortcut('6', 'a').setIcon(android.R.drawable.ic_menu_share); - menu.add(Menu.NONE, MENU_ITEM_DISCONNECT, 0, "Disconnect").setShortcut('5', 'd').setIcon(android.R.drawable.ic_menu_close_clear_cancel); - return super.onCreateOptionsMenu(menu); - } - - /** {@inheritDoc} */ - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - // Hide the button for the current scaling mode - boolean isFitToScreen = vncCanvas.getScaleType() == ScaleType.FIT_CENTER; - menu.findItem(MENU_ITEM_ONE2ONE).setVisible(isFitToScreen); - menu.findItem(MENU_ITEM_FITSCREEN).setVisible(!isFitToScreen); + getMenuInflater().inflate(R.menu.vnccanvasactivitymenu, menu); + + Menu inputMenu = menu.findItem( R.id.itemInputMode).getSubMenu(); + + inputModeMenuItems = new MenuItem[inputModeIds.length]; + for ( int i=0; i vncCanvas.getImageWidth()) - dX = 0; - if (absoluteYPosition + vncCanvas.getHeight() + dY > vncCanvas.getImageHeight()) - dY = 0; - - absoluteXPosition += dX; - absoluteYPosition += dY; - vncCanvas.scrollBy(dX, dY); + @Override + public boolean onTouchEvent(MotionEvent event) { + return inputHandler.onTouchEvent(event); hunk ./androidVNC/src/android/androidVNC/VncCanvasActivity.java 332 + + float panTouchX, panTouchY; + + /** + * Pan based on touch motions + * @param event + */ + private void pan(MotionEvent event) { + float curX = event.getX(); + float curY = event.getY(); + int dX = (int) (panTouchX - curX); + int dY = (int) (panTouchY - curY); + + // Prevent panning left or above desktop image + if (absoluteXPosition + dX < 0) + // dX = diff to 0 + dX = absoluteXPosition * -1; + if (absoluteYPosition + dY < 0) + // dY = diff to 0 + dY = absoluteYPosition * -1; + + // Prevent panning right or below desktop image + if (absoluteXPosition + vncCanvas.getWidth() + dX > vncCanvas.getImageWidth()) + dX = 0; + if (absoluteYPosition + vncCanvas.getHeight() + dY > vncCanvas.getImageHeight()) + dY = 0; + + absoluteXPosition += dX; + absoluteYPosition += dY; + vncCanvas.scrollBy(dX, dY); + } + + boolean defaultKeyDownHandler( int keyCode, KeyEvent evt) + { + if ( vncCanvas.processLocalKeyEvent(keyCode, evt)) + return true; + return super.onKeyDown(keyCode, evt); + } + + boolean defaultKeyUpHandler( int keyCode, KeyEvent evt) + { + if ( vncCanvas.processLocalKeyEvent(keyCode, evt)) + return true; + return super.onKeyUp(keyCode, evt); + } + + boolean touchPan( MotionEvent event) + { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + panTouchX = event.getX(); + panTouchY = event.getY(); + break; + case MotionEvent.ACTION_MOVE: + pan(event); + panTouchX = event.getX(); + panTouchY = event.getY(); + break; + case MotionEvent.ACTION_UP: + pan(event); + break; + } + return true; + } + + boolean trackballMouse( MotionEvent evt) + { + int dx = (int)(evt.getX() * 6.01); + int dy = (int)(evt.getY() * 6.01); + + dx = dx * dx * dx; + dy = dy * dy * dy; + + evt.offsetLocation( vncCanvas.mouseX + dx - evt.getX(), vncCanvas.mouseY + dy - evt.getY()); + + if (vncCanvas.processPointerEvent(evt)) + return true; + return VncCanvasActivity.super.onTouchEvent(evt); + } + + /** + * Touches and dpad (trackball) pan the screen + * @author Michael A. MacDonald + * + */ + class PanMode implements AbstractInputHandler + { + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + // DPAD KeyDown events are move MotionEvents in Panning Mode + final int dPos = 100; + boolean result=false; + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_CENTER: + result=true; + break; + case KeyEvent.KEYCODE_DPAD_LEFT: + onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX + dPos, panTouchY, 0)); + result=true; + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX - dPos, panTouchY, 0)); + result=true; + break; + case KeyEvent.KEYCODE_DPAD_UP: + onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX, panTouchY + dPos, 0)); + result=true; + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX, panTouchY - dPos, 0)); + result=true; + break; + default: + result=defaultKeyDownHandler( keyCode, evt); + break; + } + return result; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent evt) { + // Ignore KeyUp events for DPAD keys in Panning Mode; trackball button switches to mouse mode + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_CENTER : + inputHandler=getInputHandlerById(R.id.itemInputMouse); + for ( MenuItem item : inputModeMenuItems) + { + if ( item.getItemId()==R.id.itemInputMouse) { + item.setChecked( true ); + break; + } + } + showPanningState(); + return true; + case KeyEvent.KEYCODE_DPAD_LEFT: + return true; + case KeyEvent.KEYCODE_DPAD_RIGHT: + return true; + case KeyEvent.KEYCODE_DPAD_UP: + return true; + case KeyEvent.KEYCODE_DPAD_DOWN: + return true; + } + return defaultKeyUpHandler( keyCode, evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent) + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + return touchPan(event); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent) + */ + @Override + public boolean onTrackballEvent(MotionEvent evt) { + return false; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#handlerDescription() + */ + @Override + public CharSequence getHandlerDescription() { + return getResources().getText(R.string.input_mode_panning); + } + + } + + /** + * The touchscreen pans the screen; the trackball moves and clicks the mouse. + * @author Michael A. MacDonald + * + */ + public class TouchPanTrackballMouse implements AbstractInputHandler { + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + return defaultKeyDownHandler(keyCode, evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent evt) { + return defaultKeyUpHandler(keyCode, evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent) + */ + @Override + public boolean onTouchEvent(MotionEvent evt) { + return touchPan(evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent) + */ + @Override + public boolean onTrackballEvent(MotionEvent evt) { + return trackballMouse(evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#handlerDescription() + */ + @Override + public CharSequence getHandlerDescription() { + return getResources().getText(R.string.input_mode_touchpad_pan_trackball_mouse); + } + + } + + /** + * In fit-to-screen mode, no panning. Trackball and touchscreen work as mouse. + * @author Michael A. MacDonald + * + */ + public class FitToScreenMode implements AbstractInputHandler { + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + return defaultKeyDownHandler(keyCode, evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent evt) { + return defaultKeyUpHandler(keyCode, evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent) + */ + @Override + public boolean onTouchEvent(MotionEvent evt) { + // TODO Auto-generated method stub + return false; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent) + */ + @Override + public boolean onTrackballEvent(MotionEvent evt) { + return trackballMouse(evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#handlerDescription() + */ + @Override + public CharSequence getHandlerDescription() { + return getResources().getText(R.string.input_mode_fit_to_screen); + } + + } + + /** + * Touch screen controls, clicks the mouse. + * @author Michael A. MacDonald + * + */ + class MouseMode implements AbstractInputHandler { + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent evt) { + if ( keyCode==KeyEvent.KEYCODE_DPAD_CENTER) + return true; + return defaultKeyDownHandler( keyCode, evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent evt) { + if ( keyCode==KeyEvent.KEYCODE_DPAD_CENTER) + { + inputHandler=getInputHandlerById( R.id.itemInputPan); + for ( MenuItem item : inputModeMenuItems) + { + if ( item.getItemId()==R.id.itemInputPan) { + item.setChecked( true ); + break; + } + } + showPanningState(); + return true; + } + return defaultKeyDownHandler( keyCode, evt); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent) + */ + @Override + public boolean onTouchEvent(MotionEvent event) { + // Mouse Pointer Control Mode + // Pointer event is absolute coordinates. + + // Adjust coordinates for Android notification bar. + event.offsetLocation(0, -1f * vncCanvas.getTop()); + + // Adjust coordinates for panning position. + event.offsetLocation(absoluteXPosition, absoluteYPosition); + if (vncCanvas.processPointerEvent(event)) + return true; + return VncCanvasActivity.super.onTouchEvent(event); + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent) + */ + @Override + public boolean onTrackballEvent(MotionEvent evt) { + return false; + } + + /* (non-Javadoc) + * @see android.androidVNC.AbstractInputHandler#handlerDescription() + */ + @Override + public CharSequence getHandlerDescription() { + return getResources().getText(R.string.input_mode_mouse); + } + + }