blob: e88d8e7923c89ed5c0d97b40e3c495a85cfe476b [file] [log] [blame]
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001//
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
Adam Tkacf53e62a2009-03-13 13:20:26 +000027package com.tigervnc.vncviewer;
Constantin Kaplinsky90d8a502008-04-14 09:45:50 +000028
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +000029import java.io.*;
30import java.awt.*;
31import java.awt.event.*;
32import java.net.Socket;
33import java.util.zip.*;
34
35class RfbProto {
36
37 final static String
38 versionMsg_3_3 = "RFB 003.003\n",
39 versionMsg_3_7 = "RFB 003.007\n",
40 versionMsg_3_8 = "RFB 003.008\n";
41
42 // Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC
43 final static String
44 StandardVendor = "STDV",
45 TridiaVncVendor = "TRDV",
46 TightVncVendor = "TGHT";
47
48 // Security types
49 final static int
Adam Tkacea79b472010-11-11 11:47:11 +000050 SecTypeInvalid = 0,
51 SecTypeNone = 1,
52 SecTypeVncAuth = 2,
53 SecTypeTight = 16,
54 SecTypeVeNCrypt = 19,
55 SecTypePlain = 256,
56 SecTypeTLSNone = 257,
57 SecTypeTLSVnc = 258,
58 SecTypeTLSPlain = 259,
59 SecTypeX509None = 260,
60 SecTypeX509Vnc = 261,
61 SecTypeX509Plain = 262;
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +000062
63 // Supported tunneling types
64 final static int
65 NoTunneling = 0;
66 final static String
67 SigNoTunneling = "NOTUNNEL";
68
69 // Supported authentication types
70 final static int
71 AuthNone = 1,
72 AuthVNC = 2,
73 AuthUnixLogin = 129;
74 final static String
75 SigAuthNone = "NOAUTH__",
76 SigAuthVNC = "VNCAUTH_",
77 SigAuthUnixLogin = "ULGNAUTH";
78
79 // VNC authentication results
80 final static int
81 VncAuthOK = 0,
82 VncAuthFailed = 1,
83 VncAuthTooMany = 2;
84
85 // Standard server-to-client messages
86 final static int
87 FramebufferUpdate = 0,
88 SetColourMapEntries = 1,
89 Bell = 2,
90 ServerCutText = 3;
91
92 // Non-standard server-to-client messages
93 final static int
94 EndOfContinuousUpdates = 150;
95 final static String
96 SigEndOfContinuousUpdates = "CUS_EOCU";
97
98 // Standard client-to-server messages
99 final static int
100 SetPixelFormat = 0,
101 FixColourMapEntries = 1,
102 SetEncodings = 2,
103 FramebufferUpdateRequest = 3,
104 KeyboardEvent = 4,
105 PointerEvent = 5,
106 ClientCutText = 6;
107
108 // Non-standard client-to-server messages
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +0000109 final static int EnableContinuousUpdates = 150;
110 final static int VideoRectangleSelection = 151;
enikeyd7653562008-12-25 11:02:56 +0000111 final static int VideoFreeze = 152;
112 final static String SigVideoFreeze = "VD_FREEZ";
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +0000113 final static String SigEnableContinuousUpdates = "CUC_ENCU";
114 final static String SigVideoRectangleSelection = "VRECTSEL";
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000115
116 // Supported encodings and pseudo-encodings
117 final static int
118 EncodingRaw = 0,
119 EncodingCopyRect = 1,
120 EncodingRRE = 2,
121 EncodingCoRRE = 4,
122 EncodingHextile = 5,
123 EncodingZlib = 6,
124 EncodingTight = 7,
125 EncodingZRLE = 16,
126 EncodingCompressLevel0 = 0xFFFFFF00,
127 EncodingQualityLevel0 = 0xFFFFFFE0,
128 EncodingXCursor = 0xFFFFFF10,
129 EncodingRichCursor = 0xFFFFFF11,
130 EncodingPointerPos = 0xFFFFFF18,
131 EncodingLastRect = 0xFFFFFF20,
132 EncodingNewFBSize = 0xFFFFFF21;
133 final static String
134 SigEncodingRaw = "RAW_____",
135 SigEncodingCopyRect = "COPYRECT",
136 SigEncodingRRE = "RRE_____",
137 SigEncodingCoRRE = "CORRE___",
138 SigEncodingHextile = "HEXTILE_",
139 SigEncodingZlib = "ZLIB____",
140 SigEncodingTight = "TIGHT___",
141 SigEncodingZRLE = "ZRLE____",
142 SigEncodingCompressLevel0 = "COMPRLVL",
143 SigEncodingQualityLevel0 = "JPEGQLVL",
144 SigEncodingXCursor = "X11CURSR",
145 SigEncodingRichCursor = "RCHCURSR",
146 SigEncodingPointerPos = "POINTPOS",
147 SigEncodingLastRect = "LASTRECT",
148 SigEncodingNewFBSize = "NEWFBSIZ";
149
150 final static int MaxNormalEncoding = 255;
151
152 // Contstants used in the Hextile decoder
153 final static int
154 HextileRaw = 1,
155 HextileBackgroundSpecified = 2,
156 HextileForegroundSpecified = 4,
157 HextileAnySubrects = 8,
158 HextileSubrectsColoured = 16;
159
160 // Contstants used in the Tight decoder
161 final static int TightMinToCompress = 12;
162 final static int
163 TightExplicitFilter = 0x04,
164 TightFill = 0x08,
165 TightJpeg = 0x09,
166 TightMaxSubencoding = 0x09,
167 TightFilterCopy = 0x00,
168 TightFilterPalette = 0x01,
169 TightFilterGradient = 0x02;
170
171
172 String host;
173 int port;
174 Socket sock;
175 OutputStream os;
176 SessionRecorder rec;
177 boolean inNormalProtocol = false;
178 VncViewer viewer;
179
180 // Input stream is declared private to make sure it can be accessed
181 // only via RfbProto methods. We have to do this because we want to
182 // count how many bytes were read.
183 private DataInputStream is;
184 private long numBytesRead = 0;
185 public long getNumBytesRead() { return numBytesRead; }
186
187 // Java on UNIX does not call keyPressed() on some keys, for example
188 // swedish keys To prevent our workaround to produce duplicate
189 // keypresses on JVMs that actually works, keep track of if
enikey2f0294e2008-12-24 08:18:54 +0000190 // keyPressed() for a "broken" key was called or not.
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000191 boolean brokenKeyPressed = false;
192
193 // This will be set to true on the first framebuffer update
194 // containing Zlib-, ZRLE- or Tight-encoded data.
195 boolean wereZlibUpdates = false;
196
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000197 // This fields are needed to show warnings about inefficiently saved
198 // sessions only once per each saved session file.
199 boolean zlibWarningShown;
200 boolean tightWarningShown;
201
202 // Before starting to record each saved session, we set this field
203 // to 0, and increment on each framebuffer update. We don't flush
enikey2f0294e2008-12-24 08:18:54 +0000204 // the SessionRecorder data into the file before the second update.
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000205 // This allows us to write initial framebuffer update with zero
206 // timestamp, to let the player show initial desktop before
207 // playback.
208 int numUpdatesInSession;
209
210 // Measuring network throughput.
211 boolean timing;
212 long timeWaitedIn100us;
213 long timedKbits;
214
215 // Protocol version and TightVNC-specific protocol options.
216 int serverMajor, serverMinor;
217 int clientMajor, clientMinor;
218 boolean protocolTightVNC;
219 CapsContainer tunnelCaps, authCaps;
220 CapsContainer serverMsgCaps, clientMsgCaps;
221 CapsContainer encodingCaps;
222
223 // "Continuous updates" is a TightVNC-specific feature that allows
224 // receiving framebuffer updates continuously, without sending update
225 // requests. The variables below track the state of this feature.
226 // Initially, continuous updates are disabled. They can be enabled
227 // by calling tryEnableContinuousUpdates() method, and only if this
228 // feature is supported by the server. To disable continuous updates,
229 // tryDisableContinuousUpdates() should be called.
230 private boolean continuousUpdatesActive = false;
231 private boolean continuousUpdatesEnding = false;
232
233 // If true, informs that the RFB socket was closed.
234 private boolean closed;
235
236 //
237 // Constructor. Make TCP connection to RFB server.
238 //
239
240 RfbProto(String h, int p, VncViewer v) throws IOException {
241 viewer = v;
242 host = h;
243 port = p;
244
245 if (viewer.socketFactory == null) {
246 sock = new Socket(host, port);
enikey17ac1812008-12-19 11:22:13 +0000247 sock.setTcpNoDelay(true);
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000248 } else {
249 try {
250 Class factoryClass = Class.forName(viewer.socketFactory);
251 SocketFactory factory = (SocketFactory)factoryClass.newInstance();
252 if (viewer.inAnApplet)
253 sock = factory.createSocket(host, port, viewer);
254 else
255 sock = factory.createSocket(host, port, viewer.mainArgs);
256 } catch(Exception e) {
257 e.printStackTrace();
258 throw new IOException(e.getMessage());
259 }
260 }
261 is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
262 16384));
263 os = sock.getOutputStream();
264
265 timing = false;
266 timeWaitedIn100us = 5;
267 timedKbits = 0;
268 }
269
270
271 synchronized void close() {
272 try {
273 sock.close();
274 closed = true;
275 System.out.println("RFB socket closed");
276 if (rec != null) {
277 rec.close();
278 rec = null;
279 }
280 } catch (Exception e) {
281 e.printStackTrace();
282 }
283 }
284
285 synchronized boolean closed() {
286 return closed;
287 }
288
289 //
290 // Read server's protocol version message
291 //
292
293 void readVersionMsg() throws Exception {
294
295 byte[] b = new byte[12];
296
297 readFully(b);
298
299 if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ')
300 || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9')
301 || (b[6] < '0') || (b[6] > '9') || (b[7] != '.')
302 || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9')
303 || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n'))
304 {
305 throw new Exception("Host " + host + " port " + port +
306 " is not an RFB server");
307 }
308
309 serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
310 serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0');
311
312 if (serverMajor < 3) {
313 throw new Exception("RFB server does not support protocol version 3");
314 }
315 }
316
317
318 //
319 // Write our protocol version message
320 //
321
322 void writeVersionMsg() throws IOException {
323 clientMajor = 3;
324 if (serverMajor > 3 || serverMinor >= 8) {
325 clientMinor = 8;
326 os.write(versionMsg_3_8.getBytes());
327 } else if (serverMinor >= 7) {
328 clientMinor = 7;
329 os.write(versionMsg_3_7.getBytes());
330 } else {
331 clientMinor = 3;
332 os.write(versionMsg_3_3.getBytes());
333 }
334 protocolTightVNC = false;
335 initCapabilities();
336 }
337
338
339 //
340 // Negotiate the authentication scheme.
341 //
342
343 int negotiateSecurity() throws Exception {
344 return (clientMinor >= 7) ?
345 selectSecurityType() : readSecurityType();
346 }
347
348 //
349 // Read security type from the server (protocol version 3.3).
350 //
351
352 int readSecurityType() throws Exception {
353 int secType = readU32();
354
355 switch (secType) {
356 case SecTypeInvalid:
357 readConnFailedReason();
358 return SecTypeInvalid; // should never be executed
359 case SecTypeNone:
360 case SecTypeVncAuth:
361 return secType;
362 default:
363 throw new Exception("Unknown security type from RFB server: " + secType);
364 }
365 }
366
367 //
368 // Select security type from the server's list (protocol versions 3.7/3.8).
369 //
370
371 int selectSecurityType() throws Exception {
372 int secType = SecTypeInvalid;
373
374 // Read the list of secutiry types.
375 int nSecTypes = readU8();
376 if (nSecTypes == 0) {
377 readConnFailedReason();
378 return SecTypeInvalid; // should never be executed
379 }
380 byte[] secTypes = new byte[nSecTypes];
381 readFully(secTypes);
382
383 // Find out if the server supports TightVNC protocol extensions
384 for (int i = 0; i < nSecTypes; i++) {
385 if (secTypes[i] == SecTypeTight) {
386 protocolTightVNC = true;
387 os.write(SecTypeTight);
388 return SecTypeTight;
389 }
390 }
391
392 // Find first supported security type.
393 for (int i = 0; i < nSecTypes; i++) {
Adam Tkac053597c2010-11-11 13:21:55 +0000394 if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth
395 || secTypes[i] == SecTypeVeNCrypt) {
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000396 secType = secTypes[i];
397 break;
398 }
399 }
400
401 if (secType == SecTypeInvalid) {
402 throw new Exception("Server did not offer supported security type");
403 } else {
404 os.write(secType);
405 }
406
407 return secType;
408 }
409
Adam Tkac053597c2010-11-11 13:21:55 +0000410 int authenticateVeNCrypt() throws Exception {
411 int majorVersion = readU8();
412 int minorVersion = readU8();
413 int Version = (majorVersion << 8) | minorVersion;
414 if (Version < 0x0002) {
415 os.write(0);
416 os.write(0);
417 throw new Exception("Server reported an unsupported VeNCrypt version");
418 }
419 os.write(0);
420 os.write(2);
421 if (readU8() != 0)
422 throw new Exception("Server reported it could not support the VeNCrypt version");
423 int nSecTypes = readU8();
424 int[] secTypes = new int[nSecTypes];
425 for(int i = 0; i < nSecTypes; i++)
426 secTypes[i] = readU32();
427
428 for(int i = 0; i < nSecTypes; i++)
429 switch(secTypes[i])
430 {
431 case SecTypeNone:
432 case SecTypeVncAuth:
Adam Tkac6a663cd2010-11-11 14:06:42 +0000433 case SecTypePlain:
Adam Tkac4be9da82010-11-18 14:00:12 +0000434 case SecTypeTLSNone:
435 case SecTypeTLSVnc:
436 case SecTypeTLSPlain:
Adam Tkac28d83892010-11-18 14:17:49 +0000437 case SecTypeX509None:
438 case SecTypeX509Vnc:
439 case SecTypeX509Plain:
Adam Tkac053597c2010-11-11 13:21:55 +0000440 writeInt(secTypes[i]);
441 return secTypes[i];
442 }
443
444 throw new Exception("No valid VeNCrypt sub-type");
445 }
446
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000447 //
448 // Perform "no authentication".
449 //
450
451 void authenticateNone() throws Exception {
452 if (clientMinor >= 8)
453 readSecurityResult("No authentication");
454 }
455
456 //
457 // Perform standard VNC Authentication.
458 //
459
460 void authenticateVNC(String pw) throws Exception {
461 byte[] challenge = new byte[16];
462 readFully(challenge);
463
464 if (pw.length() > 8)
465 pw = pw.substring(0, 8); // Truncate to 8 chars
466
467 // Truncate password on the first zero byte.
468 int firstZero = pw.indexOf(0);
469 if (firstZero != -1)
470 pw = pw.substring(0, firstZero);
471
472 byte[] key = {0, 0, 0, 0, 0, 0, 0, 0};
473 System.arraycopy(pw.getBytes(), 0, key, 0, pw.length());
474
475 DesCipher des = new DesCipher(key);
476
477 des.encrypt(challenge, 0, challenge, 0);
478 des.encrypt(challenge, 8, challenge, 8);
479
480 os.write(challenge);
481
482 readSecurityResult("VNC authentication");
483 }
484
Adam Tkac4be9da82010-11-18 14:00:12 +0000485 void authenticateTLS() throws Exception {
486 TLSTunnel tunnel = new TLSTunnel(sock);
487 tunnel.setup (this);
488 }
489
Adam Tkac28d83892010-11-18 14:17:49 +0000490 void authenticateX509() throws Exception {
491 X509Tunnel tunnel = new X509Tunnel(sock);
492 tunnel.setup (this);
493 }
494
Adam Tkac6a663cd2010-11-11 14:06:42 +0000495 void authenticatePlain(String User, String Password) throws Exception {
496 byte[] user=User.getBytes();
497 byte[] password=Password.getBytes();
498 writeInt(user.length);
499 writeInt(password.length);
500 os.write(user);
501 os.write(password);
502
503 readSecurityResult("Plain authentication");
504 }
505
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000506 //
507 // Read security result.
508 // Throws an exception on authentication failure.
509 //
510
511 void readSecurityResult(String authType) throws Exception {
512 int securityResult = readU32();
513
514 switch (securityResult) {
515 case VncAuthOK:
516 System.out.println(authType + ": success");
517 break;
518 case VncAuthFailed:
519 if (clientMinor >= 8)
520 readConnFailedReason();
521 throw new Exception(authType + ": failed");
522 case VncAuthTooMany:
523 throw new Exception(authType + ": failed, too many tries");
524 default:
525 throw new Exception(authType + ": unknown result " + securityResult);
526 }
527 }
528
529 //
530 // Read the string describing the reason for a connection failure,
531 // and throw an exception.
532 //
533
534 void readConnFailedReason() throws Exception {
535 int reasonLen = readU32();
536 byte[] reason = new byte[reasonLen];
537 readFully(reason);
538 throw new Exception(new String(reason));
539 }
540
541 //
542 // Initialize capability lists (TightVNC protocol extensions).
543 //
544
545 void initCapabilities() {
546 tunnelCaps = new CapsContainer();
547 authCaps = new CapsContainer();
548 serverMsgCaps = new CapsContainer();
549 clientMsgCaps = new CapsContainer();
550 encodingCaps = new CapsContainer();
551
552 // Supported authentication methods
553 authCaps.add(AuthNone, StandardVendor, SigAuthNone,
554 "No authentication");
555 authCaps.add(AuthVNC, StandardVendor, SigAuthVNC,
556 "Standard VNC password authentication");
557
558 // Supported non-standard server-to-client messages
559 serverMsgCaps.add(EndOfContinuousUpdates, TightVncVendor,
560 SigEndOfContinuousUpdates,
561 "End of continuous updates notification");
562
563 // Supported non-standard client-to-server messages
564 clientMsgCaps.add(EnableContinuousUpdates, TightVncVendor,
565 SigEnableContinuousUpdates,
566 "Enable/disable continuous updates");
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +0000567 clientMsgCaps.add(VideoRectangleSelection, TightVncVendor,
568 SigVideoRectangleSelection,
569 "Select a rectangle to be treated as video");
enikeyd7653562008-12-25 11:02:56 +0000570 clientMsgCaps.add(VideoFreeze, TightVncVendor,
571 SigVideoFreeze,
572 "Disable/enable video rectangle");
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000573
574 // Supported encoding types
575 encodingCaps.add(EncodingCopyRect, StandardVendor,
576 SigEncodingCopyRect, "Standard CopyRect encoding");
577 encodingCaps.add(EncodingRRE, StandardVendor,
578 SigEncodingRRE, "Standard RRE encoding");
579 encodingCaps.add(EncodingCoRRE, StandardVendor,
580 SigEncodingCoRRE, "Standard CoRRE encoding");
581 encodingCaps.add(EncodingHextile, StandardVendor,
582 SigEncodingHextile, "Standard Hextile encoding");
583 encodingCaps.add(EncodingZRLE, StandardVendor,
584 SigEncodingZRLE, "Standard ZRLE encoding");
585 encodingCaps.add(EncodingZlib, TridiaVncVendor,
586 SigEncodingZlib, "Zlib encoding");
587 encodingCaps.add(EncodingTight, TightVncVendor,
588 SigEncodingTight, "Tight encoding");
589
590 // Supported pseudo-encoding types
591 encodingCaps.add(EncodingCompressLevel0, TightVncVendor,
592 SigEncodingCompressLevel0, "Compression level");
593 encodingCaps.add(EncodingQualityLevel0, TightVncVendor,
594 SigEncodingQualityLevel0, "JPEG quality level");
595 encodingCaps.add(EncodingXCursor, TightVncVendor,
596 SigEncodingXCursor, "X-style cursor shape update");
597 encodingCaps.add(EncodingRichCursor, TightVncVendor,
598 SigEncodingRichCursor, "Rich-color cursor shape update");
599 encodingCaps.add(EncodingPointerPos, TightVncVendor,
600 SigEncodingPointerPos, "Pointer position update");
601 encodingCaps.add(EncodingLastRect, TightVncVendor,
602 SigEncodingLastRect, "LastRect protocol extension");
603 encodingCaps.add(EncodingNewFBSize, TightVncVendor,
604 SigEncodingNewFBSize, "Framebuffer size change");
605 }
606
607 //
608 // Setup tunneling (TightVNC protocol extensions)
609 //
610
611 void setupTunneling() throws IOException {
612 int nTunnelTypes = readU32();
613 if (nTunnelTypes != 0) {
614 readCapabilityList(tunnelCaps, nTunnelTypes);
615
616 // We don't support tunneling yet.
617 writeInt(NoTunneling);
618 }
619 }
620
621 //
622 // Negotiate authentication scheme (TightVNC protocol extensions)
623 //
624
625 int negotiateAuthenticationTight() throws Exception {
626 int nAuthTypes = readU32();
627 if (nAuthTypes == 0)
628 return AuthNone;
629
630 readCapabilityList(authCaps, nAuthTypes);
631 for (int i = 0; i < authCaps.numEnabled(); i++) {
632 int authType = authCaps.getByOrder(i);
633 if (authType == AuthNone || authType == AuthVNC) {
634 writeInt(authType);
635 return authType;
636 }
637 }
638 throw new Exception("No suitable authentication scheme found");
639 }
640
641 //
642 // Read a capability list (TightVNC protocol extensions)
643 //
644
645 void readCapabilityList(CapsContainer caps, int count) throws IOException {
646 int code;
647 byte[] vendor = new byte[4];
648 byte[] name = new byte[8];
649 for (int i = 0; i < count; i++) {
650 code = readU32();
651 readFully(vendor);
652 readFully(name);
653 caps.enable(new CapabilityInfo(code, vendor, name));
654 }
655 }
656
657 //
658 // Write a 32-bit integer into the output stream.
659 //
660
661 void writeInt(int value) throws IOException {
662 byte[] b = new byte[4];
663 b[0] = (byte) ((value >> 24) & 0xff);
664 b[1] = (byte) ((value >> 16) & 0xff);
665 b[2] = (byte) ((value >> 8) & 0xff);
666 b[3] = (byte) (value & 0xff);
667 os.write(b);
668 }
669
670 //
671 // Write the client initialisation message
672 //
673
674 void writeClientInit() throws IOException {
675 if (viewer.options.shareDesktop) {
676 os.write(1);
677 } else {
678 os.write(0);
679 }
680 viewer.options.disableShareDesktop();
681 }
682
683
684 //
685 // Read the server initialisation message
686 //
687
688 String desktopName;
689 int framebufferWidth, framebufferHeight;
690 int bitsPerPixel, depth;
691 boolean bigEndian, trueColour;
692 int redMax, greenMax, blueMax, redShift, greenShift, blueShift;
693
694 void readServerInit() throws IOException {
695 framebufferWidth = readU16();
696 framebufferHeight = readU16();
697 bitsPerPixel = readU8();
698 depth = readU8();
699 bigEndian = (readU8() != 0);
700 trueColour = (readU8() != 0);
701 redMax = readU16();
702 greenMax = readU16();
703 blueMax = readU16();
704 redShift = readU8();
705 greenShift = readU8();
706 blueShift = readU8();
707 byte[] pad = new byte[3];
708 readFully(pad);
709 int nameLength = readU32();
710 byte[] name = new byte[nameLength];
711 readFully(name);
712 desktopName = new String(name);
713
714 // Read interaction capabilities (TightVNC protocol extensions)
715 if (protocolTightVNC) {
716 int nServerMessageTypes = readU16();
717 int nClientMessageTypes = readU16();
718 int nEncodingTypes = readU16();
719 readU16();
720 readCapabilityList(serverMsgCaps, nServerMessageTypes);
721 readCapabilityList(clientMsgCaps, nClientMessageTypes);
722 readCapabilityList(encodingCaps, nEncodingTypes);
723 }
724
725 if (!clientMsgCaps.isEnabled(EnableContinuousUpdates)) {
726 viewer.options.disableContUpdates();
727 }
728
729 inNormalProtocol = true;
730 }
731
732
733 //
734 // Create session file and write initial protocol messages into it.
735 //
736
737 void startSession(String fname) throws IOException {
738 rec = new SessionRecorder(fname);
739 rec.writeHeader();
740 rec.write(versionMsg_3_3.getBytes());
741 rec.writeIntBE(SecTypeNone);
742 rec.writeShortBE(framebufferWidth);
743 rec.writeShortBE(framebufferHeight);
744 byte[] fbsServerInitMsg = {
745 32, 24, 0, 1, 0,
746 (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF,
747 16, 8, 0, 0, 0, 0
748 };
749 rec.write(fbsServerInitMsg);
750 rec.writeIntBE(desktopName.length());
751 rec.write(desktopName.getBytes());
752 numUpdatesInSession = 0;
753
754 // FIXME: If there were e.g. ZRLE updates only, that should not
755 // affect recording of Zlib and Tight updates. So, actually
756 // we should maintain separate flags for Zlib, ZRLE and
757 // Tight, instead of one ``wereZlibUpdates'' variable.
758 //
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000759
760 zlibWarningShown = false;
761 tightWarningShown = false;
762 }
763
764 //
765 // Close session file.
766 //
767
768 void closeSession() throws IOException {
769 if (rec != null) {
770 rec.close();
771 rec = null;
772 }
773 }
774
775
776 //
777 // Set new framebuffer size
778 //
779
780 void setFramebufferSize(int width, int height) {
781 framebufferWidth = width;
782 framebufferHeight = height;
783 }
784
785
786 //
787 // Read the server message type
788 //
789
790 int readServerMessageType() throws IOException {
791 int msgType = readU8();
792
793 // If the session is being recorded:
794 if (rec != null) {
795 if (msgType == Bell) { // Save Bell messages in session files.
796 rec.writeByte(msgType);
797 if (numUpdatesInSession > 0)
798 rec.flush();
799 }
800 }
801
802 return msgType;
803 }
804
805
806 //
807 // Read a FramebufferUpdate message
808 //
809
810 int updateNRects;
811
812 void readFramebufferUpdate() throws IOException {
813 skipBytes(1);
814 updateNRects = readU16();
815
816 // If the session is being recorded:
817 if (rec != null) {
818 rec.writeByte(FramebufferUpdate);
819 rec.writeByte(0);
820 rec.writeShortBE(updateNRects);
821 }
822
823 numUpdatesInSession++;
824 }
825
enikey2f0294e2008-12-24 08:18:54 +0000826 //
827 // Returns true if encoding is not pseudo
828 //
829 // FIXME: Find better way to differ pseudo and real encodings
830 //
831
832 boolean isRealDecoderEncoding(int encoding) {
833 if ((encoding >= 1) && (encoding <= 16)) {
834 return true;
835 }
836 return false;
837 }
838
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000839 // Read a FramebufferUpdate rectangle header
840
841 int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;
842
843 void readFramebufferUpdateRectHdr() throws Exception {
844 updateRectX = readU16();
845 updateRectY = readU16();
846 updateRectW = readU16();
847 updateRectH = readU16();
848 updateRectEncoding = readU32();
849
850 if (updateRectEncoding == EncodingZlib ||
851 updateRectEncoding == EncodingZRLE ||
852 updateRectEncoding == EncodingTight)
853 wereZlibUpdates = true;
854
855 // If the session is being recorded:
856 if (rec != null) {
857 if (numUpdatesInSession > 1)
858 rec.flush(); // Flush the output on each rectangle.
859 rec.writeShortBE(updateRectX);
860 rec.writeShortBE(updateRectY);
861 rec.writeShortBE(updateRectW);
862 rec.writeShortBE(updateRectH);
enikey2f0294e2008-12-24 08:18:54 +0000863
864 //
865 // If this is pseudo encoding or CopyRect that write encoding ID
866 // in this place. All real encoding ID will be written to record stream
867 // in decoder classes.
enikey2f0294e2008-12-24 08:18:54 +0000868
enikey7716b712008-12-25 11:45:52 +0000869 if (((!isRealDecoderEncoding(updateRectEncoding))) && (rec != null)) {
enikey2f0294e2008-12-24 08:18:54 +0000870 rec.writeIntBE(updateRectEncoding);
871 }
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000872 }
873
874 if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding)
875 return;
876
877 if (updateRectX + updateRectW > framebufferWidth ||
878 updateRectY + updateRectH > framebufferHeight) {
879 throw new Exception("Framebuffer update rectangle too large: " +
880 updateRectW + "x" + updateRectH + " at (" +
881 updateRectX + "," + updateRectY + ")");
882 }
883 }
884
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000885 //
886 // Read a ServerCutText message
887 //
888
889 String readServerCutText() throws IOException {
890 skipBytes(3);
891 int len = readU32();
892 byte[] text = new byte[len];
893 readFully(text);
894 return new String(text);
895 }
896
897
898 //
899 // Read an integer in compact representation (1..3 bytes).
900 // Such format is used as a part of the Tight encoding.
901 // Also, this method records data if session recording is active and
902 // the viewer's recordFromBeginning variable is set to true.
903 //
904
905 int readCompactLen() throws IOException {
906 int[] portion = new int[3];
907 portion[0] = readU8();
908 int byteCount = 1;
909 int len = portion[0] & 0x7F;
910 if ((portion[0] & 0x80) != 0) {
911 portion[1] = readU8();
912 byteCount++;
913 len |= (portion[1] & 0x7F) << 7;
914 if ((portion[1] & 0x80) != 0) {
915 portion[2] = readU8();
916 byteCount++;
917 len |= (portion[2] & 0xFF) << 14;
918 }
919 }
920
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +0000921 return len;
922 }
923
924
925 //
926 // Write a FramebufferUpdateRequest message
927 //
928
929 void writeFramebufferUpdateRequest(int x, int y, int w, int h,
930 boolean incremental)
931 throws IOException
932 {
933 byte[] b = new byte[10];
934
935 b[0] = (byte) FramebufferUpdateRequest;
936 b[1] = (byte) (incremental ? 1 : 0);
937 b[2] = (byte) ((x >> 8) & 0xff);
938 b[3] = (byte) (x & 0xff);
939 b[4] = (byte) ((y >> 8) & 0xff);
940 b[5] = (byte) (y & 0xff);
941 b[6] = (byte) ((w >> 8) & 0xff);
942 b[7] = (byte) (w & 0xff);
943 b[8] = (byte) ((h >> 8) & 0xff);
944 b[9] = (byte) (h & 0xff);
945
946 os.write(b);
947 }
948
949
950 //
951 // Write a SetPixelFormat message
952 //
953
954 void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian,
955 boolean trueColour,
956 int redMax, int greenMax, int blueMax,
957 int redShift, int greenShift, int blueShift)
958 throws IOException
959 {
960 byte[] b = new byte[20];
961
962 b[0] = (byte) SetPixelFormat;
963 b[4] = (byte) bitsPerPixel;
964 b[5] = (byte) depth;
965 b[6] = (byte) (bigEndian ? 1 : 0);
966 b[7] = (byte) (trueColour ? 1 : 0);
967 b[8] = (byte) ((redMax >> 8) & 0xff);
968 b[9] = (byte) (redMax & 0xff);
969 b[10] = (byte) ((greenMax >> 8) & 0xff);
970 b[11] = (byte) (greenMax & 0xff);
971 b[12] = (byte) ((blueMax >> 8) & 0xff);
972 b[13] = (byte) (blueMax & 0xff);
973 b[14] = (byte) redShift;
974 b[15] = (byte) greenShift;
975 b[16] = (byte) blueShift;
976
977 os.write(b);
978 }
979
980
981 //
982 // Write a FixColourMapEntries message. The values in the red, green and
983 // blue arrays are from 0 to 65535.
984 //
985
986 void writeFixColourMapEntries(int firstColour, int nColours,
987 int[] red, int[] green, int[] blue)
988 throws IOException
989 {
990 byte[] b = new byte[6 + nColours * 6];
991
992 b[0] = (byte) FixColourMapEntries;
993 b[2] = (byte) ((firstColour >> 8) & 0xff);
994 b[3] = (byte) (firstColour & 0xff);
995 b[4] = (byte) ((nColours >> 8) & 0xff);
996 b[5] = (byte) (nColours & 0xff);
997
998 for (int i = 0; i < nColours; i++) {
999 b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff);
1000 b[6 + i * 6 + 1] = (byte) (red[i] & 0xff);
1001 b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff);
1002 b[6 + i * 6 + 3] = (byte) (green[i] & 0xff);
1003 b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff);
1004 b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff);
1005 }
enikey2f0294e2008-12-24 08:18:54 +00001006
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001007 os.write(b);
1008 }
1009
1010
1011 //
1012 // Write a SetEncodings message
1013 //
1014
1015 void writeSetEncodings(int[] encs, int len) throws IOException {
1016 byte[] b = new byte[4 + 4 * len];
1017
1018 b[0] = (byte) SetEncodings;
1019 b[2] = (byte) ((len >> 8) & 0xff);
1020 b[3] = (byte) (len & 0xff);
1021
1022 for (int i = 0; i < len; i++) {
1023 b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff);
1024 b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff);
1025 b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff);
1026 b[7 + 4 * i] = (byte) (encs[i] & 0xff);
1027 }
1028
1029 os.write(b);
1030 }
1031
1032
1033 //
1034 // Write a ClientCutText message
1035 //
1036
1037 void writeClientCutText(String text) throws IOException {
1038 byte[] b = new byte[8 + text.length()];
1039
1040 b[0] = (byte) ClientCutText;
1041 b[4] = (byte) ((text.length() >> 24) & 0xff);
1042 b[5] = (byte) ((text.length() >> 16) & 0xff);
1043 b[6] = (byte) ((text.length() >> 8) & 0xff);
1044 b[7] = (byte) (text.length() & 0xff);
1045
1046 System.arraycopy(text.getBytes(), 0, b, 8, text.length());
1047
1048 os.write(b);
1049 }
1050
1051
1052 //
1053 // A buffer for putting pointer and keyboard events before being sent. This
enikey2f0294e2008-12-24 08:18:54 +00001054 // is to ensure that multiple RFB events generated from a single Java Event
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001055 // will all be sent in a single network packet. The maximum possible
1056 // length is 4 modifier down events, a single key event followed by 4
1057 // modifier up events i.e. 9 key events or 72 bytes.
1058 //
1059
1060 byte[] eventBuf = new byte[72];
1061 int eventBufLen;
1062
1063
1064 // Useful shortcuts for modifier masks.
1065
1066 final static int CTRL_MASK = InputEvent.CTRL_MASK;
1067 final static int SHIFT_MASK = InputEvent.SHIFT_MASK;
1068 final static int META_MASK = InputEvent.META_MASK;
1069 final static int ALT_MASK = InputEvent.ALT_MASK;
1070
1071
1072 //
1073 // Write a pointer event message. We may need to send modifier key events
1074 // around it to set the correct modifier state.
1075 //
1076
1077 int pointerMask = 0;
1078
1079 void writePointerEvent(MouseEvent evt) throws IOException {
1080 int modifiers = evt.getModifiers();
1081
1082 int mask2 = 2;
1083 int mask3 = 4;
1084 if (viewer.options.reverseMouseButtons2And3) {
1085 mask2 = 4;
1086 mask3 = 2;
1087 }
1088
1089 // Note: For some reason, AWT does not set BUTTON1_MASK on left
1090 // button presses. Here we think that it was the left button if
1091 // modifiers do not include BUTTON2_MASK or BUTTON3_MASK.
1092
1093 if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
1094 if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
1095 pointerMask = mask2;
1096 modifiers &= ~ALT_MASK;
1097 } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1098 pointerMask = mask3;
1099 modifiers &= ~META_MASK;
1100 } else {
1101 pointerMask = 1;
1102 }
1103 } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) {
1104 pointerMask = 0;
1105 if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
1106 modifiers &= ~ALT_MASK;
1107 } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
1108 modifiers &= ~META_MASK;
1109 }
1110 }
1111
1112 eventBufLen = 0;
1113 writeModifierKeyEvents(modifiers);
1114
1115 int x = evt.getX();
1116 int y = evt.getY();
1117
1118 if (x < 0) x = 0;
1119 if (y < 0) y = 0;
1120
1121 eventBuf[eventBufLen++] = (byte) PointerEvent;
1122 eventBuf[eventBufLen++] = (byte) pointerMask;
1123 eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
1124 eventBuf[eventBufLen++] = (byte) (x & 0xff);
1125 eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
1126 eventBuf[eventBufLen++] = (byte) (y & 0xff);
1127
1128 //
1129 // Always release all modifiers after an "up" event
1130 //
1131
1132 if (pointerMask == 0) {
1133 writeModifierKeyEvents(0);
1134 }
1135
1136 os.write(eventBuf, 0, eventBufLen);
1137 }
1138
1139
1140 //
1141 // Write a key event message. We may need to send modifier key events
1142 // around it to set the correct modifier state. Also we need to translate
1143 // from the Java key values to the X keysym values used by the RFB protocol.
1144 //
1145
1146 void writeKeyEvent(KeyEvent evt) throws IOException {
1147
1148 int keyChar = evt.getKeyChar();
1149
1150 //
1151 // Ignore event if only modifiers were pressed.
1152 //
1153
1154 // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar().
1155 if (keyChar == 0)
1156 keyChar = KeyEvent.CHAR_UNDEFINED;
1157
1158 if (keyChar == KeyEvent.CHAR_UNDEFINED) {
1159 int code = evt.getKeyCode();
1160 if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT ||
1161 code == KeyEvent.VK_META || code == KeyEvent.VK_ALT)
1162 return;
1163 }
1164
1165 //
1166 // Key press or key release?
1167 //
1168
1169 boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
1170
1171 int key;
1172 if (evt.isActionKey()) {
1173
1174 //
1175 // An action key should be one of the following.
1176 // If not then just ignore the event.
1177 //
1178
1179 switch(evt.getKeyCode()) {
1180 case KeyEvent.VK_HOME: key = 0xff50; break;
1181 case KeyEvent.VK_LEFT: key = 0xff51; break;
1182 case KeyEvent.VK_UP: key = 0xff52; break;
1183 case KeyEvent.VK_RIGHT: key = 0xff53; break;
1184 case KeyEvent.VK_DOWN: key = 0xff54; break;
1185 case KeyEvent.VK_PAGE_UP: key = 0xff55; break;
1186 case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break;
1187 case KeyEvent.VK_END: key = 0xff57; break;
1188 case KeyEvent.VK_INSERT: key = 0xff63; break;
1189 case KeyEvent.VK_F1: key = 0xffbe; break;
1190 case KeyEvent.VK_F2: key = 0xffbf; break;
1191 case KeyEvent.VK_F3: key = 0xffc0; break;
1192 case KeyEvent.VK_F4: key = 0xffc1; break;
1193 case KeyEvent.VK_F5: key = 0xffc2; break;
1194 case KeyEvent.VK_F6: key = 0xffc3; break;
1195 case KeyEvent.VK_F7: key = 0xffc4; break;
1196 case KeyEvent.VK_F8: key = 0xffc5; break;
1197 case KeyEvent.VK_F9: key = 0xffc6; break;
1198 case KeyEvent.VK_F10: key = 0xffc7; break;
1199 case KeyEvent.VK_F11: key = 0xffc8; break;
1200 case KeyEvent.VK_F12: key = 0xffc9; break;
1201 default:
1202 return;
1203 }
1204
1205 } else {
1206
1207 //
1208 // A "normal" key press. Ordinary ASCII characters go straight through.
1209 // For CTRL-<letter>, CTRL is sent separately so just send <letter>.
1210 // Backspace, tab, return, escape and delete have special keysyms.
1211 // Anything else we ignore.
1212 //
1213
1214 key = keyChar;
1215
1216 if (key < 0x20) {
1217 if (evt.isControlDown()) {
1218 key += 0x60;
1219 } else {
1220 switch(key) {
1221 case KeyEvent.VK_BACK_SPACE: key = 0xff08; break;
1222 case KeyEvent.VK_TAB: key = 0xff09; break;
1223 case KeyEvent.VK_ENTER: key = 0xff0d; break;
1224 case KeyEvent.VK_ESCAPE: key = 0xff1b; break;
1225 }
1226 }
1227 } else if (key == 0x7f) {
1228 // Delete
1229 key = 0xffff;
1230 } else if (key > 0xff) {
1231 // JDK1.1 on X incorrectly passes some keysyms straight through,
1232 // so we do too. JDK1.1.4 seems to have fixed this.
1233 // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete
1234 // Also, we pass through foreign currency keysyms (0x20a0..0x20af).
1235 if ((key < 0xff00 || key > 0xffff) &&
1236 !(key >= 0x20a0 && key <= 0x20af))
1237 return;
1238 }
1239 }
1240
1241 // Fake keyPresses for keys that only generates keyRelease events
1242 if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring
1243 (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis
1244 (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis
1245 (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf
1246 (key == 0xa3)) { // XK_sterling
1247 // Make sure we do not send keypress events twice on platforms
1248 // with correct JVMs (those that actually report KeyPress for all
enikey2f0294e2008-12-24 08:18:54 +00001249 // keys)
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001250 if (down)
1251 brokenKeyPressed = true;
1252
1253 if (!down && !brokenKeyPressed) {
1254 // We've got a release event for this key, but haven't received
enikey2f0294e2008-12-24 08:18:54 +00001255 // a press. Fake it.
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001256 eventBufLen = 0;
1257 writeModifierKeyEvents(evt.getModifiers());
1258 writeKeyEvent(key, true);
1259 os.write(eventBuf, 0, eventBufLen);
1260 }
1261
1262 if (!down)
enikey2f0294e2008-12-24 08:18:54 +00001263 brokenKeyPressed = false;
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001264 }
1265
1266 eventBufLen = 0;
1267 writeModifierKeyEvents(evt.getModifiers());
1268 writeKeyEvent(key, down);
1269
1270 // Always release all modifiers after an "up" event
1271 if (!down)
1272 writeModifierKeyEvents(0);
1273
1274 os.write(eventBuf, 0, eventBufLen);
1275 }
1276
1277
1278 //
1279 // Add a raw key event with the given X keysym to eventBuf.
1280 //
1281
1282 void writeKeyEvent(int keysym, boolean down) {
1283 eventBuf[eventBufLen++] = (byte) KeyboardEvent;
1284 eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
1285 eventBuf[eventBufLen++] = (byte) 0;
1286 eventBuf[eventBufLen++] = (byte) 0;
1287 eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
1288 eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
1289 eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
1290 eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
1291 }
1292
1293
1294 //
1295 // Write key events to set the correct modifier state.
1296 //
1297
1298 int oldModifiers = 0;
1299
1300 void writeModifierKeyEvents(int newModifiers) {
1301 if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
1302 writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
1303
1304 if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK))
1305 writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0);
1306
1307 if ((newModifiers & META_MASK) != (oldModifiers & META_MASK))
1308 writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0);
1309
1310 if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK))
1311 writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0);
1312
1313 oldModifiers = newModifiers;
1314 }
1315
1316
1317 //
1318 // Enable continuous updates for the specified area of the screen (but
1319 // only if EnableContinuousUpdates message is supported by the server).
1320 //
1321
1322 void tryEnableContinuousUpdates(int x, int y, int w, int h)
1323 throws IOException
1324 {
1325 if (!clientMsgCaps.isEnabled(EnableContinuousUpdates)) {
1326 System.out.println("Continuous updates not supported by the server");
1327 return;
1328 }
1329
1330 if (continuousUpdatesActive) {
1331 System.out.println("Continuous updates already active");
1332 return;
1333 }
1334
1335 byte[] b = new byte[10];
1336
1337 b[0] = (byte) EnableContinuousUpdates;
1338 b[1] = (byte) 1; // enable
1339 b[2] = (byte) ((x >> 8) & 0xff);
1340 b[3] = (byte) (x & 0xff);
1341 b[4] = (byte) ((y >> 8) & 0xff);
1342 b[5] = (byte) (y & 0xff);
1343 b[6] = (byte) ((w >> 8) & 0xff);
1344 b[7] = (byte) (w & 0xff);
1345 b[8] = (byte) ((h >> 8) & 0xff);
1346 b[9] = (byte) (h & 0xff);
1347
1348 os.write(b);
1349
1350 continuousUpdatesActive = true;
1351 System.out.println("Continuous updates activated");
1352 }
1353
1354
1355 //
1356 // Disable continuous updates (only if EnableContinuousUpdates message
1357 // is supported by the server).
1358 //
1359
1360 void tryDisableContinuousUpdates() throws IOException
1361 {
1362 if (!clientMsgCaps.isEnabled(EnableContinuousUpdates)) {
1363 System.out.println("Continuous updates not supported by the server");
1364 return;
1365 }
1366
1367 if (!continuousUpdatesActive) {
1368 System.out.println("Continuous updates already disabled");
1369 return;
1370 }
1371
1372 if (continuousUpdatesEnding)
1373 return;
1374
1375 byte[] b = { (byte)EnableContinuousUpdates, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1376 os.write(b);
1377
1378 if (!serverMsgCaps.isEnabled(EndOfContinuousUpdates)) {
1379 // If the server did not advertise support for the
1380 // EndOfContinuousUpdates message (should not normally happen
1381 // when EnableContinuousUpdates is supported), then we clear
1382 // 'continuousUpdatesActive' variable immediately. Normally,
1383 // it will be reset on receiving EndOfContinuousUpdates message
1384 // from the server.
1385 continuousUpdatesActive = false;
1386 } else {
1387 // Indicate that we are waiting for EndOfContinuousUpdates.
1388 continuousUpdatesEnding = true;
1389 }
1390 }
1391
1392
1393 //
1394 // Process EndOfContinuousUpdates message received from the server.
1395 //
1396
1397 void endOfContinuousUpdates()
1398 {
1399 continuousUpdatesActive = false;
1400 continuousUpdatesEnding = false;
1401 }
1402
1403
1404 //
1405 // Check if continuous updates are in effect.
1406 //
1407
1408 boolean continuousUpdatesAreActive()
1409 {
1410 return continuousUpdatesActive;
1411 }
1412
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +00001413 /**
1414 * Send a rectangle selection to be treated as video by the server (but
1415 * only if VideoRectangleSelection message is supported by the server).
1416 * @param rect specifies coordinates and size of the rectangule.
1417 * @throws java.io.IOException
1418 */
1419 void trySendVideoSelection(Rectangle rect) throws IOException
1420 {
1421 if (!clientMsgCaps.isEnabled(VideoRectangleSelection)) {
1422 System.out.println("Video area selection is not supported by the server");
1423 return;
1424 }
1425
Constantin Kaplinsky4f652852008-09-05 02:41:15 +00001426 // Send zero coordinates if the rectangle is empty.
1427 if (rect.isEmpty()) {
1428 rect = new Rectangle();
1429 }
1430
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +00001431 int x = rect.x;
1432 int y = rect.y;
1433 int w = rect.width;
1434 int h = rect.height;
enikey2f0294e2008-12-24 08:18:54 +00001435
Constantin Kaplinskyf7cb2bf2008-05-27 08:38:28 +00001436 byte[] b = new byte[10];
1437
1438 b[0] = (byte) VideoRectangleSelection;
1439 b[1] = (byte) 0; // reserved
1440 b[2] = (byte) ((x >> 8) & 0xff);
1441 b[3] = (byte) (x & 0xff);
1442 b[4] = (byte) ((y >> 8) & 0xff);
1443 b[5] = (byte) (y & 0xff);
1444 b[6] = (byte) ((w >> 8) & 0xff);
1445 b[7] = (byte) (w & 0xff);
1446 b[8] = (byte) ((h >> 8) & 0xff);
1447 b[9] = (byte) (h & 0xff);
1448
1449 os.write(b);
1450
1451 System.out.println("Video rectangle selection message sent");
1452 }
1453
enikeyd7653562008-12-25 11:02:56 +00001454 void trySendVideoFreeze(boolean freeze) throws IOException
1455 {
1456 if (!clientMsgCaps.isEnabled(VideoFreeze)) {
1457 System.out.println("Video freeze is not supported by the server");
1458 return;
1459 }
1460
1461 byte[] b = new byte[2];
1462 byte fb = 0;
1463 if (freeze) {
1464 fb = 1;
1465 }
1466
1467 b[0] = (byte) VideoFreeze;
1468 b[1] = (byte) fb;
1469
1470 os.write(b);
1471
1472 System.out.println("Video freeze selection message sent");
1473 }
1474
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001475 public void startTiming() {
1476 timing = true;
1477
1478 // Carry over up to 1s worth of previous rate for smoothing.
1479
1480 if (timeWaitedIn100us > 10000) {
1481 timedKbits = timedKbits * 10000 / timeWaitedIn100us;
1482 timeWaitedIn100us = 10000;
1483 }
1484 }
1485
1486 public void stopTiming() {
enikey2f0294e2008-12-24 08:18:54 +00001487 timing = false;
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001488 if (timeWaitedIn100us < timedKbits/2)
1489 timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
1490 }
1491
1492 public long kbitsPerSecond() {
1493 return timedKbits * 10000 / timeWaitedIn100us;
1494 }
1495
1496 public long timeWaited() {
1497 return timeWaitedIn100us;
1498 }
1499
1500 //
1501 // Methods for reading data via our DataInputStream member variable (is).
1502 //
1503 // In addition to reading data, the readFully() methods updates variables
1504 // used to estimate data throughput.
1505 //
1506
1507 public void readFully(byte b[]) throws IOException {
1508 readFully(b, 0, b.length);
1509 }
1510
1511 public void readFully(byte b[], int off, int len) throws IOException {
1512 long before = 0;
1513 if (timing)
1514 before = System.currentTimeMillis();
1515
1516 is.readFully(b, off, len);
1517
1518 if (timing) {
1519 long after = System.currentTimeMillis();
1520 long newTimeWaited = (after - before) * 10;
1521 int newKbits = len * 8 / 1000;
1522
1523 // limit rate to between 10kbit/s and 40Mbit/s
1524
1525 if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
1526 if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
1527
1528 timeWaitedIn100us += newTimeWaited;
1529 timedKbits += newKbits;
1530 }
1531
1532 numBytesRead += len;
1533 }
1534
1535 final int available() throws IOException {
1536 return is.available();
1537 }
1538
1539 // FIXME: DataInputStream::skipBytes() is not guaranteed to skip
1540 // exactly n bytes. Probably we don't want to use this method.
1541 final int skipBytes(int n) throws IOException {
1542 int r = is.skipBytes(n);
1543 numBytesRead += r;
1544 return r;
1545 }
1546
1547 final int readU8() throws IOException {
1548 int r = is.readUnsignedByte();
1549 numBytesRead++;
1550 return r;
1551 }
1552
1553 final int readU16() throws IOException {
1554 int r = is.readUnsignedShort();
1555 numBytesRead += 2;
1556 return r;
1557 }
1558
1559 final int readU32() throws IOException {
1560 int r = is.readInt();
1561 numBytesRead += 4;
1562 return r;
1563 }
Adam Tkac4be9da82010-11-18 14:00:12 +00001564
1565 public void setStreams(InputStream is_, OutputStream os_) {
1566 is = new DataInputStream(is_);
1567 os = os_;
1568 }
Constantin Kaplinsky2844fd52008-04-14 08:02:25 +00001569}