/* Copyright (C) 2002-2005 RealVNC Ltd. 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. */ // // CConn // // Methods on CConn are called from both the GUI thread and the thread which // processes incoming RFB messages ("the RFB thread"). This means we need to // be careful with synchronization here. // // Any access to writer() must not only be synchronized, but we must also make // sure that the connection is in RFBSTATE_NORMAL. We are guaranteed this for // any code called after serverInit() has been called. Since the DesktopWindow // isn't created until then, any methods called only from DesktopWindow can // assume that we are in RFBSTATE_NORMAL. package com.iiordanov.tigervnc.vncviewer; import java.io.IOException; import java.io.InputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.net.URL; import java.util.*; import android.graphics.Bitmap; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import com.iiordanov.bVNC.RemoteCanvas; import com.iiordanov.bVNC.AbstractBitmapData; import com.iiordanov.bVNC.RfbConnectable; import com.iiordanov.bVNC.input.RemoteKeyboard; import com.iiordanov.tigervnc.rdr.*; import com.iiordanov.tigervnc.rfb.*; import com.iiordanov.tigervnc.rfb.Exception; import com.iiordanov.bVNC.*; import com.iiordanov.freebVNC.*; import com.iiordanov.aRDP.*; import com.iiordanov.freeaRDP.*; import com.iiordanov.aSPICE.*; import com.iiordanov.freeaSPICE.*; public class CConn extends CConnection implements UserPasswdGetter, UserMsgBox, RfbConnectable { private final static String TAG = "CConn"; //////////////////////////////////////////////////////////////////// // The following methods are all called from the RFB thread public CConn(RemoteCanvas viewer_, java.net.Socket sock_, String vncServerName, boolean reverse, AbstractConnectionBean connection) { super(viewer_); this.viewOnly = connection.getViewOnly(); this.connection = connection; serverHost = null; serverPort = 0; sock = sock_; currentEncoding = Encodings.encodingTight; lastServerEncoding = -1; fullColour = true; // TODO: viewer.fullColour.getValue(); lowColourLevel = 2; autoSelect = false; // TODO: viewer.autoSelect.getValue(); shared = true; // TODO: viewer.shared.getValue(); formatChange = false; encodingChange = false; sameMachine = false; fullScreen = true; //TODO: viewer.fullScreen.getValue(); menuKey = Keysyms.F8; // TODO: options = new OptionsDialog(this); // TODO: options.initDialog(); // TODO: clipboardDialog = new ClipboardDialog(this); firstUpdate = true; pendingUpdate = false; setShared(shared); upg = this; msg = this; String encStr = "Tight"; //TODO: viewer.preferredEncoding.getValue(); int encNum = Encodings.encodingNum(encStr); if (encNum != -1) { currentEncoding = encNum; } cp.supportsDesktopResize = true; cp.supportsExtendedDesktopSize = true; cp.supportsClientRedirect = true; cp.supportsDesktopRename = true; cp.supportsLocalCursor = false; // TODO: viewer.useLocalCursor.getValue(); cp.customCompressLevel = true; // TODO: viewer.customCompressLevel.getValue(); cp.compressLevel = 9; // TODO: viewer.compressLevel.getValue(); cp.noJpeg = false; // TODO: viewer.noJpeg.getValue(); cp.qualityLevel = 6; // TODO: viewer.qualityLevel.getValue(); //initMenu(); if (sock != null) { String name = sock.getRemoteSocketAddress()+"::"+sock.getPort(); vlog.info("Accepted connection from "+name); } else { if (vncServerName != null) { serverHost = Hostname.getHost(vncServerName); serverPort = Hostname.getPort(vncServerName); } else { // TODO: ServerDialog dlg = new ServerDialog(options, vncServerName, this); //if (!dlg.showDialog()) { // System.exit(1); //} serverHost = connection.getAddress(); serverPort = connection.getPort(); } try { sock = new java.net.Socket(serverHost, serverPort); } catch (java.io.IOException e) { throw new Exception(e.toString()); } vlog.info("connected to host "+serverHost+" port "+serverPort); } sameMachine = (sock.getLocalSocketAddress() == sock.getRemoteSocketAddress()); try { sock.setTcpNoDelay(true); sock.setSoTimeout(0); setServerName(serverHost); jis = new JavaInStream(sock.getInputStream()); jos = new JavaOutStream(sock.getOutputStream()); } catch (java.net.SocketException e) { throw new Exception(e.toString()); } catch (java.io.IOException e) { throw new Exception(e.toString()); } processSecurityTypes (); setStreams(jis, jos); initialiseProtocol(); } /** * Processes messages from VNC server indefinitely. */ public void processProtocol () { while (true) processMsg(); } public boolean showMsgBox(int flags, String title, String text) { //StringBuffer titleText = new StringBuffer("VNC Viewer: "+title); return true; } // getUserPasswd() is called by the CSecurity object when it needs us to read // a password from the user. public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) { String title = ("VNC Authentication [" +csecurity.description() + "]"); String passwordFileStr = ""; // TODO: viewer.passwordFile.getValue(); //PasswdDialog dlg; if (user == null && passwordFileStr != "") { InputStream fp = null; try { fp = new FileInputStream(passwordFileStr); } catch(FileNotFoundException e) { throw new Exception("Opening password file failed"); } byte[] obfPwd = new byte[256]; try { fp.read(obfPwd); fp.close(); } catch(IOException e) { throw new Exception("Failed to read VncPasswd file"); } String PlainPasswd = VncAuth.unobfuscatePasswd(obfPwd); passwd.append(PlainPasswd); passwd.setLength(PlainPasswd.length()); return true; } if (user == null) { //dlg = new PasswdDialog(title, (user == null), (passwd == null)); } else { if ((passwd == null) && false /*TODO: viewer.sendLocalUsername.getValue()*/) { user.append((String)System.getProperties().get("user.name")); return true; } //dlg = new PasswdDialog(title, viewer.sendLocalUsername.getValue(), // (passwd == null)); } //if (!dlg.showDialog()) return false; if (user != null) { if (false /*TODO: viewer.sendLocalUsername.getValue()*/) { user.append((String)System.getProperties().get("user.name")); } else { user.append(connection.getUserName()); } } if (passwd != null) passwd.append(connection.getPassword()); return true; } // CConnection callback methods // serverInit() is called when the serverInit message has been received. At // this point we create the desktop window and display it. We also tell the // server the pixel format and encodings to use and request the first update. public void serverInit() { super.serverInit(); // If using AutoSelect with old servers, start in FullColor // mode. See comment in autoSelectFormatAndEncoding. if (cp.beforeVersion(3, 8) && autoSelect) { fullColour = true; } serverPF = cp.pf(); //desktop = new DesktopWindow(cp.width, cp.height, serverPF, this); //desktopEventHandler = desktop.setEventHandler(this); //desktop.addEventMask(KeyPressMask | KeyReleaseMask); //fullColourPF = desktop.getPreferredPF(); if (!serverPF.trueColour) fullColour = true; //recreateViewport(); formatChange = true; encodingChange = true; requestNewUpdate(); } // setDesktopSize() is called when the desktop size changes (including when // it is set initially). public void setDesktopSize(int w, int h) { super.setDesktopSize(w,h); resizeFramebuffer(); } // setExtendedDesktopSize() is a more advanced version of setDesktopSize() public void setExtendedDesktopSize(int reason, int result, int w, int h, ScreenSet layout) { super.setExtendedDesktopSize(reason, result, w, h, layout); if ((reason == screenTypes.reasonClient) && (result != screenTypes.resultSuccess)) { vlog.error("SetDesktopSize failed: "+result); return; } resizeFramebuffer(); } // clientRedirect() migrates the client to another host/port public void clientRedirect(int port, String host, String x509subject) { try { getSocket().close(); setServerPort(port); sock = new java.net.Socket(host, port); sock.setTcpNoDelay(true); sock.setSoTimeout(0); setSocket(sock); vlog.info("Redirected to "+host+":"+port); setStreams(new JavaInStream(sock.getInputStream()), new JavaOutStream(sock.getOutputStream())); initialiseProtocol(); } catch (java.io.IOException e) { e.printStackTrace(); } } // setName() is called when the desktop name changes public void setName(String name) { super.setName(name); } // framebufferUpdateStart() is called at the beginning of an update. // Here we try to send out a new framebuffer update request so that the // next update can be sent out in parallel with us decoding the current // one. We cannot do this if we're in the middle of a format change // though. public void framebufferUpdateStart() { if (!formatChange) { pendingUpdate = true; requestNewUpdate(); } else pendingUpdate = false; } // framebufferUpdateEnd() is called at the end of an update. // For each rectangle, the FdInStream will have timed the speed // of the connection, allowing us to select format and encoding // appropriately, and then request another incremental update. public void framebufferUpdateEnd() { //desktop.framebufferUpdateEnd(); if (firstUpdate) { int width, height; if (cp.supportsSetDesktopSize) { width = viewer.getVisibleWidth(); height = viewer.getVisibleHeight(); ScreenSet layout; layout = cp.screenLayout; if (layout.num_screens() == 0) layout.add_screen(new Screen()); else if (layout.num_screens() != 1) { while (true) { Iterator iter = layout.screens.iterator(); Screen screen = (Screen)iter.next(); if (!iter.hasNext()) break; layout.remove_screen(screen.id); } } Screen screen0 = (Screen)layout.screens.iterator().next(); screen0.dimensions.tl.x = 0; screen0.dimensions.tl.y = 0; screen0.dimensions.br.x = width; screen0.dimensions.br.y = height; writer().writeSetDesktopSize(width, height, layout); } firstUpdate = false; } // A format change prevented us from sending this before the update, // so make sure to send it now. if (formatChange && !pendingUpdate) requestNewUpdate(); // Compute new settings based on updated bandwidth values if (autoSelect) autoSelectFormatAndEncoding(); } // The rest of the callbacks are fairly self-explanatory... // TODO: Implement // public void setColourMapEntries(int firstColour, int nColours, int[] rgbs) { // desktop.setColourMapEntries(firstColour, nColours, rgbs); // } public void serverCutText(String str, int len) { viewer.serverJustCutText = true; viewer.setClipboardText(str); } // We start timing on beginRect and stop timing on endRect, to // avoid skewing the bandwidth estimation as a result of the server // being slow or the network having high latency public void beginRect(Rect r, int encoding) { ((JavaInStream)getInStream()).startTiming(); if (encoding != Encodings.encodingCopyRect) { lastServerEncoding = encoding; } } public void endRect(Rect r, int encoding) { ((JavaInStream)getInStream()).stopTiming(); } public void fillRect(Rect r, int p) { int w = r.width(); int h = r.height(); if (viewer.bitmapData.validDraw(r.tl.x, r.tl.y, w, h)) { viewer.bitmapData.fillRect(r.tl.x, r.tl.y, w, h, p); viewer.reDraw(r.tl.x, r.tl.y, w, h); } } public void imageRect(Rect r, int[] p) { int w = r.width(); int h = r.height(); if (viewer.bitmapData.validDraw(r.tl.x, r.tl.y, w, h)) { viewer.bitmapData.imageRect(r.tl.x, r.tl.y, w, h, p); viewer.reDraw(r.tl.x, r.tl.y, w, h); } } public void imageRect(Rect r, Bitmap b) { int w = r.width(); int h = r.height(); if (viewer.bitmapData.validDraw(r.tl.x, r.tl.y, w, h)) { viewer.bitmapData.updateBitmap(b, r.tl.x, r.tl.y, w, h); viewer.reDraw(r.tl.x, r.tl.y, w, h); } } public void copyRect(Rect r, int sx, int sy) { int w = r.width(); int h = r.height(); if (viewer.bitmapData.validDraw(r.tl.x, r.tl.y, w, h)) { viewer.bitmapData.copyRect(sx, sy, r.tl.x, r.tl.y, w, h); viewer.reDraw(r.tl.x, r.tl.y, w, h); } } public PixelFormat getPreferredPF() { return fullColourPF; } public void setCursor(int width, int height, Point hotspot, int[] data, byte[] mask) { //TODO: Implement //viewer.bitmapData.setCursor(width, height, hotspot, data, mask); } private void resizeFramebuffer() { if ((cp.width == 0) && (cp.height == 0)) return; if (viewer.bitmapData == null) return; if ((viewer.bitmapData.fbWidth() == cp.width) && (viewer.bitmapData.fbHeight() == cp.height)) return; viewer.updateFBSize(); } // autoSelectFormatAndEncoding() chooses the format and encoding appropriate // to the connection speed: // // First we wait for at least one second of bandwidth measurement. // // Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality, // which should be perceptually lossless. // // If the bandwidth is below that, we choose a more lossy JPEG quality. // // If the bandwidth drops below 256 Kbps, we switch to palette mode. // // Note: The system here is fairly arbitrary and should be replaced // with something more intelligent at the server end. // private void autoSelectFormatAndEncoding() { long kbitsPerSecond = ((JavaInStream)getInStream()).kbitsPerSecond(); long timeWaited = ((JavaInStream)getInStream()).timeWaited(); boolean newFullColour = fullColour; int newQualityLevel = cp.qualityLevel; // Always use Tight if (currentEncoding != Encodings.encodingTight) { currentEncoding = Encodings.encodingTight; encodingChange = true; } // Check that we have a decent bandwidth measurement if ((kbitsPerSecond == 0) || (timeWaited < 10000)) return; // Select appropriate quality level if (!cp.noJpeg) { if (kbitsPerSecond > 16000) newQualityLevel = 8; else newQualityLevel = 6; if (newQualityLevel != cp.qualityLevel) { vlog.info("Throughput "+kbitsPerSecond+ " kbit/s - changing to quality "+newQualityLevel); cp.qualityLevel = newQualityLevel; //TODO: Implement //viewer.qualityLevel.setParam(Integer.toString(newQualityLevel)); encodingChange = true; } } if (cp.beforeVersion(3, 8)) { // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with // cursors "asynchronously". If this happens in the middle of a // pixel format change, the server will encode the cursor with // the old format, but the client will try to decode it // according to the new format. This will lead to a // crash. Therefore, we do not allow automatic format change for // old servers. return; } // Select best color level newFullColour = (kbitsPerSecond > 256); if (newFullColour != fullColour) { vlog.info("Throughput "+kbitsPerSecond+ " kbit/s - full color is now "+ (newFullColour ? "enabled" : "disabled")); fullColour = newFullColour; formatChange = true; } } // requestNewUpdate() requests an update from the server, having set the // format and encoding appropriately. private void requestNewUpdate() { if (formatChange) { /* Catch incorrect requestNewUpdate calls */ assert(pendingUpdate == false); // TODO: Implement if (fullColour) { cp.setPF(new PixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 0)); } else { if (lowColourLevel == 0) { cp.setPF(new PixelFormat(8,3,false,true,1,1,1,2,1,0)); } else if (lowColourLevel == 1) { cp.setPF(new PixelFormat(8,6,false,true,3,3,3,4,2,0)); } else { cp.setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6)); } } String str = cp.pf().print(); vlog.info("Using pixel format "+str); synchronized (this) { writer().writeSetPixelFormat(cp.pf()); } } checkEncodings(); viewer.writeFullUpdateRequest(!formatChange); formatChange = false; } //////////////////////////////////////////////////////////////////// // The following methods are all called from the GUI thread // close() closes the socket, thus waking up the RFB thread. public void close() { try { shuttingDown = true; sock.close(); } catch (java.io.IOException e) { e.printStackTrace(); } } // Menu callbacks. These are guaranteed only to be called after serverInit() // has been called, since the menu is only accessible from the DesktopWindow // private void initMenu() { // menu = new F8Menu(this); // } public void refresh() { viewer.writeFullUpdateRequest(false); pendingUpdate = true; } void processSecurityTypes () { if (state() != RFBSTATE_NORMAL) { /* Process security types which don't use encryption */ //Security.EnableSecType(Security.secTypeNone); //Security.EnableSecType(Security.secTypeVncAuth); //Security.EnableSecType(Security.secTypePlain); //Security.EnableSecType(Security.secTypeIdent); Security.DisableSecType(Security.secTypeNone); Security.DisableSecType(Security.secTypeVncAuth); Security.DisableSecType(Security.secTypePlain); Security.DisableSecType(Security.secTypeIdent); /* Process security types which use TLS encryption */ //Security.EnableSecType(Security.secTypeTLS); Security.EnableSecType(Security.secTypeTLSNone); Security.EnableSecType(Security.secTypeTLSVnc); Security.EnableSecType(Security.secTypeTLSPlain); Security.EnableSecType(Security.secTypeTLSIdent); Security.DisableSecType(Security.secTypeTLS); //Security.DisableSecType(Security.secTypeTLSNone); //Security.DisableSecType(Security.secTypeTLSVnc); //Security.DisableSecType(Security.secTypeTLSPlain); //Security.DisableSecType(Security.secTypeTLSIdent); /* Process security types which use X509 encryption */ Security.EnableSecType(Security.secTypeX509None); Security.EnableSecType(Security.secTypeX509Vnc); Security.EnableSecType(Security.secTypeX509Plain); Security.EnableSecType(Security.secTypeX509Ident); //Security.DisableSecType(Security.secTypeX509None); //Security.DisableSecType(Security.secTypeX509Vnc); //Security.DisableSecType(Security.secTypeX509Plain); //Security.DisableSecType(Security.secTypeX509Ident); /* Process *None security types Security.EnableSecType(Security.secTypeNone); Security.EnableSecType(Security.secTypeTLSNone); Security.EnableSecType(Security.secTypeX509None); //Security.DisableSecType(Security.secTypeNone); //Security.DisableSecType(Security.secTypeTLSNone); //Security.DisableSecType(Security.secTypeX509None); */ /* Process *Vnc security types Security.EnableSecType(Security.secTypeVncAuth); Security.EnableSecType(Security.secTypeTLSVnc); Security.EnableSecType(Security.secTypeX509Vnc); //Security.DisableSecType(Security.secTypeVncAuth); //Security.DisableSecType(Security.secTypeTLSVnc); //Security.DisableSecType(Security.secTypeX509Vnc); */ /* Process *Plain security types Security.EnableSecType(Security.secTypePlain); Security.EnableSecType(Security.secTypeTLSPlain); Security.EnableSecType(Security.secTypeX509Plain); //Security.DisableSecType(Security.secTypePlain); //Security.DisableSecType(Security.secTypeTLSPlain); //Security.DisableSecType(Security.secTypeX509Plain); */ /* Process *Ident security types Security.EnableSecType(Security.secTypeIdent); Security.EnableSecType(Security.secTypeTLSIdent); Security.EnableSecType(Security.secTypeX509Ident); //Security.DisableSecType(Security.secTypeIdent); //Security.DisableSecType(Security.secTypeTLSIdent); //Security.DisableSecType(Security.secTypeX509Ident); */ } } // writeClientCutText() is called from the clipboard dialog synchronized public void writeClientCutText(String str, int len) { if (state() != RFBSTATE_NORMAL || viewOnly) return; try { writer().writeClientCutText(str,len); } catch (java.lang.Exception e) { Log.e(TAG, "Could not write text to VNC server clipboard."); e.printStackTrace(); } } synchronized public void writeKeyEvent(int keysym, int metaState, boolean down) { if (state() != RFBSTATE_NORMAL || viewOnly) return; writeModifiers(metaState); writer().writeKeyEvent(keysym, down); if (!down) writeModifiers(0); } synchronized public void writeKeyEvent(int keysym, boolean down) { if (state() != RFBSTATE_NORMAL || viewOnly) return; writer().writeKeyEvent(keysym, down); } // TODO: Get this ported. synchronized public void writeKeyEvent(KeyEvent ev) { if (ev.getAction() != KeyEvent.ACTION_DOWN || viewOnly) return; int keysym = 0; switch (ev.getKeyCode()) { case KeyEvent.KEYCODE_DEL: keysym = Keysyms.BackSpace; break; case KeyEvent.KEYCODE_TAB: keysym = Keysyms.Tab; break; case KeyEvent.KEYCODE_ENTER: keysym = Keysyms.Return; break; case 111: /*KEYCODE_ESCAPE*/ keysym = Keysyms.Escape; break; default: vlog.debug("key press "+ev.getUnicodeChar()); if (ev.getUnicodeChar() < 32) { // if the ctrl modifier key is down, send the equivalent ASCII since we // will send the ctrl modifier anyway if ((ev.getMetaState() & 28672 /*KeyEvent.META_CTRL_MASK*/) != 0) { if ((ev.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) { keysym = ev.getUnicodeChar() + 64; if (keysym == -1) return; } else { keysym = ev.getUnicodeChar() + 96; if (keysym == 127) keysym = 95; } } else if (ev.getUnicodeChar() == 127) { keysym = Keysyms.Delete; } else { // KEY_ACTION vlog.debug("key action "+ev.getKeyCode()); switch (ev.getKeyCode()) { case KeyEvent.KEYCODE_HOME: keysym = Keysyms.Home; break; /* case KeyEvent.KEYCODE_END: keysym = Keysyms.End; break; case KeyEvent.KEYCODE_PAGE_UP: keysym = Keysyms.Page_Up; break; case KeyEvent.KEYCODE_PAGE_DOWN: keysym = Keysyms.Page_Down; break; case KeyEvent.KEYCODE_UP: keysym = Keysyms.Up; break; case KeyEvent.KEYCODE_DOWN: keysym = Keysyms.Down; break; case KeyEvent.KEYCODE_LEFT: keysym = Keysyms.Left; break; case KeyEvent.KEYCODE_RIGHT: keysym = Keysyms.Right; break; case KeyEvent.KEYCODE_F1: keysym = Keysyms.F1; break; case KeyEvent.KEYCODE_F2: keysym = Keysyms.F2; break; case KeyEvent.KEYCODE_F3: keysym = Keysyms.F3; break; case KeyEvent.KEYCODE_F4: keysym = Keysyms.F4; break; case KeyEvent.KEYCODE_F5: keysym = Keysyms.F5; break; case KeyEvent.KEYCODE_F6: keysym = Keysyms.F6; break; case KeyEvent.KEYCODE_F7: keysym = Keysyms.F7; break; case KeyEvent.KEYCODE_F8: keysym = Keysyms.F8; break; case KeyEvent.KEYCODE_F9: keysym = Keysyms.F9; break; case KeyEvent.KEYCODE_F10: keysym = Keysyms.F10; break; case KeyEvent.KEYCODE_F11: keysym = Keysyms.F11; break; case KeyEvent.KEYCODE_F12: keysym = Keysyms.F12; break; case KeyEvent.KEYCODE_PRINTSCREEN: keysym = Keysyms.Print; break; case KeyEvent.KEYCODE_PAUSE: keysym = Keysyms.Pause; break; case KeyEvent.KEYCODE_INSERT: keysym = Keysyms.Insert; break; */ default: keysym = UnicodeToKeysym.translate(ev.getUnicodeChar()); if (keysym == -1) return; } } } } writeModifiers(ev.getMetaState()); writeKeyEvent(keysym, true); writeKeyEvent(keysym, false); writeModifiers(0); } public void writePointerEvent(int mouseX, int mouseY, int metaState, int pointerMask) { if (state() != RFBSTATE_NORMAL || viewOnly) return; synchronized (this) { writeModifiers(metaState); } synchronized (this) { writer().writePointerEvent(new Point(mouseX,mouseY), pointerMask); } if (buttonMask == 0) writeModifiers(0); } public void writePointerEvent(MotionEvent ev) { if (state() != RFBSTATE_NORMAL || viewOnly) return; switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: android.util.Log.e("CConn", "PointerEvent action down"); buttonMask = 1; if ((ev.getMetaState() & 50) != 0) buttonMask = 2; if ((ev.getMetaState() & 458752) != 0) buttonMask = 4; break; case MotionEvent.ACTION_UP: android.util.Log.e("CConn", "PointerEvent action up"); buttonMask = 0; break; } synchronized (this) { writeModifiers(ev.getMetaState() & ~50 & ~458752); } /* if (cp.width != viewer.bitmapData.width() || cp.height != viewer.bitmapData.height()) { // TODO: Inspect int sx = (int) ((viewer.bitmapData.scaleWidthRatio == 1.00) ? ev.getX() : (int)Math.floor(ev.getX()/viewer.bitmapData.scaleWidthRatio)); int sy = (int) ((viewer.bitmapData.scaleHeightRatio == 1.00) ? ev.getY() : (int)Math.floor(ev.getY()/viewer.bitmapData.scaleHeightRatio)); ev.setLocation(sx - ev.getX(), sy - ev.getY()); } */ synchronized (this) { writer().writePointerEvent(new Point(ev.getX(),ev.getY()), buttonMask); } if (buttonMask == 0) writeModifiers(0); } /* synchronized public void writeWheelEvent(MouseWheelEvent ev) { if (state() != RFBSTATE_NORMAL) return; int x, y; int clicks = ev.getWheelRotation(); if (clicks < 0) { buttonMask = 8; } else { buttonMask = 16; } writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK); for (int i=0;i<Math.abs(clicks);i++) { x = ev.getX(); y = ev.getY(); writer().writePointerEvent(new Point(x, y), buttonMask); buttonMask = 0; writer().writePointerEvent(new Point(x, y), buttonMask); } writeModifiers(0); } */ // TODO: Get this ported. synchronized void writeModifiers(int newModifiers) { if ((newModifiers & RemoteKeyboard.CTRL_MASK) != (oldModifiers & RemoteKeyboard.CTRL_MASK)) writeKeyEvent(0xffe3, (newModifiers & RemoteKeyboard.CTRL_MASK) != 0); if ((newModifiers & RemoteKeyboard.SHIFT_MASK) != (oldModifiers & RemoteKeyboard.SHIFT_MASK)) writeKeyEvent(0xffe1, (newModifiers & RemoteKeyboard.SHIFT_MASK) != 0); if ((newModifiers & RemoteKeyboard.ALT_MASK) != (oldModifiers & RemoteKeyboard.ALT_MASK)) writeKeyEvent(0xffe9, (newModifiers & RemoteKeyboard.ALT_MASK) != 0); oldModifiers = newModifiers; } //////////////////////////////////////////////////////////////////// // The following methods are called from both RFB and GUI threads // checkEncodings() sends a setEncodings message if one is needed. synchronized private void checkEncodings() { if (encodingChange && state() == RFBSTATE_NORMAL) { vlog.info("Using "+Encodings.encodingName(currentEncoding)+" encoding"); writer().writeSetEncodings(currentEncoding, true); encodingChange = false; } } public String getEncoding () { switch (currentEncoding) { case Encodings.encodingRaw: return "RAW"; case Encodings.encodingTight: return "TIGHT"; case Encodings.encodingCoRRE: return "CoRRE"; case Encodings.encodingHextile: return "HEXTILE"; case Encodings.encodingRRE: return "RRE"; case Encodings.encodingZRLE: return "ZRLE"; } return ""; } // The following methods are implementations of the RfbConnectable interface @Override public int framebufferWidth () { return cp.width; } @Override public int framebufferHeight () { return cp.height; } @Override public String desktopName () { return cp.name(); } @Override public void requestUpdate (boolean incremental) { if (incremental) refresh(); else requestNewUpdate(); } @Override public void writeClientCutText(String text) { writeClientCutText(text, text.length()); } @Override public void setIsInNormalProtocol(boolean state) { if (state) setState (RFBSTATE_NORMAL); else setState (RFBSTATE_INVALID); } @Override public boolean isInNormalProtocol() { return (state() == RFBSTATE_NORMAL); } @Override public synchronized void writeFramebufferUpdateRequest(int x, int y, int w, int h, boolean incremental) { synchronized (this) { writer().writeFramebufferUpdateRequest(new Rect(x, y, x+w, y+h), incremental); } } public void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian, boolean trueColour, int redMax, int greenMax, int blueMax, int redShift, int greenShift, int blueShift, boolean fGreyScale) { // TODO: This is broken for anything less than true color. /* fullColour = false; formatChange = true; //encodingChange = false; lowColourLevel = 2; this.requestNewUpdate(); */ /* cp.setPF(new PixelFormat(bitsPerPixel, depth, bigEndian, trueColour, redMax, greenMax, blueMax, redShift, greenShift, blueShift)); String str = cp.pf().print(); vlog.info("Using pixel format "+str); synchronized (this) { writer().writeSetPixelFormat(cp.pf()); } checkEncodings(); synchronized (this) { writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), false); } */ } // the following never change so need no synchronization: JavaInStream jis; JavaOutStream jos; // access to desktop by different threads is specified in DesktopWindow // the following need no synchronization: ClassLoader cl = this.getClass().getClassLoader(); public static UserPasswdGetter upg; public UserMsgBox msg; // shuttingDown is set by the GUI thread and only ever tested by the RFB // thread after the window has been destroyed. boolean shuttingDown; // reading and writing int and boolean is atomic in java, so no // synchronization of the following flags is needed: int currentEncoding, lastServerEncoding; int lowColourLevel; // All menu, options, about and info stuff is done in the GUI thread (apart // from when constructed). //F8Menu menu; //OptionsDialog options; // clipboard sync issues? //ClipboardDialog clipboardDialog; // the following are only ever accessed by the GUI thread: int buttonMask; int oldModifiers; public String serverHost; public int serverPort; public int menuKey; PixelFormat serverPF; PixelFormat fullColourPF; boolean fullColour; boolean autoSelect; boolean shared; boolean formatChange; boolean encodingChange; boolean sameMachine; boolean fullScreen; boolean reverseConnection; boolean firstUpdate; boolean pendingUpdate; boolean viewOnly; AbstractConnectionBean connection; static LogWriter vlog = new LogWriter("CConn"); @Override public void requestResolution(int x, int y) { // TODO Auto-generated method stub } }