//
// Copyright (C) 2010 Michael A. MacDonald
// Copyright (C) 2004 Horizon Wimba. All Rights Reserved.
// Copyright (C) 2001-2003 HorizonLive.com, Inc. All Rights Reserved.
// Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved.
// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
// 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 2 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.
//
//
// VncCanvas is a subclass of android.view.SurfaceView which draws a VNC
// desktop on it.
//
package android.androidVNC;
import java.io.IOException;
import java.util.zip.Inflater;
import ClouDesk.AppConnection;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.Toast;
import com.antlersoft.android.bc.BCFactory;
public class VncCanvas extends ImageView {
private final static String TAG = "VncCanvas";
private final static boolean LOCAL_LOGV = true;
AbstractScaling scaling;
// Available to activity
int mouseX, mouseY;
// Connection parameters
ConnectionBean connection;
// Runtime control flags
private boolean maintainConnection = true;
private boolean showDesktopInfo = true;
private boolean repaintsEnabled = true;
/**
* Use camera button as meta key for right mouse button
*/
boolean cameraButtonDown = false;
// Keep track when a seeming key press was the result of a menu shortcut
int lastKeyDown;
boolean afterMenu;
// Color Model settings
private COLORMODEL pendingColorModel = COLORMODEL.C24bit;
private COLORMODEL colorModel = null;
private int bytesPerPixel = 0;
private int[] colorPalette = null;
// VNC protocol connection
public RfbProto rfb;
// Internal bitmap data
AbstractBitmapData bitmapData;
public Handler handler = new Handler();
// VNC Encoding parameters
private boolean useCopyRect = false; // TODO CopyRect is not working
private int preferredEncoding = -1;
// Unimplemented VNC encoding parameters
private boolean requestCursorUpdates = false;
private boolean ignoreCursorUpdates = true;
// Unimplemented TIGHT encoding parameters
private int compressLevel = -1;
private int jpegQuality = -1;
// Used to determine if encoding update is necessary
private int[] encodingsSaved = new int[20];
private int nEncodingsSaved = 0;
// ZRLE encoder's data.
private byte[] zrleBuf;
private int[] zrleTilePixels;
private ZlibInStream zrleInStream;
// Zlib encoder's data.
private byte[] zlibBuf;
private Inflater zlibInflater;
private MouseScrollRunnable scrollRunnable;
private Paint handleRREPaint;
/**
* Position of the top left portion of the <i>visible</i> part of the screen, in
* full-frame coordinates
*/
int absoluteXPosition = -300 , absoluteYPosition = 0;
/**
* Constructor used by the inflation apparatus
* @param context
*/
public VncCanvas(final Context context, AttributeSet attrs)
{
super(context, attrs);
scrollRunnable = new MouseScrollRunnable();
handleRREPaint = new Paint();
handleRREPaint.setStyle(Style.FILL);
}
/**
* Create a view showing a VNC connection
* @param context Containing context (activity)
* @param bean Connection settings
* @param setModes Callback to run on UI thread after connection is set up
*/
void initializeVncCanvas(ConnectionBean bean, final Runnable setModes) {
connection = bean;
this.pendingColorModel = COLORMODEL.valueOf(bean.getColorModel());
// Startup the RFB thread with a nifty progess dialog
final ProgressDialog pd = ProgressDialog.show(getContext(), "正在连接...", "建立连接.\n请等待...", true, true, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface arg0) {
// TODO Auto-generated method stub
}
});
pd.setCancelable(false);
final Display display = pd.getWindow().getWindowManager().getDefaultDisplay();
Thread t = new Thread() {
public void run() {
try {
connectAndAuthenticate(connection.getUserName(),connection.getPassword());
//调整分辨率
doProtocolInitialisation(display.getWidth(), display.getHeight()-43);
//Log.i("Size", Integer.toString(display.getWidth()));
handler.post(new Runnable() {
public void run() {
pd.setMessage("正在连接.\n请等待...");
}
});
processNormalProtocol(getContext(), pd, setModes);
} catch (Throwable e) {
if (maintainConnection) {
Log.e(TAG, e.toString());
e.printStackTrace();
// Ensure we dismiss the progress dialog
// before we fatal error finish
if (pd.isShowing())
pd.dismiss();
if (e instanceof OutOfMemoryError) {
// TODO Not sure if this will happen but...
// figure out how to gracefully notify the user
// Instantiating an alert dialog here doesn't work
// because we are out of memory. :(
} else {
String error = "VNC connection failed!";
if (e.getMessage() != null && (e.getMessage().indexOf("authentication") > -1)) {
error = "VNC authentication failed!";
}
final String error_ = error + "<br>" + e.getLocalizedMessage();
handler.post(new Runnable() {
public void run() {
Utils.showFatalErrorMessage(getContext(), error_);
}
});
}
}
}
}
};
t.start();
}
void connectAndAuthenticate(String us,String pw) throws Exception {
Log.i(TAG, "Connecting to " + connection.getAddress() + ", port " + connection.getPort() + "...");
rfb = new RfbProto(connection.getAddress(), connection.getPort());
if (LOCAL_LOGV) Log.v(TAG, "Connected to server");
// <RepeaterMagic>
if (connection.getUseRepeater() && connection.getRepeaterId() != null && connection.getRepeaterId().length()>0) {
Log.i(TAG, "Negotiating repeater/proxy connection");
byte[] protocolMsg = new byte[12];
rfb.is.read(protocolMsg);
byte[] buffer = new byte[250];
System.arraycopy(connection.getRepeaterId().getBytes(), 0, buffer, 0, connection.getRepeaterId().length());
rfb.os.write(buffer);
}
// </RepeaterMagic>
rfb.readVersionMsg();
Log.i(TAG, "RFB server supports protocol version " + rfb.serverMajor + "." + rfb.serverMinor);
rfb.writeVersionMsg();
Log.i(TAG, "Using RFB protocol version " + rfb.clientMajor + "." + rfb.clientMinor);
int bitPref=0;
if(connection.getUserName().length()>0)
bitPref|=1;
Log.d("debug","bitPref="+bitPref);
int secType = rfb.negotiateSecurity(bitPref);
int authType;
if (secType == RfbProto.SecTypeTight) {
rfb.initCapabilities();
rfb.setupTunneling();
authType = rfb.negotiateAuthenticationTight();
} else if (secType == RfbProto.SecTypeUltra34) {
rfb.prepareDH();
authType = RfbProto.AuthUltra;
} else {
authType = secType;
}
switch (authType) {
case RfbProto.AuthNone:
Log.i(TAG, "No authentication needed");
rfb.authenticateNone();
break;
case RfbProto.AuthVNC:
Log.i(TAG, "VNC authentication needed");
rfb.authenticateVNC(pw);
break;
case RfbProto.AuthUltra:
rfb.authenticateDH(us,pw);
break;
default:
throw new Exception("Unknown authentication scheme " + authType);
}
}
void doProtocolInitialisation(int dx, int dy) throws IOException {
rfb.writeClientInit();
rfb.readServerInit();
//modify by jiangyue
AppConnection AppConnection_ = AppConnection.getInstance();
AppConnection_.requestSetSize(dx, dy);
Log.i(TAG, "Desktop name is " + rfb.desktopName);
Log.i(TAG, "Desktop size is " + rfb.framebufferWidth + " x " + rfb.framebufferHeight);
boolean useFull = false;
int capacity = BCFactory.getInstance().getBCActivityManager().getMemoryClass(Utils.getActivityManager(getContext()));
if (connection.getForceFull() == BitmapImplHint.AUTO)
{
if (rfb.framebufferWidth * rfb.framebufferHeight * FullBufferBitmapData.CAPACITY_MULTIPLIER <= capacity * 1024 * 1024)
useFull = true;
}
else
useFull = (connection.getForceFull() == BitmapImplHint.FULL);
if (! useFull)
bitmapData=new LargeBitmapData(rfb,this,dx,dy,capacity); //modify
else
bitmapData=new FullBufferBitmapData(rfb,this, capacity);
mouseX=rfb.framebufferWidth/2;
mouseY=rfb.framebufferHeight/2;
setPixelFormat();
}
private void setPixelFormat() throws IOException {
pendingColorModel.setPixelFormat(rfb);
bytesPerPixel = pendingColorModel.bpp();
colorPalette = pendingColorModel.palette();
colorModel = pendingColorModel;
pendingColorModel = null;
}
public void setColorModel(COLORMODEL cm) {
// Only update if color model changes
if (colorModel == null || !colorModel.equals(cm))
pendingColorModel = cm;
}
public boolean isColorModel(COLORMODEL cm) {
return (colorModel != null) && colorModel.equals(cm);
}
private void mouseFollowPan()
{
if (connection.getFollowPan() && scaling.isAbleToPan())
{
int scrollx = absoluteXPosition;
int scrolly = absoluteYPosition;
int width = getVisibleWidth();
int height = 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)
{
//Log.i(TAG,"warp to " + scrollx+width/2 + "," + scrolly + height/2);
warpMouse(scrollx + width/2, scrolly + height / 2);
}
}
}
public void processNormalProtocol(final Context context, ProgressDialog pd, final Runnable setModes) throws Exception {
try {
bitmapData.writeFullUpdateRequest(false);
handler.post(setModes);
//
// main dispatch loop
//
while (maintainConnection) {
bitmapData.syncScroll();
// Read message type from the server.
int msgType = rfb.readServerMessageType();
bitmapData.doneWaiting();
// Process the message depending on its type.
switch (msgType) {
case RfbProto.FramebufferUpdate:
rfb.readFramebufferUpdate();
for (int i = 0; i < rfb.updateNRects; i++) {
rfb.readFramebufferUpdateRectHdr();
int rx = rfb.updateRectX, ry = rfb.updateRectY;
int rw = rfb.updateRectW, rh = rfb.updateRectH;
if (rfb.updateRectEncoding == RfbProto.EncodingLastRect) {
Log.v(TAG, "rfb.EncodingLastRect");
break;
}
if (rfb.updateRectEncoding == RfbProto.EncodingNewFBSize) {
rfb.setFramebufferSize(rw, rh);
// - updateFramebufferSize();
Log.v(TAG, "rfb.EncodingNewFBSize");
break;
}
if (rfb.updateRectEncoding == RfbProto.EncodingXCursor || rfb.updateRectEncoding == RfbProto.EncodingRichCursor) {
// - handleCursorShapeUpdate(rfb.updateRectEncoding,
// rx,
// ry, rw, rh);
Log.v(TAG, "rfb.EncodingCursor");
continue;
}
if (rfb.updateRectEncoding == RfbProto.EncodingPointerPos) {
// This never actually happens
mouseX=rx;
mouseY=ry;
Log.v(TAG, "rfb.EncodingPointerPos");
continue;
}
//rfb.startTiming();
switch (rfb.updateRectEncoding) {
case RfbProto.EncodingRaw:
handleRawRect(rx, ry, rw, rh);
break;
case RfbProto.EncodingCopyRect:
handleCopyRect(rx, ry, rw, rh);
Log.v(TAG, "CopyRect is Buggy!");
break;
case RfbProto.EncodingRRE:
handleRRERect(rx, ry, rw, rh);
break;
case RfbProto.EncodingCoRRE:
handleCoRRERect(rx, ry, rw, rh);
break;
case RfbProto.EncodingHextile:
handleHextileRect(rx, ry, rw, rh);
break;
case RfbProto.EncodingZRLE:
handleZRLERect(rx, ry, rw, rh);
break;
case RfbProto.EncodingZlib:
handleZlibRect(rx, ry, rw, rh);
break;
default:
Log.e(TAG, "Unknown RFB rectangle encoding " + rfb.updateRectEncoding + " (0x" + Integer.toHexString(rfb.updateRectEncoding) + ")");
}
//rfb.stopTiming();
// Hide progress dialog
if (pd.isShowing())
pd.dismiss();
}
boolean fullUpdateNeeded = false;
if (pendingColorModel != null) {
setPixelFormat();
fullUpdateNeeded = true;
}
setEncodings(true);
bitmapData.writeFullUpdateRequest(!fullUpdateNeeded);
break;
case RfbProto.SetColourMapEntries:
throw new Exception("Can't handle SetColourMapEntries message");
case RfbProto.Bell:
handler.post( new Runnable() {
public void run() { Toast.makeText( context, "VNC Beep", Toast.LENGTH_SHORT); }
});
break;
case RfbProto.ServerCutText:
String s = rfb.readServerCutText();
if (s != null && s.length() > 0) {
// TODO implement cut & paste
}
break;
case RfbProto.TextChat:
// UltraVNC extension
String msg = rfb.readTextChatMsg();
if (msg != null && msg.length() > 0) {
// TODO implement chat interface
}
break;
default:
throw new Exception("Unknown RFB message type " + msgType);
}
}
} catch (Exception e) {
throw e;
} finally {
Log.v(TAG, "Closing VNC Connection");
rfb.close();
}
}
/**
* Apply scroll offset and scaling to convert touch-space coordinates to the corresponding
* point on the full frame.
* @param e MotionEvent with the original, touch space coordinates. This event is altered in place.
* @return e -- The same event passed in, with the coordinates mapped
*/
MotionEvent changeTouchCoordinatesToFullFrame(MotionEvent e)
{
//Log.v(TAG, String.format("tap at %f,%f", e.getX(), e.getY()));
float scale = getScale();
// Adjust coordinates for Android notification bar.
e.offsetLocation(0, -1f * getTop());
e.setLocation(absoluteXPosition + e.getX() / scale, absoluteYPosition + e.getY() / scale);
return e;
}
public void onDestroy() {
Log.v(TAG, "Cleaning up resources");
if ( bitmapData!=null) bitmapData.dispose();
bitmapData = null;
}
/**
* Warp the mouse to x, y in the RFB coordinates
* @param x
* @param y
*/
void warpMouse(int x, int y)
{
bitmapData.invalidateMousePosition();
mouseX=x;
mouseY=y;
bitmapData.invalidateMousePosition();
try
{
rfb.writePointerEvent(x, y, 0, MOUSE_BUTTON_NONE);
}
catch ( IOException ioe)
{
Log.w(TAG,ioe);
}
}
/*
* f(x,s) is a function that returns the coordinate in screen/scroll space corresponding
* to the coordinate x in full-frame space with scaling s.
*
* This function returns the difference between f(x,s1) and f(x,s2)
*
* f(x,s) = (x - i/2) * s + ((i - w)/2)) * s
* = s (x - i/2 + i/2 + w/2)
* = s (x + w/2)
*
*
* f(x,s) = (x - ((i - w)/2)) * s
* @param oldscaling
* @param scaling
* @param imageDim
* @param windowDim
* @param offset
* @return
*/
/**
* Change to Canvas's scroll position to match the absoluteXPosition
*/
void scrollToAbsolute()
{
float scale = getScale();
scrollTo((int)((absoluteXPosition + ((float)getWidth() - getImageWidth()) / 2 ) * scale),
(int)((absoluteYPosition + ((float)getHeight() - getImageHeight()) / 2 ) * scale));
}
/**
* Make sure mouse is visible on displayable part of screen
*/
void panToMouse()
{
if (! connection.getFollowMouse())
return;
if (scaling != null && ! scaling.isAbleToPan())
return;
int x = mouseX;
int y = mouseY;
boolean panned = false;
int w = getVisibleWidth();
int h = getVisibleHeight();
int iw = getImageWidth();
int ih = getImageHeight();
int newX = absoluteXPosition;
int newY = absoluteYPosition;
if (x - newX >= w - 5)
{
newX = x - w + 5;
if (newX + w > iw)
newX = iw - w;
}
else if (x < newX + 5)
{
newX = x - 5;
if (newX < 0)
newX = 0;
}
if ( newX != absoluteXPosition ) {
absoluteXPosition = newX;
panned = true;
}
if (y - newY >= h - 5)
{
newY = y - h + 5;
if (newY + h > ih)
newY = ih - h;
}
else if (y < newY + 5)
{
newY = y - 5;
if (newY < 0)
newY = 0;
}
if ( newY != absoluteYPosition ) {
absoluteYPosition = newY;
panned = true;
}
if (panned)
{
scrollToAbsolute();
}
}
/**
* Pan by a number of pixels (relative pan)
* @param dX
* @param dY
* @return True if the pan changed the view (did not move view out of bounds); false otherwise
*/
boolean pan(int dX, int dY) {
double scale = getScale();
double sX = (double)dX / scale;
double sY = (double)dY / scale;
if (absoluteXPosition + sX < 0)
// dX = diff to 0
sX = -absoluteXPosition;
if (absoluteYPosition + sY < 0)
sY = -absoluteYPosition;
// Prevent panning right or below desktop image
if (absoluteXPosition + getVisibleWidth() + sX > getImageWidth())
sX = getImageWidth() - getVisibleWidth() - absoluteXPosition;
if (absoluteYPosition + getVisibleHeight() + sY > getImageHeight())
sY = getImageHeight() - getVisibleHeight() - absoluteYPosition;
absoluteXPosition += sX;
absoluteYPosition += sY;
if (sX != 0.0 || sY != 0.0)
{
scrollToAbsolute();
return true;
}
return false;
}
/* (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(absoluteXPosition, absoluteYPosition);
mouseFollowPan();
}
void handleRawRect(int x, int y, int w, int h) throws IOException {
handleRawRect(x, y, w, h, true);
}
byte[] handleRawRectBuffer = new byte[128];
void handleRawRect(int x, int y, int w, int h, boolean paint) throws IOException {
boolean valid=bitmapData.validDraw(x, y, w, h);
int[] pixels=bitmapData.bitmapPixels;
if (bytesPerPixel == 1) {
// 1 byte per pixel. Use palette lookup table.
if (w > handleRawRectBuffer.length) {
handleRawRectBuffer = new byte[w];
}
int i, offset;
for (int dy = y; dy < y + h; dy++) {
rfb.readFully(handleRawRectBuffer, 0, w);
if ( ! valid)
continue;
offset = bitmapData.offset(x, dy);
for (i = 0; i < w; i++) {
pixels[offset + i] = colorPalette[0xFF & handleRawRectBuffer[i]];
}
}
} else {
// 4 bytes per pixel (argb) 24-bit color
final int l = w * 4;
if (l>handleRawRectBuffer.length) {
handleRawRectBuffer = new byte[l];
}
int i, offset;
for (int dy = y; dy < y + h; dy++) {
rfb.readFully(handleRawRectBuffer, 0, l);
if ( ! valid)
continue;
offset = bitmapData.offset(x, dy);
for (i = 0; i < w; i++) {
final int idx = i*4;
pixels[offset + i] = // 0xFF << 24 |
(handleRawRectBuffer[idx + 2] & 0xff) << 16 | (handleRawRectBuffer[idx + 1] & 0xff) << 8 | (handleRawRectBuffer[idx] & 0xff);
}
}
}
if ( ! valid)
return;
bitmapData.updateBitmap( x, y, w, h);
if (paint)
reDraw();
}
private Runnable reDraw = new Runnable() {
public void run() {
if (showDesktopInfo) {
// Show a Toast with the desktop info on first frame draw.
showDesktopInfo = false;
showConnectionInfo();
}
if (bitmapData != null)
bitmapData.updateView(VncCanvas.this);
}
};
private void reDraw() {
if (repaintsEnabled)
handler.post(reDraw);
}
public void disableRepaints() {
repaintsEnabled = false;
}
public void enableRepaints() {
repaintsEnabled = true;
}
public void showConnectionInfo() {
String msg = rfb.desktopName;
int idx = rfb.desktopName.indexOf("(");
if (idx > -1) {
// Breakup actual desktop name from IP addresses for improved
// readability
String dn = rfb.desktopName.substring(0, idx).trim();
String ip = rfb.desktopName.substring(idx).trim();
msg = dn + "\n" + ip;
}
msg += "\n" + rfb.framebufferWidth + "x" + rfb.framebufferHeight;
String enc = getEncoding();
// Encoding might not be set when we display this message
if (enc != null && !enc.equals(""))
msg += ", " + getEncoding() + " encoding, " + colorModel.toString();
else
msg += ", " + colorModel.toString();
//Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
}
private String getEncoding() {
switch (preferredEncoding) {
case RfbProto.EncodingRaw:
return "RAW";
case RfbProto.EncodingTight:
return "TIGHT";
case RfbProto.EncodingCoRRE:
return "CoRRE";
case RfbProto.EncodingHextile:
return "HEXTILE";
case RfbProto.EncodingRRE:
return "RRE";
case RfbProto.EncodingZlib:
return "ZLIB";
case RfbProto.EncodingZRLE:
return "ZRLE";
}
return "";
}
// 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;
static final int MOUSE_BUTTON_LEFT = 1;
static final int MOUSE_BUTTON_MIDDLE = 2;
static final int MOUSE_BUTTON_RIGHT = 4;
static final int MOUSE_BUTTON_SCROLL_UP = 8;
static final int MOUSE_BUTTON_SCROLL_DOWN = 16;
/**
* 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.
* @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
*/
//修改此处的坐标;09/30
public boolean processPointerEvent(MotionEvent evt,boolean downEvent,boolean useRightButton) {
return processPointerEvent((int)evt.getX(),(int)evt.getY(), evt.getAction(), evt.getMetaState(), downEvent, useRightButton);
}
/**
* 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. cameraButton flag is interpreted as second mouse
* button
* @param downEvent True if "mouse button" (touch or trackball button) is down when this happens
* @return true if event was actually sent
*/
public boolean processPointerEvent(MotionEvent evt,boolean downEvent)
{
return processPointerEvent(evt,downEvent,cameraButtonDown);
}
boolean processPointerEvent(int x, int y, int action, int modifiers, boolean mouseIsDown, boolean useRightButton) {
if (rfb != null && rfb.inNormalProtocol) {
if (action == MotionEvent.ACTION_DOWN || (mouseIsDown && action == MotionEvent.ACTION_MOVE)) {
if (useRightButton) {
pointerMask = MOUSE_BUTTON_RIGHT;
} else {
pointerMask = MOUSE_BUTTON_LEFT;
}
} else if (action == MotionEvent.ACTION_UP) {
pointerMask = 0;
}
bitmapData.invalidateMousePosition();
mouseX= x;
mouseY= y;
if ( mouseX<0 && !VncCanvasActivity.isLinux) 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;
bitmapData.invalidateMousePosition();
try {
rfb.writePointerEvent(mouseX,mouseY,modifiers,pointerMask);
} catch (Exception e) {
e.printStackTrace();
}
panToMouse();
return true;
}
return false;
}
/**
* Moves the scroll while the volume key is held down
* @author Michael A. MacDonald
*/
class MouseScrollRunnable implements Runnable
{
int delay = 100;
int scrollButton = 0;
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try
{
rfb.writePointerEvent(mouseX, mouseY, 0, scrollButton);
rfb.writePointerEvent(mouseX, mouseY, 0, 0);
handler.postDelayed(this, delay);
}
catch (IOException ioe)
{
}
}
}
public boolean processLocalKeyEvent(int keyCode, KeyEvent evt) {
if (keyCode == KeyEvent.KEYCODE_MENU)
// Ignore menu key
return true;
if (keyCode == KeyEvent.KEYCODE_CAMERA)
{
cameraButtonDown = (evt.getAction() != KeyEvent.ACTION_UP);
}
else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP)
{
int mouseChange = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? MOUSE_BUTTON_SCROLL_DOWN : MOUSE_BUTTON_SCROLL_UP;
if (evt.getAction() == KeyEvent.ACTION_DOWN)
{
// If not auto-repeat
if (scrollRunnable.scrollButton != mouseChange)
{
pointerMask |= mouseChange;
scrollRunnable.scrollButton = mouseChange;
handler.postDelayed(scrollRunnable,200);
}
}
else
{
handler.removeCallbacks(scrollRunnable);
scrollRunnable.scrollButton = 0;
pointerMask &= ~mouseChange;
}
try
{
rfb.writePointerEvent(mouseX, mouseY, evt.getMetaState(), pointerMask);
}
catch (IOException ioe)
{
// TODO: do something with exception
}
return true;
}
if (rfb != null && rfb.inNormalProtocol) {
boolean down = (evt.getAction() == KeyEvent.ACTION_DOWN);
int key;
int metaState = evt.getMetaState();
switch(keyCode) {
case KeyEvent.KEYCODE_BACK : key = 0xff1b; break;
case KeyEvent.KEYCODE_DPAD_LEFT: key = 0xff51; break;
case KeyEvent.KEYCODE_DPAD_UP: key = 0xff52; break;
case KeyEvent.KEYCODE_DPAD_RIGHT: key = 0xff53; break;
case KeyEvent.KEYCODE_DPAD_DOWN: key = 0xff54; break;
case KeyEvent.KEYCODE_DEL: key = 0xff08; break;
case KeyEvent.KEYCODE_ENTER: key = 0xff0d; break;
case KeyEvent.KEYCODE_DPAD_CENTER: key = 0xff0d; break;
default:
key = evt.getUnicodeChar();
metaState = 0;
break;
}
try {
if (afterMenu)
{
afterMenu = false;
if (!down && key != lastKeyDown)
return true;
}
if (down)
lastKeyDown = key;
//Log.i(TAG,"key = " + key + " metastate = " + metaState + " keycode = " + keyCode);
rfb.writeKeyEvent(key, metaState, down);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
return false;
}
public void closeConnection() {
maintainConnection = false;
}
void sendMetaKey(MetaKeyBean meta)
{
if (meta.isMouseClick())
{
try {
rfb.writePointerEvent(mouseX, mouseY, meta.getMetaFlags(), meta.getMouseButtons());
rfb.writePointerEvent(mouseX, mouseY, meta.getMetaFlags(), 0);
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
else {
try {
rfb.writeKeyEvent(meta.getKeySym(), meta.getMetaFlags(), true);
rfb.writeKeyEvent(meta.getKeySym(), meta.getMetaFlags(), false);
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
float getScale()
{
if (scaling == null)
return 1;
return scaling.getScale();
}
public int getVisibleWidth() {
return (int)((double)getWidth() / getScale() + 0.5);
}
public int getVisibleHeight() {
return (int)((double)getHeight() / getScale() + 0.5);
}
public int getImageWidth() {
return bitmapData.framebufferwidth;
}
public int getImageHeight() {
return bitmapData.framebufferheight;
}
public int getCenteredXOffset() {
int xoffset = (bitmapData.framebufferwidth - getWidth()) / 2;
return xoffset;
}
public int getCenteredYOffset() {
int yoffset = (bitmapData.framebufferheight - getHeight()) / 2;
return yoffset;
}
/**
* Additional Encodings
*
*/
private void setEncodings(boolean autoSelectOnly) {
if (rfb == null || !rfb.inNormalProtocol)
return;
if (preferredEncoding == -1) {
// Preferred format is ZRLE
preferredEncoding = RfbProto.EncodingZRLE;
} else {
// Auto encoder selection is not enabled.
if (autoSelectOnly)
return;
}
int[] encodings = new int[20];
int nEncodings = 0;
encodings[nEncodings++] = preferredEncoding;
if (useCopyRect)
encodings[nEncodings++] = RfbProto.EncodingCopyRect;
// if (preferredEncoding != RfbProto.EncodingTight)
// encodings[nEncodings++] = RfbProto.EncodingTight;
if (preferredEncoding != RfbProto.EncodingZRLE)
encodings[nEncodings++] = RfbProto.EncodingZRLE;
if (preferredEncoding != RfbProto.EncodingHextile)
encodings[nEncodings++] = RfbProto.EncodingHextile;
if (preferredEncoding != RfbProto.EncodingZlib)
encodings[nEncodings++] = RfbProto.EncodingZlib;
if (preferredEncoding != RfbProto.EncodingCoRRE)
encodings[nEncodings++] = RfbProto.EncodingCoRRE;
if (preferredEncoding != RfbProto.EncodingRRE)
encodings[nEncodings++] = RfbProto.EncodingRRE;
if (compressLevel >= 0 && compressLevel <= 9)
encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + compressLevel;
if (jpegQuality >= 0 && jpegQuality <= 9)
encodings[nEncodings++] = RfbProto.EncodingQualityLevel0 + jpegQuality;
if (requestCursorUpdates) {
encodings[nEncodings++] = RfbProto.EncodingXCursor;
encodings[nEncodings++] = RfbProto.EncodingRichCursor;
if (!ignoreCursorUpdates)
encodings[nEncodings++] = RfbProto.EncodingPointerPos;
}
encodings[nEncodings++] = RfbProto.EncodingLastRect;
encodings[nEncodings++] = RfbProto.EncodingNewFBSize;
boolean encodingsWereChanged = false;
if (nEncodings != nEncodingsSaved) {
encodingsWereChanged = true;
} else {
for (int i = 0; i < nEncodings; i++) {
if (encodings[i] != encodingsSaved[i]) {
encodingsWereChanged = true;
break;
}
}
}
if (encodingsWereChanged) {
try {
rfb.writeSetEncodings(encodings, nEncodings);
} catch (Exception e) {
e.printStackTrace();
}
encodingsSaved = encodings;
nEncodingsSaved = nEncodings;
}
}
//
// Handle a CopyRect rectangle.
//
final Paint handleCopyRectPaint = new Paint();
private void handleCopyRect(int x, int y, int w, int h) throws IOException {
/**
* This does not work properly yet.
*/
rfb.readCopyRect();
if ( ! bitmapData.validDraw(x, y, w, h))
return;
// Source Coordinates
int leftSrc = rfb.copyRectSrcX;
int topSrc = rfb.copyRectSrcY;
int rightSrc = topSrc + w;
int bottomSrc = topSrc + h;
// Change
int dx = x - rfb.copyRectSrcX;
int dy = y - rfb.copyRectSrcY;
// Destination Coordinates
int leftDest = leftSrc + dx;
int topDest = topSrc + dy;
int rightDest = rightSrc + dx;
int bottomDest = bottomSrc + dy;
bitmapData.copyRect(new Rect(leftSrc, topSrc, rightSrc, bottomSrc), new Rect(leftDest, topDest, rightDest, bottomDest), handleCopyRectPaint);
reDraw();
}
byte[] bg_buf = new byte[4];
byte[] rre_buf = new byte[128];
//
// Handle an RRE-encoded rectangle.
//
private void handleRRERect(int x, int y, int w, int h) throws IOException {
boolean valid=bitmapData.validDraw(x, y, w, h);
int nSubrects = rfb.is.readInt();
rfb.readFully(bg_buf, 0, bytesPerPixel);
int pixel;
if (bytesPerPixel == 1) {
pixel = colorPalette[0xFF & bg_buf[0]];
} else {
pixel = Color.rgb(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF);
}
handleRREPaint.setColor(pixel);
if ( valid)
bitmapData.drawRect(x, y, w, h, handleRREPaint);
int len = nSubrects * (bytesPerPixel + 8);
if (len > rre_buf.length)
rre_buf = new byte[len];
rfb.readFully(rre_buf, 0, len);
if ( ! valid)
return;
int sx, sy, sw, sh;
int i = 0;
for (int j = 0; j < nSubrects; j++) {
if (bytesPerPixel == 1) {
pixel = colorPalette[0xFF & rre_buf[i++]];
} else {
pixel = Color.rgb(rre_buf[i + 2] & 0xFF, rre_buf[i + 1] & 0xFF, rre_buf[i] & 0xFF);
i += 4;
}
sx = x + ((rre_buf[i] & 0xff) << 8) + (rre_buf[i+1] & 0xff); i+=2;
sy = y + ((rre_buf[i] & 0xff) << 8) + (rre_buf[i+1] & 0xff); i+=2;
sw = ((rre_buf[i] & 0xff) << 8) + (rre_buf[i+1] & 0xff); i+=2;
sh = ((rre_buf[i] & 0xff) << 8) + (rre_buf[i+1] & 0xff); i+=2;
handleRREPaint.setColor(pixel);
bitmapData.drawRect(sx, sy, sw, sh, handleRREPaint);
}
reDraw();
}
//
// Handle a CoRRE-encoded rectangle.
//
private void handleCoRRERect(int x, int y, int w, int h) throws IOException {
boolean valid=bitmapData.validDraw(x, y, w, h);
int nSubrects = rfb.is.readInt();
rfb.readFully(bg_buf, 0, bytesPerPixel);
int pixel;
if (bytesPerPixel == 1) {
pixel = colorPalette[0xFF & bg_buf[0]];
} else {
pixel = Color.rgb(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF);
}
handleRREPaint.setColor(pixel);
if ( valid)
bitmapData.drawRect(x, y, w, h, handleRREPaint);
int len = nSubrects * (bytesPerPixel + 8);
if (len > rre_buf.length)
rre_buf = new byte[len];
rfb.readFully(rre_buf, 0, len);
if ( ! valid)
return;
int sx, sy, sw, sh;
int i = 0;
for (int j = 0; j < nSubrects; j++) {
if (bytesPerPixel == 1) {
pixel = colorPalette[0xFF & rre_buf[i++]];
} else {
pixel = Color.rgb(rre_buf[i + 2] & 0xFF, rre_buf[i + 1] & 0xFF, rre_buf[i] & 0xFF);
i += 4;
}
sx = x + (rre_buf[i++] & 0xFF);
sy = y + (rre_buf[i++] & 0xFF);
sw = rre_buf[i++] & 0xFF;
sh = rre_buf[i++] & 0xFF;
handleRREPaint.setColor(pixel);
bitmapData.drawRect(sx, sy, sw, sh, handleRREPaint);
}
reDraw();
}
//
// Handle a Hextile-encoded rectangle.
//
// These colors should be kept between handleHextileSubrect() calls.
private int hextile_bg, hextile_fg;
private void handleHextileRect(int x, int y, int w, int h) throws IOException {
hextile_bg = Color.BLACK;
hextile_fg = Color.BLACK;
for (int ty = y; ty < y + h; ty += 16) {
int th = 16;
if (y + h - ty < 16)
th = y + h - ty;
for (int tx = x; tx < x + w; tx += 16) {
int tw = 16;
if (x + w - tx < 16)
tw = x + w - tx;
handleHextileSubrect(tx, ty, tw, th);
}
// Finished with a row of tiles, now let's show it.
//reDraw();
}
reDraw();
}
//
// Handle one tile in the Hextile-encoded data.
//
Paint handleHextileSubrectPaint = new Paint();
byte[] backgroundColorBuffer = new byte[4];
private void handleHextileSubrect(int tx, int ty, int tw, int th) throws IOException {
int subencoding = rfb.is.readUnsignedByte();
// Is it a raw-encoded sub-rectangle?
if ((subencoding & RfbProto.HextileRaw) != 0) {
handleRawRect(tx, ty, tw, th, false);
return;
}
boolean valid=bitmapData.validDraw(tx, ty, tw, th);
// Read and draw the background if specified.
if (bytesPerPixel > backgroundColorBuffer.length) {
throw new RuntimeException("impossible colordepth");
}
if ((subencoding & RfbProto.HextileBackgroundSpecified) != 0) {
rfb.readFully(backgroundColorBuffer, 0, bytesPerPixel);
if (bytesPerPixel == 1) {
hextile_bg = colorPalette[0xFF & backgroundColorBuffer[0]];
} else {
hextile_bg = Color.rgb(backgroundColorBuffer[2] & 0xFF, backgroundColorBuffer[1] & 0xFF, backgroundColorBuffer[0] & 0xFF);
}
}
handleHextileSubrectPaint.setColor(hextile_bg);
handleHextileSubrectPaint.setStyle(Paint.Style.FILL);
if ( valid )
bitmapData.drawRect(tx, ty, tw, th, handleHextileSubrectPaint);
// Read the foreground color if specified.
if ((subencoding & RfbProto.HextileForegroundSpecified) != 0) {
rfb.readFully(backgroundColorBuffer, 0, bytesPerPixel);
if (bytesPerPixel == 1) {
hextile_fg = colorPalette[0xFF & backgroundColorBuffer[0]];
} else {
hextile_fg = Color.rgb(backgroundColorBuffer[2] & 0xFF, backgroundColorBuffer[1] & 0xFF, backgroundColorBuffer[0] & 0xFF);
}
}
// Done with this tile if there is no sub-rectangles.
if ((subencoding & RfbProto.HextileAnySubrects) == 0)
return;
int nSubrects = rfb.is.readUnsignedByte();
int bufsize = nSubrects * 2;
if ((subencoding & RfbProto.HextileSubrectsColoured) != 0) {
bufsize += nSubrects * bytesPerPixel;
}
if (rre_buf.length < bufsize)
rre_buf = new byte[bufsize];
rfb.readFully(rre_buf, 0, bufsize);
int b1, b2, sx, sy, sw, sh;
int i = 0;
if ((subencoding & RfbProto.HextileSubrectsColoured) == 0) {
// Sub-rectangles are all of the same color.
handleHextileSubrectPaint.setColor(hextile_fg);
for (int j = 0; j < nSubrects; j++) {
b1 = rre_buf[i++] & 0xFF;
b2 = rre_buf[i++] & 0xFF;
sx = tx + (b1 >> 4);
sy = ty + (b1 & 0xf);
sw = (b2 >> 4) + 1;
sh = (b2 & 0xf) + 1;
if ( valid)
bitmapData.drawRect(sx, sy, sw, sh, handleHextileSubrectPaint);
}
} else if (bytesPerPixel == 1) {
// BGR233 (8-bit color) version for colored sub-rectangles.
for (int j = 0; j < nSubrects; j++) {
hextile_fg = colorPalette[0xFF & rre_buf[i++]];
b1 = rre_buf[i++] & 0xFF;
b2 = rre_buf[i++] & 0xFF;
sx = tx + (b1 >> 4);
sy = ty + (b1 & 0xf);
sw = (b2 >> 4) + 1;
sh = (b2 & 0xf) + 1;
handleHextileSubrectPaint.setColor(hextile_fg);
if ( valid)
bitmapData.drawRect(sx, sy, sw, sh, handleHextileSubrectPaint);
}
} else {
// Full-color (24-bit) version for colored sub-rectangles.
for (int j = 0; j < nSubrects; j++) {
hextile_fg = Color.rgb(rre_buf[i + 2] & 0xFF, rre_buf[i + 1] & 0xFF, rre_buf[i] & 0xFF);
i += 4;
b1 = rre_buf[i++] & 0xFF;
b2 = rre_buf[i++] & 0xFF;
sx = tx + (b1 >> 4);
sy = ty + (b1 & 0xf);
sw = (b2 >> 4) + 1;
sh = (b2 & 0xf) + 1;
handleHextileSubrectPaint.setColor(hextile_fg);
if ( valid )
bitmapData.drawRect(sx, sy, sw, sh, handleHextileSubrectPaint);
}
}
}
//
// Handle a ZRLE-encoded rectangle.
//
Paint handleZRLERectPaint = new Paint();
int[] handleZRLERectPalette = new int[128];
private void handleZRLERect(int x, int y, int w, int h) throws Exception {
if (zrleInStream == null)
zrleInStream = new ZlibInStream();
int nBytes = rfb.is.readInt();
if (nBytes > 64 * 1024 * 1024)
throw new Exception("ZRLE decoder: illegal compressed data size");
if (zrleBuf == null || zrleBuf.length < nBytes) {
zrleBuf = new byte[nBytes+4096];
}
rfb.readFully(zrleBuf, 0, nBytes);
zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes);
boolean valid=bitmapData.validDraw(x, y, w, h);
for (int ty = y; ty < y + h; ty += 64) {
int th = Math.min(y + h - ty, 64);
for (int tx = x; tx < x + w; tx += 64) {
int tw = Math.min(x + w - tx, 64);
int mode = zrleInStream.readU8();
boolean rle = (mode & 128) != 0;
int palSize = mode & 127;
readZrlePalette(handleZRLERectPalette, palSize);
if (palSize == 1) {
int pix = handleZRLERectPalette[0];
int c = (bytesPerPixel == 1) ? colorPalette[0xFF & pix] : (0xFF000000 | pix);
handleZRLERectPaint.setColor(c);
handleZRLERectPaint.setStyle(Paint.Style.FILL);
if ( valid)
bitmapData.drawRect(tx, ty, tw, th, handleZRLERectPaint);
continue;
}
if (!rle) {
if (palSize == 0) {
readZrleRawPixels(tw, th);
} else {
readZrlePackedPixels(tw, th, handleZRLERectPalette, palSize);
}
} else {
if (palSize == 0) {
readZrlePlainRLEPixels(tw, th);
} else {
readZrlePackedRLEPixels(tw, th, handleZRLERectPalette);
}
}
if ( valid )
handleUpdatedZrleTile(tx, ty, tw, th);
}
}
zrleInStream.reset();
reDraw();
}
//
// Handle a Zlib-encoded rectangle.
//
byte[] handleZlibRectBuffer = new byte[128];
private void handleZlibRect(int x, int y, int w, int h) throws Exception {
boolean valid = bitmapData.validDraw(x, y, w, h);
int nBytes = rfb.is.readInt();
if (zlibBuf == null || zlibBuf.length < nBytes) {
zlibBuf = new byte[nBytes*2];
}
rfb.readFully(zlibBuf, 0, nBytes);
if (zlibInflater == null) {
zlibInflater = new Inflater();
}
zlibInflater.setInput(zlibBuf, 0, nBytes);
int[] pixels=bitmapData.bitmapPixels;
if (bytesPerPixel == 1) {
// 1 byte per pixel. Use palette lookup table.
if (w > handleZlibRectBuffer.length) {
handleZlibRectBuffer = new byte[w];
}
int i, offset;
for (int dy = y; dy < y + h; dy++) {
zlibInflater.inflate(handleZlibRectBuffer, 0, w);
if ( ! valid)
continue;
offset = bitmapData.offset(x, dy);
for (i = 0; i < w; i++) {
pixels[offset + i] = colorPalette[0xFF & handleZlibRectBuffer[i]];
}
}
} else {
// 24-bit color (ARGB) 4 bytes per pixel.
final int l = w*4;
if (l > handleZlibRectBuffer.length) {
handleZlibRectBuffer = new byte[l];
}
int i, offset;
for (int dy = y; dy < y + h; dy++) {
zlibInflater.inflate(handleZlibRectBuffer, 0, l);
if ( ! valid)
continue;
offset = bitmapData.offset(x, dy);
for (i = 0; i < w; i++) {
final int idx = i*4;
pixels[offset + i] = (handleZlibRectBuffer[idx + 2] & 0xFF) << 16 | (handleZlibRectBuffer[idx + 1] & 0xFF) << 8 | (handleZlibRectBuffer[idx] & 0xFF);
}
}
}
if ( ! valid)
return;
bitmapData.updateBitmap(x, y, w, h);
reDraw();
}
private int readPixel(InStream is) throws Exception {
int pix;
if (bytesPerPixel == 1) {
pix = is.readU8();
} else {
int p1 = is.readU8();
int p2 = is.readU8();
int p3 = is.readU8();
pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF);
}
return pix;
}
byte[] readPixelsBuffer = new byte[128];
private void readPixels(InStream is, int[] dst, int count) throws Exception {
if (bytesPerPixel == 1) {
if (count > readPixelsBuffer.length) {
readPixelsBuffer = new byte[count];
}
is.readBytes(readPixelsBuffer, 0, count);
for (int i = 0; i < count; i++) {
dst[i] = (int) readPixelsBuffer[i] & 0xFF;
}
} else {
final int l = count * 3;
if (l > readPixelsBuffer.length) {
readPixelsBuffer = new byte[l];
}
is.readBytes(readPixelsBuffer, 0, l);
for (int i = 0; i < count; i++) {
final int idx = i*3;
dst[i] = ((readPixelsBuffer[idx + 2] & 0xFF) << 16 | (readPixelsBuffer[idx + 1] & 0xFF) << 8 | (readPixelsBuffer[idx] & 0xFF));
}
}
}
private void readZrlePalette(int[] palette, int palSize) throws Exception {
readPixels(zrleInStream, palette, palSize);
}
private void readZrleRawPixels(int tw, int th) throws Exception {
int len = tw * th;
if (zrleTilePixels == null || len > zrleTilePixels.length)
zrleTilePixels = new int[len];
readPixels(zrleInStream, zrleTilePixels, tw * th); // /
}
private void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) throws Exception {
int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
int ptr = 0;
int len = tw * th;
if (zrleTilePixels == null || len > zrleTilePixels.length)
zrleTilePixels = new int[len];
for (int i = 0; i < th; i++) {
int eol = ptr + tw;
int b = 0;
int nbits = 0;
while (ptr < eol) {
if (nbits == 0) {
b = zrleInStream.readU8();
nbits = 8;
}
nbits -= bppp;
int index = (b >> nbits) & ((1 << bppp) - 1) & 127;
if (bytesPerPixel == 1) {
if (index >= colorPalette.length)
Log.e(TAG, "zrlePlainRLEPixels palette lookup out of bounds " + index + " (0x" + Integer.toHexString(index) + ")");
zrleTilePixels[ptr++] = colorPalette[0xFF & palette[index]];
} else {
zrleTilePixels[ptr++] = palette[index];
}
}
}
}
private void readZrlePlainRLEPixels(int tw, int th) throws Exception {
int ptr = 0;
int end = ptr + tw * th;
if (zrleTilePixels == null || end > zrleTilePixels.length)
zrleTilePixels = new int[end];
while (ptr < end) {
int pix = readPixel(zrleInStream);
int len = 1;
int b;
do {
b = zrleInStream.readU8();
len += b;
} while (b == 255);
if (!(len <= end - ptr))
throw new Exception("ZRLE decoder: assertion failed" + " (len <= end-ptr)");
if (bytesPerPixel == 1) {
while (len-- > 0)
zrleTilePixels[ptr++] = colorPalette[0xFF & pix];
} else {
while (len-- > 0)
zrleTilePixels[ptr++] = pix;
}
}
}
private void readZrlePackedRLEPixels(int tw, int th, int[] palette) throws Exception {
int ptr = 0;
int end = ptr + tw * th;
if (zrleTilePixels == null || end > zrleTilePixels.length)
zrleTilePixels = new int[end];
while (ptr < end) {
int index = zrleInStream.readU8();
int len = 1;
if ((index & 128) != 0) {
int b;
do {
b = zrleInStream.readU8();
len += b;
} while (b == 255);
if (!(len <= end - ptr))
throw new Exception("ZRLE decoder: assertion failed" + " (len <= end - ptr)");
}
index &= 127;
int pix = palette[index];
if (bytesPerPixel == 1) {
while (len-- > 0)
zrleTilePixels[ptr++] = colorPalette[0xFF & pix];
} else {
while (len-- > 0)
zrleTilePixels[ptr++] = pix;
}
}
}
//
// Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update.
//
private void handleUpdatedZrleTile(int x, int y, int w, int h) {
int offsetSrc = 0;
int[] destPixels=bitmapData.bitmapPixels;
for (int j = 0; j < h; j++) {
System.arraycopy(zrleTilePixels, offsetSrc, destPixels, bitmapData.offset(x, y + j), w);
offsetSrc += w;
}
bitmapData.updateBitmap(x, y, w, h);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if (processLocalKeyEvent(keyCode,event))
return true;
return super.onKeyDown(keyCode, event);
}
}