source: trunk/vnc/vnc_javasrc/RfbProto.java @ 71

Last change on this file since 71 was 66, checked in by quentin, 17 years ago

Initial checkin of modified Java VNC viewer for use as remote console

File size: 36.5 KB
Line 
1//
2//  Copyright (C) 2001-2004 HorizonLive.com, Inc.  All Rights Reserved.
3//  Copyright (C) 2001-2006 Constantin Kaplinsky.  All Rights Reserved.
4//  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
5//  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
6//
7//  This is free software; you can redistribute it and/or modify
8//  it under the terms of the GNU General Public License as published by
9//  the Free Software Foundation; either version 2 of the License, or
10//  (at your option) any later version.
11//
12//  This software is distributed in the hope that it will be useful,
13//  but WITHOUT ANY WARRANTY; without even the implied warranty of
14//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15//  GNU General Public License for more details.
16//
17//  You should have received a copy of the GNU General Public License
18//  along with this software; if not, write to the Free Software
19//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
20//  USA.
21//
22
23//
24// RfbProto.java
25//
26
27import java.io.*;
28import java.awt.*;
29import java.awt.event.*;
30import java.net.Socket;
31import java.util.zip.*;
32
33class RfbProto {
34
35  final static String
36    versionMsg_3_3 = "RFB 003.003\n",
37    versionMsg_3_7 = "RFB 003.007\n",
38    versionMsg_3_8 = "RFB 003.008\n";
39
40  // Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC
41  final static String
42    StandardVendor  = "STDV",
43    TridiaVncVendor = "TRDV",
44    TightVncVendor  = "TGHT";
45
46  // Security types
47  final static int
48    SecTypeInvalid = 0,
49    SecTypeNone    = 1,
50    SecTypeVncAuth = 2,
51    SecTypeTight   = 16;
52
53  // Supported tunneling types
54  final static int
55    NoTunneling = 0;
56  final static String
57    SigNoTunneling = "NOTUNNEL";
58
59  // Supported authentication types
60  final static int
61    AuthNone      = 1,
62    AuthVNC       = 2,
63    AuthUnixLogin = 129;
64  final static String
65    SigAuthNone      = "NOAUTH__",
66    SigAuthVNC       = "VNCAUTH_",
67    SigAuthUnixLogin = "ULGNAUTH";
68
69  // VNC authentication results
70  final static int
71    VncAuthOK      = 0,
72    VncAuthFailed  = 1,
73    VncAuthTooMany = 2;
74
75  // Server-to-client messages
76  final static int
77    FramebufferUpdate   = 0,
78    SetColourMapEntries = 1,
79    Bell                = 2,
80    ServerCutText       = 3;
81
82  // Client-to-server messages
83  final static int
84    SetPixelFormat           = 0,
85    FixColourMapEntries      = 1,
86    SetEncodings             = 2,
87    FramebufferUpdateRequest = 3,
88    KeyboardEvent            = 4,
89    PointerEvent             = 5,
90    ClientCutText            = 6;
91
92  // Supported encodings and pseudo-encodings
93  final static int
94    EncodingRaw            = 0,
95    EncodingCopyRect       = 1,
96    EncodingRRE            = 2,
97    EncodingCoRRE          = 4,
98    EncodingHextile        = 5,
99    EncodingZlib           = 6,
100    EncodingTight          = 7,
101    EncodingZRLE           = 16,
102    EncodingCompressLevel0 = 0xFFFFFF00,
103    EncodingQualityLevel0  = 0xFFFFFFE0,
104    EncodingXCursor        = 0xFFFFFF10,
105    EncodingRichCursor     = 0xFFFFFF11,
106    EncodingPointerPos     = 0xFFFFFF18,
107    EncodingLastRect       = 0xFFFFFF20,
108    EncodingNewFBSize      = 0xFFFFFF21;
109  final static String
110    SigEncodingRaw            = "RAW_____",
111    SigEncodingCopyRect       = "COPYRECT",
112    SigEncodingRRE            = "RRE_____",
113    SigEncodingCoRRE          = "CORRE___",
114    SigEncodingHextile        = "HEXTILE_",
115    SigEncodingZlib           = "ZLIB____",
116    SigEncodingTight          = "TIGHT___",
117    SigEncodingZRLE           = "ZRLE____",
118    SigEncodingCompressLevel0 = "COMPRLVL",
119    SigEncodingQualityLevel0  = "JPEGQLVL",
120    SigEncodingXCursor        = "X11CURSR",
121    SigEncodingRichCursor     = "RCHCURSR",
122    SigEncodingPointerPos     = "POINTPOS",
123    SigEncodingLastRect       = "LASTRECT",
124    SigEncodingNewFBSize      = "NEWFBSIZ";
125
126  final static int MaxNormalEncoding = 255;
127
128  // Contstants used in the Hextile decoder
129  final static int
130    HextileRaw                 = 1,
131    HextileBackgroundSpecified = 2,
132    HextileForegroundSpecified = 4,
133    HextileAnySubrects         = 8,
134    HextileSubrectsColoured    = 16;
135
136  // Contstants used in the Tight decoder
137  final static int TightMinToCompress = 12;
138  final static int
139    TightExplicitFilter = 0x04,
140    TightFill           = 0x08,
141    TightJpeg           = 0x09,
142    TightMaxSubencoding = 0x09,
143    TightFilterCopy     = 0x00,
144    TightFilterPalette  = 0x01,
145    TightFilterGradient = 0x02;
146
147
148  String host;
149  int port;
150  Socket sock;
151  DataInputStream is;
152  OutputStream os;
153  SessionRecorder rec;
154  boolean inNormalProtocol = false;
155  VncViewer viewer;
156
157  // Java on UNIX does not call keyPressed() on some keys, for example
158  // swedish keys To prevent our workaround to produce duplicate
159  // keypresses on JVMs that actually works, keep track of if
160  // keyPressed() for a "broken" key was called or not.
161  boolean brokenKeyPressed = false;
162
163  // This will be set to true on the first framebuffer update
164  // containing Zlib-, ZRLE- or Tight-encoded data.
165  boolean wereZlibUpdates = false;
166
167  // This will be set to false if the startSession() was called after
168  // we have received at least one Zlib-, ZRLE- or Tight-encoded
169  // framebuffer update.
170  boolean recordFromBeginning = true;
171
172  // This fields are needed to show warnings about inefficiently saved
173  // sessions only once per each saved session file.
174  boolean zlibWarningShown;
175  boolean tightWarningShown;
176
177  // Before starting to record each saved session, we set this field
178  // to 0, and increment on each framebuffer update. We don't flush
179  // the SessionRecorder data into the file before the second update.
180  // This allows us to write initial framebuffer update with zero
181  // timestamp, to let the player show initial desktop before
182  // playback.
183  int numUpdatesInSession;
184
185  // Measuring network throughput.
186  boolean timing;
187  long timeWaitedIn100us;
188  long timedKbits;
189
190  // Protocol version and TightVNC-specific protocol options.
191  int serverMajor, serverMinor;
192  int clientMajor, clientMinor;
193  boolean protocolTightVNC;
194  CapsContainer tunnelCaps, authCaps;
195  CapsContainer serverMsgCaps, clientMsgCaps;
196  CapsContainer encodingCaps;
197
198  // If true, informs that the RFB socket was closed.
199  private boolean closed;
200
201  //
202  // Constructor. Make TCP connection to RFB server.
203  //
204
205  RfbProto(String h, int p, VncViewer v) throws IOException {
206    viewer = v;
207    host = h;
208    port = p;
209
210    if (viewer.socketFactory == null) {
211      sock = new Socket(host, port);
212    } else {
213      try {
214        Class factoryClass = Class.forName(viewer.socketFactory);
215        SocketFactory factory = (SocketFactory)factoryClass.newInstance();
216        if (viewer.inAnApplet)
217          sock = factory.createSocket(host, port, viewer);
218        else
219          sock = factory.createSocket(host, port, viewer.mainArgs);
220      } catch(Exception e) {
221        e.printStackTrace();
222        throw new IOException(e.getMessage());
223      }
224    }
225    is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
226                                                     16384));
227    os = sock.getOutputStream();
228
229    timing = false;
230    timeWaitedIn100us = 5;
231    timedKbits = 0;
232  }
233
234
235  synchronized void close() {
236    try {
237      sock.close();
238      closed = true;
239      System.out.println("RFB socket closed");
240      if (rec != null) {
241        rec.close();
242        rec = null;
243      }
244    } catch (Exception e) {
245      e.printStackTrace();
246    }
247  }
248
249  synchronized boolean closed() {
250    return closed;
251  }
252
253  //
254  // Read server's protocol version message
255  //
256
257  void readVersionMsg() throws Exception {
258
259    byte[] b = new byte[12];
260
261    readFully(b);
262
263    if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ')
264        || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9')
265        || (b[6] < '0') || (b[6] > '9') || (b[7] != '.')
266        || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9')
267        || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n'))
268    {
269      throw new Exception("Host " + host + " port " + port +
270                          " is not an RFB server");
271    }
272
273    serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
274    serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0');
275
276    if (serverMajor < 3) {
277      throw new Exception("RFB server does not support protocol version 3");
278    }
279  }
280
281
282  //
283  // Write our protocol version message
284  //
285
286  void writeVersionMsg() throws IOException {
287    clientMajor = 3;
288    if (serverMajor > 3 || serverMinor >= 8) {
289      clientMinor = 8;
290      os.write(versionMsg_3_8.getBytes());
291    } else if (serverMinor >= 7) {
292      clientMinor = 7;
293      os.write(versionMsg_3_7.getBytes());
294    } else {
295      clientMinor = 3;
296      os.write(versionMsg_3_3.getBytes());
297    }
298    protocolTightVNC = false;
299  }
300
301
302  //
303  // Negotiate the authentication scheme.
304  //
305
306  int negotiateSecurity() throws Exception {
307    return (clientMinor >= 7) ?
308      selectSecurityType() : readSecurityType();
309  }
310
311  //
312  // Read security type from the server (protocol version 3.3).
313  //
314
315  int readSecurityType() throws Exception {
316    int secType = is.readInt();
317
318    switch (secType) {
319    case SecTypeInvalid:
320      readConnFailedReason();
321      return SecTypeInvalid;    // should never be executed
322    case SecTypeNone:
323    case SecTypeVncAuth:
324      return secType;
325    default:
326      throw new Exception("Unknown security type from RFB server: " + secType);
327    }
328  }
329
330  //
331  // Select security type from the server's list (protocol versions 3.7/3.8).
332  //
333
334  int selectSecurityType() throws Exception {
335    int secType = SecTypeInvalid;
336
337    // Read the list of secutiry types.
338    int nSecTypes = is.readUnsignedByte();
339    if (nSecTypes == 0) {
340      readConnFailedReason();
341      return SecTypeInvalid;    // should never be executed
342    }
343    byte[] secTypes = new byte[nSecTypes];
344    readFully(secTypes);
345
346    // Find out if the server supports TightVNC protocol extensions
347    for (int i = 0; i < nSecTypes; i++) {
348      if (secTypes[i] == SecTypeTight) {
349        protocolTightVNC = true;
350        os.write(SecTypeTight);
351        return SecTypeTight;
352      }
353    }
354
355    // Find first supported security type.
356    for (int i = 0; i < nSecTypes; i++) {
357      if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) {
358        secType = secTypes[i];
359        break;
360      }
361    }
362
363    if (secType == SecTypeInvalid) {
364      throw new Exception("Server did not offer supported security type");
365    } else {
366      os.write(secType);
367    }
368
369    return secType;
370  }
371
372  //
373  // Perform "no authentication".
374  //
375
376  void authenticateNone() throws Exception {
377    if (clientMinor >= 8)
378      readSecurityResult("No authentication");
379  }
380
381  //
382  // Perform standard VNC Authentication.
383  //
384
385  void authenticateVNC(String pw) throws Exception {
386    byte[] challenge = new byte[16];
387    readFully(challenge);
388
389    if (pw.length() > 8)
390      pw = pw.substring(0, 8);  // Truncate to 8 chars
391
392    // Truncate password on the first zero byte.
393    int firstZero = pw.indexOf(0);
394    if (firstZero != -1)
395      pw = pw.substring(0, firstZero);
396
397    byte[] key = {0, 0, 0, 0, 0, 0, 0, 0};
398    System.arraycopy(pw.getBytes(), 0, key, 0, pw.length());
399
400    DesCipher des = new DesCipher(key);
401
402    des.encrypt(challenge, 0, challenge, 0);
403    des.encrypt(challenge, 8, challenge, 8);
404
405    os.write(challenge);
406
407    readSecurityResult("VNC authentication");
408  }
409
410  //
411  // Read security result.
412  // Throws an exception on authentication failure.
413  //
414
415  void readSecurityResult(String authType) throws Exception {
416    int securityResult = is.readInt();
417
418    switch (securityResult) {
419    case VncAuthOK:
420      System.out.println(authType + ": success");
421      break;
422    case VncAuthFailed:
423      if (clientMinor >= 8)
424        readConnFailedReason();
425      throw new Exception(authType + ": failed");
426    case VncAuthTooMany:
427      throw new Exception(authType + ": failed, too many tries");
428    default:
429      throw new Exception(authType + ": unknown result " + securityResult);
430    }
431  }
432
433  //
434  // Read the string describing the reason for a connection failure,
435  // and throw an exception.
436  //
437
438  void readConnFailedReason() throws Exception {
439    int reasonLen = is.readInt();
440    byte[] reason = new byte[reasonLen];
441    readFully(reason);
442    throw new Exception(new String(reason));
443  }
444
445  //
446  // Initialize capability lists (TightVNC protocol extensions).
447  //
448
449  void initCapabilities() {
450    tunnelCaps    = new CapsContainer();
451    authCaps      = new CapsContainer();
452    serverMsgCaps = new CapsContainer();
453    clientMsgCaps = new CapsContainer();
454    encodingCaps  = new CapsContainer();
455
456    // Supported authentication methods
457    authCaps.add(AuthNone, StandardVendor, SigAuthNone,
458                 "No authentication");
459    authCaps.add(AuthVNC, StandardVendor, SigAuthVNC,
460                 "Standard VNC password authentication");
461
462    // Supported encoding types
463    encodingCaps.add(EncodingCopyRect, StandardVendor,
464                     SigEncodingCopyRect, "Standard CopyRect encoding");
465    encodingCaps.add(EncodingRRE, StandardVendor,
466                     SigEncodingRRE, "Standard RRE encoding");
467    encodingCaps.add(EncodingCoRRE, StandardVendor,
468                     SigEncodingCoRRE, "Standard CoRRE encoding");
469    encodingCaps.add(EncodingHextile, StandardVendor,
470                     SigEncodingHextile, "Standard Hextile encoding");
471    encodingCaps.add(EncodingZRLE, StandardVendor,
472                     SigEncodingZRLE, "Standard ZRLE encoding");
473    encodingCaps.add(EncodingZlib, TridiaVncVendor,
474                     SigEncodingZlib, "Zlib encoding");
475    encodingCaps.add(EncodingTight, TightVncVendor,
476                     SigEncodingTight, "Tight encoding");
477
478    // Supported pseudo-encoding types
479    encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
480                     SigEncodingCompressLevel0, "Compression level");
481    encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
482                     SigEncodingQualityLevel0, "JPEG quality level");
483    encodingCaps.add(EncodingXCursor, TightVncVendor,
484                     SigEncodingXCursor, "X-style cursor shape update");
485    encodingCaps.add(EncodingRichCursor, TightVncVendor,
486                     SigEncodingRichCursor, "Rich-color cursor shape update");
487    encodingCaps.add(EncodingPointerPos, TightVncVendor,
488                     SigEncodingPointerPos, "Pointer position update");
489    encodingCaps.add(EncodingLastRect, TightVncVendor,
490                     SigEncodingLastRect, "LastRect protocol extension");
491    encodingCaps.add(EncodingNewFBSize, TightVncVendor,
492                     SigEncodingNewFBSize, "Framebuffer size change");
493  }
494
495  //
496  // Setup tunneling (TightVNC protocol extensions)
497  //
498
499  void setupTunneling() throws IOException {
500    int nTunnelTypes = is.readInt();
501    if (nTunnelTypes != 0) {
502      readCapabilityList(tunnelCaps, nTunnelTypes);
503
504      // We don't support tunneling yet.
505      writeInt(NoTunneling);
506    }
507  }
508
509  //
510  // Negotiate authentication scheme (TightVNC protocol extensions)
511  //
512
513  int negotiateAuthenticationTight() throws Exception {
514    int nAuthTypes = is.readInt();
515    if (nAuthTypes == 0)
516      return AuthNone;
517
518    readCapabilityList(authCaps, nAuthTypes);
519    for (int i = 0; i < authCaps.numEnabled(); i++) {
520      int authType = authCaps.getByOrder(i);
521      if (authType == AuthNone || authType == AuthVNC) {
522        writeInt(authType);
523        return authType;
524      }
525    }
526    throw new Exception("No suitable authentication scheme found");
527  }
528
529  //
530  // Read a capability list (TightVNC protocol extensions)
531  //
532
533  void readCapabilityList(CapsContainer caps, int count) throws IOException {
534    int code;
535    byte[] vendor = new byte[4];
536    byte[] name = new byte[8];
537    for (int i = 0; i < count; i++) {
538      code = is.readInt();
539      readFully(vendor);
540      readFully(name);
541      caps.enable(new CapabilityInfo(code, vendor, name));
542    }
543  }
544
545  //
546  // Write a 32-bit integer into the output stream.
547  //
548
549  void writeInt(int value) throws IOException {
550    byte[] b = new byte[4];
551    b[0] = (byte) ((value >> 24) & 0xff);
552    b[1] = (byte) ((value >> 16) & 0xff);
553    b[2] = (byte) ((value >> 8) & 0xff);
554    b[3] = (byte) (value & 0xff);
555    os.write(b);
556  }
557
558  //
559  // Write the client initialisation message
560  //
561
562  void writeClientInit() throws IOException {
563    if (viewer.options.shareDesktop) {
564      os.write(1);
565    } else {
566      os.write(0);
567    }
568    viewer.options.disableShareDesktop();
569  }
570
571
572  //
573  // Read the server initialisation message
574  //
575
576  String desktopName;
577  int framebufferWidth, framebufferHeight;
578  int bitsPerPixel, depth;
579  boolean bigEndian, trueColour;
580  int redMax, greenMax, blueMax, redShift, greenShift, blueShift;
581
582  void readServerInit() throws IOException {
583    framebufferWidth = is.readUnsignedShort();
584    framebufferHeight = is.readUnsignedShort();
585    bitsPerPixel = is.readUnsignedByte();
586    depth = is.readUnsignedByte();
587    bigEndian = (is.readUnsignedByte() != 0);
588    trueColour = (is.readUnsignedByte() != 0);
589    redMax = is.readUnsignedShort();
590    greenMax = is.readUnsignedShort();
591    blueMax = is.readUnsignedShort();
592    redShift = is.readUnsignedByte();
593    greenShift = is.readUnsignedByte();
594    blueShift = is.readUnsignedByte();
595    byte[] pad = new byte[3];
596    readFully(pad);
597    int nameLength = is.readInt();
598    byte[] name = new byte[nameLength];
599    readFully(name);
600    desktopName = new String(name);
601
602    // Read interaction capabilities (TightVNC protocol extensions)
603    if (protocolTightVNC) {
604      int nServerMessageTypes = is.readUnsignedShort();
605      int nClientMessageTypes = is.readUnsignedShort();
606      int nEncodingTypes = is.readUnsignedShort();
607      is.readUnsignedShort();
608      readCapabilityList(serverMsgCaps, nServerMessageTypes);
609      readCapabilityList(clientMsgCaps, nClientMessageTypes);
610      readCapabilityList(encodingCaps, nEncodingTypes);
611    }
612
613    inNormalProtocol = true;
614  }
615
616
617  //
618  // Create session file and write initial protocol messages into it.
619  //
620
621  void startSession(String fname) throws IOException {
622    rec = new SessionRecorder(fname);
623    rec.writeHeader();
624    rec.write(versionMsg_3_3.getBytes());
625    rec.writeIntBE(SecTypeNone);
626    rec.writeShortBE(framebufferWidth);
627    rec.writeShortBE(framebufferHeight);
628    byte[] fbsServerInitMsg =   {
629      32, 24, 0, 1, 0,
630      (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF,
631      16, 8, 0, 0, 0, 0
632    };
633    rec.write(fbsServerInitMsg);
634    rec.writeIntBE(desktopName.length());
635    rec.write(desktopName.getBytes());
636    numUpdatesInSession = 0;
637
638    // FIXME: If there were e.g. ZRLE updates only, that should not
639    //        affect recording of Zlib and Tight updates. So, actually
640    //        we should maintain separate flags for Zlib, ZRLE and
641    //        Tight, instead of one ``wereZlibUpdates'' variable.
642    //
643    if (wereZlibUpdates)
644      recordFromBeginning = false;
645
646    zlibWarningShown = false;
647    tightWarningShown = false;
648  }
649
650  //
651  // Close session file.
652  //
653
654  void closeSession() throws IOException {
655    if (rec != null) {
656      rec.close();
657      rec = null;
658    }
659  }
660
661
662  //
663  // Set new framebuffer size
664  //
665
666  void setFramebufferSize(int width, int height) {
667    framebufferWidth = width;
668    framebufferHeight = height;
669  }
670
671
672  //
673  // Read the server message type
674  //
675
676  int readServerMessageType() throws IOException {
677    int msgType = is.readUnsignedByte();
678
679    // If the session is being recorded:
680    if (rec != null) {
681      if (msgType == Bell) {    // Save Bell messages in session files.
682        rec.writeByte(msgType);
683        if (numUpdatesInSession > 0)
684          rec.flush();
685      }
686    }
687
688    return msgType;
689  }
690
691
692  //
693  // Read a FramebufferUpdate message
694  //
695
696  int updateNRects;
697
698  void readFramebufferUpdate() throws IOException {
699    is.readByte();
700    updateNRects = is.readUnsignedShort();
701
702    // If the session is being recorded:
703    if (rec != null) {
704      rec.writeByte(FramebufferUpdate);
705      rec.writeByte(0);
706      rec.writeShortBE(updateNRects);
707    }
708
709    numUpdatesInSession++;
710  }
711
712  // Read a FramebufferUpdate rectangle header
713
714  int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;
715
716  void readFramebufferUpdateRectHdr() throws Exception {
717    updateRectX = is.readUnsignedShort();
718    updateRectY = is.readUnsignedShort();
719    updateRectW = is.readUnsignedShort();
720    updateRectH = is.readUnsignedShort();
721    updateRectEncoding = is.readInt();
722
723    if (updateRectEncoding == EncodingZlib ||
724        updateRectEncoding == EncodingZRLE ||
725        updateRectEncoding == EncodingTight)
726      wereZlibUpdates = true;
727
728    // If the session is being recorded:
729    if (rec != null) {
730      if (numUpdatesInSession > 1)
731        rec.flush();            // Flush the output on each rectangle.
732      rec.writeShortBE(updateRectX);
733      rec.writeShortBE(updateRectY);
734      rec.writeShortBE(updateRectW);
735      rec.writeShortBE(updateRectH);
736      if (updateRectEncoding == EncodingZlib && !recordFromBeginning) {
737        // Here we cannot write Zlib-encoded rectangles because the
738        // decoder won't be able to reproduce zlib stream state.
739        if (!zlibWarningShown) {
740          System.out.println("Warning: Raw encoding will be used " +
741                             "instead of Zlib in recorded session.");
742          zlibWarningShown = true;
743        }
744        rec.writeIntBE(EncodingRaw);
745      } else {
746        rec.writeIntBE(updateRectEncoding);
747        if (updateRectEncoding == EncodingTight && !recordFromBeginning &&
748            !tightWarningShown) {
749          System.out.println("Warning: Re-compressing Tight-encoded " +
750                             "updates for session recording.");
751          tightWarningShown = true;
752        }
753      }
754    }
755
756    if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding)
757      return;
758
759    if (updateRectX + updateRectW > framebufferWidth ||
760        updateRectY + updateRectH > framebufferHeight) {
761      throw new Exception("Framebuffer update rectangle too large: " +
762                          updateRectW + "x" + updateRectH + " at (" +
763                          updateRectX + "," + updateRectY + ")");
764    }
765  }
766
767  // Read CopyRect source X and Y.
768
769  int copyRectSrcX, copyRectSrcY;
770
771  void readCopyRect() throws IOException {
772    copyRectSrcX = is.readUnsignedShort();
773    copyRectSrcY = is.readUnsignedShort();
774
775    // If the session is being recorded:
776    if (rec != null) {
777      rec.writeShortBE(copyRectSrcX);
778      rec.writeShortBE(copyRectSrcY);
779    }
780  }
781
782
783  //
784  // Read a ServerCutText message
785  //
786
787  String readServerCutText() throws IOException {
788    byte[] pad = new byte[3];
789    readFully(pad);
790    int len = is.readInt();
791    byte[] text = new byte[len];
792    readFully(text);
793    return new String(text);
794  }
795
796
797  //
798  // Read an integer in compact representation (1..3 bytes).
799  // Such format is used as a part of the Tight encoding.
800  // Also, this method records data if session recording is active and
801  // the viewer's recordFromBeginning variable is set to true.
802  //
803
804  int readCompactLen() throws IOException {
805    int[] portion = new int[3];
806    portion[0] = is.readUnsignedByte();
807    int byteCount = 1;
808    int len = portion[0] & 0x7F;
809    if ((portion[0] & 0x80) != 0) {
810      portion[1] = is.readUnsignedByte();
811      byteCount++;
812      len |= (portion[1] & 0x7F) << 7;
813      if ((portion[1] & 0x80) != 0) {
814        portion[2] = is.readUnsignedByte();
815        byteCount++;
816        len |= (portion[2] & 0xFF) << 14;
817      }
818    }
819
820    if (rec != null && recordFromBeginning)
821      for (int i = 0; i < byteCount; i++)
822        rec.writeByte(portion[i]);
823
824    return len;
825  }
826
827
828  //
829  // Write a FramebufferUpdateRequest message
830  //
831
832  void writeFramebufferUpdateRequest(int x, int y, int w, int h,
833                                     boolean incremental)
834       throws IOException
835  {
836    byte[] b = new byte[10];
837
838    b[0] = (byte) FramebufferUpdateRequest;
839    b[1] = (byte) (incremental ? 1 : 0);
840    b[2] = (byte) ((x >> 8) & 0xff);
841    b[3] = (byte) (x & 0xff);
842    b[4] = (byte) ((y >> 8) & 0xff);
843    b[5] = (byte) (y & 0xff);
844    b[6] = (byte) ((w >> 8) & 0xff);
845    b[7] = (byte) (w & 0xff);
846    b[8] = (byte) ((h >> 8) & 0xff);
847    b[9] = (byte) (h & 0xff);
848
849    os.write(b);
850  }
851
852
853  //
854  // Write a SetPixelFormat message
855  //
856
857  void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian,
858                           boolean trueColour,
859                           int redMax, int greenMax, int blueMax,
860                           int redShift, int greenShift, int blueShift)
861       throws IOException
862  {
863    byte[] b = new byte[20];
864
865    b[0]  = (byte) SetPixelFormat;
866    b[4]  = (byte) bitsPerPixel;
867    b[5]  = (byte) depth;
868    b[6]  = (byte) (bigEndian ? 1 : 0);
869    b[7]  = (byte) (trueColour ? 1 : 0);
870    b[8]  = (byte) ((redMax >> 8) & 0xff);
871    b[9]  = (byte) (redMax & 0xff);
872    b[10] = (byte) ((greenMax >> 8) & 0xff);
873    b[11] = (byte) (greenMax & 0xff);
874    b[12] = (byte) ((blueMax >> 8) & 0xff);
875    b[13] = (byte) (blueMax & 0xff);
876    b[14] = (byte) redShift;
877    b[15] = (byte) greenShift;
878    b[16] = (byte) blueShift;
879
880    os.write(b);
881  }
882
883
884  //
885  // Write a FixColourMapEntries message.  The values in the red, green and
886  // blue arrays are from 0 to 65535.
887  //
888
889  void writeFixColourMapEntries(int firstColour, int nColours,
890                                int[] red, int[] green, int[] blue)
891       throws IOException
892  {
893    byte[] b = new byte[6 + nColours * 6];
894
895    b[0] = (byte) FixColourMapEntries;
896    b[2] = (byte) ((firstColour >> 8) & 0xff);
897    b[3] = (byte) (firstColour & 0xff);
898    b[4] = (byte) ((nColours >> 8) & 0xff);
899    b[5] = (byte) (nColours & 0xff);
900
901    for (int i = 0; i < nColours; i++) {
902      b[6 + i * 6]     = (byte) ((red[i] >> 8) & 0xff);
903      b[6 + i * 6 + 1] = (byte) (red[i] & 0xff);
904      b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff);
905      b[6 + i * 6 + 3] = (byte) (green[i] & 0xff);
906      b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff);
907      b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff);
908    }
909 
910    os.write(b);
911  }
912
913
914  //
915  // Write a SetEncodings message
916  //
917
918  void writeSetEncodings(int[] encs, int len) throws IOException {
919    byte[] b = new byte[4 + 4 * len];
920
921    b[0] = (byte) SetEncodings;
922    b[2] = (byte) ((len >> 8) & 0xff);
923    b[3] = (byte) (len & 0xff);
924
925    for (int i = 0; i < len; i++) {
926      b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff);
927      b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff);
928      b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff);
929      b[7 + 4 * i] = (byte) (encs[i] & 0xff);
930    }
931
932    os.write(b);
933  }
934
935
936  //
937  // Write a ClientCutText message
938  //
939
940  void writeClientCutText(String text) throws IOException {
941    byte[] b = new byte[8 + text.length()];
942
943    b[0] = (byte) ClientCutText;
944    b[4] = (byte) ((text.length() >> 24) & 0xff);
945    b[5] = (byte) ((text.length() >> 16) & 0xff);
946    b[6] = (byte) ((text.length() >> 8) & 0xff);
947    b[7] = (byte) (text.length() & 0xff);
948
949    System.arraycopy(text.getBytes(), 0, b, 8, text.length());
950
951    os.write(b);
952  }
953
954
955  //
956  // A buffer for putting pointer and keyboard events before being sent.  This
957  // is to ensure that multiple RFB events generated from a single Java Event
958  // will all be sent in a single network packet.  The maximum possible
959  // length is 4 modifier down events, a single key event followed by 4
960  // modifier up events i.e. 9 key events or 72 bytes.
961  //
962
963  byte[] eventBuf = new byte[72];
964  int eventBufLen;
965
966
967  // Useful shortcuts for modifier masks.
968
969  final static int CTRL_MASK  = InputEvent.CTRL_MASK;
970  final static int SHIFT_MASK = InputEvent.SHIFT_MASK;
971  final static int META_MASK  = InputEvent.META_MASK;
972  final static int ALT_MASK   = InputEvent.ALT_MASK;
973
974
975  //
976  // Write a pointer event message.  We may need to send modifier key events
977  // around it to set the correct modifier state.
978  //
979
980  int pointerMask = 0;
981
982  void writePointerEvent(MouseEvent evt) throws IOException {
983    int modifiers = evt.getModifiers();
984
985    int mask2 = 2;
986    int mask3 = 4;
987    if (viewer.options.reverseMouseButtons2And3) {
988      mask2 = 4;
989      mask3 = 2;
990    }
991
992    // Note: For some reason, AWT does not set BUTTON1_MASK on left
993    // button presses. Here we think that it was the left button if
994    // modifiers do not include BUTTON2_MASK or BUTTON3_MASK.
995
996    if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
997      if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
998        pointerMask = mask2;
999        modifiers &= ~ALT_MASK;
1000      } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1001        pointerMask = mask3;
1002        modifiers &= ~META_MASK;
1003      } else {
1004        pointerMask = 1;
1005      }
1006    } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) {
1007      pointerMask = 0;
1008      if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
1009        modifiers &= ~ALT_MASK;
1010      } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1011        modifiers &= ~META_MASK;
1012      }
1013    }
1014
1015    eventBufLen = 0;
1016    writeModifierKeyEvents(modifiers);
1017
1018    int x = evt.getX();
1019    int y = evt.getY();
1020
1021    if (x < 0) x = 0;
1022    if (y < 0) y = 0;
1023
1024    eventBuf[eventBufLen++] = (byte) PointerEvent;
1025    eventBuf[eventBufLen++] = (byte) pointerMask;
1026    eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
1027    eventBuf[eventBufLen++] = (byte) (x & 0xff);
1028    eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
1029    eventBuf[eventBufLen++] = (byte) (y & 0xff);
1030
1031    //
1032    // Always release all modifiers after an "up" event
1033    //
1034
1035    if (pointerMask == 0) {
1036      writeModifierKeyEvents(0);
1037    }
1038
1039    os.write(eventBuf, 0, eventBufLen);
1040  }
1041
1042
1043  //
1044  // Write a key event message.  We may need to send modifier key events
1045  // around it to set the correct modifier state.  Also we need to translate
1046  // from the Java key values to the X keysym values used by the RFB protocol.
1047  //
1048
1049  void writeKeyEvent(KeyEvent evt) throws IOException {
1050
1051    int keyChar = evt.getKeyChar();
1052
1053    //
1054    // Ignore event if only modifiers were pressed.
1055    //
1056
1057    // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar().
1058    if (keyChar == 0)
1059      keyChar = KeyEvent.CHAR_UNDEFINED;
1060
1061    if (keyChar == KeyEvent.CHAR_UNDEFINED) {
1062      int code = evt.getKeyCode();
1063      if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT ||
1064          code == KeyEvent.VK_META || code == KeyEvent.VK_ALT)
1065        return;
1066    }
1067
1068    //
1069    // Key press or key release?
1070    //
1071
1072    boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
1073
1074    int key;
1075    if (evt.isActionKey()) {
1076
1077      //
1078      // An action key should be one of the following.
1079      // If not then just ignore the event.
1080      //
1081
1082      switch(evt.getKeyCode()) {
1083      case KeyEvent.VK_HOME:      key = 0xff50; break;
1084      case KeyEvent.VK_LEFT:      key = 0xff51; break;
1085      case KeyEvent.VK_UP:        key = 0xff52; break;
1086      case KeyEvent.VK_RIGHT:     key = 0xff53; break;
1087      case KeyEvent.VK_DOWN:      key = 0xff54; break;
1088      case KeyEvent.VK_PAGE_UP:   key = 0xff55; break;
1089      case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break;
1090      case KeyEvent.VK_END:       key = 0xff57; break;
1091      case KeyEvent.VK_INSERT:    key = 0xff63; break;
1092      case KeyEvent.VK_F1:        key = 0xffbe; break;
1093      case KeyEvent.VK_F2:        key = 0xffbf; break;
1094      case KeyEvent.VK_F3:        key = 0xffc0; break;
1095      case KeyEvent.VK_F4:        key = 0xffc1; break;
1096      case KeyEvent.VK_F5:        key = 0xffc2; break;
1097      case KeyEvent.VK_F6:        key = 0xffc3; break;
1098      case KeyEvent.VK_F7:        key = 0xffc4; break;
1099      case KeyEvent.VK_F8:        key = 0xffc5; break;
1100      case KeyEvent.VK_F9:        key = 0xffc6; break;
1101      case KeyEvent.VK_F10:       key = 0xffc7; break;
1102      case KeyEvent.VK_F11:       key = 0xffc8; break;
1103      case KeyEvent.VK_F12:       key = 0xffc9; break;
1104      default:
1105        return;
1106      }
1107
1108    } else {
1109
1110      //
1111      // A "normal" key press.  Ordinary ASCII characters go straight through.
1112      // For CTRL-<letter>, CTRL is sent separately so just send <letter>.
1113      // Backspace, tab, return, escape and delete have special keysyms.
1114      // Anything else we ignore.
1115      //
1116
1117      key = keyChar;
1118
1119      if (key < 0x20) {
1120        if (evt.isControlDown()) {
1121          key += 0x60;
1122        } else {
1123          switch(key) {
1124          case KeyEvent.VK_BACK_SPACE: key = 0xff08; break;
1125          case KeyEvent.VK_TAB:        key = 0xff09; break;
1126          case KeyEvent.VK_ENTER:      key = 0xff0d; break;
1127          case KeyEvent.VK_ESCAPE:     key = 0xff1b; break;
1128          }
1129        }
1130      } else if (key == 0x7f) {
1131        // Delete
1132        key = 0xffff;
1133      } else if (key > 0xff) {
1134        // JDK1.1 on X incorrectly passes some keysyms straight through,
1135        // so we do too.  JDK1.1.4 seems to have fixed this.
1136        // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete
1137        // Also, we pass through foreign currency keysyms (0x20a0..0x20af).
1138        if ((key < 0xff00 || key > 0xffff) &&
1139            !(key >= 0x20a0 && key <= 0x20af))
1140          return;
1141      }
1142    }
1143
1144    // Fake keyPresses for keys that only generates keyRelease events
1145    if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring
1146        (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis
1147        (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis
1148        (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf
1149        (key == 0xa3)) {                  // XK_sterling
1150      // Make sure we do not send keypress events twice on platforms
1151      // with correct JVMs (those that actually report KeyPress for all
1152      // keys) 
1153      if (down)
1154        brokenKeyPressed = true;
1155
1156      if (!down && !brokenKeyPressed) {
1157        // We've got a release event for this key, but haven't received
1158        // a press. Fake it.
1159        eventBufLen = 0;
1160        writeModifierKeyEvents(evt.getModifiers());
1161        writeKeyEvent(key, true);
1162        os.write(eventBuf, 0, eventBufLen);
1163      }
1164
1165      if (!down)
1166        brokenKeyPressed = false; 
1167    }
1168
1169    eventBufLen = 0;
1170    writeModifierKeyEvents(evt.getModifiers());
1171    writeKeyEvent(key, down);
1172
1173    // Always release all modifiers after an "up" event
1174    if (!down)
1175      writeModifierKeyEvents(0);
1176
1177    os.write(eventBuf, 0, eventBufLen);
1178  }
1179
1180
1181  //
1182  // Add a raw key event with the given X keysym to eventBuf.
1183  //
1184
1185  void writeKeyEvent(int keysym, boolean down) {
1186    eventBuf[eventBufLen++] = (byte) KeyboardEvent;
1187    eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
1188    eventBuf[eventBufLen++] = (byte) 0;
1189    eventBuf[eventBufLen++] = (byte) 0;
1190    eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
1191    eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
1192    eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
1193    eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
1194  }
1195
1196
1197  //
1198  // Write key events to set the correct modifier state.
1199  //
1200
1201  int oldModifiers = 0;
1202
1203  void writeModifierKeyEvents(int newModifiers) {
1204    if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
1205      writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
1206
1207    if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK))
1208      writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0);
1209
1210    if ((newModifiers & META_MASK) != (oldModifiers & META_MASK))
1211      writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0);
1212
1213    if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK))
1214      writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0);
1215
1216    oldModifiers = newModifiers;
1217  }
1218
1219
1220  //
1221  // Compress and write the data into the recorded session file. This
1222  // method assumes the recording is on (rec != null).
1223  //
1224
1225  void recordCompressedData(byte[] data, int off, int len) throws IOException {
1226    Deflater deflater = new Deflater();
1227    deflater.setInput(data, off, len);
1228    int bufSize = len + len / 100 + 12;
1229    byte[] buf = new byte[bufSize];
1230    deflater.finish();
1231    int compressedSize = deflater.deflate(buf);
1232    recordCompactLen(compressedSize);
1233    rec.write(buf, 0, compressedSize);
1234  }
1235
1236  void recordCompressedData(byte[] data) throws IOException {
1237    recordCompressedData(data, 0, data.length);
1238  }
1239
1240  //
1241  // Write an integer in compact representation (1..3 bytes) into the
1242  // recorded session file. This method assumes the recording is on
1243  // (rec != null).
1244  //
1245
1246  void recordCompactLen(int len) throws IOException {
1247    byte[] buf = new byte[3];
1248    int bytes = 0;
1249    buf[bytes++] = (byte)(len & 0x7F);
1250    if (len > 0x7F) {
1251      buf[bytes-1] |= 0x80;
1252      buf[bytes++] = (byte)(len >> 7 & 0x7F);
1253      if (len > 0x3FFF) {
1254        buf[bytes-1] |= 0x80;
1255        buf[bytes++] = (byte)(len >> 14 & 0xFF);
1256      }
1257    }
1258    rec.write(buf, 0, bytes);
1259  }
1260
1261  public void startTiming() {
1262    timing = true;
1263
1264    // Carry over up to 1s worth of previous rate for smoothing.
1265
1266    if (timeWaitedIn100us > 10000) {
1267      timedKbits = timedKbits * 10000 / timeWaitedIn100us;
1268      timeWaitedIn100us = 10000;
1269    }
1270  }
1271
1272  public void stopTiming() {
1273    timing = false; 
1274    if (timeWaitedIn100us < timedKbits/2)
1275      timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
1276  }
1277
1278  public long kbitsPerSecond() {
1279    return timedKbits * 10000 / timeWaitedIn100us;
1280  }
1281
1282  public long timeWaited() {
1283    return timeWaitedIn100us;
1284  }
1285
1286  public void readFully(byte b[]) throws IOException {
1287    readFully(b, 0, b.length);
1288  }
1289
1290  public void readFully(byte b[], int off, int len) throws IOException {
1291    long before = 0;
1292    if (timing)
1293      before = System.currentTimeMillis();
1294
1295    is.readFully(b, off, len);
1296
1297    if (timing) {
1298      long after = System.currentTimeMillis();
1299      long newTimeWaited = (after - before) * 10;
1300      int newKbits = len * 8 / 1000;
1301
1302      // limit rate to between 10kbit/s and 40Mbit/s
1303
1304      if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
1305      if (newTimeWaited < newKbits/4)    newTimeWaited = newKbits/4;
1306
1307      timeWaitedIn100us += newTimeWaited;
1308      timedKbits += newKbits;
1309    }
1310  }
1311
1312}
Note: See TracBrowser for help on using the repository browser.