source: trunk/packages/invirt-vnc-client/RfbProto.java @ 1705

Last change on this file since 1705 was 1438, checked in by broder, 16 years ago

Isolate our patches to the VNC client from the upstream TightVNC
source

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.